/* ============================================================================
   CASE STUDIES — shared layer for the Work grid and the case-study pages.
   Loads after assets/site.css. Tokens and conventions: docs/design-system/SUPERCOLOR.md
   Art slots, specs and what fills them: docs/case-studies-2026-09-03/10-asset-inventory-and-specs.md

   The shell:
     card  = color panel (.wc-panel) + ONE art element (.wc-art: a subject cutout, a logo
             mark, anything transparent) + lower-third chyron. Hover contracts the panel to
             a circle of the photo (SUPERCOLOR §5) and the art lifts / signal-splits.
     click = the signal drop (§4b): the page falls to moving static and only the card's art
             keeps its picture, riding on top of the snow to its spot in the featured band, then
             losing its signal as the static clears. Cross-document View Transitions (browser-
             native, no router). Fallback: the channel wipe. Calm modes (reduced-motion / .a11y): plain link.
     slot  = .cs-stage[data-art=…] "tunes in" on arrival: static burst, then the media
             behind the art appears. Art types: still · loop · facade · pan · anim · idents
   ============================================================================ */

/* ---------- WORK GRID CARDS ----------
   Hover/focus is the card's live state. On touch (no hover) case.js gives .is-on to the card nearest the
   viewport centre as you scroll, so the one-column grid flips through the cards like channels (Phil, 2026-09-11). */
.wgrid{display:grid; grid-template-columns:repeat(3,1fr); gap:26px; padding-top:48px}  /* headroom: art stands above the panel */
.wcard{position:relative; display:block; aspect-ratio:3/4; color:inherit; text-decoration:none; border-radius:16px; --cx:50%; --cy:54%; isolation:isolate}   /* its own stacking context, so neighbours never paint over each other's art */
.wcard:focus-visible{outline:3px solid var(--focus); outline-offset:10px}
.wc-panel, .wc-photo{position:absolute; inset:0; border-radius:16px;
  -webkit-clip-path:circle(150% at var(--cx) var(--cy)); clip-path:circle(150% at var(--cx) var(--cy));
  transition:clip-path .55s cubic-bezier(.6,0,.2,1), -webkit-clip-path .55s cubic-bezier(.6,0,.2,1), opacity .3s ease}
.wc-panel{background:var(--pc); z-index:1; overflow:hidden}
.wc-panel .spd{position:absolute; inset:0; background:repeating-linear-gradient(120deg, transparent 0 20px, rgba(18,25,18,.26) 20px 22px);
  -webkit-mask:linear-gradient(120deg,#000,transparent 60%); mask:linear-gradient(120deg,#000,transparent 60%)}
.wc-photo{z-index:2; opacity:0; overflow:hidden}   /* the reveal window; the image inside may be larger than the card */
.wc-photo img{position:absolute; left:var(--photo-left, 0); bottom:var(--photo-bottom, 0); height:var(--photo-h, 100%); width:auto; max-width:none; display:block}
.wc-photo img:not([style]){left:0; width:100%; height:100%; object-fit:cover}
.wcard:hover .wc-panel, .wcard:focus-visible .wc-panel, .wcard.is-on .wc-panel,
.wcard:hover .wc-photo, .wcard:focus-visible .wc-photo, .wcard.is-on .wc-photo{-webkit-clip-path:circle(40% at var(--cx) var(--cy)); clip-path:circle(40% at var(--cx) var(--cy))}
.wcard:hover .wc-photo, .wcard:focus-visible .wc-photo, .wcard.is-on .wc-photo{opacity:1}
/* the art: auto-sized against BOTH max constraints so its aspect is never squished (the
   2026-08-13 bug was height + max-width together). Cutouts: portrait, subject touches the
   bottom edge. Marks/logos: centered instead — add .wc-art--mark */
.wc-art{position:absolute; left:50%; bottom:var(--art-bottom, 0); transform:translateX(-50%); width:auto; height:auto; max-height:104%; max-width:var(--art-maxw, 108%);
  z-index:3; pointer-events:none; filter:drop-shadow(0 12px 20px rgba(0,0,0,.42));
  transition:transform .55s cubic-bezier(.6,0,.2,1), filter .4s ease}
.wc-art--mark{bottom:auto; top:var(--mark-top, 38%); transform:translate(-50%,-50%); max-height:var(--mark-h, 52%); max-width:var(--mark-w, 72%)}
/* hover state-change on the art: lifts, and the signal splits (chromatic fringes) */
.wcard:hover .wc-art, .wcard:focus-visible .wc-art, .wcard.is-on .wc-art{transform:translateX(-50%) translateY(-10px) scale(1.04);
  filter:drop-shadow(0 18px 24px rgba(0,0,0,.5))
         drop-shadow(4px 0 0 color-mix(in srgb, var(--cyan) 75%, transparent))
         drop-shadow(-4px 0 0 color-mix(in srgb, var(--magenta) 75%, transparent))}
.wcard:hover .wc-art--mark, .wcard:focus-visible .wc-art--mark, .wcard.is-on .wc-art--mark{transform:translate(-50%,-50%) translateY(-10px) scale(1.06)}
/* DE{CODE} mark = the bracket-to-flower spin (alpha video). Idle: the poster, which is the open orange
   flower four seconds into the loop rather than the brace it starts on. Hover/focus: the loop plays
   (brace → orange → purple → teal → brace). Where hover doesn't exist or motion is calm, the keyed
   flower still stands in (.wc-spin-fallback). */
/* WebKit drops a <video>'s alpha when a CSS filter is on it (iPhone showed the flower in a white
   box, 2026-09-18: the spin is the only alpha video here that carried one). No shadow or fringe
   on the video in Safari; every other browser keeps them. Never put a filter on an alpha video. */
:root.is-safari video.wc-spin{filter:none !important}
.wc-spin{display:none; background:transparent; pointer-events:none}   /* size and position come from .wc-art--mark, so the video and the still are identical */
@media (prefers-reduced-motion:no-preference){   /* touch too: the scroll flip plays it (2026-09-11) */
  :root:not(.a11y) .wc-spin{display:block}
  :root:not(.a11y) .wc-spin-fallback{display:none}
}
/* rolling scanlines masked to the art's silhouette (the hero carousel's treatment); needs --cut on the card */
.wc-scan{position:absolute; left:50%; bottom:var(--art-bottom, 0); transform:translateX(-50%); width:var(--art-maxw, 108%); height:104%; z-index:4; pointer-events:none; opacity:0;
  background:repeating-linear-gradient(to bottom, transparent 0 2px, rgba(0,0,0,.55) 2px 3px); mix-blend-mode:multiply;
  -webkit-mask:var(--cut) center bottom / contain no-repeat; mask:var(--cut) center bottom / contain no-repeat;
  transition:opacity .3s ease}
.wcard:hover .wc-scan, .wcard:focus-visible .wc-scan, .wcard.is-on .wc-scan{opacity:.6; animation:scanRoll 2.4s linear infinite}
/* meta as a lower-third chyron band: readable over any art */
.wc-num{font-weight:700; color:var(--dim)}   /* lives in the chyron band, where overhanging art can never cover it */
.wc-body{position:absolute; left:14px; right:14px; bottom:14px; z-index:5; padding:14px 16px 15px; pointer-events:none; color:var(--cream);
  background:rgba(12,17,12,.9); border-left:4px solid var(--pc); border-radius:3px; box-shadow:0 3px 0 rgba(0,0,0,.4);
  transition:transform .45s cubic-bezier(.6,0,.2,1)}
.wcard:hover .wc-body, .wcard:focus-visible .wc-body, .wcard.is-on .wc-body{transform:translateY(-8px)}
.wc-tag{display:block; font-family:var(--mono); font-size:10.5px; font-weight:700; letter-spacing:.12em; text-transform:uppercase; color:var(--pc); margin-bottom:6px}
.wc-title{display:block; font-family:var(--disp); font-weight:900; font-variation-settings:"wght" 900,"wdth" 115; text-transform:uppercase; font-size:1.3rem; line-height:1.0; letter-spacing:-.01em; overflow-wrap:break-word; -webkit-hyphens:auto; hyphens:auto}
.wc-tease{display:block; font-size:.9rem; color:var(--dim); margin-top:8px; text-wrap:pretty; max-height:0; opacity:0; overflow:hidden; transition:max-height .45s cubic-bezier(.6,0,.2,1), opacity .35s ease .1s}
.wcard:hover .wc-tease, .wcard:focus-visible .wc-tease, .wcard.is-on .wc-tease{max-height:6em; opacity:1}
/* LEAVING: set by JS the instant the navigation starts. Snaps the card to its rest pose so
   the snapshot the browser carries into the next page is a clean panel + art. */
.wcard.leaving .wc-panel, .wcard.leaving .wc-photo, .wcard.leaving .wc-art, .wcard.leaving .wc-scan{transition:none; animation:none}
.wcard.leaving .wc-panel, .wcard.leaving .wc-photo{-webkit-clip-path:circle(150% at var(--cx) var(--cy)); clip-path:circle(150% at var(--cx) var(--cy))}
.wcard.leaving .wc-photo, .wcard.leaving .wc-scan, .wcard.leaving .wc-body{opacity:0}
.wcard.leaving .wc-art{transform:translateX(-50%); filter:drop-shadow(0 12px 20px rgba(0,0,0,.42))}
.wcard.leaving .wc-art--mark{transform:translate(-50%,-50%)}
.wcard.leaving .wc-art{view-transition-name:cs-art}   /* only the art travels; the panel stays behind under the static */
/* The traveller is the STILL, never the spin video: Safari snapshots a <video> for the view
   transition without its alpha, so the flower/cog crossed to the case page in a white box on
   iPhone (2026-09-18). The still is eager-loaded so it is already decoded at the swap. */
.wcard.leaving .wc-spin{display:none !important; view-transition-name:none}
.wcard.leaving .wc-spin-fallback{display:block !important}
@media(max-width:820px){ .wgrid{grid-template-columns:repeat(2,1fr)} }
@media(max-width:560px){ .wgrid{grid-template-columns:1fr; max-width:22rem; margin-left:auto; margin-right:auto} }
:root.a11y .wc-panel, :root.a11y .wc-photo, :root.a11y .wc-art{transition:none}
:root.a11y .wc-scan{display:none}
:root.a11y .wc-tease{opacity:1; max-height:6em}

/* ---------- CASE-STUDY PAGE ---------- */
.cs{--pc:var(--orange); --chan:var(--pc)}
.cs-hero{padding-top:var(--s4)}
.cs-back{display:inline-flex; align-items:center; gap:8px; font-family:var(--mono); font-size:12px; letter-spacing:.2em; text-transform:uppercase; color:var(--dim); margin-bottom:var(--s3)}
.cs-back:hover, .cs-back:focus-visible{color:var(--cream)}
.cs-kicker{font-family:var(--mono); font-size:12px; letter-spacing:.28em; text-transform:uppercase; color:var(--dim); display:flex; align-items:center; gap:12px; margin-bottom:var(--s2)}
.cs-kicker::before{content:""; width:26px; height:10px; background:var(--chan); flex:none}
h1.big.cs-title{max-width:16ch}

/* THE FEATURED SLOT. clip-path opens the top and sides so the art can stand above the
   frame, and clips the bottom flush so the art can step out under the frame when media plays */
/* the band sits directly under the site header: a skinny window into the work (cf. cydstumpel.nl/work) */
.cs-fig{position:relative; margin-top:var(--s2); padding-top:min(4vw, 40px)}
.cs-fig--flat{padding-top:0}                                        /* pages whose art is a mark inside the frame, not a standing subject */
.cs-stage{position:relative; aspect-ratio:3.5/1; -webkit-clip-path:inset(-60% -12% 0 -12%); clip-path:inset(-60% -12% 0 -12%)}   /* a skinny window, not a full-height video (Phil, 2026-09-09) */
.cs-stage--placed{-webkit-clip-path:inset(-60% 0 0 0); clip-path:inset(-60% 0 0 0)}   /* placed art may break the top edge (Cyd's featured state); sides and bottom clip flush */
.cs-frame{position:absolute; inset:0; border-radius:16px; overflow:hidden; background:var(--pc); container-type:size}
.cs-frame .spd{position:absolute; inset:0; background:repeating-linear-gradient(120deg, transparent 0 26px, rgba(18,25,18,.26) 26px 28px);
  -webkit-mask:linear-gradient(120deg,#000,transparent 60%); mask:linear-gradient(120deg,#000,transparent 60%)}
/* the media layer: whatever tunes in behind the art (still, video loop, poster, pan, built animation) */
.cs-media{position:absolute; inset:0; opacity:0; transition:opacity .5s ease; background:var(--media-bg, transparent)}   /* --media-bg: a backing for letterboxed media */
.cs-media > img, .cs-media > video{position:absolute; inset:0; width:100%; height:100%; object-fit:var(--media-fit, cover); object-position:var(--media-pos, 50% 50%); display:block}
.cs-stage[data-state="on"] .cs-media, .cs-stage[data-state="live"] .cs-media, .cs-stage[data-state="playing"] .cs-media{opacity:1}
.cs-static{position:absolute; inset:0; width:100%; height:100%; display:block; opacity:0; mix-blend-mode:screen; pointer-events:none; z-index:3}
/* the art that arrived from the card */
.cs-art{position:absolute; left:6%; bottom:0; width:auto; height:auto; max-height:118%; max-width:40%; z-index:4; pointer-events:none;
  filter:drop-shadow(0 14px 24px rgba(0,0,0,.5)); view-transition-name:cs-art;
  transition:transform .6s cubic-bezier(.6,0,.2,1), opacity .4s ease}
.cs-art--mark{left:50%; bottom:auto; top:50%; transform:translate(-50%,-50%); max-height:60%; max-width:40%}
.cs-stage[data-state="playing"] .cs-art{transform:translateY(112%); opacity:0}          /* steps off under the frame when the picture plays */
.cs-stage[data-state="live"] .cs-art{animation:csSignalLoss .7s steps(1,end) .15s both; transition:none}   /* once a loop is running, the art it arrived with loses its signal and the picture takes over */
.cs-stage[data-state="playing"] .cs-art--mark{transform:translate(-50%,-50%) scale(.6); opacity:0}
.cs-stage[data-art="anim"][data-state="on"] .cs-art, .cs-stage[data-art="pan"][data-state="on"] .cs-art{animation:csSignalLoss .7s steps(1,end) .3s both; transition:none}   /* a built animation or a pan takes the whole frame */
/* SIGNAL LOSS: how carried-in art leaves. Not a fade: the picture jitters sideways, the colour channels split further
   each step, the level flickers down and it is gone. steps() so it cuts between poses like a bad signal, never glides. */
@keyframes csSignalLoss{
  0%  {opacity:1;   translate:0 0;      filter:drop-shadow(0 14px 24px rgba(0,0,0,.5))}
  12% {opacity:.9;  translate:-6px 0;   filter:drop-shadow(3px 0 0 var(--cyan)) drop-shadow(-3px 0 0 var(--magenta))}
  24% {opacity:1;   translate:5px 0;    filter:drop-shadow(-2px 0 0 var(--cyan)) drop-shadow(2px 0 0 var(--magenta))}
  36% {opacity:.55; translate:-10px 0;  filter:drop-shadow(9px 0 0 var(--cyan)) drop-shadow(-9px 0 0 var(--magenta))}
  48% {opacity:.8;  translate:3px -2px; filter:drop-shadow(-5px 0 0 var(--cyan)) drop-shadow(5px 0 0 var(--magenta))}
  60% {opacity:.35; translate:12px 0;   filter:drop-shadow(14px 0 0 var(--cyan)) drop-shadow(-14px 0 0 var(--magenta))}
  72% {opacity:.5;  translate:-8px 1px; filter:drop-shadow(-11px 0 0 var(--cyan)) drop-shadow(11px 0 0 var(--magenta))}
  84% {opacity:.15; translate:16px 0;   filter:drop-shadow(20px 0 0 var(--cyan)) drop-shadow(-20px 0 0 var(--magenta))}
  100%{opacity:0;   translate:0 0;      filter:drop-shadow(26px 0 0 var(--cyan)) drop-shadow(-26px 0 0 var(--magenta))}
}
/* arrived by transition: the art left with the static; it stays in the DOM (transparent) so Back can carry it home */
.cs-stage.arrived .cs-art{opacity:0; animation:none; transition:none}
/* controls that live on the frame */
.cs-ctl{position:absolute; right:14px; bottom:14px; z-index:7; display:flex; gap:8px}
.cs-btn{font-family:var(--mono); font-weight:700; font-size:11px; letter-spacing:.14em; text-transform:uppercase; color:var(--black); background:var(--cream);
  border:0; border-radius:4px; padding:9px 13px; cursor:pointer; box-shadow:0 2px 0 rgba(0,0,0,.4); opacity:0; transition:opacity .3s ease .3s, background .2s ease}
.cs-btn:hover, .cs-btn:focus-visible{background:var(--yellow)}
.cs-stage[data-state="on"] .cs-btn, .cs-stage[data-state="live"] .cs-btn, .cs-stage[data-state="playing"] .cs-btn{opacity:1}
/* pan: a tall composite drifts slowly inside the frame (brand-guide pages) */
.cs-media--pan > img{height:auto; object-fit:unset; top:0; bottom:auto; animation:csPan 28s ease-in-out infinite alternate}
@keyframes csPan{from{transform:translateY(0)}to{transform:translateY(calc(-100% + 100cqh))}}   /* 100cqh = the frame height, no JS */
.cs-cap{font-family:var(--mono); font-size:11px; letter-spacing:.14em; text-transform:uppercase; color:var(--dim); margin-top:14px; display:flex; gap:14px; flex-wrap:wrap}
.cs-cap a{color:var(--cyan)}
@media(max-width:640px){ .cs-stage{aspect-ratio:2/1} .cs-art{max-width:48%; left:4%} .cs-art--mark{max-width:56%} .cs-frame{border-radius:12px} }
:root.a11y .cs-media, :root.a11y .cs-art, :root.a11y .cs-btn{transition:none}
:root.a11y .cs-stage .cs-art{animation:none}
:root.a11y .cs-stage[data-state="live"] .cs-art, :root.a11y .cs-stage[data-art="anim"][data-state="on"] .cs-art, :root.a11y .cs-stage[data-art="pan"][data-state="on"] .cs-art{opacity:0}
@media (prefers-reduced-motion:reduce){ .cs-media, .cs-art, .cs-btn{transition:none} .cs-media--pan > img{animation:none}
  .cs-stage .cs-art{animation:none}
  .cs-stage[data-state="live"] .cs-art, .cs-stage[data-art="anim"][data-state="on"] .cs-art, .cs-stage[data-art="pan"][data-state="on"] .cs-art{opacity:0} }

/* body copy at the reading measure; media rows can run the full column */
.cs-body{margin-top:var(--s5)}
.cs-body p:not(.cs-note), .cs-body h2, .cs-body .cs-ph{max-width:var(--maxread)}
.cs-body h2{font-family:var(--disp); font-weight:900; font-variation-settings:"wght" 900,"wdth" 115; text-transform:uppercase; font-size:1.35rem; line-height:1.05; letter-spacing:-.01em;
  color:var(--cream); margin:var(--s4) 0 var(--s2); padding-left:14px; border-left:5px solid var(--chan)}
.cs-body h2:first-child{margin-top:0}
.cs-ph{font-family:var(--mono); font-size:11.5px; letter-spacing:.1em; text-transform:uppercase; line-height:1.7; color:var(--dim);
  border:1px dashed var(--line); border-radius:6px; padding:14px 16px; margin-bottom:var(--s2)}   /* copy placeholder; Phase 5 removes */
.cs-row{margin:var(--s4) 0}
.cs-next{margin-top:var(--s5); font-family:var(--mono); font-size:12px; letter-spacing:.14em; text-transform:uppercase}

/* inline figure: a still or a silent loop with a caption (logo evolution, sage still, cog explosion) */
.fig{margin:0}
.fig-frame{position:relative; aspect-ratio:16/9; border-radius:12px; overflow:hidden; background:rgba(0,0,0,.35); border:1px solid var(--line)}
.fig-frame > img, .fig-frame > video{position:absolute; inset:0; width:100%; height:100%; object-fit:cover; display:block}
.fig-frame.is-4x3{aspect-ratio:4/3}
.fig figcaption{font-family:var(--mono); font-size:11px; letter-spacing:.12em; text-transform:uppercase; color:var(--dim); margin-top:10px}

/* ---------- YOUTUBE FACADE: poster + play, real iframe only on click ---------- */
.yt{position:relative; display:block; aspect-ratio:16/9; border-radius:12px; overflow:hidden; background:#0a0f0a; color:var(--cream)}
.yt img{width:100%; height:100%; object-fit:cover; display:block; opacity:.86; transition:opacity .3s ease, transform .6s cubic-bezier(.6,0,.2,1)}
.yt:hover img, .yt:focus-visible img{opacity:1; transform:scale(1.03)}
.yt-btn{position:absolute; top:50%; left:50%; width:64px; height:64px; margin:-32px 0 0 -32px; border-radius:50%; background:var(--orange); color:var(--black);
  display:flex; align-items:center; justify-content:center; font-family:var(--mono); font-weight:700; font-size:22px; padding-left:5px;
  box-shadow:0 8px 24px rgba(0,0,0,.5); transition:background .2s ease, transform .3s cubic-bezier(.6,0,.2,1)}
.yt:hover .yt-btn, .yt:focus-visible .yt-btn{background:var(--yellow); transform:scale(1.08)}
.yt:focus-visible{outline:3px solid var(--focus); outline-offset:4px}
.yt iframe{position:absolute; inset:0; width:100%; height:100%; border:0; z-index:6}
.yt.playing img, .yt.playing .yt-btn{display:none}
.yt-ext{display:inline-block; margin-top:8px; font-family:var(--mono); font-size:10.5px; letter-spacing:.12em; text-transform:uppercase; color:var(--cyan)}   /* always present: the escape hatch if embeds are blocked */
/* hero facade: the play control covers the frame; the iframe lands inside .cs-frame */
.cs-play{position:absolute; inset:0; z-index:5; border-radius:0; background:transparent; aspect-ratio:auto; opacity:0; transition:opacity .4s ease .2s}
.cs-stage[data-state="on"] .cs-play{opacity:1}
.cs-stage[data-state="playing"] .cs-play{display:none}
.cs-play .yt-btn{left:auto; right:12%; margin-left:0}
.cs-frame iframe{position:absolute; inset:0; width:100%; height:100%; border:0; z-index:6}

/* ---------- MEDIA ROWS ---------- */
/* strip: side-scroll, snap per item, caption under each (the campaign spots) */
.strip-wrap{position:relative}
.strip{display:flex; gap:22px; overflow-x:auto; scroll-snap-type:x mandatory; padding:4px 0 14px; scrollbar-width:thin; scrollbar-color:var(--line) transparent}
.strip > *{flex:0 0 min(72%, 560px); scroll-snap-align:start}
.strip-nav{display:flex; gap:8px; justify-content:flex-end; margin-bottom:12px}
.strip-nav button{font-family:var(--mono); font-weight:700; font-size:12px; letter-spacing:.12em; text-transform:uppercase; color:var(--cream);
  background:rgba(0,0,0,.35); border:1.5px solid var(--line); border-radius:4px; padding:8px 12px; cursor:pointer}
.strip-nav button:hover, .strip-nav button:focus-visible{border-color:var(--cream)}
.film-cap{margin-top:12px}
.film-cap b{display:block; font-family:var(--disp); font-weight:900; font-variation-settings:"wght" 900,"wdth" 112; text-transform:uppercase; font-size:1.05rem; line-height:1.05; color:var(--cream); margin-bottom:6px}
.film-cap .cs-ph{margin:0}
/* film grid: the five documentaries */
.filmgrid{display:grid; grid-template-columns:repeat(auto-fill, minmax(300px,1fr)); gap:26px}
/* idents: the five retreat events as channel idents; each expands to its description (native <details>, keyboard-free) */
.idents{display:grid; grid-template-columns:repeat(auto-fill, minmax(280px,1fr)); gap:22px; align-items:start}
.ident{border:1px solid var(--line); border-radius:12px; overflow:hidden; background:var(--panel)}
.ident summary{list-style:none; cursor:pointer; display:block}
.ident summary::-webkit-details-marker{display:none}
.ident summary:focus-visible{outline:3px solid var(--focus); outline-offset:-3px}
.ident-tile{position:relative; aspect-ratio:16/9; background:#0a0f0a}
.ident-tile > img, .ident-tile > video{position:absolute; inset:0; width:100%; height:100%; object-fit:cover; display:block; transition:transform .6s cubic-bezier(.6,0,.2,1)}
.ident summary:hover .ident-tile > img{transform:scale(1.03)}
.ident-lbl{display:flex; justify-content:space-between; align-items:center; gap:12px; padding:14px 16px; font-family:var(--disp); font-weight:900; font-variation-settings:"wght" 900,"wdth" 112; text-transform:uppercase; font-size:1.02rem; line-height:1.05; color:var(--cream)}
.ident-lbl small{font-family:var(--mono); font-weight:700; font-size:10.5px; letter-spacing:.12em; color:var(--dim); text-transform:uppercase; white-space:nowrap}
.ident-lbl::after{content:"+"; font-family:var(--mono); color:var(--chan); flex:none}
.ident[open] .ident-lbl::after{content:"−"}
.ident-body{padding:0 16px 18px; font-size:.95rem; color:var(--cream); text-wrap:pretty}
.ident-body .cs-ph{margin:0}
@media(max-width:640px){ .strip > *{flex-basis:86%} .filmgrid, .idents{grid-template-columns:1fr} }

/* ---------- CROSS-DOCUMENT VIEW TRANSITIONS: THE SIGNAL DROP (SUPERCOLOR §4b) ----------
   Grid → case page: the whole picture drops to moving canvas static (.cs-cover, drawn live by TVStatic
   during the transition) from the first frame, so the page switch underneath is never seen. Only the
   clicked card's art keeps its signal: it rides on top of the snow to the spot it occupies in the
   featured picture. As the static clears, the art loses its signal too (csSignalLoss) and is gone,
   leaving the new page with the band already on. Back: the art rides the static home to its card.
   Snapshot order is paint order, so the cover sits at z-index:-1: under everything in the live page,
   above the root snapshot and below the art in the transition. (Phil, 2026-09-11: option B.) */
@view-transition{ navigation:auto }
.cs-cover{position:fixed; inset:0; z-index:-1; width:100%; height:100%; display:block; background:#141814; opacity:0; pointer-events:none}
:root.vt .cs-cover{opacity:1; view-transition-name:cs-cover}       /* named only while a transition is in flight */
:root.vt .cs-stage .cs-art{animation:none; opacity:1}              /* the pseudo-element runs the exit; the live element holds still under it */
::view-transition-old(cs-cover){display:none}
::view-transition-new(cs-cover){height:100%; object-fit:cover; mix-blend-mode:normal}
/* the snow is solid while the art travels, then clears in one quick, even drop (no flicker: it read as a blink,
   and a half-clear veil let the page show through). Forward: .9s in all, snow gone by .72s, the art then loses
   its signal over the picture. Back: shorter, snow gone by .42s, nothing left to settle. */
@keyframes csCoverOut{0%,84%{opacity:1} 100%{opacity:0}}
::view-transition-group(cs-cover){animation-duration:.5s}
::view-transition-new(cs-cover){animation:csCoverOut .5s linear both}
:root.vt-case::view-transition-group(cs-cover){animation-duration:.72s}
:root.vt-case::view-transition-new(cs-cover){animation:csCoverOut .72s linear both}
/* the art travels while the snow is solid, then (case page only) loses its signal over the picture */
::view-transition-group(cs-art), ::view-transition-group(cs-flare){animation-duration:.4s; animation-timing-function:cubic-bezier(.6,0,.2,1)}
:root.vt-case::view-transition-group(cs-art), :root.vt-case::view-transition-group(cs-flare){animation-duration:.55s}
::view-transition-old(cs-art){display:none}                        /* one picture of the art, never a cross-fade of two */
::view-transition-new(cs-art){height:100%; object-fit:contain; mix-blend-mode:normal; animation:none; opacity:1}
:root.vt-case::view-transition-new(cs-art){animation:csSignalLoss .35s steps(1,end) .55s both}
:root.vt-case::view-transition-group(root){animation-duration:.9s}   /* keeps the transition open for the signal loss */
::view-transition-old(cs-flare), ::view-transition-new(cs-flare){height:100%; object-fit:contain; mix-blend-mode:normal; animation:none}
::view-transition-old(root), ::view-transition-new(root){animation:none}   /* the new page is simply there under the static */
@media (prefers-reduced-motion:reduce){
  ::view-transition-group(*), ::view-transition-old(*), ::view-transition-new(*){animation:none !important}
}

/* ---------- FALLBACK: CHANNEL WIPE (SUPERCOLOR §4b) for browsers without cross-document transitions ---------- */
.pagefx{position:fixed; inset:0; z-index:80; pointer-events:none; display:none; overflow:hidden}
.pagefx.on{display:block}
.pf-bars{position:absolute; inset:0; display:flex; transform:translateX(-100%)}
.pf-bars span{flex:1}
.pagefx.in .pf-bars{animation:pfIn .5s cubic-bezier(.7,0,.2,1) both}
@keyframes pfIn{0%{transform:translateX(-100%); filter:none}100%{transform:translateX(0); filter:drop-shadow(10px 0 var(--cyan)) drop-shadow(-10px 0 var(--magenta))}}
.pagefx.out .pf-bars{animation:pfOut .55s cubic-bezier(.7,0,.2,1) both}
@keyframes pfOut{0%{transform:translateX(0); filter:drop-shadow(10px 0 var(--cyan)) drop-shadow(-10px 0 var(--magenta))}100%{transform:translateX(100%); filter:none}}
:root.a11y .pagefx{display:none !important}

/* ---------- LABELED ART SLOTS (placeholders until Phil's art lands; see images/work/README.md) ---------- */
.slot{color:var(--cream)}
.cs-stage[data-art="pan"][data-state="on"] .cs-art{opacity:0}                          /* the brand board hands off to the panning pages */

/* ---------- FLARE: award sticker that sits on the card's corner and rides into the hero ---------- */
.flare{display:block; filter:drop-shadow(0 8px 14px rgba(0,0,0,.45)); pointer-events:none}
.wc-flare{position:absolute; top:-7%; right:-7%; width:36%; z-index:6; transform:rotate(-12deg); transition:transform .5s cubic-bezier(.6,0,.2,1)}
.wcard:hover .wc-flare, .wcard:focus-visible .wc-flare, .wcard.is-on .wc-flare{transform:rotate(-4deg) scale(1.08)}
.wcard.leaving .wc-flare{transition:none; transform:rotate(-12deg); view-transition-name:cs-flare}
.cs-flare{position:absolute; bottom:-11%; right:-1.5%; width:15%; max-width:170px; z-index:6; transform:rotate(-12deg); view-transition-name:cs-flare}
.cs-fig:has(.cs-stage[data-state="playing"]) .cs-flare{opacity:0; transition:opacity .3s ease}
::view-transition-group(cs-flare){animation-duration:.62s; animation-timing-function:cubic-bezier(.6,0,.2,1)}
::view-transition-old(cs-flare), ::view-transition-new(cs-flare){height:100%; object-fit:contain; mix-blend-mode:normal; animation:none}
@media(max-width:640px){ .wc-flare{width:40%} .cs-flare{width:24%; bottom:-14%} }
:root.a11y .flare{filter:none}
/* the ring text turns slowly inside its band; calm modes stop it via the global animation kill */
.flare-ring{transform-box:view-box; transform-origin:50% 50%; animation:flareSpin 26s linear infinite}
@keyframes flareSpin{to{transform:rotate(360deg)}}

/* ---------- PLAYLIST: one big player, caption under it, a queue of spots; autoplays muted on scroll, chains spot to spot ---------- */
/* one panel holds the player, its caption and the queue, so the caption can't be read as body copy (Phil, 2026-09-10).
   The player runs flush to the panel's edges, so it keeps the full column width. */
.playlist{background:var(--panel); border:1px solid var(--line); border-radius:14px; padding-bottom:24px}
.playlist > :not(.pl-player){margin-left:24px; margin-right:24px}
.pl-player{position:relative; aspect-ratio:16/9; border-radius:13px 13px 0 0; overflow:hidden; background:#0a0f0a}
.pl-player > div, .pl-player iframe{position:absolute; inset:0; width:100%; height:100%; border:0}
.pl-facade{position:absolute; inset:0; z-index:2; border-radius:0}
.playlist.is-live .pl-facade{display:none}
.pl-bar{display:flex; justify-content:space-between; align-items:center; gap:12px; margin-top:16px}
.pl-count{font-family:var(--mono); font-weight:700; font-size:11px; letter-spacing:.14em; color:var(--dim)}
.pl-mute{opacity:1}
.pl-cap{margin-top:10px; max-width:var(--maxread)}
.pl-cap b{display:block; font-family:var(--disp); font-weight:900; font-variation-settings:"wght" 900,"wdth" 112; text-transform:uppercase; font-size:1.05rem; line-height:1.05; color:var(--cream); margin-bottom:8px}   /* smaller than the section heads, so it never reads as one */
.pl-cap p{font-size:.95rem; color:var(--cream); text-wrap:pretty; margin-bottom:6px}
.pl-queue{list-style:none; display:grid; grid-template-columns:repeat(5,1fr); gap:12px; margin-top:20px; margin-bottom:0; padding:20px 0 0; border-top:1px solid var(--line)}
.pl-item{display:block; width:100%; text-align:left; background:none; border:0; padding:0; cursor:pointer; color:var(--dim);
  font-family:var(--mono); font-size:10.5px; font-weight:700; letter-spacing:.1em; text-transform:uppercase; line-height:1.4}
.pl-item img{display:block; width:100%; aspect-ratio:16/9; object-fit:cover; border-radius:8px; border:2px solid transparent; opacity:.7; margin-bottom:8px;
  transition:opacity .25s ease, border-color .25s ease, transform .4s cubic-bezier(.6,0,.2,1)}
.pl-item:hover img, .pl-item:focus-visible img{opacity:1; transform:translateY(-3px)}
.pl-item.is-current{color:var(--cream)}
.pl-item.is-current img{opacity:1; border-color:var(--pc)}
.pl-item:focus-visible{outline:3px solid var(--focus); outline-offset:4px; border-radius:8px}
@media(max-width:640px){
  .playlist{padding-bottom:16px; border-radius:12px}
  .playlist > :not(.pl-player){margin-left:16px; margin-right:16px}
  .pl-player{border-radius:11px 11px 0 0}
  .pl-queue{display:flex; overflow-x:auto; scroll-snap-type:x mandatory; padding-bottom:8px; scrollbar-width:thin}
  .pl-queue li{flex:0 0 46%; scroll-snap-align:start}
}
:root.a11y .pl-item img{transition:none}

/* PLACED ART: the cutout sits exactly where its subject stands in the source frame, so it lands on itself and the
   frame comes alive underneath. The bar shows the middle 16/21 of a 16:9 source (object-fit: cover), so the source's
   bottom edge sits below the window; the stage clips there, matching the crop. With the crop pinned to the top of
   a 16:9 source (--media-pos 50% 0%): source height = 0.5625 × width; window = width / 3.5 → drop = 96.9% of the
   window height on desktop; on phones (2:1) 12.5%. */
.cs-art--placed{left:calc(var(--src-x, 0%) + var(--art-left, .06) * var(--src-w, 100%)); width:calc(var(--art-w, .3) * var(--src-w, 100%)); max-width:none; max-height:none; height:auto;
  bottom:calc(-1 * (1 - var(--crop-y, 0)) * var(--art-overflow, 96.9%))}   /* --art-left/-w/-top: fractions of the source frame; --crop-y = the media-pos y fraction; overflow = source height beyond the window */
/* top-anchored placement (a subject that floats in the frame rather than standing on its floor) */
.cs-art--top{bottom:auto; top:calc(var(--art-top, .5) * (100% + var(--art-overflow, 96.9%)) - var(--crop-y, 0) * var(--art-overflow, 96.9%))}
/* contain-fit media (white sources): the 16:9 source is pillarboxed inside the window, so its box is narrower than the window */
.cs-stage--contain{--src-w:50.79%; --src-x:24.6%; --art-overflow:0%}
@media(max-width:640px){ .cs-art--placed{--art-overflow:12.5%} .cs-stage--contain{--src-w:88.89%; --src-x:5.56%} .cs-stage--contain .cs-art--placed{--art-overflow:0%} }

/* ---------- SOUND BUTTON: glows while muted so people know there is sound to turn on ---------- */
.pl-mute[aria-pressed="false"]{animation:soundGlow 1.8s ease-in-out infinite}
@keyframes soundGlow{0%,100%{box-shadow:0 2px 0 rgba(0,0,0,.4), 0 0 0 0 color-mix(in srgb, var(--pc) 0%, transparent)}
  50%{box-shadow:0 2px 0 rgba(0,0,0,.4), 0 0 0 7px color-mix(in srgb, var(--pc) 45%, transparent), 0 0 26px color-mix(in srgb, var(--pc) 70%, transparent)}}

/* ---------- SHORTLIST BOARD: who we were up against, as a broadcast nominee list beside the TL;DR ---------- */
.cs-hero-grid{display:grid; grid-template-columns:minmax(0,1fr) 340px; gap:var(--s5); align-items:start}
.cs-hero-grid .lead{margin-bottom:0}
.shortlist{border:1px solid var(--line); border-radius:12px; background:var(--panel); padding:16px 18px 14px; box-shadow:0 0 0 1px rgba(0,0,0,.3)}
.sl-title{font-family:var(--disp); font-weight:900; font-variation-settings:"wght" 900,"wdth" 118; text-transform:uppercase; font-size:1.6rem; line-height:1; letter-spacing:-.02em; color:var(--cream); margin:0 0 6px}
.sl-label{display:block; font-family:var(--mono); font-size:10.5px; font-weight:700; letter-spacing:.14em; text-transform:uppercase; color:var(--dim); line-height:1.5; margin-bottom:12px}
.sl-label:hover, .sl-label:focus-visible{color:var(--cream)}
.sl-list{list-style:none; margin:0; padding:0}
.sl-list li{display:flex; align-items:center; gap:12px; padding:8px 0; border-top:1px solid var(--line); font-family:var(--mono); font-size:12px; letter-spacing:.06em; color:var(--cream);
  animation:slIn .5s cubic-bezier(.6,0,.2,1) both; animation-delay:calc(.35s + var(--i, 0) * .09s)}
@keyframes slIn{from{opacity:0; transform:translateX(-8px)}to{opacity:1; transform:none}}
.sl-name{flex:1; min-width:0}
.sl-list li.is-us{color:var(--black); background:var(--pc); margin:0 -18px; padding:9px 18px; border-top-color:transparent; border-radius:3px}
.sl-list li.is-us .sl-name{font-weight:700}
.sl-us{display:inline-flex; align-items:center; gap:7px; font-weight:700; font-size:10.5px; letter-spacing:.12em; text-transform:uppercase; white-space:nowrap}
.sl-us .rec{width:8px; height:8px; border-radius:50%; background:var(--black); animation:dotblink 1.3s steps(1) infinite}
@media(max-width:860px){ .cs-hero-grid{grid-template-columns:1fr; gap:var(--s4)} }
:root.a11y .sl-list li, :root.a11y .pl-mute{animation:none}
/* placed MARK: a mark is placed like anything else (from its top edge, .cs-art--top); undo the centred-mark transform */
.cs-art--placed.cs-art--mark{transform:none; max-height:none; max-width:none}
.cs-stage[data-state="playing"] .cs-art--placed.cs-art--mark{transform:none}

/* PHOTO CARDS: a supplied graphic fills the panel (no cutout); hover contracts it to the circle and zooms a touch */
.wc-cover{position:absolute; inset:0; width:100%; height:100%; object-fit:cover; display:block; transition:transform .6s cubic-bezier(.6,0,.2,1)}
.wcard:hover .wc-cover, .wcard:focus-visible .wc-cover, .wcard.is-on .wc-cover{transform:scale(1.06)}
.wcard.leaving .wc-cover{transition:none; transform:none}
.wc-panel:has(.wc-cover) .spd{display:none}

/* ---------- GREATEST HITS MARQUEE: a lit sign scrolling above the Work section. Full-bleed, the type glows in the
   section's channel and hums like a neon tube. Reduced motion: still, steady glow. High contrast: plain type. ---------- */
.marquee{position:relative; width:100vw; margin:-.2em 0 var(--s1) calc(50% - 50vw); padding:1.25em 0 .95em;   /* the type is 2.5× tall and the glow bleeds past the line box, so the box is sized from the type */
  font-size:clamp(3.2rem, 9.5vw, 8.4rem); line-height:1; overflow-x:clip; overflow-y:visible}   /* clip only sideways so the track never widens the page; the glow stays free above and below */
.marquee::before, .marquee::after{content:""; position:absolute; top:-1em; bottom:-1em; width:9%; z-index:2; pointer-events:none}   /* soft edges, full height so the glow fades too */
.marquee::before{left:0; background:linear-gradient(90deg, var(--black), transparent)}
.marquee::after{right:0; background:linear-gradient(270deg, var(--black), transparent)}
.mq-track{display:flex; align-items:center; gap:.35em; width:max-content; white-space:nowrap; animation:mqScroll 38s linear infinite;
  font-family:var(--disp); font-weight:900; font-variation-settings:"wght" 900,"wdth" 125; text-transform:uppercase; letter-spacing:-.02em; color:var(--cyan)}
.mq-item{display:inline-block; transform:scaleY(2.5); transform-origin:50% 50%;   /* 2.5× taller, same width */
  text-shadow:0 0 6px color-mix(in srgb, var(--cyan) 70%, transparent), 0 0 22px color-mix(in srgb, var(--cyan) 55%, transparent),
  0 0 64px color-mix(in srgb, var(--cyan) 40%, transparent), 0 0 140px color-mix(in srgb, var(--cyan) 25%, transparent);
  animation:mqHum 3.6s ease-in-out infinite}
.mq-item:nth-child(4n+3){animation-delay:-1.2s} .mq-item:nth-child(4n+5){animation-delay:-2.3s}
.mq-sep{color:var(--yellow); font-size:.55em; margin:0 .4em; text-shadow:0 0 14px color-mix(in srgb, var(--yellow) 70%, transparent); animation:mqBlink 2.4s steps(1) infinite}
@keyframes mqScroll{to{transform:translateX(-50%)}}
@keyframes mqHum{0%,100%{opacity:1}50%{opacity:.88}92%{opacity:.97}94%{opacity:.8}96%{opacity:1}}   /* mains hum with the odd stutter */
@keyframes mqBlink{0%,60%{opacity:1}61%,100%{opacity:.35}}
@media (prefers-reduced-motion:reduce){ .mq-track, .mq-item, .mq-sep{animation:none} }
:root.a11y .mq-item, :root.a11y .mq-sep{text-shadow:none}

/* ---------- STATS RAIL: big bold numbers beside the TL;DR, counting up on arrival ---------- */
.stats{border-left:5px solid var(--chan); padding:4px 0 4px 22px}
.stat-list{list-style:none; margin:0; padding:0; display:grid; gap:18px}
.stat-list li{animation:slIn .5s cubic-bezier(.6,0,.2,1) both; animation-delay:calc(.3s + var(--i, 0) * .12s)}
.stat-n{display:block; font-family:var(--disp); font-weight:900; font-variation-settings:"wght" 900,"wdth" 125; font-size:clamp(3rem, 6vw, 4.6rem); line-height:.9; letter-spacing:-.03em; color:var(--chan);
  text-shadow:0 0 22px color-mix(in srgb, var(--chan) 40%, transparent); font-variant-numeric:tabular-nums}
.stat-l{display:block; font-family:var(--mono); font-size:11px; font-weight:700; letter-spacing:.14em; text-transform:uppercase; color:var(--dim); margin-top:6px}
.stat-src{font-family:var(--mono); font-size:10px; letter-spacing:.08em; color:var(--dim); margin-top:18px; opacity:.7}
@media(max-width:860px){ .stat-list{grid-template-columns:1fr 1fr; gap:22px 16px} }
:root.a11y .stat-n{text-shadow:none}
:root.a11y .stat-list li{animation:none}

/* ---------- PULL QUOTE: a line worth stopping on, in the section's channel colour ---------- */
.pull{margin:var(--s4) 0; max-width:var(--maxread); border-left:5px solid var(--chan); padding:2px 0 2px 22px}
.pull blockquote{margin:0; font-family:var(--disp); font-weight:900; font-variation-settings:"wght" 900,"wdth" 112; text-transform:none;
  font-size:clamp(1.35rem, 2.6vw, 1.95rem); line-height:1.12; letter-spacing:-.02em; color:var(--cream); text-wrap:pretty;
  text-shadow:0 0 26px color-mix(in srgb, var(--chan) 28%, transparent)}
.pull cite{display:block; margin-top:12px; font-family:var(--mono); font-style:normal; font-size:11px; font-weight:700; letter-spacing:.14em; text-transform:uppercase; color:var(--chan)}
:root.a11y .pull blockquote{text-shadow:none}

/* ---------- SONIC MOOD BOARD: a facade for the project's Spotify playlist. Spotify's iframe is heavy and
   tracks, so it loads only when someone asks for it; the card itself is the site's own type. ---------- */
.sonic{display:flex; align-items:center; gap:20px; width:100%; max-width:var(--maxread); text-align:left; cursor:pointer;
  color:var(--cream); background:var(--panel); border:1px solid var(--line); border-left:5px solid var(--chan); border-radius:12px;
  padding:18px 22px; transition:border-color .25s ease, transform .25s ease}
.sonic:hover, .sonic:focus-visible{border-color:var(--chan); transform:translateY(-3px)}
.sonic:focus-visible{outline:3px solid var(--focus); outline-offset:3px}
.sonic-eq{display:inline-flex; align-items:flex-end; gap:3px; height:32px; flex:none}
.sonic-eq i{width:4px; height:5px; background:var(--chan); border-radius:1px; animation:eqbar 1.1s ease-in-out infinite}
.sonic-eq i:nth-child(2){animation-delay:.18s} .sonic-eq i:nth-child(3){animation-delay:.36s}
.sonic-eq i:nth-child(4){animation-delay:.54s} .sonic-eq i:nth-child(5){animation-delay:.72s}
@keyframes eqbar{0%,100%{height:5px}50%{height:32px}}
.sonic-txt{display:flex; flex-direction:column; gap:3px; min-width:0; flex:1}
.sonic-kicker{font-family:var(--mono); font-size:10.5px; font-weight:700; letter-spacing:.16em; text-transform:uppercase; color:var(--dim)}
.sonic-name{font-family:var(--disp); font-weight:900; font-variation-settings:"wght" 900,"wdth" 112; text-transform:uppercase; font-size:1.25rem; line-height:1.05; letter-spacing:-.01em}
.sonic-meta{font-family:var(--mono); font-size:11px; letter-spacing:.1em; color:var(--dim)}
.sonic-go{font-family:var(--mono); font-weight:700; font-size:11px; letter-spacing:.14em; text-transform:uppercase; color:var(--chan); white-space:nowrap}
.sonic-frame{max-width:var(--maxread); border-radius:12px; overflow:hidden; border:1px solid var(--line)}
.sonic-frame iframe{display:block; width:100%; height:352px; border:0}
@media(max-width:560px){ .sonic{gap:14px; padding:16px} .sonic-go{display:none} }
:root.a11y .sonic-eq i{animation:none; height:18px}
@media (prefers-reduced-motion:reduce){ .sonic-eq i{animation:none; height:18px} .sonic{transition:none} }

/* ---------- FOOTNOTE: small print under a figure, for context the picture cannot carry ---------- */
.cs-note{font-size:.88rem; line-height:1.55; color:var(--dim); text-wrap:pretty;
  border-left:2px solid var(--line); padding-left:14px; margin:calc(-1 * var(--s2)) 0 var(--s4)}   /* runs the full width of the figure it belongs to; body copy keeps the reading measure */
.cs-note a{color:var(--cyan)}
video.autoloop{cursor:pointer}   /* click toggles it, since it carries no controls while it plays itself */

/* ---------- FADE CARDS: no photo behind the art, the panel just warms to another colour on hover.
   For art that was drawn on white in the first place, so hovering puts it back where it belongs. ---------- */
.wcard--fade .wc-panel{transition:background-color .5s ease, clip-path .55s cubic-bezier(.6,0,.2,1), -webkit-clip-path .55s cubic-bezier(.6,0,.2,1)}
.wcard--fade .wc-panel .spd{transition:opacity .45s ease}
.wcard--fade:hover .wc-panel, .wcard--fade:focus-visible .wc-panel{background:var(--hover-bg, #fff);
  -webkit-clip-path:circle(150% at var(--cx) var(--cy)); clip-path:circle(150% at var(--cx) var(--cy))}
.wcard--fade:hover .wc-panel .spd, .wcard--fade:focus-visible .wc-panel .spd{opacity:0}
:root.a11y .wcard--fade .wc-panel{transition:none}

/* ---------- TRAILER: a promo for another case study, floated into the paragraph ----------
   Phil, 2026-09-18: not a full-width band. It is part of the paragraph block, a thumbnail the words
   wrap around that opens to about half the measure as the reader reaches it and tucks back once it
   leaves. It never renders wide, which is why the reel encodes at 854px.
   The width changes in ONE layout pass and assets/case.js animates only the transform from where the
   thumbnail sat (a FLIP), so the paragraph re-wraps once instead of on every frame. */
.cs-body--float{display:flow-root}                                        /* a late float never hangs into the footer */
.cs-body--float > h2, .cs-body--float > .cs-row, .cs-body--float > .pull,
.cs-body--float > .cs-next, .cs-body--float > .tm{clear:both}

.trailer{float:right; clear:both; width:124px; margin:.4em 0 .7em 1.5em; transform-origin:top right}
.trailer[data-side="left"]{float:left; margin:.4em 1.5em .7em 0; transform-origin:top left}
.trailer.is-open{width:min(46%, 420px)}
.trailer.is-flip{transition:transform .46s cubic-bezier(.22,.7,.2,1); will-change:transform}

.tr-win{position:relative; display:block; width:100%; aspect-ratio:16/9; padding:0; border:1px solid var(--line); border-radius:8px;
  overflow:hidden; background:#0a0f0a; color:var(--cream); cursor:pointer; -webkit-appearance:none; appearance:none;
  transition:box-shadow .3s ease, border-color .3s ease, border-radius .3s ease}
.trailer.is-open .tr-win{border-radius:12px}
.tr-win:hover, .tr-win:focus-visible{border-color:var(--chan); box-shadow:0 0 0 2px color-mix(in srgb, var(--chan) 45%, transparent)}
.tr-win:focus-visible{outline:none}
.tr-media{position:absolute; inset:0; width:100%; height:100%; object-fit:cover; display:block}
/* closed, the thumbnail is the poster and the video is not even decoding; the reel takes over once it opens */
.tr-vid{display:none; background:#0a0f0a}
.trailer.is-open .tr-vid{display:block}
.tr-poster{transition:opacity .3s ease}
.trailer.is-live .tr-poster{opacity:0}
.tr-static{position:absolute; inset:0; width:100%; height:100%; display:block; opacity:0; mix-blend-mode:screen; pointer-events:none; z-index:3}

/* the bug: a lower third naming the piece, only once there is room to read it */
.tr-bug{position:absolute; left:0; right:0; bottom:0; z-index:2; display:flex; align-items:center; gap:10px; flex-wrap:wrap;
  padding:24px 12px 9px; text-align:left; font-family:var(--mono); font-size:10px; font-weight:700; letter-spacing:.13em;
  text-transform:uppercase; line-height:1.3; color:var(--cream); opacity:0; transition:opacity .3s ease; pointer-events:none;
  background:linear-gradient(to top, rgba(10,15,10,.92), rgba(10,15,10,.5) 58%, rgba(10,15,10,0))}
.tr-bug b{display:inline-flex; align-items:center; gap:.5em; color:var(--chan); font-weight:700}
.tr-bug b::before{content:""; width:12px; height:7px; background:var(--chan); flex:none}
.trailer.is-open .tr-bug{opacity:1; transition-delay:.3s}

/* closed: a play cue on the thumbnail, so it reads as something you can open */
.tr-cue{position:absolute; left:50%; top:50%; transform:translate(-50%,-50%); z-index:2; display:grid; place-items:center;
  width:30px; height:30px; border-radius:50%; background:var(--orange); color:var(--black); font-size:14px; line-height:1;
  padding-left:2px; box-shadow:0 2px 10px rgba(0,0,0,.45); transition:opacity .3s ease, background .3s ease}
.tr-win:hover .tr-cue, .tr-win:focus-visible .tr-cue{background:var(--yellow)}
.trailer.is-open .tr-cue{opacity:0}

.tr-go{display:none; margin-top:8px; font-family:var(--mono); font-size:11px; font-weight:700; letter-spacing:.13em;
  text-transform:uppercase; color:var(--chan)}
.trailer.is-open .tr-go{display:block}

@media(max-width:640px){
  .trailer{width:96px; margin:.35em 0 .6em 1.1em}
  .trailer[data-side="left"]{margin:.35em 1.1em .6em 0}
  .trailer.is-open{float:none; clear:both; width:100%; margin:.6em 0 .9em; transform-origin:top center}
  .tr-bug{font-size:9.5px}
}

:root.a11y .trailer, :root.a11y .tr-win, :root.a11y .tr-cue, :root.a11y .tr-bug{transition:none}
@media (prefers-reduced-motion:reduce){ .trailer, .tr-win, .tr-cue, .tr-bug{transition:none} }

/* ---------- RAIL LAYOUT: the numbers and a sidebar card run down beside the TL;DR and the first section ----------
   (Phil, 2026-09-18, on DE{CODE}: the soundtrack becomes a sidebar under the stats, alongside The work.)
   The left column carries the TL;DR, the video under it and the whole first section; the right column is the
   rail. Everything after that first section returns to the full column width in .cs-body. */
.cs-hero-grid--rail{align-items:start}
.cs-lede{min-width:0}
.cs-lede .lead{margin-bottom:var(--s3)}
.cs-lede h2:first-of-type{margin-top:var(--s4)}
.cs-rail{display:grid; gap:var(--s4); min-width:0}

.rail-card{border:1px solid var(--line); border-radius:12px; background:var(--panel); padding:16px 18px 18px; box-shadow:0 0 0 1px rgba(0,0,0,.3)}
.rail-card h2{font-size:1.15rem; margin:0 0 10px; padding-left:12px}
.rail-card p.body{font-size:.94rem; line-height:1.55; margin-bottom:14px; max-width:none; color:var(--dim)}
.rail-card .cs-row{margin:0}

/* the sonic card stacks in a 340px rail instead of running as a row */
.rail-card .sonic{flex-direction:column; align-items:flex-start; gap:12px; max-width:none; padding:14px 16px}
.rail-card .sonic-eq{height:22px}
.rail-card .sonic-name{font-size:1.1rem}
.rail-card .sonic-go{display:block}
.rail-card .sonic-frame{max-width:none}
.rail-card .sonic-frame iframe{height:352px}
.rail-card .yt-ext{margin-top:10px}

@media(max-width:860px){
  .cs-rail{grid-template-columns:1fr; gap:var(--s4)}
  .rail-card p.body{max-width:var(--maxread)}
}

/* ---------- CAPTIONED BODY VIDEO: the sound button that trades captions for audio ----------
   Phil, 2026-09-18: the recap plays itself muted with its captions on, and one press turns the sound up
   and the captions off. The button glows until it has been pressed, the same cue the playlist uses. */
.fig-bar{display:flex; justify-content:flex-end; align-items:center; gap:12px; margin-top:12px}
.fig-mute{opacity:1}
.fig-mute[aria-pressed="false"]{animation:soundGlow 1.8s ease-in-out infinite}
:root.a11y .fig-mute{animation:none}
@media (prefers-reduced-motion:reduce){ .fig-mute{animation:none} }
/* the cues sit above the controls and read in the site's own type */
figure[data-capvid] video::cue{font-family:var(--mono); font-size:.82em; line-height:1.4; background:rgba(10,15,10,.82); color:var(--cream)}
@media(max-width:640px){ .fig-bar{justify-content:stretch} .fig-mute{width:100%} }
