/* chapters.css: Chapter I (piano) and Chapter II (football) — the intro column, the film portal (device, screen,
   captions, HUD, iris / shutter) and their reduced-motion / no-JS forms. SPEC §3.11, §3.21.
   Pinned heights, underlaps and the sticky .stage are owned by base.css (§3.9.1) and are NOT redefined here.
   film.js writes per-frame transforms / clip-paths / opacities inline and these custom properties on .film (refresh):
     --dw/--dh  the full-bleed box = the stage + 1 px bleed per side (px): the device is laid out at this size
     --sw/--sh  the resting 16:9 card (px)  · --mw/--mh  the 16:9 media box that covers --dw × --dh (px)
   Full-bleed at every aspect: once zoomed in, the film covers the whole stage on phones and portrait windows too
   (film.js crops it around a focus that follows the subject). No hex colours here: tokens.css or rgba() only. */

/* ================================================================ chapter stage */
.chapter{color:var(--t-fg)}
.chapter>.stage>.view,#between>.stage>.view{position:absolute;inset:0}

/* ---------------------------------------------------------------- intro column (§3.21) */
.chapter-intro{position:absolute;z-index:2;top:16vh;inset-inline-start:var(--gutter);width:38vw;
  display:flex;flex-direction:column;align-items:flex-start;pointer-events:none}
[data-intro]{--exit:var(--exit-far)}
.chapter-intro :is(a,button){pointer-events:auto}
.chapter-intro .topline{align-self:stretch;margin-bottom:26px}
.chapter-intro .eyebrow{margin-bottom:18px}
.h-chapter{font-size:var(--fs-h2);font-weight:500;letter-spacing:var(--tr-display);line-height:var(--lh-display)}
.chapter-copy{margin-top:28px;max-width:38ch;font-size:var(--fs-body);line-height:var(--lh-body)}
.chapter-meta{margin-top:18px;font:600 var(--fs-label)/1.45 var(--font-latin);letter-spacing:var(--tr-label);text-transform:uppercase;opacity:.72}
:lang(he) :is(.chapter-meta,.score){font-family:var(--font-he);letter-spacing:.04em}
.chapter-intro :is(.sound-cta,.kick-btn){margin-top:26px}
.chapter-intro .hint{margin-top:14px}
.chapter-intro .hint+.hint{margin-top:4px}
.score{margin-top:14px;font:600 var(--fs-label)/1.45 var(--font-latin);letter-spacing:var(--tr-label);text-transform:uppercase}
.score b{font-weight:700;font-variant-numeric:tabular-nums}
@media (max-width:700px){
  .chapter-intro{top:12vh;top:12svh;inset-inline:var(--gutter);width:auto}
  .chapter-intro .topline{margin-bottom:18px}
  .chapter-copy{margin-top:18px;font-size:15px}
  .chapter-intro :is(.sound-cta,.kick-btn){margin-top:18px}
}
/* Short phones (≤ 700 px tall — most phones with their toolbars showing): the personal story stays (QA: hiding it
   meant many visitors never read it). The column tightens instead — a higher start, tighter gaps, a smaller title,
   the copy at 13 px (≤ 5 lines) — and gives up only the piano's credit line and the football's closing hint. */
@media (max-width:700px) and (max-height:700px){
  .chapter-intro{top:8vh;top:8svh}
  .chapter-intro .topline{margin-bottom:12px}
  .chapter-intro .eyebrow{margin-bottom:12px}
  .chapter-intro .h-chapter{font-size:clamp(36px,10.5vw,44px)}
  .chapter-copy{margin-top:12px;font-size:13px;line-height:1.4;max-height:7em;overflow-y:clip;overflow-x:visible}
  .chapter-intro :is(.sound-cta,.kick-btn){margin-top:14px}
  #piano .chapter-meta,#football .chapter-intro>.hint{display:none}
}

/* GOAL! / SAVED! bursts (football.js plays / resets them through kinetic; this is their look). Anchored over the goal,
   not across the stage (review 2026-10-02): football.js writes --bx / --by (stage px, physical: the top centre of the
   box, sitting on the crossbar) — inside the end-side half beside the intro column on desktop, below the intro column
   on phones — so they never cover the intro copy or the kick button. The fallbacks hold the same places. */
.goal-burst{position:absolute;z-index:3;left:var(--bx,71%);top:var(--by,16%);width:max-content;margin:0;transform:translateX(-50%);
  text-align:center;white-space:nowrap;pointer-events:none;
  font-size:calc(var(--fs-h2) * 1.5);font-weight:500;letter-spacing:var(--tr-display);line-height:.9;color:var(--dark-ink);
  opacity:0;transition:opacity 300ms linear}
[dir=rtl] .goal-burst{left:var(--bx,29%)}
@media (max-width:700px){.goal-burst,[dir=rtl] .goal-burst{left:var(--bx,50%);top:var(--by,64%)}}
.goal-burst:is([data-k=in],[data-k=done]){opacity:1}
.goal-burst[data-k^=pre] .k-c{transform:translate3d(0,45%,0) scale(.35,2.1);opacity:0}
.goal-burst[data-k=in] .k-c{transform-origin:50% 100%;transition:transform calc(var(--d-land) * var(--tempo)) var(--ease-back) calc(var(--ci,0) * 45ms),opacity 300ms linear calc(var(--ci,0) * 45ms)}

/* WebGL off: each chapter anchor shows its poster at 35 % (§3.13.2) */
html.no-webgl :is(#piano,#football)>.stage>.view::before{content:"";position:absolute;inset:0;background:center/cover no-repeat;opacity:.35}
html.no-webgl #piano>.stage>.view::before{background-image:url(/assets/media/piano-v2-first.jpg)}
html.no-webgl #football>.stage>.view::before{background-image:url(/assets/media/football-v2-first.jpg)}
/* … until the film is full-bleed (film.js .is-film-full): the iris / shutter then reveal the next section, not it */
html.no-webgl .is-film-full>.stage>.view::before{visibility:hidden}

/* ================================================================ the film portal (§3.11) */
.film{position:absolute;inset:0;z-index:4;pointer-events:none;visibility:hidden}
.film.is-active{visibility:visible}
.film-frame{position:absolute;inset:0;overflow:hidden}
/* (the old band-mode letterbox: the film is full-bleed at every aspect now — the element is unused) */
.film-letterbox{display:none}
.film-floor{position:absolute;left:50%;top:calc(50% + var(--sh,470px) * .5 + 14px);width:calc(var(--sw,835px) * 1.08);height:calc(var(--sh,470px) * .2);
  transform:translate(-50%,-50%);border-radius:50%;background:radial-gradient(closest-side,rgba(20,21,34,.36),rgba(20,21,34,0));filter:blur(6px);opacity:0}

/* Device: laid out at the full-bleed size (always sharp at e = 1), scaled uniformly by JS and clip-path-ed to the
   16:9 card while it rises and opens (the screen's clip; the rim's reaches 14 px past it with negative insets). */
.film-device{position:absolute;left:50%;top:50%;width:var(--dw,100vw);height:var(--dh,100vh);transform:translate(-50%,-50%);transform-origin:50% 50%;
  pointer-events:auto;backface-visibility:hidden}
.film-rim{position:absolute;inset:0;background:linear-gradient(145deg,rgba(42,44,58,1),rgba(14,15,22,1))}
.film-screen{position:absolute;inset:0;overflow:hidden;background:var(--dark);isolation:isolate}
/* The pictures: a 16:9 box covering the full-bleed box, centred; film.js moves it with a transform (the cover scale
   into the card + the focus pan + Ken Burns / iris push), so the crop follows the subject on a tall screen. */
.film-poster,.film-screen video{position:absolute;left:50%;top:50%;width:var(--mw,100%);height:var(--mh,100%);max-width:none;
  margin:calc(var(--mh,0px) * -.5) 0 0 calc(var(--mw,0px) * -.5);object-fit:cover;transform-origin:50% 50%}
.film-screen video{opacity:0;transition:opacity 300ms linear}
.film.is-ready .film-screen video{opacity:1}
.film-glass{position:absolute;inset:0;z-index:3;pointer-events:none;opacity:.55;
  background:linear-gradient(108deg,rgba(255,255,255,0) 60%,rgba(255,255,255,.2) 68%,rgba(255,255,255,.05) 76%,rgba(255,255,255,0) 84%)}

/* (The v2 films are clean renders: the old ✦ watermark patch is gone.) */

/* Caption shade: lifts contrast behind the active caption (tone + anchor written by film.js). */
.film-shade{position:absolute;inset:0;z-index:2;pointer-events:none;opacity:0;transition:opacity 400ms linear}
.film-shade.is-on{opacity:1}
.film-shade[data-tone=dark]{background:radial-gradient(ellipse 52% 58% at var(--sx,30%) var(--sy,45%),rgba(240,240,250,.55),rgba(240,240,250,0) 60%)}
.film-shade[data-tone=light]{background:radial-gradient(ellipse 50% 55% at var(--sx,30%) var(--sy,45%),rgba(11,13,24,.4),rgba(11,13,24,0) 70%),
  linear-gradient(to top,rgba(11,13,24,.6),rgba(11,13,24,0) 50%)}

/* Football shutter: the frozen frame in 5 vertical strips (drawn once into canvases by film.js). */
.film-strips{position:absolute;inset:0;z-index:2;visibility:hidden;pointer-events:none}
.film-strips canvas{position:absolute;top:0;left:calc(var(--i,0) * 20%);display:block;width:calc(20% + .5px);height:100%;max-width:none}
.film.is-shutter .film-strips{visibility:visible}
.film.is-shutter :is(.film-device,.film-floor){visibility:hidden}
/* While an exit runs (film.js .is-film-exit on the section, x > 0) the chapter lets the pointer through to the section
   it reveals (#between / #work lie under it). Only explicitly live parts still catch it: the device inside the iris
   circle (clip-path hit-testing), the HUD buttons while the HUD shows, the F5 link until it is half gone (.is-gone).
   The shutter's full-frame device is off (.film.is-exit), so #work is live as soon as it shows between the strips. */
.chapter.is-film-exit{pointer-events:none}
.film.is-exit .film-device{pointer-events:none}
.cap.is-gone .cap-cta{pointer-events:none!important}
/* Football flare (17.0 s, DOM film only — the GL film grades its own bloom): a soft lilac glow around the vortex,
   opacity written by film.js (≤ .55 × a 2.4 s envelope). Never white, never a flash. */
.film-flash{position:absolute;inset:0;z-index:3;opacity:0;pointer-events:none;
  background:radial-gradient(ellipse 62% 54% at 50% 34%,rgba(223,226,250,.62),rgba(223,226,250,.18) 48%,rgba(223,226,250,0) 74%)}

/* ---------------------------------------------------------------- the GL film (SPEC-LUSION L6, scenes/cinema.js)
   .film.is-gl (film.js): the stage draws the film as a bending, graded plane behind <main>. The DOM tablet keeps
   only its box (the cursor target), the caption shade and the iris clip; its pictures go transparent. The <video>
   stays the decoder (opacity 0 still decodes: it is the plane's texture). */
.film.is-gl :is(.film-rim,.film-glass,.film-floor,.film-strips,.film-flash){visibility:hidden}
.film.is-gl .film-screen{background:none}
/* should GL take over while the film is on screen, the DOM picture fades out over the (graded) plane */
.film.is-gl :is(.film-poster,.film-screen video){opacity:0!important;transition:opacity 400ms linear}
/* The section under the football stage (O6: #craft, u 35) would paint over the canvas while the GL film is live:
   base.css makes it see-through on #football.is-cine-gl and holds its content until the shutter's gate (x ≥ .58). */

/* Native play (reduced motion only; film.js unhides it) */
.film-play{position:absolute;z-index:4;left:50%;top:50%;transform:translate(-50%,-50%);display:inline-flex;align-items:center;gap:12px;
  min-height:52px;padding-inline:18px 24px;border-radius:var(--r-pill);background:var(--glass-dark);color:var(--dark-ink);
  font:600 var(--fs-label)/1 var(--font-latin);letter-spacing:var(--tr-label);text-transform:uppercase;pointer-events:auto;
  -webkit-backdrop-filter:blur(10px);backdrop-filter:blur(10px);transition:background-color var(--d-xs) linear}
.film-play::before{content:"";width:0;height:0;border-style:solid;border-width:7px 0 7px 11px;border-color:transparent transparent transparent currentColor}
[dir=rtl] .film-play::before{transform:scaleX(-1)}
@media (hover:hover){.film-play:hover{background:var(--accent)}}
.film-play:focus-visible{background:var(--accent)}
:lang(he) .film-play{font-family:var(--font-he);letter-spacing:.04em}

/* ================================================================ captions (§3.11.6) */
/* Above the HUD (z 3): its top and bottom scrims must never grey a caption that reaches into them (a lower-third one
   sits right in the bottom scrim). No caption position overlaps a HUD control, so nothing is covered. */
.film-captions{position:absolute;inset:0;z-index:4;margin:0;padding:0;list-style:none;pointer-events:none}
.cap{position:absolute;--d:1;max-width:46vw;color:var(--dark-ink);transition:color 400ms linear}
.cap:not(.is-pre,.is-in,.is-out){visibility:hidden}
.cap{opacity:calc(1 - var(--xk,0))}   /* --xk = x² of a scroll-linked caption exit (P6, F5): the kicker leaves with the lines */
.cap[data-tone=dark]{color:var(--ink);text-shadow:0 1px 22px rgba(240,240,250,.45)}
.cap[data-tone=light]{color:var(--dark-ink);text-shadow:0 2px 26px rgba(11,13,24,.38)}
/* The small type of a caption (kicker, body) needs a tighter halo than the display title: the silver v2 frames put
   dark ink over a dark lid or sky and light ink over snow or cloud within the same caption (QA: caption 1). */
.cap[data-tone=dark] :is(.cap-kicker,.cap-body){text-shadow:0 0 2px rgba(240,240,250,.55),0 1px 16px rgba(240,240,250,.75)}
.cap[data-tone=light] :is(.cap-kicker,.cap-body){text-shadow:0 0 2px rgba(11,13,24,.45),0 1px 16px rgba(11,13,24,.6)}

/* positions (logical insets) */
.cap[data-pos=start-mid]{top:38%;inset-inline-start:var(--gutter);max-width:46vw}
.cap[data-pos=center]{top:50%;inset-inline:var(--gutter);max-width:none;text-align:center;transform:translateY(-50%)}
.cap[data-pos=start]{top:30%;inset-inline-start:var(--gutter);max-width:calc(100vw - 2 * var(--gutter))}
.cap[data-pos=end]{top:34%;inset-inline-end:var(--gutter);max-width:32ch;text-align:end}
.cap[data-pos=center-top]{top:24%;inset-inline:var(--gutter);max-width:none;text-align:center}
.cap[data-pos=start-bottom]{bottom:14%;inset-inline-start:var(--gutter);max-width:60vw}
.cap[data-pos=end-top]{top:max(14%,calc(var(--header-h) + 64px));inset-inline-end:var(--gutter);max-width:52vw;text-align:end}
.cap:is([data-pos=center],[data-pos=center-top]) .cap-body{margin-inline:auto}
.cap:is([data-pos=end],[data-pos=end-top]) .cap-body{margin-inline-start:auto}

/* type */
.cap-kicker{margin:0 0 20px;font:600 12px/1.4 var(--font-latin);letter-spacing:.16em;text-transform:uppercase}
:lang(he) .cap-kicker{font-family:var(--font-he);letter-spacing:.04em}
.cap-title{margin:0;font-size:var(--fs-h2);font-weight:500;letter-spacing:var(--tr-display);line-height:var(--lh-display)}
.cap-title--xl{font-size:10vw}
.cap-title--cta{font-size:9vw}
.cap-body{margin-top:22px;max-width:34ch;font-size:clamp(16px,1.4vw,22px);line-height:1.5}
.cap-kicker+.cap-body{margin-top:0}
.cap-body--lead{font-size:clamp(22px,2.4vw,40px);line-height:1.25;letter-spacing:-.01em;max-width:26ch}
.cap-cta{display:inline-block;color:inherit;text-decoration:none;transition:color var(--d-xs) linear}
.cap.is-in .cap-cta{pointer-events:auto}
@media (hover:hover){.cap-cta:hover{color:var(--accent-soft)}}
.cap-cta:focus-visible{color:var(--accent-soft)}

/* Screen-reader copy of the captions (film.js builds one <ol class="film-sr sr-only"> per film): it must stay in the
   accessibility tree while the film itself is visibility:hidden. Reduced motion lists the real captions instead. */
.film .film-sr{visibility:visible}
html.reduced .film .film-sr{display:none}

/* Caption motion. --side (per line) and --k/--ci/--cd (per char) come from kinetic's split; --d (1 forward, -1 back)
   is written by film.js at entry and at exit. A forward entry flows THROUGH the frame; scrolling back rewinds from
   the mirrored side. The !important + specificity keeps the timeline independent of kinetic's data-k state
   (kinetic's `[data-k^=pre] *{transition:none!important}` must not freeze a caption). */
.cap .k-l{--o:calc(var(--side,1) * var(--d))}
.cap .k-c{--kk:calc((1 + var(--d)) * .5 * var(--k,0) + (1 - var(--d)) * .5 * (1 - var(--k,0)))}
.cap :is(.cap-title,.cap-body) .k-li{opacity:0;transform:translate3d(calc(var(--o) * var(--dx-far)),0,0) skewX(calc(var(--o) * var(--skew)))}
.cap :is(.cap-title,.cap-body) .k-c{transform:translate3d(calc(var(--o) * var(--kk) * var(--lag)),0,0)}
.cap.is-pre :is(.cap-title,.cap-body) :is(.k-li,.k-c){transition:none!important}
.cap.is-in :is(.cap-title,.cap-body) .k-li{opacity:1;transform:none;
  transition:transform 1150ms var(--ease-out) calc(var(--li,0) * 90ms),opacity 500ms linear calc(var(--li,0) * 90ms),
    filter 300ms ease-out calc(var(--li,0) * 90ms)!important}
.cap.is-in :is(.cap-title,.cap-body) .k-c{transform:none;transition:transform 1350ms var(--ease-out) calc(var(--li,0) * 90ms)!important}
.cap.is-out :is(.cap-title,.cap-body) .k-li{opacity:0;transform:translate3d(calc(var(--o) * -12vw),0,0);
  transition:transform 520ms var(--ease-in) calc((2 - var(--li,0)) * 40ms),opacity 420ms linear,filter 420ms linear!important}
.cap.is-out :is(.cap-title,.cap-body) .k-c{transform:translate3d(calc(var(--o) * var(--kk) * .45em),0,0);transition:transform 520ms var(--ease-in)!important}
/* desktop: titles also come into focus (a transition, not a keyframe animation: kinetic's re-measure pass sets
   animation:none on split spans, which would restart a keyframe blur) */
@media (min-width:701px){
  .cap .cap-title .k-li{filter:blur(8px)}
  .cap.is-in .cap-title .k-li{filter:blur(0)}
}

/* wave (P1): chars rise through a mask (forward: up from below; back: down from above).
   The travel must clear the mask completely: the mask reaches .2em above and .28em below a .9em (Hebrew 1em) line,
   so a char parked 110 % (~.99em) away still showed a horizontal sliver of its glyphs at the mask's edge (below the
   line before the entry, above it for 1.4 s after the exit). 1.6em clears it in both languages, and the exit also
   fades the chars once they are out of the line. */
.cap[data-cap=wave] .cap-title .k-l{clip-path:inset(-.2em -.08em -.28em -.08em)}
.cap[data-cap=wave] .cap-title .k-li{opacity:1;transform:none;filter:none}
.cap[data-cap=wave] .cap-title .k-c{transform:translate3d(0,calc(var(--d) * 1.6em),0)}
.cap.is-in[data-cap=wave] .cap-title .k-li{transition:none!important}
.cap.is-in[data-cap=wave] .cap-title .k-c{transform:none;opacity:1;transition:transform 760ms var(--ease-back) min(calc(var(--ci,0) * 28ms),520ms)!important}
.cap.is-out[data-cap=wave] .cap-title .k-li{opacity:1;transform:none;transition:none!important}
.cap.is-out[data-cap=wave] .cap-title .k-c{transform:translate3d(0,calc(var(--d) * -1.6em),0);opacity:0;
  transition:transform 460ms var(--ease-in) min(calc(var(--ci,0) * 16ms),280ms),opacity 160ms linear calc(min(calc(var(--ci,0) * 16ms),280ms) + 300ms)!important}

/* stretch (F1, F5): chars stretch up from the baseline, centre outward; they flatten into the floor on exit */
.cap[data-cap=stretch] .cap-title .k-li{opacity:1;transform:none;filter:none}
.cap[data-cap=stretch] .cap-title .k-c{transform:translate3d(0,35%,0) scale(.35,2.1);transform-origin:50% 100%;opacity:0}
.cap.is-in[data-cap=stretch] .cap-title .k-li{transition:none!important}
.cap.is-in[data-cap=stretch] .cap-title .k-c{transform:none;opacity:1;
  transition:transform 760ms var(--ease-back) calc(var(--cd,0) * 30ms),opacity 350ms linear calc(var(--cd,0) * 30ms)!important}
.cap.is-out[data-cap=stretch] .cap-title .k-li{opacity:1;transform:none;transition:none!important}
.cap.is-out[data-cap=stretch] .cap-title .k-c{transform:translate3d(0,10%,0) scale(1.35,.2);opacity:0;
  transition:transform 420ms var(--ease-in) calc(var(--cd,0) * 14ms),opacity 300ms linear calc(var(--cd,0) * 14ms + 80ms)!important}

/* scroll-linked caption exits (P6 with the iris, F5 with the shutter): fade all the way out */
.cap [data-exit] .k-l{transform:translate3d(calc(var(--side,1) * var(--x,0) * var(--x,0) * var(--exit-far)),0,0);opacity:calc(1 - var(--x,0) * var(--x,0))}

/* kicker: a short lift */
.cap-kicker{opacity:0;transform:translate3d(0,8px,0);transition:opacity 320ms linear,transform 320ms var(--ease-out)}
.cap.is-pre .cap-kicker{transition:none}
.cap.is-in .cap-kicker{opacity:1;transform:none;transition-delay:200ms}

/* Unsplit fallback (kinetic missing): the whole title / body slides and fades */
.cap :is(.cap-title,.cap-body):not(:has(.k-l)){opacity:0;transform:translate3d(calc(var(--d) * 6vw),0,0)}
.cap.is-in :is(.cap-title,.cap-body):not(:has(.k-l)){opacity:1;transform:none;transition:transform 900ms var(--ease-out),opacity 500ms linear}
.cap.is-out :is(.cap-title,.cap-body):not(:has(.k-l)){opacity:0;transform:translate3d(calc(var(--d) * -6vw),0,0);transition:transform 520ms var(--ease-in),opacity 420ms linear}

/* ================================================================ HUD (§3.11.7) */
.film-hud{position:absolute;inset-inline:0;top:0;height:100%;height:100svh;z-index:3;pointer-events:none;color:var(--dark-ink);
  text-shadow:0 1px 12px rgba(0,0,0,.35);opacity:0;transition:opacity 400ms linear}
.film.is-hud .film-hud,.film-hud:focus-within{opacity:1}
/* Scrims so the small HUD type stays legible over the bright, silver v2 frames (QA: the hint and the tagline fell
   to ≈ 2:1 over the snow and the clouds). The bottom one carries the hint, the tagline, Skip and the sound button:
   .66 → 0 over 240 px, eased (a long, low shoulder instead of a visible band; the tagline sits ≈ 100 px up).
   They sit UNDER the HUD's type (the HUD is its own stacking context, the scrims at z −1): ::after used to paint
   last, over the bottom row, and greyed the very text it was meant to lift. */
.film-hud{isolation:isolate}
.film-hud::before,.film-hud::after{content:"";position:absolute;z-index:-1;inset-inline:0;pointer-events:none}
.film-hud::before{top:0;height:calc(var(--header-h) + 190px);background:linear-gradient(to bottom,rgba(11,13,24,.5),rgba(11,13,24,.32) 48%,rgba(11,13,24,.12) 80%,rgba(11,13,24,0))}
.film-hud::after{bottom:0;height:240px;background:linear-gradient(to top,rgba(11,13,24,.66),rgba(11,13,24,.52) 34%,rgba(11,13,24,.3) 62%,rgba(11,13,24,0))}
.film.is-hud .film-hud :is(button,a){pointer-events:auto}
.film-hud>*{position:absolute;margin:0}
.film-label,.film-hint,.film-time{font:600 var(--fs-label)/1.3 var(--font-latin);letter-spacing:var(--tr-label);text-transform:uppercase}
:lang(he) :is(.film-label,.film-hint,.film-sound-off,.film-skip){font-family:var(--font-he);letter-spacing:.04em}
.film-label{top:calc(var(--header-h) + 14px);inset-inline-start:var(--gutter)}
/* .film-time carries dir=ltr (digits read left-to-right), so its insets are physical and mirrored by the page dir */
.film-time{top:calc(var(--header-h) + 14px);right:var(--gutter);display:flex;align-items:flex-start;line-height:1;
  font-variant-numeric:tabular-nums;direction:ltr;opacity:.92}
[dir=rtl] .film-time{right:auto;left:var(--gutter)}
.ft-now{display:inline-flex}
.ft-d{display:inline-block;height:1em;overflow:hidden}
.ft-strip{display:block;transition:transform 280ms var(--ease-out)}
.ft-strip>span{display:block;height:1em;line-height:1}
.ft-sep{white-space:pre;opacity:.6}
.ft-total{opacity:.6}
.film-ring{top:calc(var(--header-h) + 12px);inset-inline-end:calc(var(--gutter) + 118px);width:16px;height:16px}
.film-ring svg{display:block;width:16px;height:16px;overflow:visible}
.film-ring circle{fill:none;stroke-width:1.5}
.fr-track{stroke:rgba(241,242,250,.25)}
.fr-fill{stroke:currentColor;stroke-dasharray:1 2;stroke-dashoffset:calc(1 - var(--p,0));transform:rotate(-90deg);transform-box:fill-box;transform-origin:50% 50%;
  transition:stroke-dashoffset 200ms linear}
.film-line{left:0;right:0;bottom:0;height:1px;background:rgba(241,242,250,.18);overflow:hidden}
.film-line>span{position:absolute;inset:0;background:currentColor;transform:scaleX(0);transform-origin:left center}
[dir=rtl] .film-line>span{transform-origin:right center}
.film-hint{bottom:30px;inset-inline-start:var(--gutter);max-width:40vw;text-shadow:0 1px 3px rgba(11,13,24,.55),0 0 18px rgba(11,13,24,.55)}
.film-sound-off{bottom:18px;inset-inline-start:calc(var(--gutter) - 12px);display:inline-flex;align-items:center;min-height:44px;padding-inline:12px;
  border-radius:var(--r-pill);font:600 var(--fs-label)/1.2 var(--font-latin);letter-spacing:var(--tr-label);text-transform:uppercase;text-align:start;
  text-shadow:0 1px 3px rgba(11,13,24,.55),0 0 18px rgba(11,13,24,.55);transition:background-color var(--d-xs) linear}
.film-skip{bottom:18px;inset-inline-end:calc(var(--gutter) + 56px);display:inline-flex;align-items:center;min-height:44px;padding-inline:18px;
  border:1px solid rgba(241,242,250,.5);border-radius:var(--r-pill);background:rgba(11,13,24,.56);
  font:600 var(--fs-label)/1 var(--font-latin);letter-spacing:var(--tr-label);text-transform:uppercase;white-space:nowrap;
  transition:background-color var(--d-xs) linear,border-color var(--d-xs) linear,opacity 300ms linear,visibility 0s linear}
.film-sound{bottom:18px;inset-inline-end:var(--gutter);display:grid;place-items:center;width:44px;height:44px;border-radius:50%;
  border:1px solid rgba(241,242,250,.5);background:rgba(11,13,24,.56);
  transition:background-color var(--d-xs) linear}
/* hover fills only for a real hover: after a tap a touch screen keeps :hover, and a filled Skip read as "nothing
   happened" (QA mobile). Keyboard focus gets the same fill. */
@media (hover:hover){
  .film-sound-off:hover{background:rgba(241,242,250,.12)}
  .film-skip:hover{background:var(--accent);border-color:var(--accent);color:var(--white);text-shadow:none}
  .film-sound:hover{background:rgba(241,242,250,.16)}
}
.film-sound-off:focus-visible{background:rgba(241,242,250,.12)}
.film-skip:focus-visible{background:var(--accent);border-color:var(--accent);color:var(--white);text-shadow:none}
.film-sound:focus-visible{background:rgba(241,242,250,.16)}
/* Skip lands on the exit start (film.js .is-x from there on): nothing is left to skip, so it fades — the tap visibly
   registered, and it never sits over the iris / shutter. Focus moves on to the next heading (film.js). */
.film.is-x .film-skip{opacity:0;visibility:hidden;pointer-events:none!important;transition:opacity 300ms linear,visibility 0s linear 300ms}
/* No backdrop blur on the HUD pills: over a film that changes every frame it re-blurs every frame (QA perf: the
   '(program)' cost while scrolling the films); the solid scrim-dark fill reads the same at this size. */
@media (max-width:700px),(pointer:coarse){
  .film-play{-webkit-backdrop-filter:none;backdrop-filter:none;background:rgba(11,13,24,.5)}
}

/* Lusion HUD (SPEC-LUSION L6, built by film.js): a small-caps statement under the label (top-start), a tagline above
   the buttons (bottom-end) and five "+" crosshair markers across the frame at 52 % (the row drifts 4 px with the
   pointer; film.js writes its transform). The statement rises line by line and the tagline decodes (kinetic) every
   time the HUD comes on; the markers pop in from the centre out. */
.lux-say{top:calc(var(--header-h) + 44px);inset-inline-start:var(--gutter);max-width:24ch;
  font:600 13px/1.34 var(--font-latin);letter-spacing:.16em;text-transform:uppercase}
/* the top-start column and the time read over bright cloud too: the same tight halo as the bottom row */
:is(.film-label,.lux-say,.film-time){text-shadow:0 1px 3px rgba(11,13,24,.55),0 0 18px rgba(11,13,24,.55)}
.lux-tag{bottom:88px;inset-inline-end:var(--gutter);max-width:40vw;text-align:end;white-space:nowrap;
  font:600 var(--fs-label)/1.3 var(--font-latin);letter-spacing:var(--tr-label);text-transform:uppercase;
  text-shadow:0 1px 3px rgba(11,13,24,.55),0 0 18px rgba(11,13,24,.55)}
/* O9: past the hero the header's wordmark stands vertical in the top-start corner (a 44 px strip, ≈ 130 px tall, in
   a glass pill while a film plays). The HUD's top-start column moves past that strip so they never overprint. */
@media (min-width:701px){
  .site-header.is-vertical~main .film-hud :is(.film-label,.lux-say){inset-inline-start:calc(var(--gutter) + 56px)}
}
:lang(he) :is(.lux-say,.lux-tag){font-family:var(--font-he);letter-spacing:.05em}
:lang(he) .lux-say{font-size:14px}
.lux-marks{top:52%;left:var(--gutter);right:var(--gutter);height:0;display:flex;justify-content:space-between;pointer-events:none}
.lux-marks i{position:relative;display:block;flex:none;width:13px;height:13px;margin-top:-6.5px;opacity:0;transform:scale(.2) rotate(-90deg)}
.lux-marks i::before,.lux-marks i::after{content:"";position:absolute;background:currentColor;opacity:.8;box-shadow:0 0 5px rgba(11,13,24,.45)}
.lux-marks i::before{left:6px;top:0;width:1px;height:13px}
.lux-marks i::after{left:0;top:6px;width:13px;height:1px}
.film.is-hud .lux-marks i{opacity:1;transform:none;
  transition:opacity 400ms linear calc(var(--i,0) * 80ms + 260ms),transform calc(var(--d-land) * var(--tempo)) var(--ease-back) calc(var(--i,0) * 80ms + 260ms)}
.film:not(.is-hud) .lux-marks i{transition:opacity 240ms linear,transform 240ms var(--ease-in)}
/* a caption crossing the 52 % row (film.js .is-mark-dim): the markers step back, so no "+" sits inside its glyphs */
.film.is-hud.is-mark-dim .lux-marks i{opacity:.16;transition:opacity 360ms linear}
/* (film.js fades the whole row with the exit: inline opacity on .lux-marks) */

/* ================================================================ portrait / narrow screens
   Same rule as film.js f.narrow (vw ≤ 700 or vh > 1.1·vw); pure CSS, so kinetic measures caption lines at the right
   width. The film is full-bleed here too (film.js crops it around the subject: piano + pianist, ball + player).
   Captions sit over it, start-aligned and phone-sized: data-zone=low (data-pos "…bottom") in the lower third, just
   above the HUD's bottom row; every other one in the upper third, under the header. Every caption reads light here,
   on a vertical scrim (the shade, behind the lead caption's third) — ≥ 4.5:1 over the brightest frames (snow, cloud,
   the vortex). The captions and the HUD live in 100svh (a phone's toolbars never hide them); the HUD keeps only its
   bottom row (time, Skip, sound) and the "+" row, and its top scrim gives way to the shade. */
@media (max-width:700px),(max-aspect-ratio:10/11){
  .film .film-captions{bottom:auto;height:100%;height:100svh}
  .film .cap{top:calc(var(--header-h) + 18px);bottom:auto;inset-inline:var(--gutter);max-width:none;text-align:start;transform:none;
    color:var(--dark-ink);text-shadow:0 1px 3px rgba(11,13,24,.4),0 2px 24px rgba(11,13,24,.5)}
  .film .cap[data-zone=low]{top:auto;bottom:96px}
  .film .cap :is(.cap-kicker,.cap-body){text-shadow:0 0 2px rgba(11,13,24,.55),0 1px 14px rgba(11,13,24,.7)}
  .film .cap .cap-kicker{margin-bottom:10px;font-size:11px}
  .film :is(.cap-title,.cap-title--xl,.cap-title--cta){font-size:clamp(32px,9.4vw,54px)}
  .film .cap .cap-body{margin-inline:0;max-width:32ch;margin-top:10px;font-size:15px;line-height:1.42}
  .film .cap .cap-kicker+.cap-body{margin-top:0}
  .film .cap .cap-body--lead{max-width:24ch;font-size:clamp(19px,5.4vw,28px);line-height:1.24}
  /* the scrims are sized in px from the caption's edge of the screen (the captions are px-sized, so a short phone
     gets the same cover under its text as a tall one); the lower one starts at the visible bottom (100svh) */
  .film .film-shade{background:linear-gradient(to bottom,rgba(11,13,24,.72),rgba(11,13,24,.64) calc(var(--header-h) + 200px),
    rgba(11,13,24,.3) calc(var(--header-h) + 290px),rgba(11,13,24,0) calc(var(--header-h) + 380px))}
  .film .film-shade[data-zone=low]{
    background:linear-gradient(to top,rgba(11,13,24,.72) 60px,rgba(11,13,24,.64) 360px,rgba(11,13,24,.3) 450px,rgba(11,13,24,0) 540px);
    background:linear-gradient(to top,rgba(11,13,24,.72) calc(100lvh - 100svh),rgba(11,13,24,.64) calc(100lvh - 100svh + 300px),
      rgba(11,13,24,.3) calc(100lvh - 100svh + 390px),rgba(11,13,24,0) calc(100lvh - 100svh + 480px))}
  .film .film-hud::before{display:none}
  .film .film-label,.film .film-hint,.film .film-ring,.film .lux-say,.film .lux-tag{display:none}
  .film .lux-marks i{width:11px;height:11px;margin-top:-5.5px}
  .film .lux-marks i::before{left:5px;height:11px}
  .film .lux-marks i::after{top:5px;width:11px}
  .film .film-time{top:auto;bottom:32px;left:var(--gutter);right:auto}
  [dir=rtl] .film .film-time{left:auto;right:var(--gutter)}
  .film .film-hud:has(.film-sound-off:not([hidden])) .film-time{display:none}
  .film .film-skip{padding-inline:14px}
}
/* a portrait desktop window (> 700 px): the header's vertical wordmark holds the top-start corner (O9) */
@media (min-width:701px) and (max-aspect-ratio:10/11){
  .site-header.is-vertical~main .film .cap{inset-inline-start:calc(var(--gutter) + 56px)}
}

/* ================================================================ reduced motion + no JS (§3.11.9)
   The chapter is static: the intro, the 3D view, then a 16:9 frame with the poster and a native play button,
   then every caption as a plain list. No zoom, no scrub, no iris or shutter. */
:is(html.reduced,html.no-js) .chapter>.stage{display:flex;flex-direction:column}
:is(html.reduced,html.no-js) .chapter-intro{order:-1;position:relative;top:auto;inset:auto;width:auto;max-width:64ch;
  padding:calc(var(--header-h) + 8vh) var(--gutter) 4vh;opacity:1!important;visibility:visible!important}
:is(html.reduced,html.no-js) .chapter>.stage>.view{position:relative;inset:auto;height:62vh;height:62svh}
:is(html.reduced,html.no-js) .goal-burst{display:none}
:is(html.reduced,html.no-js) .film{position:relative;inset:auto;visibility:visible;padding:4vh var(--gutter) 12vh;pointer-events:auto}
:is(html.reduced,html.no-js) .film-frame{position:relative;inset:auto;aspect-ratio:16/9;border-radius:var(--r-card);overflow:hidden;
  clip-path:none!important;transform:none!important}
:is(html.reduced,html.no-js) .film-device{left:0;top:0;width:100%;height:100%;transform:none!important}
:is(html.reduced,html.no-js) .film-screen{clip-path:none!important}
:is(html.reduced,html.no-js) :is(.film-poster,.film-screen video){left:0;top:0;width:100%;height:100%;margin:0;transform:none!important}
:is(html.reduced,html.no-js) :is(.film-rim,.film-glass,.film-floor,.film-strips,.film-flash,.film-shade,.film-hud){display:none}
html.reduced .film-screen video{opacity:0}
html.reduced .film.is-playing .film-screen video{opacity:1}
html.reduced .film.is-playing .film-poster{visibility:hidden}
:is(html.reduced,html.no-js) .film-captions{position:static;display:grid;gap:44px;margin-top:56px;max-width:72ch}
:is(html.reduced,html.no-js) .cap{position:static;inset:auto;max-width:none;transform:none;visibility:visible;opacity:1;color:inherit;text-shadow:none;text-align:start}
:is(html.reduced,html.no-js) .film .cap :is(.cap-kicker,.cap-title,.cap-body,.k-l,.k-li,.k-w,.k-c){opacity:1!important;transform:none!important;
  filter:none!important;animation:none!important;clip-path:none!important}
:is(html.reduced,html.no-js) :is(.cap-title,.cap-title--xl,.cap-title--cta){font-size:clamp(32px,4.4vw,72px)}
:is(html.reduced,html.no-js) .film .cap .cap-body{margin-inline:0;max-width:60ch}
:is(html.reduced,html.no-js) .cap :is(.cap-kicker,.cap-body){text-shadow:none}
/* The static caption list ends at the chapter's bottom edge, but the theme band (a mid-viewport probe) turns the body
   to the next section's colour while the last captions are still on screen (QA: ink on the morph navy). The film
   block paints its own chapter colour, so its text always sits on it. (The 3D view above it stays transparent.) */
html.reduced #piano .film{background:var(--lilac);color:var(--ink)}
html.reduced #football .film{background:var(--dark);color:var(--dark-ink)}
/* (No JS: base.css paints #football / #between dark themselves — QA: ivory on the light body, 1.01:1.) */
html.no-js [data-film=piano] .film-screen{background:var(--dark) url(/assets/media/piano-v2-first.jpg) center/cover no-repeat}
html.no-js [data-film=football] .film-screen{background:var(--dark) url(/assets/media/football-v2-first.jpg) center/cover no-repeat}
html.no-js .film-poster{display:none}

/* integration: keys hint per input type */
@media (hover:none){#piano .hint--keys{display:none}}
@media (hover:hover){#piano .hint--keys-touch{display:none}}
/* integration: the kick button + score stay hidden until the football gate opens (they showed through #between) */
html.js:not(.reduced) #football:not(.is-gate-open) :is(.kick-btn,.score){opacity:0;visibility:hidden}
#football :is(.kick-btn,.score){transition:opacity var(--d-s) linear,visibility 0s linear}
