/* ============================================================
   Scene C — Work. Revealed in place from the side-nav (index.html stageExit).
   Mirrors the homepage stage grammar so it reads as the SAME site:
   - Hyperlegible Sans, the homepage type/colour tokens, the 8-col --col grid.
   - #sC is transparent so the shared #bgCanvas dot-grid shows through.
   - Desktop = viewport-locked stage. The left rail is styled IDENTICALLY to the
     homepage's .sa-mini-nav and sits at the same anchor, so when the stage swaps
     home→work the nav appears to STAY PUT — only the portrait + greeting exit and
     the grid takes their place (cols 3–8, where the head sat). Mobile = a vertical
     scrolling flow (the ring is dropped; the grid rises into a single column).
   Everything scoped under #sC. No visible borders anywhere.
   ============================================================ */

/* WK-6 (values LOCKED from Jacky's live trial 2026-08-20, three rounds; the
   ?wk6* switches are stripped): the old bloom DID stagger (80ms step) but read
   as one block — 0.9s soft rises over a 24px travel overlap so much they
   merge. The cascade reads tile-by-tile with a bigger step (onsets separate,
   120ms) and deeper travel; the 1s rise is his "柔" pick — the softness lives
   in the duration (About's reveal is the same easing at 1.6s and reads
   gentle). The home→Work swap also fires 600ms early (WK6_LEAD, index.html)
   so the grid rises into the portrait's dissolve. */
#sC { font-family: 'Hyperlegible Sans', system-ui, sans-serif; --wk-rise-dur: 1s; --wk-stagger-step: 120ms; }
#sC.scene { z-index: 2; background: transparent; color: var(--fg); }

/* Entry — ONLY the tiles bloom. The scene itself does NOT fade: fading the whole
   #sC would fade the rail too, and since the rail is meant to read as the same
   (already-visible) home nav, that produced a one-frame flick at the swap. So the
   rail stays solid at opacity 1 throughout; just the grid animates in. */
/* WK-6: no scale — a scale read as tiles POPPING; the rise is a pure bottom-up
   translateY + opacity fade. Base delay 60ms: the grid starts flooding the
   moment the scene lands. */
@keyframes wk-rise  { from { opacity: 0; transform: translateY(40px); } to { opacity: 1; transform: none; } }

/* Tiles bloom in, staggered — a calm stand-in for the eventual particle→ring→grid
   morph (that lands later, driven from ascii-head.js). --i set per tile in markup
   (DOM order ≈ bento visual order: featured → right column → bottom row). */
#sC.scene.active .wk-tile {
  animation: wk-rise var(--wk-rise-dur) cubic-bezier(0.22, 1, 0.36, 1) both;
  animation-delay: calc(60ms + var(--i, 0) * var(--wk-stagger-step));   /* WK-6 cascade */
}

/* ---------------- mobile (default flow; body scrolls) ---------------- */
#sC .wk-rail { display: none; }                 /* desktop-only side rail */

/* Mobile chrome = the GLOBAL wordmark (home) + hamburger menu (nav) — same as the
   home stage — so #sC has no topbar of its own. The grid clears the fixed wordmark/
   burger via the same top inset the home flow uses (32 + 28 + space-lg). */
#sC .wk-main { padding: calc(32px + 28px + var(--space-lg)) var(--gutter) var(--space-3xl); }

#sC .wk-grid {
  /* WK-11 r2 (Jacky 2026-08-27): not the level 2-up rows — PINTEREST.
     "好似Pinterest咁…衣家啲比例唔好睇": the flat 3/2 pairs read wrong,
     he wants every artwork at its own proportion in two ragged columns.
     That is exactly the desktop masonry's structure (WK-9), so mobile
     borrows it: the two .wk-col wrappers become real column stacks and
     each tile's media rides its own --wk-ar. Captions stay below the
     tile (the desktop hover-band model needs hover); the column raggedness
     includes them, which is the Pinterest rhythm. */
  list-style: none; display: flex; align-items: flex-start;
  /* WK-5 r2 (Jacky 2026-08-18, two rounds): --space-xl read as too much
     air between the rounded cards; 24px still too much — he called 12-16px
     on the token scale, and --space-xs (12px) is the one token in that
     band. The below-tile caption's own margin keeps the text off the next
     card. */
  gap: var(--space-xs);
}
/* WK-9 r2: the markup carries two .wk-col wrappers — the desktop masonry
   stacks. It USED to be a clean odd-left / even-right split, but dropping
   the 06 placeholder left five tiles at 3/2 and the right column ended
   730px short of the left (Jacky, 2026-08-25). 05 moved across, so the
   split is now 01/03 left, 02/04/05 right and the columns land within
   ~120px of each other. The Z reading order holds for the first four; 05
   simply lands under 04. In the mobile flow the wrappers dissolve
   (display: contents) and CSS order restores the 01→05 sequence
   in the single column. */
/* WK-11 r2: the wrappers are REAL columns at every width now (they were
   display:contents in the old single-column flow). The intra-column
   order rules keep each stack ascending; across columns the Z reading
   (01 left, 02 right, 03 left…) is the markup split, same as desktop. */
#sC .wk-col {
  display: flex; flex-direction: column; gap: var(--space-xs);
  flex: 1 1 0; min-width: 0;
}
#sC .wk-col:nth-child(1) .wk-tile:nth-child(1) { order: 0; }   /* 01 */
#sC .wk-col:nth-child(1) .wk-tile:nth-child(2) { order: 2; }   /* 03 */
#sC .wk-col:nth-child(2) .wk-tile:nth-child(1) { order: 1; }   /* 02 */
#sC .wk-col:nth-child(2) .wk-tile:nth-child(2) { order: 3; }   /* 04 */
#sC .wk-col:nth-child(2) .wk-tile:nth-child(3) { order: 4; }   /* 05 */

#sC .wk-tile-link { display: block; text-decoration: none; color: inherit; }
/* WK-5 (Jacky 2026-08-13): tiles round off — the gallery reads softer, closer
   to the brand's quiet voice. Surface-scale token; overflow already clips the
   artwork to the corner. The focus ring follows the same radius. */
#sC .wk-tile-link:focus-visible { border-radius: var(--radius-md); }
#sC .wk-tile-media {
  /* WK-11 r2: each artwork at its own proportion (the markup's --wk-ar),
     3/2 only as the placeholder fallback — the Pinterest rag comes from
     here. r3: --wk-ar-m (set per case in the <=1099 block) wins on the
     phone — the mobile re-cuts. Desktop overrides all of this (the LINK
     owns the aspect there, for the hover-band mechanics). */
  display: block; position: relative; aspect-ratio: var(--wk-ar-m, var(--wk-ar, 3 / 2));
  background: var(--neutral); overflow: hidden;
  border-radius: var(--radius-md);
}
#sC .wk-tile-idx {
  position: absolute; top: 50%; left: 50%; transform: translate(-50%, -50%);
  font-size: var(--fz-microcopy); font-variant-numeric: tabular-nums;
  /* Sits ON the --neutral tile, not the page bg — --fg-faint/-muted both fail
     WCAG AA there (3.39–4.27:1). Mix toward the surface it actually sits on:
     65% fg → 6.1:1 dark / 5.5:1 light, still reads as a quiet watermark. */
  color: color-mix(in srgb, var(--fg) 65%, var(--neutral));
}
/* WK-2: tiles with real artwork. The hero rides its cover-crop safe zone
   (built for arbitrary crops — see case-study.md), so object-fit: cover is
   safe at any tile aspect. The watermark idx yields to the image — its role
   on placeholder tiles was "something lives here"; artwork says that itself.
   (Whether an index reappears over art is a WK-1 design call.) */
#sC .wk-tile-img {
  position: absolute; inset: 0;
  width: 100%; height: 100%;
  object-fit: cover; display: block;
}
#sC .wk-tile-media--art .wk-tile-idx { display: none; }
#sC .wk-tile-cap  { display: block; margin-top: var(--space-xs); }
#sC .wk-tile-name { display: block; font-size: var(--fz-body); font-weight: 500; color: var(--fg); }
#sC .wk-tile-meta { display: block; font-size: var(--fz-microcopy); color: var(--fg-muted); margin-top: 2px; }
/* WK-11: on the 2-up bento a ~165px card can't carry a 16px name —
   "Modernizing a Legacy Enterprise UI" ran three lines. The caption
   steps down to the microcopy tier; name and meta then share a size and
   split by weight + ink, the rail's own hierarchy grammar. Desktop's
   hover band keeps the 16px name (its tiles are far wider). */
@media (max-width: 1099px) {
  #sC .wk-tile-name { font-size: var(--fz-microcopy); }

  /* ── WK-11 r3 (Jacky 2026-08-27) — four calls on the phone masonry ──
     ① smaller cards, smaller corner: radius-md's 16px read heavy at
        ~170px, step down to --radius-sm (the focus ring follows);
     ② the grid hugs the screen: the 24px gutter breaks down to the
        hairline --space-2xs so the cards claim the width;
     ③ less horizontal: per-case mobile re-cuts (--wk-ar-m) pull the
        wide thumbs toward square/portrait — the desktop crops stay
        untouched. Cover-crops are safe (WK-2 safe-zone rule);
     ④ captions hide like desktop — they return through the PEEK
        interaction below. */
  #sC .wk-tile-media { border-radius: var(--radius-sm); }
  #sC .wk-tile-link:focus-visible { border-radius: var(--radius-sm); }
  #sC .wk-main { padding-inline: var(--space-2xs); }
  #sC .wk-tile-link[data-case="splunkbase"]  { --wk-ar-m: 1 / 1; }
  #sC .wk-tile-link[data-case="splunk"]      { --wk-ar-m: 4 / 3; }
  #sC .wk-tile-link[data-case="google-maps"] { --wk-ar-m: 1 / 1; }
  #sC .wk-tile-link[data-case="solo"]        { --wk-ar-m: 4 / 5; }
  /* (splunk-internship keeps its 4/5 — already portrait.) */

  /* ── WK-11 r3/r4: the PEEK. r4 (Jacky): the lift is a real EXPANSION —
     the tapped card grows into a wide 16:9 feature card floating over
     the blurred gallery, with the caption BELOW the artwork like the
     desktop resting grammar, and collapses back to its grid spot when
     you tap outside. Mechanics: .is-peek turns the LINK into a fixed,
     vertically-centred card (inset + margin:auto — no transform, that
     axis belongs to the FLIP animation index.html drives for the
     grow/shrink travel); the tile keeps its slot via a JS height lock
     so the masonry never reflows behind the blur. Second tap enters. */
  #sC .wk-tile-link { position: relative; display: block; }
  #sC .wk-tile-cap {
    /* Hidden in the grid (r3: captions hide like desktop) but kept in
       the layer — absolute, so it costs the card no height until the
       expansion needs it. r10: the type doesn't just fade — it rests
       8px LOW, so entering the peek it rises into place and leaving it
       sinks back out (Jacky: 「微微向上 fade in… exit 落去」). This rule
       is the exit clock too (destination state on collapse): no delay,
       t-base — the caption clears first, then the card flies home. */
    position: absolute; left: 0; right: 0; top: 100%;
    margin-top: 0; padding-top: var(--space-xs);
    opacity: 0; pointer-events: none;
    transform: translateY(var(--space-2xs));
    transition:
      opacity var(--t-base) cubic-bezier(0.22, 1, 0.36, 1),
      transform var(--t-base) cubic-bezier(0.22, 1, 0.36, 1);
  }
  /* The filled wk-rise animation makes .wk-tile a CONTAINING BLOCK for
     fixed descendants (measured: the card anchored to its own cell, not
     the viewport). The entry cascade is long over by tap time — drop it
     while peeked so the fixed card really answers to the viewport. */
  #sC.scene.active .wk-tile.is-peek { animation: none; }   /* must outrank the (1,3,0) entry rule */
  #sC .wk-tile.is-peek .wk-tile-link {
    position: fixed; z-index: 41;
    left: var(--gutter); width: calc(100vw - 2 * var(--gutter));
    top: 0; bottom: 0; margin-block: auto;   /* explicit width; the auto pair centres vertically */
    height: fit-content;
    will-change: transform;
  }
  #sC .wk-tile.is-peek .wk-tile-media {
    aspect-ratio: 16 / 9;                 /* the feature cut (Jacky: 打橫 16:9) */
    border-radius: var(--radius-md);      /* big card, big corner again */
  }
  #sC .wk-tile.is-peek .wk-tile-cap {
    position: static; padding-top: var(--space-xs);
    opacity: 1; pointer-events: auto;
    transform: none;                      /* r10: …and rises the 8px home */
    /* r11: the rise must play on a STILL card. r10 ran it at .15s/.3s —
       entirely inside the 0.6s FLIP flight, where the card's own scaling
       swallowed the 8px whole (Jacky: 「睇唔到有冇 animation」). Wait out
       the flight, then rise on the slow clock. */
    transition-duration: var(--t-slow);
    transition-delay: var(--t-slow);
  }
  /* r11: choreographed exit, phase 1 — on dismissal JS holds the card for
     ~170ms with this class on: the type sinks out FAST on the still card,
     and only then does the collapse FLIP fly it home (phase 2). Delay 0,
     t-fast, and (1,4,0) so it outranks the entry rule above. */
  #sC .wk-tile.is-peek.is-peek-leaving .wk-tile-cap {
    opacity: 0; transform: translateY(var(--space-2xs));
    transition-duration: var(--t-fast);
    transition-delay: 0s;
  }
  #sC .wk-tile.is-peek .wk-tile-name { font-size: var(--fz-body); }   /* desktop-scale name on the wide card */
  /* The ground gives way — gate-scrim recipe (blur + desaturate). r6: the
     blur must FADE both ways — declaring it only while up made the dismiss
     snap (backdrop-filter jumped to none the instant the class dropped,
     Jacky: 「一眨眼就即刻消失」). So the resting state declares an explicit
     blur(0) — interpolating from `none` is the classic snap bug — and the
     transition covers backdrop-filter too. The old "no idle blur layer"
     perf concern is handled by visibility instead: hidden at rest (skips
     paint entirely), flipped with 0s delay on open / flip-out delay on close
     so it never clips the fade. Lives inside #sC: hides with the scene.
     r7→r9 — the collapse clock (--wk-flip-out, also read by flipLink's
     inline transition in index.html). r7 stretched it to 0.9s because the
     shrink "felt faster" than the expand — but that feeling was really the
     r8 LAYERING bug (the card dived under later tiles mid-flight and read
     as vanishing early). With stacking fixed, 0.9s read as dragging, and
     Jacky called it back to the expand's own speed: both directions ride
     --t-slow. The token stays (one knob for card + dim together) and the
     resting rule below carries it so the CLOSE transition (the destination
     state's transition wins) and the card travel share one clock. */
  #sC { --wk-flip-out: var(--t-slow); }
  #sC .wk-peek-dim {
    position: fixed; inset: 0; z-index: 40;
    background: color-mix(in srgb, var(--bg) 60%, transparent);
    opacity: 0; pointer-events: none; visibility: hidden;
    -webkit-backdrop-filter: blur(0px) saturate(1);
    backdrop-filter: blur(0px) saturate(1);
    transition:
      opacity var(--wk-flip-out) cubic-bezier(0.22, 1, 0.36, 1),
      -webkit-backdrop-filter var(--wk-flip-out) cubic-bezier(0.22, 1, 0.36, 1),
      backdrop-filter var(--wk-flip-out) cubic-bezier(0.22, 1, 0.36, 1),
      visibility 0s var(--wk-flip-out);
  }
  body.wk-peek #sC .wk-peek-dim {
    opacity: 1; pointer-events: auto; cursor: pointer; visibility: visible;
    -webkit-backdrop-filter: blur(10px) saturate(0.9);
    backdrop-filter: blur(10px) saturate(0.9);
    transition:
      opacity var(--t-slow) cubic-bezier(0.22, 1, 0.36, 1),
      -webkit-backdrop-filter var(--t-slow) cubic-bezier(0.22, 1, 0.36, 1),
      backdrop-filter var(--t-slow) cubic-bezier(0.22, 1, 0.36, 1),
      visibility 0s;
  }
}
@media (prefers-reduced-motion: reduce) {
  #sC .wk-tile-link, #sC .wk-tile-cap, #sC .wk-peek-dim { transition: none; }
}

/* ---------------- desktop stage (>=1100; viewport-locked) ---------------- */
@media (min-width: 1100px) {
  /* Slim fixed rail down the left gutter; clicks pass through to the grid.
     Geometry + type mirror .sa-mini / .sa-mini-nav EXACTLY (see index.html)
     so the swap from the home nav is seamless — same anchor, width, rhythm. */
  #sC .wk-rail { display: block; position: fixed; inset: 0; pointer-events: none; z-index: 30; }
  #sC .wk-rail > * { pointer-events: auto; }
  #sC .wk-railnav {
    /* 53% = the site-wide rail anchor (home .sa-mini / About .ab-railnav) —
       the nav must NOT move during scene swaps; 56% was the pre-redesign
       value and made the rail jump on home→Work. */
    position: absolute; left: var(--gutter); top: 53%; transform: translateY(-50%);
    width: 200px; display: flex; flex-direction: column;
  }
  #sC .wk-railnav a {
    display: flex; align-items: baseline; gap: var(--space-sm); padding: var(--space-xs) 0;
    width: 100%; line-height: 1; text-decoration: none; color: var(--fg-muted);
    transition: opacity var(--t-base) ease, color var(--t-base) ease;
  }
  #sC .wk-railnav a.is-active { color: var(--fg); }   /* current scene highlighted */
  #sC .wk-rl-meta {
    font-size: var(--fz-microcopy); font-weight: 500; letter-spacing: .05em;
    color: var(--fg-faint); min-width: 22px; line-height: 1;
  }
  #sC .wk-railnav a.is-active .wk-rl-meta { color: var(--fg); }
  /* Match the home nav EXACTLY: that nav renders each letter as a separate
     inline-block <span class="ch">, so the browser can't kern across the letters.
     Plain text here would kern (auto) and read tighter — so disable kerning +
     ligatures to mirror the home nav's letter spacing. */
  #sC .wk-rl-label {
    /* BR-7 bold diet: rest = regular; bold marks hover + the current scene. */
    font-size: var(--fz-mid); font-weight: 400; line-height: 1; text-transform: uppercase; color: inherit;
    letter-spacing: var(--nav-tracking);              /* "letter-spaced" nav per the brief */
    font-kerning: none; font-variant-ligatures: none; /* …and un-kerned, to match the home nav's per-char rendering */
  }
  #sC .wk-railnav a.is-active .wk-rl-label { font-weight: 700; }
  /* Dim-siblings hover — identical to the home nav (bold voice, BR-7). */
  /* .rail-settle: dim suppressed until the first mousemove after a swap-in
     — see .sa-mini-nav (anti-flicker without hover lag). */
  #sC .wk-railnav:hover a { opacity: .4 !important; } /* BR-8: .25 → .4, see index.html note */
  #sC .wk-railnav a:hover { opacity: 1 !important; color: var(--fg); }
  #sC .wk-railnav a:not(.is-soon):hover .wk-rl-label { font-weight: 700; }
  #sC .wk-railnav.rail-settle:hover a { opacity: 1 !important; }
  /* PLAY is disabled site-wide (no scene yet) — the label refuses to light.
     Cursor + SOON-tag rules are global in index.html (a.is-soon / .nav-soon). */
  #sC .wk-railnav a.is-soon:hover { color: var(--fg-muted); }

  /* The grid lands in cols 3–8 — the spatial region the portrait occupied. */
  /* WK-9 (Jacky 2026-08-22): the viewport-locked bento retires. Squeezing
     all six cases into one unscrolled stage was exactly what flattened
     tiles 02/03 into letterbox slivers — he'd rather the gallery SCROLL
     ("好似 Pinterest 咁…不斷碌落去") with every artwork at an honest
     proportion and the sizes varying tile to tile. So: .wk-main becomes
     the scene's scroller (the case-study .cs-main pattern — html/body are
     scroll-locked on desktop), and the grid becomes a two-column masonry
     via CSS columns — equal widths, per-tile aspect ratios (--wk-ar,
     nth-child pattern below) giving the varied-height Pinterest rhythm.
     The WK-8 hover (lift + pinned zoom + caption band) carries over: the
     link now owns its height through aspect-ratio and the absolute media
     fills it, so the band mechanics are untouched.
     WK-9 r2 (Jacky 2026-08-22): reading order is a Z — 01 top-left, 02
     top-right, 03 back on the left… CSS columns fill column-first, so the
     masonry is two REAL column stacks instead (markup .wk-col wrappers,
     odd tiles left / even right — the Pinterest structure). Each tile's
     aspect rides inline as --wk-ar in the markup (4/3 · 16/10 · 4/5 ·
     3/2 · 1/1 · 4/3). */
  /* padding: 0 kills the mobile-flow inset — the grid is in flow inside the
     scroller now, so the mobile padding would double up on the left margin. */
  #sC .wk-main { position: absolute; inset: 0; overflow-y: auto; padding: 0; }
  #sC .wk-grid {
    display: flex; align-items: flex-start; gap: var(--space-sm);
    margin: 0 0 0 calc(var(--gutter) + 2 * var(--col));
    width: calc(6 * var(--col));
    padding: var(--space-3xl) 0;
  }
  #sC .wk-col {
    display: flex; flex-direction: column; gap: var(--space-sm);
    flex: 1 1 0; min-width: 0;
  }
  /* The tile OWNS its proportion — media fills it absolutely (WK-8 block). */
  #sC .wk-tile-link { display: block; aspect-ratio: var(--wk-ar, 3 / 2); }
  #sC .wk-tile-media { height: 100%; aspect-ratio: auto; }
}
/* Masonry + touch-desktop edge (≥1100, no hover): the tile height is fixed
   by --wk-ar, so the below-tile caption of WK-4's touch branch would break
   the column rhythm — on this rare combo the caption stays an always-visible
   overlay (same scrim as the old hover one). */
@media (min-width: 1100px) and (hover: none) {
  #sC .wk-tile-link { position: relative; }
  #sC .wk-tile-cap {
    position: absolute; left: 0; right: 0; bottom: 0; margin-top: 0;
    padding: var(--space-xs) var(--space-xs);
    background: color-mix(in srgb, var(--bg) 82%, transparent);
    -webkit-backdrop-filter: blur(10px);
    backdrop-filter: blur(10px);
    border-radius: 0 0 var(--radius-md) var(--radius-md);
  }
}

/* WK-8 r2 (Jacky 2026-08-21/22, direction A — 空間回應): WK-3's Ken Burns
   zoom + WK-4's blur scrim stay retired ("old school"), and r1's A+B
   cursor-lean + trailing chip are OUT too — the lean read as jitter ("核突"),
   and a hover-only cursor chip made the caption (important content) both
   inconspicuous and an a11y worry. Direction A instead: the ARTWORK NEVER
   TRANSFORMS and nothing ever overlays it. On hover/focus the whole card
   lifts gently (5px + a soft shadow) and — on the desktop bento, where the
   caption has no resting home — the photo YIELDS SPACE: its bottom edge
   eases up and the caption rises into the opened band through a clipped
   mask, line by line (name then meta) — the mobile menu's reveal grammar
   moved onto the tile. Pure CSS, no JS. Below 1100 the caption already
   rests under the tile, so only the lift applies. Touch: unchanged
   (caption below tile / no-hover overlay fallback). Reduced motion:
   no lift, no travel — the caption band simply appears.
   WK-8 r4 (Jacky 2026-08-22): the zoom-in RETURNS — his r3 note was a
   correction, the thumbnail SHOULD push in on hover ("我想要果個 Zoom in
   效果"). WK-3's Ken Burns grammar (scale 1 → 1.05 over --t-reveal) rides
   on top of r3's pinned base: the pin stops the band-open from reading as
   a zoom-OUT, the scale adds the push-IN.
   WK-11 r6: min-width added — below 1100 the PEEK owns the interaction,
   and a mouse on the narrow layout made hover fight the collapse FLIP
   (tap holds :hover, dim-tap releases it → the −5px lift dropped DURING
   the flight, then re-applied when the card landed under the cursor —
   Jacky's 「退落咗跟住退上咗」wobble). Hover motion is desktop-bento-only. */
@media (min-width: 1100px) and (hover: hover) and (pointer: fine) {
  #sC .wk-tile-link {
    transition: transform var(--t-slow) cubic-bezier(0.22, 1, 0.36, 1);
  }
  #sC .wk-tile-link:hover,
  #sC .wk-tile-link:focus-visible { transform: translateY(-5px); }
  /* Soft ink-navy shadow under the lifted surface (the media is the visible
     surface; the link box is transparent). Quiet by design — in dark it
     reads as a faint deepening, in light as a soft paper lift. */
  #sC .wk-tile-media {
    transition: box-shadow var(--t-slow) cubic-bezier(0.22, 1, 0.36, 1);
  }
  #sC .wk-tile-link:hover .wk-tile-media,
  #sC .wk-tile-link:focus-visible .wk-tile-media {
    box-shadow: 0 16px 40px -14px rgba(1, 5, 14, 0.4);
  }
  /* r4 zoom-in — the media's overflow: hidden clips the overscan. */
  #sC .wk-tile-img {
    transition: transform var(--t-reveal) cubic-bezier(0.22, 1, 0.36, 1);
  }
  #sC .wk-tile-link:hover .wk-tile-img,
  #sC .wk-tile-link:focus-visible .wk-tile-img { transform: scale(1.05); }
}

/* Desktop bento + cursor: the in-card caption reveal. The media is absolute
   over the full cell; the caption sits in the card's bottom band BEHIND it
   (opaque photo / --neutral hides it at rest). Hover shrinks the media's
   height by the band — space yields — while the lines rise through the
   band's overflow mask with the menu's stagger (name, then meta 60ms
   later; collapse drops delays to 0, the menu-close grammar).
   WK-8 r3 (Jacky 2026-08-22): the IMAGE must not shrink with the box —
   r2 let object-fit: cover re-solve against the shortened media, so the
   photo visibly scaled down (worst on the wide 1-row tiles, ~30% of
   their height). Now the img's height counter-grows by the same band
   (100% → 100% + --wk-cap-h) on the same clock/easing, so its rendered
   size stays EXACTLY the cell size the whole way — the shrinking media
   is just a crop window sliding up over a pinned photo. */
@media (min-width: 1100px) and (hover: hover) and (pointer: fine) {
  #sC { --wk-cap-h: 64px; }
  #sC .wk-tile-link { position: relative; }
  #sC .wk-tile-media {
    position: absolute; top: 0; left: 0; right: 0; height: 100%;
    transition:
      height var(--t-slow) cubic-bezier(0.22, 1, 0.36, 1),
      box-shadow var(--t-slow) cubic-bezier(0.22, 1, 0.36, 1);
  }
  #sC .wk-tile-link:hover .wk-tile-media,
  #sC .wk-tile-link:focus-visible .wk-tile-media { height: calc(100% - var(--wk-cap-h)); }
  /* bottom: auto frees the img from inset's over-constraint so height rules;
     identical duration + easing as the media keeps the two in lockstep.
     transition re-lists transform (r4 zoom) — it would otherwise be
     clobbered, the shorthand replaces the wider block's list wholesale. */
  #sC .wk-tile-img {
    bottom: auto; height: 100%;
    transition:
      height var(--t-slow) cubic-bezier(0.22, 1, 0.36, 1),
      transform var(--t-reveal) cubic-bezier(0.22, 1, 0.36, 1);
  }
  #sC .wk-tile-link:hover .wk-tile-img,
  #sC .wk-tile-link:focus-visible .wk-tile-img { height: calc(100% + var(--wk-cap-h)); }
  #sC .wk-tile-cap {
    position: absolute; left: 0; right: 0; bottom: 0; height: var(--wk-cap-h);
    margin: 0; padding-top: var(--space-xs);
    display: flex; flex-direction: column; justify-content: flex-end;
    overflow: hidden;          /* the reveal mask */
    pointer-events: none;      /* the whole tile stays the click target */
  }
  #sC .wk-tile-name, #sC .wk-tile-meta {
    transform: translateY(130%); opacity: 0;
    transition:
      transform 0.45s cubic-bezier(0.22, 1, 0.36, 1),
      opacity 0.45s cubic-bezier(0.22, 1, 0.36, 1);
  }
  #sC .wk-tile-link:hover .wk-tile-name,
  #sC .wk-tile-link:focus-visible .wk-tile-name {
    transform: none; opacity: 1; transition-delay: 90ms;
  }
  #sC .wk-tile-link:hover .wk-tile-meta,
  #sC .wk-tile-link:focus-visible .wk-tile-meta {
    transform: none; opacity: 1; transition-delay: 150ms;
  }
}

/* Reduced motion: no lift, no shadow bloom, no travel — but the caption
   band still appears on hover/focus (content stays reachable, instantly). */
@media (prefers-reduced-motion: reduce) {
  #sC .wk-tile-link { transform: none !important; transition: none !important; }
  #sC .wk-tile-media, #sC .wk-tile-img, #sC .wk-tile-name, #sC .wk-tile-meta { transition: none !important; }
  #sC .wk-tile-img { transform: none !important; }
  #sC .wk-tile-name, #sC .wk-tile-meta { transform: none; }
}

/* Reduced motion — show everything, no rise/fade. */
@media (prefers-reduced-motion: reduce) {
  #sC.scene.active .wk-tile { animation: none; }
}
