/* ===================================================================
   case-light.css — the case study template, in the site's own system.

   Structure follows the panel's own checklist order, so a reviewer never
   has to hunt: context, problem, the flow, constraints and iterations,
   the decision and its trade-off, the outcome. The flow is the biggest
   section by design; everything else earns its space against it.

   Pairs with tokens.css, which owns type and colour.
   =================================================================== */

*, *::before, *::after { box-sizing: border-box; }
body { margin: 0; background: var(--bg); }
img { max-width: 100%; height: auto; display: block; }

/* ---- shell ---- */
 /* Scoped to case pages so the home keeps its own width.
    Text sits in a column and images run wider: that contrast is what makes
    the column read as a measure rather than as an unfinished layout. */
/* 78rem printed a screenshot wider than anyone ever saw the screen, and a page
   of them reads as shouting. 62rem is 80% of that: still clearly outside the
   text column, still bigger than the argument around it, but it stops being
   the only thing on screen. Rails and pinned flows are sized on their own
   (72% / 100vw) because a step at column width is a thumbnail, so this does
   not touch them. */
.case { --page-max: 54rem; --wide-max: 62rem; --measure: 100%; }
/* The gutter is carried by WIDTH, not by padding, and that is deliberate.
   These containers are always combined with another class — .mast.wrap,
   .sec.wrap, .next.wrap — and every one of those rules sets its vertical
   rhythm with the `padding` SHORTHAND, which also writes 0 into the
   horizontal sides. They load after this rule at equal specificity, so they
   won, and all six case studies rendered edge to edge on a phone: 67
   containers with no gutter at 390px, invisible on desktop because the auto
   margins were doing the insetting instead.
   Padding would also be wrong on its own terms here: box-sizing is
   border-box, so `padding: 0 var(--gutter)` inside `max-width: 54rem` would
   have narrowed the desktop text column by 80px. Width sidesteps both:
   the column is exactly --page-max whenever it fits, and inset by the
   gutter when it does not. Nothing a later shorthand can undo. */
.wrap  { width: min(100% - 2 * var(--gutter), var(--page-max)); margin-inline: auto; }
.wide  { width: min(100% - 2 * var(--gutter), var(--wide-max)); margin-inline: auto; }
.narrow{ max-width: var(--measure); }

 /* clip, not hidden: the rail bleeds to the page edge and must not be able to
    widen the document. The sticky bar is a sibling, so nothing it needs breaks.
    The clip lives on the viewport as well, because a pinned section has to be
    allowed out of the column and can only be held in by the page itself. */
html { overflow-x: clip; }
.sec { padding-block: clamp(3.5rem, 9vh, 7rem); overflow-x: clip; }
/* A pinned flow is 100vw on purpose: clipping it here would cut the frames
   back to the text column, which is the whole thing it is escaping. */
.sec:has(.pin[data-on="1"]), .sec:has(.ba--wide), .sec:has(.fgrid) { overflow-x: visible; }
.sec + .sec { border-top: 1px solid var(--line); }

/* ---- top bar ---- */
.bar {
  position: sticky; top: 0; z-index: 40;
  background: rgba(247,247,245,.82);
  backdrop-filter: saturate(180%) blur(14px);
  -webkit-backdrop-filter: saturate(180%) blur(14px);
  border-bottom: 1px solid var(--line);
}
.bar__in {
  max-width: var(--wide-max); margin: 0 auto; padding: 0 var(--gutter);
  height: 3.25rem; display: flex; align-items: center; justify-content: space-between;
  font-family: var(--font-mono); font-size: var(--t-meta);
  letter-spacing: var(--track-meta); text-transform: uppercase;
}
.bar a { color: var(--fg-2); text-decoration: none; }
.bar a:hover { color: var(--fg); }
/* `justify-content: space-between` puts no floor under the space between the
   two items, so at 360 they met exactly and rendered as ANTONIO LOZANOSUBLY.
   A real gap, and the case name gives way first: the way home must stay whole
   because it is the only navigation on the page. */
.bar__in { gap: 1.25rem; }
.bar__in > a { flex: none; }
.bar__in > span { min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }

/* ---- masthead ---- */
.mast { padding: clamp(2.5rem, 6vh, 4rem) 0 clamp(1.5rem, 3.5vh, 2.25rem); }
/* The section straight after the masthead does not need the full gap: the rule
   above it is doing that job, and the work should reach the first screen. */
.mast + .sec { padding-top: clamp(1.5rem, 3.5vh, 2.25rem); }
.mast__kicker {
  margin: 0 0 clamp(1rem, 2.5vh, 1.5rem);
  font-family: var(--font-mono); font-size: var(--t-meta);
  letter-spacing: var(--track-meta); text-transform: uppercase; color: var(--fg-3);
}
.mast__kicker i { font-style: normal; opacity: .4; margin: 0 .55rem; }
.mast h1 {
  margin: 0; max-width: 20ch;
  font-family: var(--font-display); font-weight: 500;
  font-size: var(--t-h1); line-height: 1.04; letter-spacing: -.032em; color: var(--fg-1);
}
/* The lede sits at 19px, not body size: it is the second thing read and it
   needs its own step in the scale (72 / 19 / 15). At 56ch that puts its right
   edge within 2% of the longest title line, so the two read as one block. */
.mast__lede {
  margin: clamp(1.25rem, 3vh, 1.75rem) 0 0; max-width: 56ch;
  font-size: 1.1875rem; line-height: 1.6; color: var(--fg-2);
}
/* The definition stands alone: it is the sentence that makes the title
   readable, and everything after it is the situation. Same weight, same
   colour, only the break. */
.mast__lede b { display: block; margin-top: .9em; font-weight: inherit; color: inherit; }

/* Status is stated once, plainly, next to the claim it qualifies. */
.status {
  display: inline-flex; align-items: center; gap: .55rem;
  margin-top: clamp(1.25rem, 3vh, 1.75rem);
  padding: .5rem .85rem; border: 1px solid var(--line-strong); border-radius: 999px;
  font-family: var(--font-mono); font-size: var(--t-meta);
  letter-spacing: .08em; text-transform: uppercase; color: var(--fg-2);
}
.status b { font-weight: 400; color: var(--fg-1); }
.status i { font-style: normal; width: 6px; height: 6px; border-radius: 50%; background: #16A34A; flex: none; }
.status[data-kind="concept"] i { background: #F59E0B; }

.facts {
  margin-top: clamp(1.5rem, 3.5vh, 2.25rem); padding-top: clamp(1.1rem, 2.5vh, 1.5rem);
  border-top: 1px solid var(--line);
  display: grid; grid-template-columns: repeat(4, minmax(0,1fr)); gap: 1.5rem;
}
.facts dt {
  font-family: var(--font-mono); font-size: var(--t-meta);
  letter-spacing: var(--track-meta); text-transform: uppercase; color: var(--fg-3);
}
.facts dd { margin: .4rem 0 0; font-size: var(--t-sm); color: var(--fg-1); }

/* ---- section headings ---- */
.sec__label {
  margin: 0 0 clamp(.75rem, 2vh, 1rem);
  font-family: var(--font-mono); font-size: var(--t-meta);
  letter-spacing: var(--track-meta); text-transform: uppercase; color: var(--fg-3);
}
.sec h2 {
  margin: 0; max-width: 22ch;
  font-family: var(--font-display); font-weight: 500;
  font-size: var(--t-h2); line-height: 1.08; letter-spacing: -.026em; color: var(--fg-1);
}
/* An h3 that follows a block rather than a paragraph is opening a new part of
   the argument, so it needs the air a paragraph does not. */
.sec .edges + h3, .sec .tradeoff + h3, .sec .metrics + h3,
.sec .quote + h3, .sec .shot + h3, .sec .ba + h3, .sec .perm + h3 {
  margin-top: clamp(2.75rem, 7vh, 4.5rem);
}
.sec h3 {
  margin: 0 0 .5rem;
  font-family: var(--font-display); font-weight: 500;
  font-size: var(--t-h3); letter-spacing: -.02em; color: var(--fg-1);
}
.sec p { margin: 1rem 0 0; max-width: var(--measure); line-height: var(--lh-body); color: var(--fg-2); }
.sec p b { color: var(--fg-1); font-weight: 500; }

/* ---- media ---- */
.shot { margin: clamp(2rem, 5vh, 3rem) 0 0; }

/* A screen that is close to square does not want the full 78rem: at
   that width it stands a metre tall and you scroll past its own
   content to reach the caption. Capped and centred it reads at once.
   Wide strips keep the full width, because there the height is not the
   problem. */
.shot.shot--sm img { display: block; width: 60%; margin-inline: auto; }
/* The caption follows the narrowed image rather than the column it sits in,
   so it still reads as a footnote to the shot and not to the page. */
.shot.shot--sm figcaption { width: 60%; margin-inline: auto; }
@media (max-width: 860px) {
  .shot.shot--sm img { width: 100%; }
  .shot.shot--sm figcaption { width: 100%; }
}
.shot img {
  width: 100%; border-radius: var(--radius-card);
  border: 1px solid var(--line); background: var(--bg-raised); box-shadow: var(--shadow-card);
}
/* Captions carry argument, not credits, but at body size in fg-3 they read as
   more grey prose competing with the paragraphs. One step down in size and one
   step up in colour separates them from the text and raises the contrast at
   the same time: 13px at 60% clears AA, 15px at 40% does not. The measure is
   the image's own: a footnote stops where the thing it annotates stops. */
.shot figcaption, .ph figcaption, .open > figcaption {
  margin-top: .9rem; max-width: none;
  font-size: var(--t-sm); line-height: 1.55; color: var(--fg-2);
}

/* ---- placeholder: doubles as the shot list ----
   Every gap says what the image has to prove and whether it is a capture
   we already own, one to take, or a screen still to design. */
.ph { margin: clamp(2rem, 5vh, 3rem) 0 0; }
 /* Capped while empty: a page full of full-bleed placeholders is unreadable
    to review. Real captures replace these as .shot and are uncapped. */
.ph__box {
  position: relative; aspect-ratio: var(--ar, 16/10); max-height: 26rem;
  border: 1px dashed var(--line-strong); border-radius: var(--radius-card);
  background:
    repeating-linear-gradient(-45deg, transparent 0 11px, rgba(17,17,17,.028) 11px 22px),
    var(--bg-sunken);
  display: grid; place-content: center; gap: .5rem; text-align: center; padding: 1.5rem;
}
.ph__n {
  font-family: var(--font-mono); font-size: 2rem; line-height: 1;
  color: var(--fg-3); letter-spacing: -.02em;
}
.ph__what { font-family: var(--font-display); font-weight: 500; font-size: var(--t-h3); color: var(--fg-2); }
.ph__tag {
  position: absolute; top: .85rem; left: .85rem;
  font-family: var(--font-mono); font-size: var(--t-meta);
  letter-spacing: .1em; text-transform: uppercase;
  padding: .3rem .55rem; border-radius: 999px; background: var(--bg-raised);
  border: 1px solid var(--line); color: var(--fg-3);
}
.ph[data-status="design"] .ph__tag { color: #B45309; border-color: rgba(180,83,9,.28); }
.ph[data-status="shoot"]  .ph__tag { color: #1D4ED8; border-color: rgba(29,78,216,.28); }
.ph[data-status="have"]   .ph__tag { color: #15803D; border-color: rgba(21,128,61,.28); }
.ph[data-status="recreate"] .ph__tag { color: #15803D; border-color: rgba(21,128,61,.28); }

/* A design brief sits under each gap, so the page is also the assignment. */
/* A brief needs the full column, not the caption measure. */
.ph figcaption:has(.brief) { max-width: none; }
.brief { margin-top: 1rem; display: grid; gap: .7rem; max-width: 54rem; }
.brief__row { display: grid; grid-template-columns: 8.5rem minmax(0,1fr); gap: 1rem;
              padding-top: .7rem; border-top: 1px solid var(--line); }
.brief__row:first-child { border-top: 0; padding-top: 0; }
.brief__k { font-family: var(--font-mono); font-size: var(--t-meta);
            letter-spacing: var(--track-meta); text-transform: uppercase; color: var(--fg-3); }
.brief__v { font-size: var(--t-sm); line-height: 1.6; color: var(--fg-2); }
.brief__v b { color: var(--fg-1); font-weight: 500; }
.brief__v ul { margin: .4rem 0 0; padding-left: 1.1rem; display: grid; gap: .3rem; }

@media (max-width: 700px) { .brief__row { grid-template-columns: minmax(0,1fr); gap: .3rem; } }

/* ---- the flow ---- */
.flow { margin-top: clamp(2rem, 5vh, 3rem); display: grid; gap: clamp(2.5rem, 6vh, 4rem); }
.flow__step { display: grid; gap: .75rem; }
.flow__head { display: flex; align-items: baseline; gap: .85rem; }
.flow__i {
  font-family: var(--font-mono); font-size: var(--t-meta); letter-spacing: .1em;
  color: var(--fg-3); flex: none;
}
.flow__t { font-family: var(--font-display); font-weight: 500; font-size: var(--t-h3); color: var(--fg-1); }
.flow__d { margin: 0; max-width: var(--measure); font-size: var(--t-sm); color: var(--fg-2); line-height: 1.6; }

/* ---- edge cases ---- */
.edges { margin-top: clamp(2rem, 5vh, 3rem); display: grid; gap: 1px; background: var(--line);
         border: 1px solid var(--line); border-radius: var(--radius-card); overflow: hidden; }
.edge { background: var(--bg-raised); padding: clamp(1.25rem, 3vw, 1.75rem); display: grid; gap: .5rem; }
.edge__tag {
  font-family: var(--font-mono); font-size: var(--t-meta);
  letter-spacing: var(--track-meta); text-transform: uppercase; color: var(--fg-3);
}
.edge h4 { margin: 0; font-family: var(--font-display); font-weight: 500; font-size: var(--t-h3);
           letter-spacing: -.02em; color: var(--fg-1); }
.edge p { margin: 0; max-width: 54ch; font-size: var(--t-sm); color: var(--fg-2); line-height: 1.6; }

/* ---- a line the project was steered by ----
   Set as a statement rather than as body copy, because it was written before
   the work and everything after it had to agree with it. */
.quote { margin: clamp(2rem, 5vh, 3rem) 0 0; padding-left: clamp(1rem, 2.5vw, 1.5rem);
         border-left: 2px solid var(--fg-1); }
.quote blockquote { margin: 0; font-family: var(--font-display); font-weight: 400;
  font-size: var(--t-h3); line-height: 1.35; letter-spacing: -.015em; color: var(--fg-1); }
.quote figcaption { margin-top: .9rem; max-width: 62ch;
  font-size: var(--t-sm); line-height: 1.6; color: var(--fg-3); }

/* ---- the opening screen ----
   The page asked for a lot of reading before it showed anything. This is the
   product as it ended up, so the argument that follows has something to point
   at. Wider than the text column, because a workspace at 54rem is a thumbnail. */
.shot--hero { margin-top: clamp(2.5rem, 6vh, 4rem);
  width: min(var(--wide-max), 100vw - var(--gutter) * 2);
  margin-inline: calc(50% - min(var(--wide-max), 100vw - var(--gutter) * 2) / 2); }

/* ---- a key for the folder icons ----
   The opening screen resolves the whole access model in three icons, which is
   easy to scroll past. The icons are the product's own exports, so the key and
   the screen cannot drift apart. Figma shipped them with a 2048px grain bitmap
   embedded at 5-12% multiply, 29MB across the three; it is invisible at 22px
   and was stripped. Re-export means re-stripping. */
.iconkey { margin: 1.1rem 0 0; padding: 0; list-style: none; max-width: 78ch;
  display: grid; grid-template-columns: repeat(3, minmax(0,1fr)); gap: clamp(1rem, 2.5vw, 2rem); }
.iconkey li { display: grid; grid-template-columns: auto minmax(0,1fr); gap: .6rem .7rem; align-items: start; }
/* These sit inside the hero <figure>, so .shot img would give each one a card
   border, radius, background and shadow. At 22px that radius is a white disc
   that clips the folder. Reset all four. */
.iconkey img { width: 22px; height: 22px; flex: none; grid-row: span 2; margin-top: .05rem;
  border: 0; border-radius: 0; background: none; box-shadow: none; }
.iconkey b { font-family: var(--font-mono); font-weight: 400; font-size: var(--t-meta);
  letter-spacing: var(--track-meta); text-transform: uppercase; color: var(--fg-1); align-self: center; }
.iconkey span { grid-column: 2; font-size: var(--t-sm); line-height: 1.5; color: var(--fg-3); }
@media (max-width: 760px) { .iconkey { grid-template-columns: minmax(0,1fr); gap: .85rem; }
  .iconkey img { grid-row: span 2; } }

/* ---- a flow inside the design chapter ----
   Three flows under one heading rather than three sections: the design is one
   chapter with parts, not three siblings of The problem. The rule marks each
   part without giving it the weight of a section of its own. */
.chapter__n { margin: 0; padding-top: clamp(2.5rem, 7vh, 4.5rem);
  border-top: 1px solid var(--line);
  font-family: var(--font-mono); font-size: var(--t-meta);
  letter-spacing: var(--track-meta); text-transform: uppercase; color: var(--fg-3); }
.sec h3.chapter { margin: .5rem 0 0; max-width: 22ch;
  font-size: var(--t-h2); line-height: 1.08; letter-spacing: -.026em; }
.sec h3.chapter + p { margin-top: .9rem; }

/* ---- the permission matrix ----
   Rebuilt rather than screenshotted: it is a table, the page can set a table,
   and a dark slide dropped into a light page reads as a foreign object.
   Scrolls inside itself on narrow screens so the page never does. */
.perm { margin: clamp(2rem, 5vh, 3rem) 0 0; }
.perm__scroll { overflow-x: auto; overscroll-behavior-x: contain;
  border: 1px solid var(--line); border-radius: var(--radius-card); background: var(--bg-raised); }
.perm table { width: 100%; border-collapse: collapse; font-size: var(--t-sm); }
.perm th, .perm td { padding: .7rem clamp(.6rem, 1.4vw, 1rem); text-align: left;
  border-bottom: 1px solid var(--line); white-space: nowrap; }
/* The role columns are a matrix, so they read as one when they are even and
   centred. Left-aligned they drift apart and the object column eats the row. */
.perm thead th:not(:first-child), .perm tbody td { text-align: center; width: 15%; }
.perm thead th:first-child, .perm tbody th { width: 40%; }
.perm tbody tr:last-child th, .perm tbody tr:last-child td { border-bottom: 0; }
.perm thead th {
  font-family: var(--font-mono); font-size: var(--t-meta); font-weight: 400;
  letter-spacing: var(--track-meta); text-transform: uppercase; color: var(--fg-3);
}
.perm tbody th { font-weight: 400; color: var(--fg-1); }
.perm td { font-family: var(--font-mono); font-size: var(--t-meta);
  letter-spacing: .04em; color: var(--fg-2); }
.perm td[data-none] { color: var(--fg-3); opacity: .5; }
/* Manager is the column the whole feature turns on, so it is the one marked. */
.perm [data-col="manager"] { background: var(--bg-sunken); }
.perm thead [data-col="manager"] { color: var(--fg-1); }
.perm tbody [data-col="manager"]:not([data-none]) { color: var(--fg-1); }
.perm__key { margin: .85rem 0 0; display: flex; flex-wrap: wrap; gap: .35rem 1.1rem;
  font-family: var(--font-mono); font-size: var(--t-meta);
  letter-spacing: var(--track-meta); text-transform: uppercase; color: var(--fg-3); }
.perm__cap { margin: .9rem 0 0; max-width: 78ch; font-size: var(--t-body);
  line-height: 1.6; color: var(--fg-3); }

/* ---- the trade-off, side by side ---- */
.tradeoff { margin-top: clamp(2rem, 5vh, 3rem); display: grid; grid-template-columns: repeat(2, minmax(0,1fr)); gap: 1.25rem; }
.opt { border: 1px solid var(--line); border-radius: var(--radius-card);
       background: var(--bg-raised); padding: clamp(1.25rem, 3vw, 1.75rem); }
.opt[data-chosen] { border-color: var(--line-strong); box-shadow: var(--shadow-card); }
.opt__label {
  font-family: var(--font-mono); font-size: var(--t-meta);
  letter-spacing: var(--track-meta); text-transform: uppercase; color: var(--fg-3);
  display: flex; justify-content: space-between; align-items: center;
}
.opt[data-chosen] .opt__label b { color: var(--fg-1); font-weight: 400; }
.opt h3 { margin: .75rem 0 0; }
.opt ol { margin: 1rem 0 0; padding: 0; list-style: none; display: grid; gap: .5rem; counter-reset: s; }
.opt li { counter-increment: s; font-size: var(--t-sm); color: var(--fg-2); display: flex; gap: .6rem; }
.opt li::before {
  content: counter(s, decimal-leading-zero);
  font-family: var(--font-mono); font-size: var(--t-meta); color: var(--fg-3); flex: none; padding-top: .15rem;
}

/* ---- outcome ---- */
.metrics { margin-top: clamp(2rem, 5vh, 3rem); display: grid;
           grid-template-columns: repeat(3, minmax(0,1fr)); gap: clamp(1.5rem, 4vw, 3rem); }
.metric b { display: block; font-family: var(--font-mono); font-weight: 400;
            font-size: clamp(2rem, 4.4vw, 3.25rem); line-height: 1; letter-spacing: -.03em;
            color: var(--fg-1); font-variant-numeric: tabular-nums; }
.metric span { display: block; margin-top: .6rem; font-family: var(--font-mono);
               font-size: var(--t-meta); letter-spacing: var(--track-meta);
               text-transform: uppercase; color: var(--fg-3); }
.metric em { display: block; margin-top: .5rem; font-style: normal; font-size: var(--t-sm); color: var(--fg-2); }

/* ---- foot ----
   Back to the index is already in the sticky bar, so repeating it here bought
   nothing. What the bottom of a case is actually for is choosing the next
   thing to read, so it offers the obvious next one at size, and then every
   case as a picker underneath. The current one stays in the list, marked, so
   the reader can see where they are in the set rather than losing the row.
   =================================================================== */
/* The two ways out are the same size on purpose: leaving the set is
   as legitimate an ending as reading the next one, and one of them
   dressed smaller would be an opinion nobody asked for. Back on the
   left, next on the right, the dock centred under both. */
.next { padding: clamp(3rem, 8vh, 5rem) 0 clamp(4rem, 10vh, 6.5rem);
        border-top: 1px solid var(--line);
        display: grid; gap: clamp(1.5rem, 4vh, 2.5rem); }

/* Aligned to the top, not the bottom: "Subly · Audio Description" runs
   to two lines and bottom alignment shoved its kicker up out of line
   with the other one. The names now grow downward and the two kickers
   stay on the same line. */
.next__ends { display: grid; grid-template-columns: 1fr 1fr;
              align-items: start; gap: clamp(1.5rem, 4vw, 3rem); }

.next__case, .next__home {
  display: grid; gap: .45rem;
  text-decoration: none; color: var(--fg-1);
  font-family: var(--font-display); font-weight: 500;
  font-size: var(--t-h2); letter-spacing: -.026em;
}
.next__home { justify-items: start; }
.next__case { justify-items: end; text-align: right; }
.next__case:hover, .next__home:hover { color: var(--fg); }

/* Inline, not inline-flex: as a flex item the arrow stayed up on the
   first line while the name wrapped under it. */
.next__case b, .next__home b { font-weight: inherit; display: inline; }
.next__case i { margin-left: .25em; }
.next__home i { margin-right: .1em; }
.next__case i, .next__home i { font-style: normal; display: inline-block;
                               transition: transform .3s var(--ease); }
.next__case:hover i { transform: translateX(5px); }
.next__home:hover i { transform: translateX(-5px); }

.next__case span, .next__home span {
  font-family: var(--font-mono); font-size: var(--t-meta);
  letter-spacing: var(--track-meta); text-transform: uppercase; color: var(--fg-3); }

@media (max-width: 700px) {
  .next__ends { grid-template-columns: minmax(0,1fr); gap: clamp(1.25rem, 4vh, 2rem); }
  .next__case { justify-items: start; text-align: left; }
}



/* What next. A list of names told you where you could go; a row of
   screens tells you whether you want to. The reader has just finished
   a case, which is the one moment a rich picker is not chrome, so this
   is a grid rather than the dock: no fixed bar, no magnification, and
   the case you just read is not in it. */
.cases { border-top: 1px solid var(--line); padding-top: clamp(1.25rem, 3vh, 1.75rem); }
.cases__label { margin: 0 0 clamp(1rem, 2.5vh, 1.5rem);
                font-family: var(--font-mono); font-size: var(--t-meta);
                letter-spacing: var(--track-meta); text-transform: uppercase; color: var(--fg-3); }

.cases__grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(min(100%, 14rem), 1fr));
  gap: clamp(1rem, 2vw, 1.5rem);
}

.cases__card {
  display: grid;
  gap: .7rem;
  text-decoration: none;
  color: var(--fg-1);
}

.cases__shot {
  display: block;
  aspect-ratio: 16 / 10;
  border-radius: 12px;
  overflow: hidden;
  background: var(--bg-sunken);
  border: 1px solid var(--line);
  transition: transform .35s var(--ease), box-shadow .35s var(--ease);
}
.cases__shot img { width: 100%; height: 100%; object-fit: cover; display: block; }
.cases__card:hover .cases__shot {
  transform: translateY(-3px);
  box-shadow: 0 2px 4px rgba(17,17,17,.05), 0 20px 40px -18px rgba(17,17,17,.26);
}

/* Home is not a project, so it does not get a screenshot pretending
   to be one. */
.cases__shot--all {
  display: grid;
  place-items: center;
  background: var(--bg-sunken);
}
.cases__shot--all b {
  font-family: var(--font-display);
  font-weight: 500;
  font-size: 1.0625rem;
  letter-spacing: -.02em;
  color: var(--fg-2);
}
.cases__card--all:hover .cases__shot--all b { color: var(--fg-1); }

.cases__name {
  display: flex;
  align-items: baseline;
  gap: .6rem;
  font-size: var(--t-body);
  line-height: 1.3;
  transition: color .2s;
}
.cases__name i {
  font-style: normal;
  font-family: var(--font-mono);
  font-size: var(--t-meta);
  letter-spacing: var(--track-meta);
  color: var(--fg-3);
  flex: none;
}
.cases__card:hover .cases__name { color: var(--fg); }

.cases__card--all .cases__name i { transition: transform .3s var(--ease); }
.cases__card--all:hover .cases__name i { transform: translateX(-4px); }

@media (max-width: 800px) {
  .facts { grid-template-columns: repeat(2, minmax(0,1fr)); }
  .tradeoff, .metrics { grid-template-columns: minmax(0,1fr); }
}

/* ===================================================================
   Diagrams, built in the page.

   Drawn rather than pasted: they stay sharp at any size, they inherit
   the page's type and colour, and they can carry an argument in their
   labels instead of being a flat picture of one.
   =================================================================== */

.dgm { margin: clamp(2rem, 5vh, 3rem) 0 0; }
.dgm__cap { margin-top: .85rem; max-width: 52ch; font-size: var(--t-sm); line-height: 1.55; color: var(--fg-3); }

/* ---- pipeline: numbered stages in sequence ---- */
.pipe { display: grid; grid-template-columns: repeat(4, minmax(0,1fr)); gap: 1px;
        background: var(--line); border: 1px solid var(--line);
        border-radius: var(--radius-card); overflow: hidden; }
.pipe__s { background: var(--bg-raised); padding: clamp(1.1rem, 2.6vw, 1.6rem); display: grid;
           gap: .5rem; align-content: start; position: relative; }
.pipe__n { font-family: var(--font-mono); font-size: var(--t-meta);
           letter-spacing: var(--track-meta); color: var(--fg-3); }
.pipe__t { font-family: var(--font-display); font-weight: 500; font-size: var(--t-h3);
           letter-spacing: -.02em; color: var(--fg-1); line-height: 1.15; }
.pipe__d { margin: 0; font-size: var(--t-sm); line-height: 1.55; color: var(--fg-2); }
/* The arrow lives on the divider, so it reads as flow and not as a list. */
.pipe__s + .pipe__s::before {
  content: ""; position: absolute; left: -1px; top: 50%; width: 9px; height: 9px;
  border-top: 1px solid var(--line-strong); border-right: 1px solid var(--line-strong);
  transform: translate(-50%,-50%) rotate(45deg); background: var(--bg-raised); z-index: 2;
}

/* ---- three prompt stages, with the guardrail each one adds ---- */
.stages { display: grid; grid-template-columns: repeat(3, minmax(0,1fr)); gap: 1.25rem; }
.stage { border: 1px solid var(--line); border-radius: var(--radius-card);
         background: var(--bg-raised); padding: clamp(1.1rem, 2.6vw, 1.6rem); display: grid; gap: .6rem; }
.stage__n { font-family: var(--font-mono); font-size: var(--t-meta);
            letter-spacing: var(--track-meta); text-transform: uppercase; color: var(--fg-3); }
.stage h4 { margin: 0; font-family: var(--font-display); font-weight: 500;
            font-size: var(--t-h3); letter-spacing: -.02em; color: var(--fg-1); }
.stage p { margin: 0; font-size: var(--t-sm); line-height: 1.55; color: var(--fg-2); }
.stage__rule { margin-top: .4rem; padding-top: .7rem; border-top: 1px solid var(--line);
               font-family: var(--font-mono); font-size: var(--t-meta); line-height: 1.5;
               color: var(--fg-2); }
.stage__rule b { display: block; color: var(--fg-3); letter-spacing: var(--track-meta);
                 text-transform: uppercase; margin-bottom: .3rem; font-weight: 400; }

/* ---- the speech-gap problem, on a timeline ---- */
.gapd { display: grid; gap: 1.5rem; }
.gapd__row { display: grid; gap: .6rem; }
.gapd__label { display: flex; justify-content: space-between; align-items: baseline; gap: 1rem;
               font-family: var(--font-mono); font-size: var(--t-meta);
               letter-spacing: var(--track-meta); text-transform: uppercase; color: var(--fg-3); }
.gapd__label b { color: var(--fg-1); font-weight: 400; }
.gapd__track { position: relative; height: 5.25rem; border: 1px solid var(--line);
               border-radius: 12px; background: var(--bg-sunken); overflow: hidden; }
.gapd__speech, .gapd__desc {
  position: absolute; left: calc(var(--a) * 1%); width: calc((var(--b) - var(--a)) * 1%);
  display: grid; place-items: center; text-align: center; padding: 0 .35rem;
  font-size: .6875rem; line-height: 1.2; border-radius: 8px; overflow: hidden;
}
.gapd__speech { top: .4rem; height: 2rem; background: var(--bg-raised);
                border: 1px solid var(--line-strong); color: var(--fg-2); }
.gapd__desc   { bottom: .4rem; height: 2rem; background: #111; color: #fff; }
 /* The failure is drawn at the width the line actually needs, so it visibly
    overruns the dialogue on both sides instead of being a small marker. */
.gapd__desc[data-fail] {
  background: repeating-linear-gradient(-45deg,#B45309 0 7px,#92400E 7px 14px);
  outline: 2px solid var(--bg-sunken); z-index: 3; height: 2.25rem; bottom: .3rem;
}
.gapd__pause  { position: absolute; top: 0; bottom: 0; left: calc(var(--a) * 1%);
                width: calc((var(--b) - var(--a)) * 1%); background: rgba(17,17,17,.05);
                border-left: 1px dashed var(--line-strong); border-right: 1px dashed var(--line-strong); }
.gapd__note { margin: 0; font-size: var(--t-sm); line-height: 1.55; color: var(--fg-2); max-width: 54ch; }

@media (max-width: 800px) {
  .pipe, .stages { grid-template-columns: minmax(0,1fr); }
  .pipe__s + .pipe__s::before { left: 50%; top: -1px; transform: translate(-50%,-50%) rotate(135deg); }
}

/* ---- the suite, and the one thing it could not do ---- */
.suite { display: grid; gap: .75rem; }
.suite__row { display: flex; flex-wrap: wrap; gap: .5rem; align-items: stretch; }
.suite__c { flex: 1 1 9rem; padding: .9rem 1rem; border-radius: 14px;
            border: 1px solid var(--line); background: var(--bg-raised);
            display: grid; gap: .25rem; align-content: center; }
.suite__c b { font-weight: 500; font-size: var(--t-sm); color: var(--fg-1); }
.suite__c span { font-family: var(--font-mono); font-size: var(--t-meta);
                 letter-spacing: var(--track-meta); text-transform: uppercase; color: var(--fg-3); }
.suite__c[data-gap] { border-style: dashed; border-color: var(--line-strong);
  background: repeating-linear-gradient(-45deg, transparent 0 9px, rgba(17,17,17,.03) 9px 18px), var(--bg-sunken); }
.suite__c[data-gap] b { color: var(--fg-2); }
.suite__check { padding: .9rem 1rem; border-radius: 14px; border: 1px solid var(--line);
                background: var(--bg-sunken); font-size: var(--t-sm); color: var(--fg-2); }
.suite__check b { color: var(--fg-1); font-weight: 500; }

/* ---- coverage: where each version actually spoke ---- */
.cov { display: grid; gap: 1.1rem; }
.cov__row { display: grid; gap: .5rem; }
.cov__h { display: flex; justify-content: space-between; align-items: baseline; gap: 1rem;
          font-family: var(--font-mono); font-size: var(--t-meta);
          letter-spacing: var(--track-meta); text-transform: uppercase; color: var(--fg-3); }
.cov__h b { color: var(--fg-1); font-weight: 400; }
.cov__track { position: relative; height: 2.5rem; border: 1px solid var(--line);
              border-radius: 10px; background: var(--bg-sunken); overflow: hidden; }
.cov__b { position: absolute; top: .3rem; bottom: .3rem; border-radius: 5px; background: #111;
          left: calc(var(--a) * 1%); width: calc((var(--b) - var(--a)) * 1%); min-width: 3px; }
.cov__row[data-thin] .cov__b { background: var(--fg-3); }
.cov__silence { position: absolute; top: 0; bottom: 0; left: calc(var(--a) * 1%);
                width: calc((var(--b) - var(--a)) * 1%);
                background: repeating-linear-gradient(-45deg,rgba(180,83,9,.14) 0 7px,transparent 7px 14px);
                border-left: 1px dashed rgba(180,83,9,.5); border-right: 1px dashed rgba(180,83,9,.5); }
.cov__scale { display: flex; justify-content: space-between;
              font-family: var(--font-mono); font-size: var(--t-meta); color: var(--fg-3); }

/* ---- a row of phone screens, used where the work is the argument ---- */
.phones { display: flex; gap: clamp(.75rem, 2vw, 1.25rem); overflow-x: auto;
          margin: clamp(2rem, 5vh, 3rem) 0 0; padding-bottom: .5rem;
          scrollbar-width: thin; }
.phones figure { margin: 0; flex: none; width: clamp(7.5rem, 16vw, 10.5rem); }
.phones img { width: 100%; border-radius: 12px; filter: drop-shadow(0 18px 36px rgba(17,17,17,.20)); }
.phones figcaption { margin-top: .6rem; font-family: var(--font-mono);
                     font-size: var(--t-meta); letter-spacing: var(--track-meta);
                     text-transform: uppercase; color: var(--fg-3); }

/* ---- the ceiling, as a bar model ---- */
.model { display: grid; gap: .55rem; margin: clamp(1.5rem,4vh,2.25rem) 0 0; }
.model__r { display: grid; grid-template-columns: 7.5rem minmax(0,1fr) 5rem; gap: 1rem; align-items: center; }
.model__k, .model__v { font-family: var(--font-mono); font-size: var(--t-meta);
                       letter-spacing: var(--track-meta); text-transform: uppercase; color: var(--fg-3); }
.model__v { text-align: right; color: var(--fg-1); }
.model__bar { height: 1.5rem; border-radius: 5px; background: var(--bg-sunken);
              border: 1px solid var(--line); position: relative; overflow: hidden; }
.model__bar i { position: absolute; inset: 0 auto 0 0; width: calc(var(--w) * 1%); background: #111; }
.model__r[data-now] .model__v { color: var(--fg-1); }
.model__r[data-now] .model__bar i { background: var(--fg-3); }
.model__cap { display: grid; grid-template-columns: 7.5rem minmax(0,1fr) 5rem; gap: 1rem;
              margin: 0 0 .35rem; font-family: var(--font-mono); font-size: var(--t-meta);
              letter-spacing: var(--track-meta); text-transform: uppercase; color: var(--fg-3); }
.model__cap span:first-child { grid-column: 1; }
.model__cap span:last-child { grid-column: 2 / 4; text-align: right; }
.model__note { margin: .9rem 0 0; font-size: var(--t-sm); color: var(--fg-3); }
@media (max-width: 800px) { .model__cap { grid-template-columns: 5.5rem minmax(0,1fr) 4rem; gap: .6rem; } }

/* ---- the four paths ---- */
.paths { display: grid; grid-template-columns: repeat(2, minmax(0,1fr)); gap: 1.25rem;
         margin: clamp(2rem,5vh,3rem) 0 0; }
.path { border: 1px solid var(--line); border-radius: var(--radius-card);
        background: var(--bg-raised); padding: clamp(1.1rem,2.6vw,1.6rem); display: grid; gap: .55rem; align-content: start; }
.path__h { display: flex; justify-content: space-between; align-items: baseline; gap: 1rem;
           font-family: var(--font-mono); font-size: var(--t-meta);
           letter-spacing: var(--track-meta); text-transform: uppercase; color: var(--fg-3); }
.path h4 { margin: 0; font-family: var(--font-display); font-weight: 500;
           font-size: var(--t-h3); letter-spacing: -.02em; color: var(--fg-1); }
.path p { margin: 0; font-size: var(--t-sm); line-height: 1.6; color: var(--fg-2); }
.path__risk { margin-top: .4rem; padding-top: .6rem; border-top: 1px solid var(--line);
              font-size: var(--t-sm); color: var(--fg-3); }
@media (max-width: 800px) { .paths { grid-template-columns: minmax(0,1fr); }
  .model__r { grid-template-columns: 5.5rem minmax(0,1fr) 4rem; gap: .6rem; } }

/* ---- a flow, on a rail ----
   Consecutive steps read better sideways than stacked: it is the shape of the
   thing being described, and it keeps a four-step flow from costing four
   screens of scroll. Frames stay large on purpose. A rail of thumbnails would
   shrink exactly the modals and menus that are the point of showing them. */
.rail {
  margin: clamp(2rem, 5vh, 3rem) 0 0;
  display: grid; grid-auto-flow: column;
  grid-auto-columns: min(72%, 46rem);
  gap: clamp(1rem, 2.5vw, 1.75rem);
  overflow-x: auto; overscroll-behavior-x: contain;
  scroll-snap-type: x mandatory;
  /* Bleed to the page edge so the next frame is visibly cut, which is the
     affordance. Padding restores the gutter on the first frame. */
  margin-left: calc(var(--gutter) * -1); margin-right: calc(var(--gutter) * -1);
  padding: 0 var(--gutter) 1rem;
  scrollbar-width: thin;
}
.rail::-webkit-scrollbar { height: 6px; }
.rail::-webkit-scrollbar-thumb { background: var(--line-strong); border-radius: 99px; }

.step { scroll-snap-align: start; display: grid; gap: .75rem; align-content: start; margin: 0; }
.step__h { display: flex; align-items: baseline; gap: .6rem; flex-wrap: wrap; }
.step__i { font-family: var(--font-mono); font-size: var(--t-meta); letter-spacing: .1em;
           color: var(--fg-3); flex: none; }
.step__flag { flex: none; font-family: var(--font-mono); font-size: var(--t-meta);
  letter-spacing: var(--track-meta); text-transform: uppercase; color: #B45309;
  border: 1px solid rgba(180,83,9,.28); border-radius: 999px; padding: .15rem .5rem; }
.step__t { flex: 1 1 100%; font-size: var(--t-sm); line-height: 1.55; color: var(--fg-1); font-weight: 500; }
.step__t em { font-style: normal; font-weight: 400; color: var(--fg-2); }
.step img { width: 100%; border-radius: 14px; border: 1px solid var(--line);
            background: var(--bg-raised); box-shadow: var(--shadow-card); }

.rail__hint { margin: .85rem 0 0; font-family: var(--font-mono); font-size: var(--t-meta);
              letter-spacing: var(--track-meta); text-transform: uppercase; color: var(--fg-3); }

@media (max-width: 700px) { .rail { grid-auto-columns: min(88%, 30rem); } }

/* ---- before and after, same chart, same source ----
   Paired rather than stacked, because the comparison is the evidence and a
   reader should not have to hold one image in their head to see it. */
.ba { margin: clamp(2rem, 5vh, 3rem) 0 0; display: grid; gap: clamp(1.5rem, 4vh, 2.25rem); }
.ba__pair { display: grid; grid-template-columns: repeat(2, minmax(0,1fr)); gap: clamp(1rem, 2.5vw, 1.75rem); }
.ba__f { margin: 0; display: grid; gap: .7rem; }
.ba__f img { width: 100%; border-radius: 12px; border: 1px solid var(--line);
             background: var(--bg-raised); box-shadow: var(--shadow-card); }
.ba__l { display: flex; justify-content: space-between; align-items: baseline; gap: .75rem;
         font-family: var(--font-mono); font-size: var(--t-meta);
         letter-spacing: var(--track-meta); text-transform: uppercase; color: var(--fg-3); }
.ba__l b { color: var(--fg-1); font-weight: 400; }
.ba__cap { margin: 0; font-size: var(--t-sm); line-height: 1.6; color: var(--fg-2); max-width: 60ch; }
.ba__cap b { color: var(--fg-1); font-weight: 500; }
@media (max-width: 760px) { .ba__pair { grid-template-columns: minmax(0,1fr); } }

/* ---- pinned flow ----
   The stage holds still while the rail moves sideways under it. Only on
   screens wide enough to be worth it; pin.js clears the height otherwise
   and the rail goes back to being a normal swipeable row. */
.pin { position: relative; }
/* Pinned, the flow leaves the text column: a step at 620px is a thumbnail,
   and the modals in these captures are the reason to show them at all. */
.pin[data-on="1"] { margin-inline: calc(50% - 50vw); width: 100vw; }
.pin[data-on="1"] .pin__stage {
  position: sticky; top: 0; min-height: 100svh;
  display: flex; align-items: center; overflow: hidden;
}
.pin[data-on="1"] .rail {
  overflow: visible; scroll-snap-type: none;
  width: 100%; margin: 0; padding: 0 clamp(1.5rem, 5vw, 4rem);
  grid-auto-columns: min(68vw, 58rem);
  will-change: transform;
}
.pin__stage { padding: clamp(2rem,6vh,4rem) 0; }

/* ---- a flow of phone screens ----
   Same rail, different proportion. A portrait capture at 68vw would be taller
   than the viewport, so these are sized to fit four across at desktop with the
   fifth visibly cut: the filmstrip reads as a sequence without needing a pin.
   345px is 88% of a real iPhone's point width, which is the smallest size at
   which the body copy inside a modal is still readable. */
.rail--phone { grid-auto-columns: clamp(14rem, 23vw, 21.5rem); scroll-snap-type: x proximity; }
.rail--phone .step img { border-radius: 18px; border: 0;
  box-shadow: 0 22px 44px rgba(17,17,17,.22), 0 2px 6px rgba(17,17,17,.10); }
.rail--phone .step { align-content: start; }
.rail--phone .step__t { min-height: 5.5em; }
@media (max-width: 700px) { .rail--phone { grid-auto-columns: min(62%, 15rem); }
  .rail--phone .step__t { min-height: 0; } }

/* Two rails under one heading: the label repeats, the argument does not. */
.flow__t { display: flex; align-items: baseline; gap: .75rem; flex-wrap: wrap;
           margin: clamp(2.5rem, 6vh, 3.5rem) 0 0; }
.flow__t h3 { margin: 0; font-family: var(--font-display); font-weight: 500;
              font-size: var(--t-h3); letter-spacing: -.02em; color: var(--fg-1); }
.flow__t span { font-family: var(--font-mono); font-size: var(--t-meta);
                letter-spacing: var(--track-meta); text-transform: uppercase; color: var(--fg-3); }
.flow__t + p { margin-top: .6rem; }

/* Two states of the same panel, stacked rather than paired. Side by side they
   fall to half the size the app actually renders at, and the thing being
   compared here is a 14px label strip. Full column width puts it back at
   roughly the size a user saw it. */
.ba--stack .ba__pair { grid-template-columns: minmax(0,1fr); gap: clamp(1.25rem, 3vh, 2rem); }



/* ---- facts inline in a section ----
   Small siblings of .metrics. Used where a paragraph was carrying three
   separate facts and reading as a wall. */
.metrics--sm { grid-template-columns: repeat(3, minmax(0,1fr)); gap: clamp(1.25rem, 3vw, 2rem);
               margin-top: clamp(1.5rem, 4vh, 2.25rem); }
.metrics--sm .metric b { font-size: clamp(1.4rem, 2.4vw, 2rem); }
.metrics--sm .metric span { margin-top: .45rem; }
.metrics--sm .metric em { margin-top: .4rem; }
@media (max-width: 700px) { .metrics--sm { grid-template-columns: repeat(2, minmax(0,1fr)); } }

/* ---- a short sequence of days ----
   Three dates read faster as marks on a line than as a sentence. */
.tl { margin-top: clamp(1.75rem, 4vh, 2.5rem); display: grid;
      grid-template-columns: repeat(3, minmax(0,1fr)); gap: clamp(1rem, 2.5vw, 1.75rem); }
.tl__i { display: grid; gap: .5rem; align-content: start;
         padding-top: .9rem; border-top: 2px solid var(--fg-1); }
.tl__i:nth-child(n+2) { border-top-color: var(--line-strong); }
.tl__w { font-family: var(--font-mono); font-size: var(--t-meta); letter-spacing: var(--track-meta);
         text-transform: uppercase; color: var(--fg-3); }
.tl__t { font-size: var(--t-sm); line-height: 1.55; color: var(--fg-1); }
@media (max-width: 700px) { .tl { grid-template-columns: minmax(0,1fr); } }

/* ---- the pipeline and how each part fails ---- */
.pipe { margin-top: clamp(1.75rem, 4vh, 2.5rem); display: grid; gap: 1px; background: var(--line);
        border: 1px solid var(--line); border-radius: var(--radius-card); overflow: hidden; }
.pipe__r { display: grid; grid-template-columns: 1.5rem 11rem minmax(0,1fr); gap: 1rem;
           align-items: baseline; padding: .9rem clamp(1rem,2.4vw,1.4rem); background: var(--bg-raised); }
.pipe__n { font-family: var(--font-mono); font-size: var(--t-meta); color: var(--fg-3); }
.pipe__k { font-size: var(--t-sm); font-weight: 500; color: var(--fg-1); }
.pipe__v { font-size: var(--t-sm); line-height: 1.55; color: var(--fg-2); }
.pipe__r[data-loss] .pipe__k { color: #B45309; }
@media (max-width: 700px) { .pipe__r { grid-template-columns: 1.5rem minmax(0,1fr); }
  .pipe__v { grid-column: 2; } }
.metrics--two { grid-template-columns: repeat(2, minmax(0,1fr)); }
@media (max-width: 700px) { .metrics--two { grid-template-columns: minmax(0,1fr); } }

/* ---- a chain of consequence ----
   Four steps where each one is caused by the one above it. Drawn as a rail so
   the causality is visible: a paragraph makes the reader hold four links in
   their head, a rail holds them on the page. */
/* The chain sets its own top gap: it follows a paragraph, not a heading, and
   without one the first marker sits on the last line of body copy. */
.chain { list-style: none; margin: clamp(2rem, 5vh, 3rem) 0 0; padding: 0; display: grid; }
.chain__i { position: relative; padding: 0 0 clamp(1.4rem,3.5vh,2rem) 3rem; }
.chain__i:last-child { padding-bottom: 0; }
.chain__i::before { content: ""; position: absolute; left: .68rem; top: 1.35rem; bottom: -.35rem;
                    width: 1px; background: var(--line-strong); }
.chain__i:last-child::before { display: none; }
.chain__n { position: absolute; left: 0; top: .1rem; display: grid; place-items: center;
            width: 1.45rem; height: 1.45rem; border-radius: 50%;
            border: 1px solid var(--line-strong); background: var(--bg-raised);
            font-family: var(--font-mono); font-size: .625rem; color: var(--fg-3); }
.chain__i[data-out] .chain__n { background: #111; border-color: #111; color: #fff; }
.chain__h { margin: 0; font-size: var(--t-body); font-weight: 500; color: var(--fg-1); line-height: 1.4; }
.chain__d { margin: .4rem 0 0; font-size: var(--t-sm); line-height: 1.6; color: var(--fg-2); max-width: 62ch; }

/* A pair that needs more room than the text column: the comparison is between
   two 14px label strips, and half of 54rem is not enough to read them. */
.ba--wide { margin-inline: calc(50% - 50vw); width: 100vw; }
.ba--wide > div { max-width: var(--wide-max); margin-inline: auto; padding-inline: var(--gutter); }

/* Full-bleed rail with a centred wide column. Children sit in that column, so
   an image and its caption share a left edge instead of each being centred on
   its own width. */
.open, .shot--wide {
  margin-inline: calc(50% - 50vw); width: 100vw;
  display: grid;
  grid-template-columns:
    [full-start] 1fr
    [wide-start] min(var(--wide-max), calc(100% - 2 * var(--gutter)))
    [wide-end] 1fr [full-end];
}
.open > *, .shot--wide > * { grid-column: wide; }
.open { margin-block: clamp(1.5rem, 4vh, 2.5rem) clamp(2.5rem, 7vh, 4rem); }
.shot--wide { margin-block: clamp(2rem, 5vh, 3rem) 0; }
.open > img, .shot--wide > img { width: 100%; }
/* A caption is a footnote to its image, so it runs the image's measure and
   stops where the image stops. Capping it shorter left it sitting under the
   left two thirds of the shot and reading as a stray paragraph. */
.open > figcaption, .shot--wide > figcaption { max-width: none; }
/* .open is the same full-bleed grid, so a section holding one must not clip. */
.sec:has(.shot--wide), .sec:has(.open) { overflow-x: visible; }

/* ---- two states of one screen, on the product's own switch ----
   The feature has a Captions toggle. Rather than print both states and ask the
   reader to compare them, the case carries the same control: flip it and the
   screen changes underneath, which is what the user does. */
.swap__bar { display: flex; justify-content: flex-end; align-items: center;
             gap: .6rem; margin-bottom: .7rem; }
.swap__btn { display: inline-flex; align-items: center; gap: .6rem; cursor: pointer;
             font-family: var(--font-mono); font-size: var(--t-meta);
             letter-spacing: var(--track-meta); text-transform: uppercase;
             user-select: none; }
/* Both states are named, so the reader knows what the switch compares
   before touching it. The live one is dark, the other recedes. */
.swap__st { color: var(--fg-3); transition: color .18s ease; }
.swap__in:not(:checked) ~ .swap__bar .swap__st[data-off],
.swap__in:checked ~ .swap__bar .swap__st[data-on] { color: var(--fg-1); }
.swap__btn i { position: relative; flex: none; width: 2.1rem; height: 1.15rem; border-radius: 999px;
               background: var(--line-strong); transition: background .18s ease; }
.swap__btn i::after { content: ""; position: absolute; top: 2px; left: 2px;
                      width: calc(1.15rem - 4px); height: calc(1.15rem - 4px);
                      border-radius: 50%; background: #fff; transition: transform .18s ease;
                      box-shadow: 0 1px 2px rgba(17,17,17,.3); }
.swap__in:checked ~ .swap__bar .swap__btn i { background: #1D4ED8; }
.swap__in:checked ~ .swap__bar .swap__btn i::after { transform: translateX(0.95rem); }
.swap__in:focus-visible ~ .swap__bar .swap__btn i { outline: 2px solid #1D4ED8; outline-offset: 3px; }
.swap__stage { position: relative; }
.swap__stage img { display: block; width: 100%; border-radius: var(--radius-card);
                   border: 1px solid var(--line); background: var(--bg-raised); box-shadow: var(--shadow-card); }
.swap__on { position: absolute; inset: 0; opacity: 0; transition: opacity .22s ease; }
.swap__in:checked ~ .swap__stage .swap__on { opacity: 1; }
@media (prefers-reduced-motion: reduce) { .swap__on, .swap__btn i, .swap__btn i::after { transition: none; } }

/* ---- the same video, described twice ---- */
.scomp { margin-top: clamp(2rem, 5vh, 3rem); display: grid;
         grid-template-columns: repeat(2, minmax(0,1fr)); gap: 1px;
         background: var(--line); border: 1px solid var(--line);
         border-radius: var(--radius-card); overflow: hidden; }
.scomp__col { background: var(--bg-raised); padding: clamp(1.1rem, 2.4vw, 1.6rem); }
.scomp__h { display: flex; align-items: baseline; justify-content: space-between; gap: .75rem;
            padding-bottom: .8rem; margin-bottom: .3rem; border-bottom: 1px solid var(--line);
            font-family: var(--font-mono); font-size: var(--t-meta);
            letter-spacing: var(--track-meta); text-transform: uppercase; color: var(--fg-3); }
.scomp__h b { color: var(--fg-1); font-weight: 400; }
.scomp__e { padding: .55rem 0; display: grid; gap: .15rem; }
.scomp__e + .scomp__e { border-top: 1px solid var(--line); }
.scomp__tc { font-family: var(--font-mono); font-size: .6875rem; color: var(--fg-3);
             font-variant-numeric: tabular-nums; }
.scomp__d { font-size: var(--t-sm); line-height: 1.5; color: var(--fg-1); }
.scomp__col[data-thin] .scomp__d { color: var(--fg-2); }
.scomp__note { padding: .7rem 0 0; font-size: var(--t-sm); color: var(--fg-3); }
@media (max-width: 760px) { .scomp { grid-template-columns: minmax(0,1fr); } }

/* ---- the verdict, attached to the work ----
   Scanned rather than read, this case looked like eleven finished screens in a
   row. Each release now carries what happened to it, in the same place every
   time, so the arc is legible without a paragraph. */
/* flex, not inline-flex: an inline box's vertical margins do not push the
   heading below it, so the pill sat flush against the h2. */
/* .sec p sets margin-bottom:0 and outranks a single class, so this needs two. */
.sec .verdict, .verdict { display: flex; width: max-content; align-items: center; gap: .5rem;
           margin: .4rem 0 clamp(1.5rem, 3.5vh, 2.25rem);
           font-family: var(--font-mono); font-size: var(--t-meta);
           letter-spacing: var(--track-meta); text-transform: uppercase;
           padding: .32rem .75rem; border-radius: 999px; border: 1px solid; }
.verdict::before { content: ""; width: .45rem; height: .45rem; border-radius: 50%; background: currentColor; }
.verdict[data-v="out"]  { color: #B45309; border-color: rgba(180,83,9,.3);  background: rgba(180,83,9,.05); }
.verdict[data-v="hold"] { color: #6B5B2E; border-color: rgba(107,91,46,.3); background: rgba(107,91,46,.05); }
.verdict[data-v="in"]   { color: #15803D; border-color: rgba(21,128,61,.3); background: rgba(21,128,61,.05); }

/* ---- three attempts, before any of them are shown ----
   The index a reader who scrolls past everything still takes away. */
.tried { margin-top: clamp(2rem, 5vh, 3rem); display: grid; gap: 1px;
         background: var(--line); border: 1px solid var(--line);
         border-radius: var(--radius-card); overflow: hidden; }
.tried__r { display: grid; grid-template-columns: 2.2rem minmax(0,1.1fr) minmax(0,1.4fr);
            gap: clamp(.75rem,2vw,1.5rem); align-items: baseline;
            padding: clamp(.9rem,2vw,1.15rem) clamp(1rem,2.4vw,1.4rem); background: var(--bg-raised); }
.tried__n { font-family: var(--font-mono); font-size: var(--t-meta); color: var(--fg-3); }
.tried__w { font-size: var(--t-body); font-weight: 500; color: var(--fg-1); line-height: 1.4; }
.tried__w span { display: block; font-family: var(--font-mono); font-size: var(--t-meta);
                 letter-spacing: var(--track-meta); text-transform: uppercase;
                 color: var(--fg-3); font-weight: 400; margin-top: .25rem; }
.tried__o { font-size: var(--t-sm); line-height: 1.55; color: var(--fg-2); }
.tried__o b { font-weight: 500; }
.tried__r[data-v="out"] .tried__o b  { color: #B45309; }
.tried__r[data-v="hold"] .tried__o b { color: #6B5B2E; }
.tried__r[data-v="in"] .tried__o b   { color: #15803D; }
@media (max-width: 700px) { .tried__r { grid-template-columns: 2rem minmax(0,1fr); }
  .tried__o { grid-column: 2; } }

/* ---- size carries status ----
   Everything at one width reads as a row of finished designs. What shipped runs
   the full 78rem; what was tried and dropped sits in the text column, on the
   sunken ground. Still full resolution, still legible: smaller, not cropped,
   because the point is that these were real screens, not sketches. */
.shot--inset {
  grid-template-columns:
    [full-start] 1fr
    [wide-start] min(var(--page-max), calc(100% - 2 * var(--gutter)))
    [wide-end] 1fr [full-end];
}
.shot--inset > img, .shot--inset .swap__stage img { box-shadow: none; }
.shot--inset .swap__stage, .shot--inset > img { outline: 1px solid var(--line); border-radius: var(--radius-card); }

/* Two states of the same failure, side by side. They are one message and one
   button each, so they do not need the room a flow screen needs, and paired
   they read as a set rather than as two more finished screens. */
/* A duo after the edge-case table is a second block, not a continuation. */
.edges + .duo { margin-top: clamp(2rem, 5vh, 3rem); }
.duo { display: grid; grid-template-columns: repeat(2, minmax(0,1fr));
       gap: clamp(1rem, 2.5vw, 1.75rem); }
.duo figure { margin: 0; display: grid; gap: .8rem; align-content: start; }
.duo img { width: 100%; border-radius: var(--radius-card); border: 1px solid var(--line);
           background: var(--bg-raised); box-shadow: var(--shadow-card); }
/* Same size, spacing and colour as every other caption. It sat at fg-3, which
   is the 40% the rule above says 13px does not clear AA at. */
.duo figcaption { font-size: var(--t-sm); line-height: 1.55; color: var(--fg-2); }
@media (max-width: 760px) { .duo { grid-template-columns: minmax(0,1fr); } }

/* ---- the audit, as an index ----
   Thirteen dark sheets in a row prove there are thirteen sheets. A list with
   real severities and real titles proves the pass was systematic, which is the
   part worth showing. The three that are reproduced in full sit on the sunken
   ground so a scanner can see at a glance what is shown and what is only
   listed. Text stays text: it is legible at any width and it is searchable. */
.audit { margin-top: clamp(2rem, 5vh, 3rem); display: grid; gap: 1px;
         background: var(--line); border: 1px solid var(--line);
         border-radius: var(--radius-card); overflow: hidden; }
.audit__r { display: grid;
            grid-template-columns: 2.6rem 3.2rem minmax(0,1fr) minmax(0,1.25fr);
            gap: clamp(.75rem, 2vw, 1.5rem); align-items: baseline;
            padding: clamp(.85rem,2vw,1.05rem) clamp(1rem,2.4vw,1.4rem);
            background: var(--bg-raised); }
.audit__r[data-shown="1"] { background: var(--bg-sunken); }
.audit__n { font-family: var(--font-mono); font-size: var(--t-meta); color: var(--fg-3); }
.audit__s { font-family: var(--font-mono); font-size: var(--t-meta);
            letter-spacing: var(--track-meta); text-transform: uppercase;
            justify-self: start; padding: .15rem .4rem; border-radius: 4px; line-height: 1.4; }
.audit__s[data-s="high"] { color: #B45309; background: rgba(180,83,9,.10); }
.audit__s[data-s="med"]  { color: #6B5B2E; background: rgba(107,91,46,.10); }
.audit__s[data-s="low"]  { color: var(--fg-2); background: var(--accent-tint); }
.audit__t { font-size: var(--t-body); font-weight: 500; color: var(--fg-1); line-height: 1.4; }
.audit__t em { display: block; font-family: var(--font-mono); font-size: var(--t-meta);
               letter-spacing: var(--track-meta); text-transform: uppercase;
               color: var(--fg-3); font-style: normal; margin-top: .3rem; }
.audit__d { font-size: var(--t-sm); line-height: 1.55; color: var(--fg-2); }
@media (max-width: 700px) { .audit__r { grid-template-columns: 2.2rem 3rem minmax(0,1fr); }
  .audit__d { grid-column: 3; } }

/* ---- visual directions, side by side ----
   Explorations only mean something next to each other, so the two survivors run
   as three phone screens each, same crop points, same width. The verdict sits in
   the header rather than in a paragraph underneath, because the first question a
   reader has is which one won. */
.dirs { display: grid; gap: clamp(2rem, 5vh, 3.25rem);
        margin: clamp(2rem, 5vh, 3rem) 0 0; }
.dir__h { display: flex; align-items: baseline; gap: .75rem; flex-wrap: wrap;
          padding-bottom: .75rem; border-bottom: 1px solid var(--line); }
.dir__h h3 { margin: 0; font-size: var(--t-h3); letter-spacing: var(--track-h); }
.dir__v { font-family: var(--font-mono); font-size: var(--t-meta);
          letter-spacing: var(--track-meta); text-transform: uppercase;
          padding: .15rem .45rem; border-radius: 4px; }
.dir__v[data-v="in"]  { color: #15803D; background: rgba(21,128,61,.10); }
.dir__v[data-v="out"] { color: #B45309; background: rgba(180,83,9,.10); }
.dir__why { margin: .85rem 0 0; font-size: var(--t-sm); line-height: 1.6;
            color: var(--fg-2); max-width: 70ch; }
.dir__why b { color: var(--fg-1); font-weight: 500; }
.dir__set { display: grid; grid-template-columns: repeat(3, minmax(0,1fr));
            gap: clamp(.75rem, 2vw, 1.25rem); margin-top: clamp(1.25rem,3vh,1.75rem); }
.dir__set img { width: 100%; display: block; border-radius: 14px;
                box-shadow: 0 22px 44px rgba(17,17,17,.22), 0 2px 6px rgba(17,17,17,.10); }
@media (max-width: 700px) { .dir__set { grid-template-columns: repeat(2, minmax(0,1fr)); }
  .dir__set figure:last-child { display: none; } }

/* ---- yellow's job list ----
   The argument for the redesign is not that there is too much yellow, it is
   that yellow means five things at once. Counted, with the surface named, so a
   reader can check every line against the screenshot beside it. */
.jobs { display: grid; gap: 1px; background: var(--line);
        border: 1px solid var(--line); border-radius: var(--radius-card);
        overflow: hidden; margin: clamp(1.5rem,4vh,2.25rem) 0 0; }
.jobs__r { display: grid; grid-template-columns: minmax(0,1fr) minmax(0,1.3fr);
           gap: clamp(.75rem,2vw,1.25rem); background: var(--bg-raised);
           padding: clamp(.7rem,1.8vw,.9rem) clamp(1rem,2.4vw,1.4rem); align-items: baseline; }
.jobs__k { font-family: var(--font-mono); font-size: var(--t-meta);
           letter-spacing: var(--track-meta); text-transform: uppercase; color: var(--fg-3); }
.jobs__v { font-size: var(--t-sm); line-height: 1.5; color: var(--fg-1); }

/* ---- evidence from the shipped app ----
   A portrait phone capture rendered at the text column is nearly two thousand
   pixels tall, which buys a reader nothing: the screen is legible long before
   that and the caption is the part that has to be read. So the shot runs small
   and the reasoning sits beside it, which also makes before-and-after obvious
   without a label saying so. */
.evid { display: grid; grid-template-columns: minmax(0, 13.5rem) minmax(0, 1fr);
        gap: clamp(1.5rem, 4vw, 2.75rem); align-items: start;
        margin: clamp(2rem, 5vh, 3rem) 0 0; }
.evid img { width: 100%; display: block; border-radius: 14px;
            box-shadow: 0 22px 44px rgba(17,17,17,.22), 0 2px 6px rgba(17,17,17,.10); }
.evid figcaption { margin: 0; font-size: var(--t-sm); line-height: 1.6; color: var(--fg-2); }
.evid figcaption b { color: var(--fg-1); font-weight: 500; }
@media (max-width: 700px) { .evid { grid-template-columns: minmax(0,1fr); }
  .evid img { max-width: 15rem; } }

/* The five jobs are the caption for the screenshot beside them, so they sit in
   the same row: a reader checks each line against the image without scrolling
   between the two. */
.evid--jobs { grid-template-columns: minmax(0, 17rem) minmax(0, 1fr);
              align-items: center; }
.evid--jobs .jobs { margin: 0; }
.evid--jobs .jobs__r { padding-block: clamp(.95rem, 2.4vw, 1.3rem); }
@media (max-width: 700px) { .evid--jobs { grid-template-columns: minmax(0,1fr); } }

/* ---- a flow, whole ----
   A sideways rail hides four screens in seven and asks the reader to drag for
   the rest. The claim being made is that the flow is complete, so the flow has
   to be visible: every step at once, nothing cut at an edge, no interaction
   required to see the end of it. The grid leaves the text column and runs to
   the wide measure, which buys back most of what the smaller frame costs. */
.fgrid {
  /* --flow-max is the one knob for how large every phone screen on the page
     runs. Screens at the full wide measure crowd the reader: a flow is read as
     a sequence first and inspected second, and at four across the sequence is
     already legible. Pulling the measure in also lands the first screen close
     to the text column it is explaining. */
  --flow-max: 58rem;
  margin: clamp(2rem, 5vh, 3rem) calc(50% - 50vw) 0;
  width: 100vw;
  padding-inline: max(var(--gutter), calc(50vw - var(--flow-max) / 2));
  display: grid; gap: clamp(1rem, 2.2vw, 1.75rem);
  grid-template-columns: repeat(4, minmax(0, 1fr));
}
.fgrid figure { margin: 0; }   /* a bare <figure> keeps the UA's 40px side
                                  margins, which eats half the track at 390 */
.fgrid .step { margin: 0; display: grid; gap: .7rem; align-content: start; }
.fgrid .step img { width: 100%; display: block; border-radius: 16px; border: 0;
  box-shadow: 0 22px 44px rgba(17,17,17,.22), 0 2px 6px rgba(17,17,17,.10); }
.fgrid .step__h { display: block; }
.fgrid .step__i { font-family: var(--font-mono); font-size: var(--t-meta);
  letter-spacing: var(--track-meta); color: var(--fg-3); margin-right: .5rem; }
.fgrid .step__flag { display: inline-block; font-family: var(--font-mono);
  font-size: var(--t-meta); letter-spacing: var(--track-meta); text-transform: uppercase;
  color: #B45309; background: rgba(180,83,9,.10); padding: .1rem .35rem;
  border-radius: 4px; margin-right: .5rem; }
.fgrid .step__t { font-size: var(--t-sm); line-height: 1.55; color: var(--fg-2); }
/* Captions run to different lengths, and a caption above its screen pushes that
   screen down by however long it is. Reserving the height of the longest one in
   the row puts every screen on the same line, which is the whole reason to show
   a flow as a grid rather than a stack. */
/* Reserving a guessed height for the caption only works until a caption wraps
   one line further, which it does as soon as the column narrows. Subgrid makes
   the captions in a row share a real row height, so the screens below them line
   up exactly whatever the copy does. The min-height stays as the fallback for
   engines without subgrid: approximate alignment beats none. */
.fgrid .step__h { min-height: 5.6em; }
@supports (grid-template-rows: subgrid) {
  .fgrid .step { grid-row: span 2; grid-template-rows: subgrid; }
  .fgrid .step__h { min-height: 0; }
}
@media (max-width: 720px) { .fgrid .step__h { min-height: 0; } }
.fgrid .step__t em { color: var(--fg-1); font-style: normal; }
@media (max-width: 1000px) { .fgrid { grid-template-columns: repeat(3, minmax(0,1fr)); } }
@media (max-width: 720px)  { .fgrid { grid-template-columns: repeat(2, minmax(0,1fr)); } }

/* Two screens rather than three: the same set, centred, so a pair does not
   stretch to fill a row built for a scroll. */
.dir__set--two { grid-template-columns: repeat(2, minmax(0, 1fr));
                 max-width: 62%; margin-inline: auto; }
@media (max-width: 900px) { .dir__set--two { max-width: none; } }

/* The profile is the largest claim the case makes, so it runs at the largest
   size the page has: three screens across the wide measure rather than four
   across the text column. A hero that is smaller than the flow supporting it
   reads as the less important of the two, which is backwards. */
.fgrid--3 { grid-template-columns: repeat(3, minmax(0, 1fr)); }
.fgrid--2 { grid-template-columns: repeat(2, minmax(0, 1fr));
            max-width: 52%; margin-inline: auto; padding-inline: 0; width: auto; }
@media (max-width: 1000px) { .fgrid--3 { grid-template-columns: repeat(2, minmax(0,1fr)); } }
@media (max-width: 900px)  { .fgrid--2 { max-width: none; } }

/* ---- an interaction, running ----
   Some things only exist in motion. A reel that reads as one gesture on a
   still image is two axes and a state change when you watch it, so this one
   plays: muted, looping, no controls, sized like the phone screens around it
   so it sits in the same family rather than announcing itself as a video.
   A poster covers the case where autoplay is refused, and the caption carries
   the argument so nothing is lost if the video never runs at all. */
.reel { margin: clamp(2rem, 5vh, 3rem) 0 0;
        display: grid; grid-template-columns: minmax(0, 13.2rem) minmax(0, 1fr);
        gap: clamp(1.5rem, 4vw, 3rem); align-items: center; }
.reel video { width: 100%; display: block; border-radius: 20px; background: #000;
              box-shadow: 0 28px 56px rgba(17,17,17,.26), 0 2px 6px rgba(17,17,17,.12); }
.reel__notes { display: grid; gap: 1.15rem; }
.reel__ax { display: grid; grid-template-columns: 5.5rem minmax(0,1fr); gap: 1rem;
            align-items: baseline; padding-bottom: 1rem; border-bottom: 1px solid var(--line); }
.reel__ax:last-child { border-bottom: 0; padding-bottom: 0; }
.reel__k { font-family: var(--font-mono); font-size: var(--t-meta);
           letter-spacing: var(--track-meta); text-transform: uppercase; color: var(--fg-3); }
.reel__v { font-size: var(--t-sm); line-height: 1.55; color: var(--fg-2); }
.reel__v b { color: var(--fg-1); font-weight: 500; }
@media (max-width: 820px) { .reel { grid-template-columns: minmax(0,1fr); }
  .reel video { max-width: 13.2rem; margin-inline: auto; } }

/* ---- contained images ----
   Set on <body> per page. A 1440 capture at 78rem renders the app at nearly
   1:1, which invites the reader to audit pixels instead of reading the
   argument, and it costs two thousand pixels of page. Everything drops to the
   text column. Status stops being carried by width, which is fine: the verdict
   pills already say it, and the inset ground still separates what was dropped.
   Checked at 864px: the download modal, the densest thing on the page, still
   reads. */
.case--contained .open,
.case--contained .shot--wide {
  grid-template-columns:
    [full-start] 1fr
    [wide-start] min(var(--page-max), calc(100% - 2 * var(--gutter)))
    [wide-end] 1fr [full-end];
}

/* A before-and-after of two phone screens is two portrait captures side by
   side: at half the text column each they run a thousand pixels tall and the
   comparison stops fitting on one screen, which is the only thing it is for. */
.ba--phone .ba__pair { max-width: 62%; margin-inline: auto; }
@media (max-width: 760px) { .ba--phone .ba__pair { max-width: none; } }

/* ---- the bezel ----
   The captures are screen content with no device chrome, which keeps them
   consistent whatever tool they came from. The frame is drawn here instead, in
   one place: a dark bezel rather than none, because a bare rectangle of UI
   floating on paper reads as a crop, and a dark one rather than a light one
   because every screen in this app is black and a pale frame draws a bright
   line around the very thing it is meant to present. */
.fgrid .step img,
.dir__set img,
.ba--phone .ba__f img,
.evid img,
.reel video,
.phones img {
  border: 5px solid #0B0B0D;
  border-radius: 22px;
  background: #0B0B0D;
  box-sizing: border-box;
}
.phones img { border-width: 4px; border-radius: 16px; }
.ba--phone .ba__f img { box-shadow: 0 22px 44px rgba(17,17,17,.22), 0 2px 6px rgba(17,17,17,.10); }

/* A single phone standing on its own — the before shot. Narrower than the
   pair, because one screen presented at the width of two reads as a claim
   rather than as evidence. */
.ba__solo { margin: clamp(1.5rem,4vh,2.25rem) auto 0; max-width: 30%; }
.ba__solo img { display: block; width: 100%;
                border: 5px solid #0B0B0D; border-radius: 22px; background: #0B0B0D;
                box-sizing: border-box;
                box-shadow: 0 22px 44px rgba(17,17,17,.22), 0 2px 6px rgba(17,17,17,.10); }
.ba__solo figcaption { margin-top: .7rem; font-family: var(--font-mono);
                       font-size: var(--t-meta); letter-spacing: var(--track-meta);
                       text-transform: uppercase; color: var(--fg-3); text-align: center; }
@media (max-width: 760px) { .ba__solo { max-width: 62%; } }

/* ---- calmer media ----
   booqd's captures are whole guest pages rather than app screens, so at the
   sizes the rest of the system uses they read as zoomed in: a 680px page
   printed at 1248px is bigger than anyone ever saw it. Everything here renders
   at about 80%, which is the zoom Antonio actually reads pages at. Scoped to a
   modifier so the Subly and SHOWBOAT cases keep their own scale. */
/* --wide-max is 62rem for every case now, so this modifier is only the extra
   step booqd needs: its column shots are whole guest pages, not app screens. */
/* The bar frames the page, not the media, so it keeps the full width. */
.case--calm .bar__in { max-width: 78rem; }
/* Only the shots inside the text column. The hero and the full-bleed blocks
   are already governed by --wide-max above, so scaling them again would
   shrink them twice and knock the hero off centre. */
.case--calm .shot:not(.shot--hero):not(.shot--wide) > img { max-width: 80%; }
.case--calm .rail { grid-auto-columns: min(72%, 37rem); }
@media (max-width: 860px) {
  /* On a phone the column is already the constraint; shrinking again just
     wastes the width. */
  .case--calm .shot:not(.shot--hero):not(.shot--wide) > img { max-width: 100%; }
}

/* ---- a fixed window for a long capture ----
   booqd's flows are whole guest pages, not app screens, so a rail of them takes
   the height of the longest one: flow three measured 1370px in a 900px window,
   which means no frame and its caption ever shared the screen, and the short
   frames sat beside 860px of nothing. Every frame gets the same window instead.
   Hovering pans the capture to its end over ~3s, so cropping the fold costs
   nothing: the rest is still reachable without leaving the row. */
.win {
  --win-h: clamp(26rem, 58vh, 39rem);
  position: relative; height: var(--win-h); overflow: hidden;
  border-radius: 14px; border: 1px solid var(--line);
  background: var(--bg-raised); box-shadow: var(--shadow-card);
}
.win img { display: block; width: 100%; border: 0; border-radius: 0; box-shadow: none;
           transition: transform 2.8s cubic-bezier(.4, 0, .2, 1); }
.win:hover img, .win:focus-within img { transform: translateY(calc(var(--win-h) - 100%)); }
/* Only the frames that actually run past the fold say so. */
.win[data-more]::after {
  content: ""; position: absolute; left: 0; right: 0; bottom: 0; height: 4.5rem;
  pointer-events: none; transition: opacity .35s;
  background: linear-gradient(to bottom, transparent, var(--bg-raised));
}
.rail .step:not(:first-child) .win::before { content: none; }
.win[data-more]::before {
  content: "Hover to see the rest"; position: absolute; z-index: 2;
  left: 50%; bottom: .9rem; transform: translateX(-50%);
  font-family: var(--font-mono); font-size: .625rem; letter-spacing: .1em;
  text-transform: uppercase; color: var(--fg-3);
  background: var(--bg-raised); border: 1px solid var(--line);
  border-radius: 999px; padding: .3rem .6rem; transition: opacity .35s;
}
.win:hover::after, .win:hover::before, .win:focus-within::before { opacity: 0; }
/* No pointer, or motion turned down: the window scrolls instead of panning. */
@media (hover: none), (prefers-reduced-motion: reduce) {
  .win { overflow-y: auto; overscroll-behavior-y: contain; }
  .win img, .win:hover img, .win:focus-within img { transition: none; transform: none; }
  .win[data-more]::before { content: "Scroll inside"; position: sticky; float: right;
                            top: calc(var(--win-h) - 2.4rem); right: .9rem; left: auto;
                            transform: none; }
}

/* ---- Subly Enterprise, one size down ----
   The only case whose captures are a desktop app in a browser: at 62rem the
   opening screen printed nearly a full viewport tall and the flow frames came
   past the reader wider than the app is ever actually used. Down a step, so a
   page of them stops crowding.

   The floor is legibility, not taste: the modals and menus inside these frames
   ARE the argument, and a step below ~50rem turns their body copy to grey.
   52rem was measured against 46rem, which lost it. Rails elsewhere (booqd)
   keep their own size — this is scoped to the page, not to `.rail`. */
.case--tight { --wide-max: 56rem; }
.case--tight .pin[data-on="1"] .rail { grid-auto-columns: min(62vw, 52rem); }


/* ===================================================================
   Phone adjustments, 2026-08-24. All three come from reading the live
   site on a real handset rather than from a headless viewport.
   =================================================================== */

@media (max-width: 620px) {

  /* The section rhythm was tuned on a desktop: 81px top and bottom means
     162px of blank paper between every two sections, which on a 390 screen
     is a fifth of a viewport spent on nothing, twice per section. */
  .sec { padding-block: clamp(2.25rem, 5.5vh, 3.25rem); }

  /* The three days were a line of marks read left to right, and its own
     rationale was that this beats reading them as a sentence. Stacked into
     one column that rationale is gone and it becomes three labelled
     paragraphs 315px tall, with a meaningless gradient of rule weights.
     Deleting it would orphan the paragraph after it, which opens "the reason
     was the same every time" and needs the three beats to refer to. So it
     becomes what it always was underneath: a tight list. */
  .tl { gap: 0; }
  .tl__i {
    grid-template-columns: 5.5rem minmax(0, 1fr);
    align-items: baseline;
    gap: .75rem;
    padding-block: .7rem;
    border-top-width: 1px;
    border-top-color: var(--line);
  }
  .tl__i:first-child { border-top-color: var(--fg-1); border-top-width: 2px; }
  .tl__i:nth-child(n+2) { border-top-color: var(--line); }

  /* A description block is 16% of the track. On a 350px track that is 56px,
     and no type size fits 33 characters into 56px — the words were being
     clipped mid-syllable. The picture is about proportion, not about the
     jaguar, and each row's note already carries the sentence in full, so the
     words come out of the narrow blocks and the shapes make the argument.
     They stay in the DOM, so a screen reader still gets them.
     The failing block keeps its text: it is the one moment where the words
     overrunning their gap IS the point, and it is wide enough to hold them. */
  .gapd__speech,
  .gapd__desc:not([data-fail]) { color: transparent; }
  .gapd__desc[data-fail] { height: auto; min-height: 2.25rem; padding-block: .3rem; }
}
