@font-face { font-family:"Display"; src:url("../assets/fonts/Sora-700.woff2") format("woff2"); font-weight:700; font-display:swap; }
@font-face { font-family:"Display"; src:url("../assets/fonts/Sora-500.woff2") format("woff2"); font-weight:500; font-display:swap; }
@font-face { font-family:"Display"; src:url("../assets/fonts/Sora-400.woff2") format("woff2"); font-weight:400; font-display:swap; }
@font-face { font-family:"Serif"; src:url("../assets/fonts/Fraunces-400.woff2") format("woff2"); font-weight:400; font-style:normal; font-display:swap; }
@font-face { font-family:"Serif"; src:url("../assets/fonts/Fraunces-400-italic.woff2") format("woff2"); font-weight:400; font-style:italic; font-display:swap; }

:root {
  --font-display:"Display", system-ui, sans-serif;
  --font-body:system-ui, -apple-system, "Helvetica Neue", Arial, sans-serif;
  /* dark (primary) */
  --ground:#1a1a1a; --ink:#f1ebdd; --ink-body:#d9d4c7; --dim:#8f8b80;
  --accent:#ff5a1f; --rule:#f1ebdd1f; --raise:#242424;
  /* Ember drawn DIRECTLY on the ground — text, link underlines, focus rings.
     Split from --accent because #ff5a1f is only 2.57:1 on the light ground and
     fails WCAG 1.4.3 at every size there; see the light block for the override.
     --accent stays the raw brand ember for fills, washes, and anything sitting
     on the lightbox's dark scrim, where it is not read as text. */
  --accent-ink:#ff5a1f;
  /* type scale (role tokens — no raw sizes at call sites) */
  --display-xl:clamp(52px,9vw,132px); --display-l:clamp(34px,5vw,64px);
  --title:clamp(22px,2.4vw,30px); --lead:clamp(18px,1.7vw,22px);
  --body:17px; --meta:13px;
  --gutter:40px;
  --bone:#f1ebdd; /* fixed bone tint, same in both themes */
  /* Ink for the hero client strip. Neutral ash keeps the logos subordinate so the
     headline's ember "most-used" stays the focal point, and it's the one value that
     clears WCAG 1.4.11's 3:1 on BOTH grounds (4.03:1 dark, 3.56:1 light) — hence no
     per-theme override. Full ember failed light at 2.57:1. */
  --client-ink:#7a7a73;
  --font-serif:"Serif", Georgia, "Times New Roman", serif;
}
:root[data-theme="light"] {
  /* --dim is #696963, not #6a6a64: on this ground #6a6a64 lands at 4.49:1 and
     WCAG 1.4.3 wants 4.5 — one step down clears it at 4.55:1, invisibly. It
     carries the work-history roles and years, card meta, and the footer. */
  --ground:#ece9e1; --ink:#1a1a18; --ink-body:#33322c; --dim:#696963;
  --accent:#ff5a1f; --rule:#1a1a1820; --raise:#f5f2ea;
  /* Ember banked down for this ground: #ff5a1f reads 2.57:1 here, #b83c0c reads
     4.69:1. Chosen over the softer #af441d, which clears contrast too but goes
     rust — and rust on a warm off-white is terracotta-on-cream. Saturation is
     what keeps this reading as ember rather than as a different colour. */
  --accent-ink:#b83c0c;
}
* { box-sizing:border-box; -webkit-font-smoothing:antialiased; }
html,body { margin:0; background:var(--ground); color:var(--ink-body); font-family:var(--font-body); font-size:var(--body); line-height:1.6; overflow-x:clip; transition:background-color .4s ease,color .4s ease; }
h1,h2,h3 { font-family:var(--font-display); color:var(--ink); letter-spacing:-.03em; line-height:.95; text-wrap:balance; }
a { color:inherit; text-decoration:none; }
::selection { background:var(--accent); color:var(--ground); }

/* ---------- accessibility ---------- */
/* One focus ring for the whole site, matching the one the lightbox already had.
   --accent-ink rather than --accent so it clears 1.4.11's 3:1 against the light
   ground too, where raw ember is only 2.57:1. The lightbox's own controls keep
   --accent via a more specific rule — they sit on a scrim that is dark in both
   themes. */
:focus-visible { outline:2px solid var(--accent-ink); outline-offset:3px; }

/* Skip link — off-canvas until focused, then a chip in the top-left.
   Ground-coloured text on an --accent-ink chip is the only ember pairing that
   passes 4.5:1 in BOTH themes (5.58 dark / 4.69 light). The obvious choice,
   white on ember, is only 3.12:1 and fails as text. */
.skip { position:fixed; top:12px; left:12px; z-index:100;
  background:var(--accent-ink); color:var(--ground);
  font-family:var(--font-display); font-weight:500; font-size:var(--meta);
  padding:12px 18px; border-radius:8px; white-space:nowrap;
  transform:translateY(-180%); transition:transform .18s ease; }
.skip:focus { transform:none; }
@media (prefers-reduced-motion: reduce) { .skip { transition:none; } }

/* Hit targets. Nav links render ~31x21 and footer links ~47x15 — short of the
   24x24 floor in WCAG 2.2 (2.5.8). Widths already clear 24, so only height is
   the problem: an invisible pseudo-element grows the tappable area without
   moving anything on screen. No horizontal growth, so adjacent links never
   overlap. The theme toggle already measures 28x26 and is left alone. */
.site-nav a, .site-foot a, .contact-links a { position:relative; }
.site-nav a::after, .site-foot a::after, .contact-links a::after {
  content:""; position:absolute; left:0; right:0; top:50%;
  transform:translateY(-50%); height:26px; }

/* pull-quote voice — shares the italic serif with the hero intro */
blockquote { font-family:var(--font-serif); font-style:italic; letter-spacing:-0.012em;
  font-size:clamp(22px,2.2vw,30px); line-height:1.42; color:var(--ink);
  margin:1.6em 0; padding-left:24px; border-left:2px solid var(--accent); border-radius:0; }

/* header */
.site-head { position:sticky; top:0; z-index:20; padding:26px 0; transition:background-color .3s ease; }
.head-inner { max-width:1200px; margin:0 auto; padding:0 var(--gutter); display:flex; justify-content:space-between; align-items:center; }
.site-head.scrolled { background:color-mix(in oklab, var(--ground) 88%, transparent); backdrop-filter:blur(8px); }
.wordmark { font-family:var(--font-display); font-weight:500; color:var(--accent-ink); }
.site-nav { display:flex; gap:24px; align-items:center; font-size:var(--meta); }
.theme-btn { background:none; border:0; color:inherit; font-size:16px; cursor:pointer; }

/* hero */
/* section gaps are vh so they scale together — see .work/.about/.contact */
.hero { max-width:1200px; margin:0 auto; padding:28px var(--gutter) 5vh;
  display:grid; grid-template-columns:auto minmax(0,1fr); column-gap:48px; align-items:start; }
/* Portrait tint = the multiply backdrop. Mixing white into --bone weakens the
   tint without washing the photo out; 100% = full bone, 0% = untinted B&W. */
/* No border-radius: rounded corners read as an app card, and this is an editorial
   page. The real problem was never the corners though — the photo carries a lit
   studio backdrop, so on the dark ground it sat as a bright slab. The mask below
   dissolves the frame so he emerges from the dark instead, and the grain takes
   the digital flatness off the backdrop. Neither touches the tint (073e27f).
   Mask geometry is measured, not guessed: the hair starts 12.2% down the source,
   so the solid core (out to 40%, centred at 36%) clears it with margin. */
.hero-portrait { width:clamp(180px,19vw,260px); aspect-ratio:4 / 5; border-radius:0; overflow:hidden;
  align-self:start; background:color-mix(in srgb, var(--bone) 50%, #fff); isolation:isolate;
  position:relative;
  -webkit-mask-image:radial-gradient(ellipse 76% 74% at 50% 36%, #000 40%, transparent 86%);
          mask-image:radial-gradient(ellipse 76% 74% at 50% 36%, #000 40%, transparent 86%); }
.hero-portrait img { width:100%; height:100%; object-fit:cover; object-position:center; display:block; mix-blend-mode:multiply; }
/* Inlined fractal noise — no image asset, and it tiles, so it costs nothing. */
.hero-portrait::after { content:""; position:absolute; inset:0; pointer-events:none;
  opacity:.5; mix-blend-mode:overlay; background-repeat:repeat;
  background-image:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='140' height='140'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.85' numOctaves='3' stitchTiles='stitch'/%3E%3C/filter%3E%3Crect width='140' height='140' filter='url(%23n)'/%3E%3C/svg%3E"); }
/* The dissolve is DARK-ONLY, and deliberately so. The bright-slab problem it
   solves is a dark-room problem: on the cream ground a light studio backdrop is
   already at home, so there is nothing to dissolve — and masking it there fades
   the dark shirt into the paper, which reads as evaporating rather than
   emerging. Light keeps the squared frame and the grain; those are the
   constants. Consistent with light being a considered ground of its own rather
   than an inverted dark. */
:root[data-theme="light"] .hero-portrait { -webkit-mask-image:none; mask-image:none; }
.hero-title { align-self:start; font-size:clamp(34px,5.2vw,76px); font-weight:700; line-height:1.03; letter-spacing:-.03em; margin:0; }
.hero-title .hero-soft { color:var(--ink-body); font-weight:400; }
mark.hl { background:none; color:var(--accent-ink); }
.hero-foot { grid-column:1 / -1; margin-top:48px; }
.hero-say { font-family:var(--font-serif); font-style:italic; font-size:clamp(19px,1.7vw,23px); line-height:1.55; letter-spacing:-0.012em; color:var(--ink-body); margin:0; }
.hero-clients { grid-column:1 / -1; margin-top:40px; }
/* Client strip is a mask: the PNG's alpha is the shape, background-color is the
   ink. aspect-ratio must track the asset (1423x179) or it letterboxes.
   Banked ember — the accent's family at lower volume, so the strip stays warm
   without out-shouting the hero headline's ember. */
.hero-clients-img { display:block; width:100%; aspect-ratio:1423 / 179;
  background-color:var(--client-ink);
  -webkit-mask:url(../assets/jc-companies.png) left center / contain no-repeat;
  mask:url(../assets/jc-companies.png) left center / contain no-repeat; }

/* work index — cover grid */
.work { max-width:1200px; margin:0 auto; padding:5vh var(--gutter) 8vh; }
.section-title { font-size:var(--display-l); font-weight:700; margin:0 0 40px; padding-bottom:22px; border-bottom:1px solid var(--rule); }
.wk-grid { display:grid; grid-template-columns:repeat(3,1fr); gap:44px 30px; }
.wk { display:flex; flex-direction:column; gap:16px; text-decoration:none; color:var(--ink); }
.wk-img { position:relative; aspect-ratio:4/3; overflow:hidden; border-radius:10px; background:var(--raise); }
.wk-img img { width:100%; height:100%; object-fit:cover; display:block; transition:transform .7s cubic-bezier(.16,.8,.24,1); }
.wk:hover .wk-img img { transform:scale(1.05); }
.wk-go { position:absolute; top:14px; right:14px; width:34px; height:34px; display:flex; align-items:center; justify-content:center;
         border-radius:50%; background:var(--accent); color:#fff; font-size:16px; opacity:0; transform:translateY(-4px);
         transition:opacity .35s ease, transform .35s ease; }
.wk:hover .wk-go { opacity:1; transform:translateY(0); }
.wk-cap { display:flex; flex-direction:column; gap:5px; }
.wk-title { font-size:var(--title); font-weight:500; margin:0; transition:color .3s ease; }
.wk:hover .wk-title { color:var(--accent-ink); }
.wk-sub { font-size:var(--meta); color:var(--dim); font-family:var(--font-body); }

/* about */
.about { max-width:1200px; margin:0 auto; padding:5vh var(--gutter); }
.about-copy { columns:2; column-gap:56px; }
.about-copy p { font-size:clamp(17px,1.45vw,20px); line-height:1.65; color:var(--ink-body); margin:0 0 1.15em; break-inside:avoid; }
.about-copy p:first-child { color:var(--ink); }
.about-copy p:last-child { margin-bottom:0; color:var(--dim); }
.about-aside { font-family:var(--font-serif); font-style:italic; line-height:1.5; letter-spacing:-.01em; }

/* work history — sits at the foot of About, no header of its own. Company is a
   spanning label; its roles stack beside it, dates on one right edge. Ember
   marks what's still true. Column is wide enough for "Conn Creative Media". */
.wh { list-style:none; margin:64px 0 0; padding:0; }
/* rules divide companies from each other — none at the outer edges */
.wh-group { display:grid; grid-template-columns:minmax(0,300px) minmax(0,1fr); gap:32px;
  align-items:start; padding:22px 0; border-bottom:1px solid var(--rule); }
.wh-group:last-child { border-bottom:0; }
.wh-co { font-family:var(--font-display); font-weight:500; font-size:clamp(19px,2.1vw,26px);
  letter-spacing:-.02em; line-height:1.25; margin:0; color:var(--ink); }
.wh-roles { list-style:none; margin:0; padding:0; }
.wh-role-row { display:flex; justify-content:space-between; align-items:baseline; gap:24px; padding:3px 0; }
.wh-role { font-size:15px; color:var(--dim); margin:0; }
.wh-years { flex:none; font-size:15px; color:var(--dim); font-variant-numeric:tabular-nums; white-space:nowrap; }
.wh-years em { font-style:normal; color:var(--accent-ink); }

/* contact */
.contact { max-width:1200px; margin:0 auto; padding:5vh var(--gutter) 8vh; }
.contact-lead { font-size:var(--lead); color:var(--ink-body); margin:0 0 8px; }
.contact-mail { font-family:var(--font-display); font-weight:500; font-size:clamp(24px,3vw,42px); letter-spacing:-.02em; display:inline-block; background-image:linear-gradient(var(--accent-ink),var(--accent-ink)); background-size:0% 2px; background-repeat:no-repeat; background-position:left bottom; transition:background-size .35s ease; }
.contact-mail:hover { background-size:100% 2px; }
.contact-links { color:var(--dim); font-size:var(--meta); margin-top:28px; }
.contact-links a:hover { color:var(--ink); }

/* footer */
.site-foot { padding:30px 0; color:var(--dim); font-size:var(--meta); border-top:1px solid var(--rule); }
.foot-inner { max-width:1200px; margin:0 auto; padding:0 var(--gutter); display:flex; justify-content:space-between; }
.site-foot a:hover { color:var(--ink); }

/* ---------- cinematic reveals ---------- */
.js .reveal { opacity:0; transform:translateY(40px) scale(.98); }
.reveal { transition:opacity .95s cubic-bezier(.2,.7,.15,1), transform .95s cubic-bezier(.2,.7,.15,1); }
.js .reveal.in { opacity:1; transform:none; }
.hero .reveal { transition-duration:1.15s; }

/* hero headline — masked line-by-line wipe */
.js .hero-title { opacity:0; }
.hero-title.lines-in { opacity:1; }
.hero-title .word { display:inline-block; overflow:hidden; vertical-align:top; padding-bottom:.16em; margin-bottom:-.16em; }
.js .hero-title .w-in { display:inline-block; transform:translateY(115%); transition:transform 1.05s cubic-bezier(.2,.7,.15,1); }
.hero-title.lines-in .w-in { transform:none; }
.reveal-all .hero-title { opacity:1 !important; }
.reveal-all .hero-title .w-in { transform:none !important; }
.reveal-all .reveal { opacity:1 !important; transform:none !important; transition:none !important; }
@media (prefers-reduced-motion: reduce) {
  .reveal, .js .reveal, .js .hero-title, .js .hero-title .w-in { opacity:1 !important; transform:none !important; transition:none !important; }
}

/* ---------- case study ---------- */
/* hero sits inside the 1200 grid; 2:1 window trims ~5% top/bottom of the 16:9
   source, and the image is overscanned (top:-6%/height:112%) so the parallax has
   ~±5% of safe travel without ever revealing a gap. */
.case-hero { max-width:1200px; margin:4px auto 44px; padding:0 var(--gutter); }
.case-hero-frame { position:relative; aspect-ratio:2 / 1; overflow:hidden; border-radius:12px; background:var(--raise); }
.case-hero-img { position:absolute; left:0; right:0; top:-6%; height:112%; will-change:transform; }
.case-hero-img img { width:100%; height:100%; object-fit:cover; display:block; }
/* hero entrance = opacity + settle (no translateY slide for a full-bleed block) */
.js .case-hero.reveal { opacity:0; transform:scale(1.03); }
.case-hero.reveal { transition:opacity 1.15s cubic-bezier(.2,.7,.15,1), transform 1.15s cubic-bezier(.2,.7,.15,1); transform-origin:center; }
.js .case-hero.reveal.in { opacity:1; transform:none; }

.case-wrap { max-width:1200px; margin:0 auto; padding:0 var(--gutter); }
.case-back { display:inline-block; color:var(--dim); font-size:var(--meta); margin:34px 0 22px; transition:color .3s ease; }
.case-back:hover { color:var(--accent-ink); }
.case-title { font-size:clamp(32px,5vw,66px); font-weight:700; line-height:1.03; letter-spacing:-.03em; margin:0 0 22px; }
.case-title .case-dash { color:var(--dim); font-weight:400; }
.case-tagline { font-family:var(--font-serif); font-style:italic; font-size:clamp(20px,2vw,27px); line-height:1.45; letter-spacing:-.012em; color:var(--ink-body); margin:0 0 40px; max-width:none; }
/* Optional link to the product's own site (content.js `site`). Only the two CCM
   apps carry one — they're the only cases where a reader can go use the thing,
   which is the evidence behind "products I've shipped entirely on my own", so it
   sits above the hero rather than filed in the rail. Reuses .contact-mail's
   big-link pattern rather than adding a button: the site has no button
   component, and pill chips are deliberately absent everywhere. */
.case-site { display:inline-block; margin:0 0 44px; font-family:var(--font-display);
  font-weight:500; font-size:clamp(22px,2vw,28px); letter-spacing:-.02em; color:var(--accent-ink);
  background-image:linear-gradient(var(--accent-ink),var(--accent-ink));
  background-size:0% 2px; background-repeat:no-repeat; background-position:left bottom;
  transition:background-size .35s ease; }
.case-site:hover { background-size:100% 2px; }
/* Tighten the tagline's 40px so the link groups with the intro. Without this it
   lands ~40px from the tagline and ~44px from the hero — floating between the
   two, belonging to neither. */
.case-tagline:has(+ .case-site) { margin-bottom:26px; }

.case-meta { display:flex; flex-wrap:wrap; gap:22px 46px; padding:26px 0; border-top:1px solid var(--rule); border-bottom:1px solid var(--rule); }
.mcol { display:flex; flex-direction:column; gap:6px; min-width:0; }
.mlab { font-size:11px; letter-spacing:.02em; color:var(--dim); }
.mval { font-family:var(--font-display); font-weight:500; font-size:15px; color:var(--ink); line-height:1.3; }

/* body (left) + right rail (meta, then press) sharing one vertical hairline.
   body spans all rows so meta+press hug the top of the right column while the
   3rd (1fr) row absorbs the body's extra height. */
.case-main { display:grid; grid-template-columns:minmax(0,68ch) minmax(0,1fr);
  grid-template-rows:auto auto 1fr; column-gap:56px; row-gap:0; align-items:start; }
.case-body { grid-column:1; grid-row:1 / 4; max-width:none; margin:0; }
.case-rail { grid-column:2; grid-row:1; border-left:1px solid var(--rule); padding-left:32px; }
.case-rail .case-meta { flex-direction:column; flex-wrap:nowrap; gap:26px; padding:0; border-top:0; border-bottom:0; }
.case-main .case-press { grid-column:2; grid-row:2; margin:0;
  border-left:1px solid var(--rule); padding-left:32px; padding-top:60px; }
.case-main .case-press .press-grid { grid-template-columns:1fr; }
.case-body p { font-size:clamp(17px,1.35vw,19px); line-height:1.7; color:var(--ink-body); margin:0 0 1.3em; }
.case-body strong { color:var(--ink); font-weight:700; }
.hl-mark { background:color-mix(in oklab, var(--accent) 20%, transparent); color:var(--ink);
  padding:.06em .14em; border-radius:2px; -webkit-box-decoration-break:clone; box-decoration-break:clone; }

.case-press { margin:74px 0 0; }
.case-h { font-size:16px; font-weight:600; color:var(--ink); margin:0 0 22px; padding-bottom:14px; border-bottom:1px solid var(--rule); }
.press-grid { display:grid; grid-template-columns:repeat(3,1fr); gap:18px 26px; }
.press-card { display:flex; flex-direction:row; align-items:flex-start; gap:13px; color:var(--ink-body); }
.press-thumb { flex:0 0 64px; width:64px; aspect-ratio:1/1; overflow:hidden; border-radius:6px; background:var(--raise); }
.press-thumb img { width:100%; height:100%; object-fit:cover; display:block; transition:transform .7s cubic-bezier(.16,.8,.24,1); }
.press-card:hover .press-thumb img { transform:scale(1.05); }
.press-body { display:flex; flex-direction:column; min-width:0; }
.press-src { font-size:11px; color:var(--dim); }
.press-head { font-size:13px; line-height:1.34; color:var(--ink); margin-top:2px; transition:color .3s ease; }
.press-card:hover .press-head { color:var(--accent-ink); }

/* gallery — full-width showcase; wide items span the row, others 2-up */
.case-gallery { margin:80px 0 0; }
.case-gallery .gal img[data-lb] { cursor:zoom-in; }

/* lightbox — dark scrim (theme-independent); controls in literal light tones */
.lightbox { position:fixed; inset:0; z-index:1000; display:none; align-items:center; justify-content:center;
  padding:40px; background:rgba(9,8,7,.93); opacity:0; transition:opacity .25s ease; }
.lightbox.open { display:flex; opacity:1; }
.lb-fig { margin:0; max-width:min(1400px,92vw); display:flex; flex-direction:column; gap:14px; }
.lb-img { max-width:100%; max-height:82vh; object-fit:contain; display:block; margin:0 auto; border-radius:8px;
  transform:scale(.985); transition:transform .25s ease; }
.lightbox.open .lb-img { transform:none; }
.lb-cap { text-align:center; font-size:var(--meta); line-height:1.5; color:rgba(241,235,221,.72); max-width:74ch; margin:0 auto; }
.lb-cap[hidden] { display:none; }
.lb-close, .lb-prev, .lb-next { position:fixed; background:none; border:0; color:#F1EBDD; cursor:pointer;
  opacity:.72; transition:opacity .2s ease; line-height:1; }
.lb-close:hover, .lb-prev:hover, .lb-next:hover { opacity:1; }
.lb-close:focus-visible, .lb-prev:focus-visible, .lb-next:focus-visible { opacity:1; outline:2px solid var(--accent); outline-offset:4px; border-radius:4px; }
.lb-close { top:22px; right:26px; font-size:36px; }
.lb-prev, .lb-next { top:50%; transform:translateY(-50%); font-size:56px; padding:8px 18px; }
.lb-prev { left:10px; } .lb-next { right:10px; }
.lb-prev[hidden], .lb-next[hidden] { display:none; }
@media (prefers-reduced-motion: reduce) { .lightbox, .lb-img { transition:none; } }
.gallery-grid { display:grid; grid-template-columns:repeat(2,1fr); gap:20px; align-items:start; }
.gal { margin:0; }
.gal-wide { grid-column:1 / -1; }
.gal img { width:100%; height:auto; display:block; border-radius:10px; }
.gal figcaption { margin-top:10px; font-size:var(--meta); color:var(--dim); }

.case-nav { display:flex; justify-content:space-between; gap:24px; margin:82px 0 100px; padding-top:30px; border-top:1px solid var(--rule); }
.cn-link { display:flex; flex-direction:column; gap:7px; color:var(--ink); max-width:46%; }
.cn-link.next { text-align:right; align-items:flex-end; }
.cn-lab { font-size:var(--meta); color:var(--dim); }
.cn-title { font-family:var(--font-display); font-weight:500; font-size:clamp(18px,2vw,24px); letter-spacing:-.02em;
  background-image:linear-gradient(var(--accent-ink),var(--accent-ink)); background-size:0% 2px; background-repeat:no-repeat;
  background-position:left bottom; transition:background-size .35s ease; }
.cn-link.next .cn-title { background-position:right bottom; }
.cn-link:hover .cn-title { background-size:100% 2px; }

.case-missing { max-width:820px; margin:0 auto; padding:16vh var(--gutter); }
.case-missing .case-title { margin-bottom:14px; }
.case-missing a { color:var(--accent-ink); }

/* ---------- responsive ---------- */
@media (max-width:900px) {
  .wk-grid { grid-template-columns:repeat(2,1fr); }
  /* No room for a rail, so the stack becomes meta → body → gallery → press.
     The press lives inside .case-main while the gallery is .case-main's
     SIBLING, so `order` can't reach across them — the two aren't in the same
     formatting context. display:contents dissolves .case-main and promotes its
     children into .case-wrap's flex flow, where the whole run can be ordered
     together. Safe because .case-main paints nothing and no JS queries it.
     Dissolving it also drops its row-gap, so the body carries that space now. */
  .case-wrap:not(.case-intro) { display:flex; flex-direction:column; }
  .case-main { display:contents; }
  .case-rail { order:1; border-left:0; padding-left:0; }
  .case-body { order:2; margin-top:40px; }
  .case-gallery { order:3; }
  .case-main .case-press { order:4; margin-top:74px; border-left:0; padding-left:0; padding-top:0; }
  .case-nav { order:5; }
  .case-rail .case-meta { flex-direction:row; flex-wrap:wrap; gap:22px 46px; padding:0 0 24px; border-top:0; border-bottom:1px solid var(--rule); }
  .case-main .case-press .press-grid { grid-template-columns:repeat(2,1fr); }
  /* company label can't hold its own column — stack it above its roles */
  .wh-group { grid-template-columns:1fr; gap:12px; }
}
@media (max-width:600px) {
  /* long role titles + years won't share a line on a phone */
  .wh-role-row { flex-direction:column; align-items:flex-start; gap:1px; padding:7px 0; }
  .wh { margin-top:44px; }
  .case-hero-frame { aspect-ratio:16 / 9; }
  .case-hero-img { top:0; height:100%; transform:none; }
  .case-meta { gap:18px 30px; }
  .case-main .case-press .press-grid { grid-template-columns:1fr; }
  .gallery-grid { grid-template-columns:1fr; }
  .gal-wide { grid-column:auto; }
  .case-nav { flex-direction:column; gap:24px; }
  .cn-link, .cn-link.next { max-width:100%; text-align:left; align-items:flex-start; }
  .cn-link.next .cn-title { background-position:left bottom; }
  :root { --gutter:22px; }
  .wk-grid { grid-template-columns:1fr; gap:36px; }
  .hero { grid-template-columns:1fr; padding-bottom:40px; }
  .hero-portrait { width:clamp(150px,46vw,200px); margin-bottom:24px; }
  .hero-foot { margin-top:36px; }
  .hero-clients { margin-top:32px; }
  .hero-clients-img { width:100%; }
  .about-copy { columns:1; }
  .site-nav { gap:18px; }
  .section-title { margin-bottom:28px; }
}
@media (max-width:380px) {
  .site-nav { gap:14px; font-size:12px; }
}
