/* ═══ /design ═════════════════════════════════════════════════════════════════
   The page is an EXHIBIT, so everything in here is in service of one rule: the
   artwork is the content and the chrome around it has to disappear. It rides on
   work.css's .item / .item__lead / .tags idiom rather than inventing a second
   section system — /kova and /sipful already make this move and a third grid
   would drift from those two the first time any of them changed.

   ⚠️ THE ONE THING THAT WILL BITE. work.css caps every direct child of
   `#rail > .item` at 62ch:

       #rail > .item > *:not(.item__lead):not(.rig):not(.lead__grid):not(.vid)…

   A media block that is not in that exclusion list is silently squeezed to a
   text measure — it still renders, it is just wrong, which is the failure mode
   this repo keeps paying for. `.dgrid` is added to that list IN work.css, beside
   the others, rather than fought with a specificity tie from this file. If you
   add a new wide container here, add it there too.
   ------------------------------------------------------------------------- */

/* ─── THE LEAD PAIR ───────────────────────────────────────────────────────────
   Two lids, not one. A single lid reads as "a lid"; the pair reads as a line
   with a system behind it, which is what the section is about. They overlap
   slightly so they read as one object rather than two thumbnails in a row. */
.dlids {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: clamp(10px, 1.4vw, 16px);
}
.dlids picture { display: block; width: min(38vw, 210px); }
/* 🔴 NOT `border-radius: 50%`, WHICH IS WHAT THIS SHIPPED WITH FIRST AND WAS
   WRONG. The finished lid IS a circle — the printer's proof further down the
   page is a 3.5in round die — so clipping the artwork to a circle looked like
   the obvious move. But these two files are the SQUARE artboards, composed edge
   to edge, and a 50% radius cut the corners off the "30G PROTEIN" and "200
   CALORIES" badges. It read as a lid and quietly destroyed part of the artwork
   it was showing.
   ⇒ A round DIE does not make the ARTWORK round. The circular versions are the
   KovaLux files in §01, which were drawn as circles; these are artboards and
   are shown as artboards. Caught by looking at a WebKit screenshot — every rect
   measured correctly the whole time, because clipping is not a layout error. */
.dlids img {
  display: block; width: 100%; height: auto;
  border-radius: 8px;
  box-shadow: 0 18px 44px rgba(0, 0, 0, .5);
}

/* ─── THE MEDIA GRIDS ─────────────────────────────────────────────────────────
   One container class with modifiers, so the 62ch exclusion in work.css needs a
   single entry rather than one per layout.

   ⚠️ minmax(0, 1fr) rather than 1fr: a grid track's default `min-width: auto`
   is the content's intrinsic size, so a wide image refuses to shrink and pushes
   the track past the container instead of scaling down inside it. This file's
   first draft used 1fr and every three-up row overflowed on a phone. */
.dgrid {
  display: grid;
  gap: clamp(12px, 1.6vw, 22px);
  margin: 0 0 20px;
  width: 100%;
}
.dgrid--2     { grid-template-columns: repeat(2, minmax(0, 1fr)); max-width: 1000px; }
/* ⚠️ 920, NOT 1000. The section index is fixed to the right edge, and at an iPad's
   1032px a 1000px grid put its last caption's right edge at 960 against a rail
   starting at 971 — an 11px gap. Not an overlap, so nothing measured as broken,
   but it reads as text crowding the rail. Measured, then widened to ~90px. */
.dgrid--3     { grid-template-columns: repeat(3, minmax(0, 1fr)); max-width: 1200px; }
.dgrid--wide  { grid-template-columns: minmax(0, 1fr);            max-width: 860px; }
.dgrid--phone { grid-template-columns: minmax(0, 1fr);            max-width: 340px; }
/* the Onword pair is two screenshots at wildly different native sizes (678px
   and 232px). Left alone they read as a big picture and a small one rather than
   as a comparison, so this modifier gives them a common box and centres each
   inside it — the COMPARISON is the content, not either frame's dimensions. */
.dgrid--tight { max-width: 820px; align-items: end; }
/* ⚠️ A DEFINITE HEIGHT, NOT `max-height` WITH `width: auto`. The first version
   used `max-height: 190px; width: auto`, which collapsed the whole row to 25px —
   the caption's height and nothing else. Both images are `loading="lazy"` and
   below the fold, so at layout time there is no intrinsic size to fall back on;
   and setting BOTH dimensions to auto throws away the aspect-ratio box the
   width/height attributes would otherwise reserve. The result is a zero-height
   box that reports no error and looks like a missing section.
   A definite height plus object-fit keeps the two frames in one box while
   preserving each one's proportions, which is the entire point of the modifier. */
.dgrid--tight .dfig img {
  height: 190px; width: 100%;
  object-fit: contain; object-position: center bottom;
  background: transparent; box-shadow: none;
}

/* ─── A ROW OF MIXED ASPECTS ──────────────────────────────────────────────────
   The three rejected frames are two 16:9 landscapes and one 768x1376 portrait.
   Left to stretch, the portrait was nearly twice the height of its neighbours,
   so two captions sat stranded halfway up the row and the third hung far below
   them — the row stopped reading as three comparable items, which is the only
   reason they are side by side.

   ⚠️ `object-fit: cover` is WRONG here even though it would tidy the row
   perfectly. #201's entire point is the headline typed across its top —
   "Delivered free,." with the stray comma — and cover on a portrait in a
   landscape box crops exactly that off. The defect would be invisible in the
   picture the caption is describing. `contain` letterboxes instead, and on this
   background the letterboxing is not visible anyway. */
.dgrid--mixed .dfig { display: flex; flex-direction: column; }
.dgrid--mixed .dfig picture { display: block; }
.dgrid--mixed .dfig img {
  height: clamp(260px, 26vw, 380px); width: 100%;
  object-fit: contain; object-position: center top;
  background: rgba(220, 230, 221, .03);
}
.dgrid--mixed .dfig figcaption { margin-top: auto; }

.dfig { margin: 0; min-width: 0; }
.dfig picture { display: block; }
.dfig img {
  display: block; width: 100%; height: auto;
  border-radius: 5px;
  background: rgba(220, 230, 221, .04);
}
/* the flat vector marks (Bear's Select, Sipful, the lids) are drawn on their own
   coloured fields and need no plate under them; the screenshots and photographs
   do, or they float on the phosphor with a hard edge. One hairline covers both
   cases without a second class. */
.dfig img { box-shadow: 0 0 0 1px rgba(220, 230, 221, .07); }

.dfig .vid__cap { margin-top: 8px !important; }

/* ─── "GENERATED" IS A LABEL, NOT A DISCLAIMER ────────────────────────────────
   The brief for this page is explicit that generated imagery gets labelled as
   generated. It sits INSIDE the caption rather than over the picture: an overlay
   would cover the artwork it is describing, and a badge floating on an image is
   also the one piece of text a screenshot of the page would carry away without
   its context.

   ⚠️ Text first, then colour. The word "generated" is doing the work; the amber
   is only helping you find it. A coloured dot on its own would announce nothing
   to a screen reader and nothing to anyone who does not already know the key. */
.gen {
  display: inline-block;
  margin-right: 6px; padding: 1px 5px;
  border: 1px solid rgba(240, 168, 60, .5); border-radius: 3px;
  color: var(--amber); background: rgba(240, 168, 60, .08);
  font-size: 9px; letter-spacing: .1em; text-transform: uppercase;
}
.gen--no {
  border-color: rgba(255, 107, 53, .55);
  color: var(--warm); background: rgba(255, 107, 53, .09);
}

/* a heading inside a section, between the prose and a grid. Smaller than
   .item__h so the section still reads as one thing. */
.dsub {
  margin: 26px 0 12px;
  font-size: clamp(15px, 1.6vw, 17.5px); line-height: 1.3;
  letter-spacing: -.008em; font-weight: 700; color: var(--fg);
}

/* ─── THE VERTICAL REEL ───────────────────────────────────────────────────────
   1080x1920, 83 seconds, cut for a phone. It is framed at 9:16 rather than
   letterboxed into a 16:9 slot, because the black bars either side of a
   letterboxed vertical video are not neutral — they say the piece was made for
   a shape it was not made for.

   ⚠️ aspect-ratio on the ELEMENT, not padding-top on a wrapper. The old trick
   needs the child absolutely positioned, and an absolutely positioned <video>
   inside .phosphor picks up the same filtered-containing-block problem the
   section index already had to be moved out of .phosphor to escape. */
.dfig--phone { position: relative; }
.dreel {
  display: block; width: 100%; height: auto;
  aspect-ratio: 9 / 16;
  border-radius: 10px;
  background: #000;
  box-shadow: 0 0 0 1px rgba(220, 230, 221, .1), 0 20px 50px rgba(0, 0, 0, .55);
  object-fit: cover;
}
/* NOTE: there is no custom sound toggle here any more. An earlier version
   autoplayed the reel muted and put an unmute button on the frame; the reel
   turned out to be Juan talking rather than product b-roll, so it now uses the
   browser's own controls and starts with sound when somebody presses play.
   Native controls also carry volume, scrubbing and captions support for free,
   which a hand-rolled button did not. */

/* ─── NARROW ──────────────────────────────────────────────────────────────────
   The three-up rows go to two, then one. Two-up rows stay two for as long as
   they are legible, because both of them are COMPARISONS — a lid against a lid,
   a panel against a panel, one selection model against the other — and a
   comparison stacked vertically stops being one. .dgrid--tight is the exception
   and stacks last, since its two frames are only 232px and 678px wide. */
@media (max-width: 860px) {
  .dgrid--3 { grid-template-columns: repeat(2, minmax(0, 1fr)); }
}
@media (max-width: 620px) {
  .dgrid--3 { grid-template-columns: minmax(0, 1fr); }
  .dgrid--2 { gap: 10px; }
  .dlids picture { width: 40vw; }
}
@media (max-width: 460px) {
  .dgrid--2, .dgrid--tight { grid-template-columns: minmax(0, 1fr); }
  .dgrid--tight .dfig img { max-height: none; }
}

/* NOTE ON REDUCED MOTION: nothing to suppress here any more. The reel does not
   move until somebody presses play, which is what the preference asks for in the
   first place — a video the visitor started is not "motion they did not ask
   for". The earlier autoplay version needed a Reduce Motion branch and got one;
   removing the autoplay removed the need for it, rather than the rule being
   dropped by accident.
   ⚠️ Which also means shot.py can photograph this section honestly now. It
   forces Reduce Motion, and while the reel autoplayed that made every screenshot
   of it a different branch from the one visitors saw. It no longer does. */

/* ═══ THE HUB ══════════════════════════════════════════════════════════════════
   ADDED Sep 2 2026, second pass. /design stopped being one 11,000px scroll and
   became an index that links out. The research behind that is in
   site-docs/HANDOFF.md; the short version is that a hub with linked case studies
   beats one long page for the way recruiters scan, and the previous first screen
   said nothing about the page holding four projects.

   ⚠️ THE TILES ARE REAL LINKS TO REAL PAGES. The alternative considered was a
   bento grid whose tiles expand in place, which is the layout 2026 actually
   adopted — and it lost on a measured fact: AI answer engines read visible HTML
   in source order and do not run JS, so content behind a click is content they
   never see. That finding is the whole reason /resume is the entity home and it
   applies here unchanged. Do not "modernise" these into expanders.
   -------------------------------------------------------------------------- */
.lead__copy--wide { max-width: 62ch; }

.dtiles {
  display: grid; gap: clamp(10px, 1.4vw, 16px);
  grid-template-columns: repeat(2, minmax(0, 1fr));
  margin-top: clamp(26px, 4vw, 44px);
  max-width: 1200px;
}
@media (max-width: 620px) { .dtiles { grid-template-columns: minmax(0, 1fr); } }



.dtile {
  display: grid;
  grid-template-columns: 78px minmax(0, 1fr);
  grid-template-areas: "art k" "art t" "art d";
  gap: 2px 14px; align-content: start;
  padding: 14px; text-decoration: none; color: inherit;
  border: 1px solid rgba(220, 230, 221, .12);
  background: rgba(220, 230, 221, .022);
  border-radius: 4px;
}
.dtile:hover, .dtile:focus-visible {
  border-color: rgba(240, 168, 60, .5);
  background: rgba(240, 168, 60, .05);
  outline: none;
}
.dtile__art { grid-area: art; align-self: start; }
.dtile__art img {
  display: block; width: 78px; aspect-ratio: 16 / 9; height: auto;
  object-fit: cover; border-radius: 3px;
  background: rgba(220, 230, 221, .04);
}
.dtile__k {
  grid-area: k;
  font-family: var(--mono); font-size: 10px; letter-spacing: .13em;
  text-transform: uppercase; color: var(--fg-dim);
}
.dtile__t { grid-area: t; font-size: 16.5px; line-height: 1.24; font-weight: 700; color: var(--fg); }
.dtile__d { grid-area: d; margin-top: 4px; font-size: 13.6px; line-height: 1.5; color: var(--fg-dim); }

/* 🔴 THIS BLOCK MUST STAY BELOW THE `.dtile` RULES ABOVE. It first sat directly
   under `.dtiles`, before them, and the four-up grid applied while the CARD
   never re-stacked: `@media { .dtile }` and a bare `.dtile` are both (0,1,0),
   so the later one wins, and the later one was the base. Nothing errored — the
   tiles simply kept the thumbnail-beside-text layout inside a 288px track and
   crushed every title. Equal specificity means SOURCE ORDER decides, which is
   the trap work.css already warns about in § CHOOSE YOUR PLAYER. */
/* ⚠️ AT ≥1000px THE INDEX GOES FOUR-UP AND THE CARD RE-STACKS. Two things made
   this necessary rather than decorative. The index is the most important object
   on the hub — it is the thing the whole restructure exists for — and left at
   two columns it ended up NARROWER than the contact sheets further down the
   page, which reads as the page burying its own map. And a 78px thumbnail
   beside text in a 300px track crushes the title to three lines.
   So on a wide screen the art goes on TOP and runs the full card width, which
   is what turns four links into an index strip you take in at once. */
@media (min-width: 1000px) {
  .dtiles { grid-template-columns: repeat(4, minmax(0, 1fr)); }
  .dtile {
    grid-template-columns: minmax(0, 1fr);
    grid-template-areas: "art" "k" "t" "d";
    gap: 0;
  }
  .dtile__art { margin-bottom: 11px; }
  .dtile__art img { width: 100%; height: auto; }
  .dtile__t { margin-top: 3px; }
}

/* ═══ THE BRAND SYSTEM BLOCK ═══════════════════════════════════════════════════
   The research gap the first cut had: brand work is judged as a SYSTEM — the
   logo's applications, the colour roles, the type roles, the grid — and the page
   was showing finished marks only. These three components are what closed it.
   -------------------------------------------------------------------------- */

/* 🔴 THE FIVE LOGO APPLICATIONS, AND THE FIRST VERSION OF THIS RULE BROKE THE
   WHOLE PAGE ON A PHONE. It read:

       grid-template-columns: repeat(auto-fit, minmax(140px, 1fr));

   `auto-fit` cannot collapse a track whose MINIMUM is 140px, so five tracks
   demanded 700px plus gaps on a 402px viewport. The grid measured 748px wide,
   the row it sat in stretched to match, and `document.scrollWidth` went to 776
   against a 402px client width — the page scrolled sideways, which is the one
   thing this repo's layout rules say must never happen. It also dragged .tspec
   out to 625px, so the damage did not look like it came from here.

   ⚠️ AND IT IS THE EXACT TRAP DOCUMENTED 60 LINES ABOVE in this same file, about
   `minmax(0, 1fr)` versus a floor that cannot shrink. Writing the warning did not
   stop me reintroducing it. Explicit counts at breakpoints, and a zero floor. */
.dgrid--marks {
  grid-template-columns: repeat(5, minmax(0, 1fr));
  max-width: 1100px; align-items: end;
}
@media (max-width: 760px) { .dgrid--marks { grid-template-columns: repeat(3, minmax(0, 1fr)); } }
@media (max-width: 440px) { .dgrid--marks { grid-template-columns: repeat(2, minmax(0, 1fr)); } }
.dmark img {
  height: 84px; width: 100%; object-fit: contain; object-position: center bottom;
  background: transparent; box-shadow: none;
}
.dnote { margin-top: -6px !important; }

/* colour, as chips with their role rather than as a row of squares. A swatch
   without its rule is decoration; the rule is the part that is design work. */
.sw { list-style: none; margin: 0 0 18px; padding: 0; display: grid; gap: 9px; max-width: 62ch; }
.sw li {
  display: grid; grid-template-columns: 30px auto 1fr; gap: 12px; align-items: baseline;
}
.sw__chip {
  width: 30px; height: 30px; border-radius: 3px; background: var(--c);
  box-shadow: inset 0 0 0 1px rgba(220, 230, 221, .18);
  align-self: center;
}
.sw b { font-size: 14.5px; color: var(--fg); font-weight: 600; white-space: nowrap; }
.sw code {
  font-family: var(--mono); font-size: 11.5px; color: var(--amber);
  letter-spacing: .04em;
}
.sw li > span:last-child { grid-column: 2 / -1; font-size: 13.8px; color: var(--fg-dim); }
/* ⚠️ FLEX, NOT THE 3-COLUMN GRID. The pair rows have no description, so the
   grid's last track absorbed all the leftover room and threw the hex ~280px
   away from the name it labels. A label separated from its value by half a
   column reads as two unrelated things. */
.sw--pair li { display: flex; align-items: center; gap: 12px; }

/* type roles. A definition list, because that is what it is: a name and the one
   job it does. */
.tspec { margin: 0 0 16px; max-width: 62ch; display: grid; gap: 1px;
         background: rgba(220, 230, 221, .09); border: 1px solid rgba(220, 230, 221, .09); }
.tspec > div {
  display: grid; grid-template-columns: minmax(0, 15em) minmax(0, 1fr); gap: 0;
  background: var(--void);
}
.tspec dt {
  padding: 9px 12px; font-size: 13.8px; color: var(--fg); font-weight: 600;
  border-right: 1px solid rgba(220, 230, 221, .09);
}
.tspec dt em { font-style: normal; font-weight: 400; color: var(--fg-dim); }
.tspec dd { margin: 0; padding: 9px 12px; font-size: 13.8px; color: var(--fg-dim); }
@media (max-width: 560px) {
  .tspec > div { grid-template-columns: minmax(0, 1fr); }
  .tspec dt { border-right: 0; padding-bottom: 0; }
  .tspec dd { padding-top: 2px; }
}

/* ═══ THE MODIFIER TABLE on /onword ════════════════════════════════════════════
   ⚠️ It scrolls inside its OWN container. A table wide enough to hold three
   columns of key names will overflow a phone, and the one thing the page body
   must never do is scroll sideways. tabindex makes the scroll container
   keyboard-reachable, which it is not by default. */
.dtable { overflow-x: auto; margin: 0 0 16px; max-width: 100%; -webkit-overflow-scrolling: touch; }
.dtable:focus-visible { outline: 2px solid var(--amber); outline-offset: 3px; }
.dtable table { border-collapse: collapse; min-width: 480px; width: 100%; }
.dtable th, .dtable td {
  text-align: left; padding: 8px 12px; font-size: 13.6px;
  border-bottom: 1px solid rgba(220, 230, 221, .09);
}
.dtable th {
  font-family: var(--mono); font-size: 10px; letter-spacing: .12em;
  text-transform: uppercase; color: var(--fg-dim); font-weight: 500;
}
.dtable td { color: var(--fg-dim); }
.dtable td:first-child { color: var(--fg); white-space: nowrap; }
.dtable kbd {
  font-family: var(--mono); font-size: 11.5px; padding: 1px 5px;
  border: 1px solid rgba(220, 230, 221, .22); border-radius: 3px; color: var(--fg);
}

/* the Onword lead mark, which is wide rather than square, so it does not reuse
   .dlids' two-object row */
.donword { max-width: min(78vw, 420px); }
.donword img {
  display: block; width: 100%; height: auto; border-radius: 4px;
  box-shadow: 0 16px 40px rgba(0, 0, 0, .45);
}

/* the hub's through-line. It sits UNDER the index rather than above it, because
   the index is what the first screen is for — a reader who wants the argument
   scrolls four lines to find it, and a reader who wants the work never has to. */
.dpov {
  margin-top: clamp(22px, 3vw, 34px);
  max-width: 62ch;
  padding-left: 14px;
  border-left: 2px solid rgba(240, 168, 60, .32);
}
.dpov p { margin: 0; font-size: clamp(14.5px, 1.45vw, 16.5px); line-height: 1.68; color: var(--fg-dim); }
