/* ════════════════════════════════════════════════════════════════════════
   scene.css — the STRUCTURE of the shared-camera stage. No chrome, no type,
   no colour beyond the ground. Body chrome belongs in the page's own sheet.
   service.tnaado.ca · pairs with js/camera.js + js/bodies.js

   THE ONLY STRUCTURAL IDEA IN THIS FILE
   -------------------------------------
   Everything outside `.sc-live` is the page as it exists with NO JavaScript:
   bodies in normal flow, in reading order, at a readable measure. Everything
   inside `.sc-live` turns that same markup into a 3D stage. The class is added
   by script and removed by script on every failure path, so the fallback is
   not a fallback — it is the base state, and the effect has to earn its way in.

   Consequence, and it is not negotiable: NO RULE OUTSIDE `.sc-live` MAY
   POSITION, TRANSFORM, HIDE OR CLIP A BODY. If you write
   `.sc__body { position: absolute }` the degradation is gone.

   THE FOUR-BOX NESTING, AND WHY EACH BOX EXISTS
   ---------------------------------------------
     .sc          the TRACK. Its height is the scroll length of the act.
     .sc__stage   the sticky viewport. Owns the ground and the clip. The canvas
                  is a child of THIS, deliberately outside the perspective
                  context, because a canvas has no business being projected.
     .sc__space   owns `perspective` and `perspective-origin: 50% 50%`. Its box
                  is the stage box, so the perspective origin is the stage
                  centre, which is therefore the WORLD ORIGIN. js/camera.js
                  writes the perspective value from the viewport height, because
                  it is half of a camera the GL side has to agree with to the
                  pixel. NEVER hard-code a perspective here — a literal
                  silently desynchronises the two consumers, which is the exact
                  bug this whole mechanism exists to make impossible.
     .sc__cam     owns the camera. js/camera.js writes
                  `translateZ(Ppx) matrix3d(<the view matrix, verbatim>)`.
                  `transform-style: preserve-3d` is what lets the bodies inside
                  it live in that 3D space instead of being flattened into it.

   WHY THE BODIES ARE CENTRED BY LAYOUT
   ------------------------------------
   `inset: 0; margin: auto; height: max-content` centres a box of known width on
   both axes with no JS ever measuring its height. That puts the body's
   transform-origin exactly on the perspective origin, so its matrix3d is read
   as a pure world transform about the world origin. Shift that centring and
   every body acquires a silent offset.

   NO BACKDROP-FILTER, ANYWHERE. A blurred backdrop over a full-screen animated
   canvas is a per-frame full-screen blur on the compositor, which is the one
   thing on this page that could actually cost the frame budget. Flat alpha.
   ════════════════════════════════════════════════════════════════════════ */

/* ── STATIC: THE DOCUMENT ─────────────────────────────────────────────── */
.sc { position: relative; }
.sc__stage { position: relative; }
.sc__space,
.sc__cam {
  display: flex;
  flex-direction: column;
  gap: clamp(3rem, 7vw, 6rem);
}
.sc__space { padding: clamp(2.25rem, 4.6vw, 4rem) 1.5rem; max-width: 68rem; margin-inline: auto; }
.sc__body { max-width: 46rem; }
.sc__gl { display: block; }

/* ── A PLATE MAY NEVER BE WIDER THAN ITS OWN COLUMN ───────────────────────
   MEASURED on /apps, not reasoned about. The `shots-carousel` plate held a
   LAYOUT width (offsetWidth) of 543px from a 320px viewport all the way to
   540px — 223px past the right edge at 320 — and only body's
   `overflow-x: clip` hid it. It stretches normally from 600px up, which is
   the width at which 543 finally fits, and that is the tell: 543 is the
   plate's MAX-CONTENT, and here max-content behaves as a FLOOR.

   WHY, because three obvious fixes do nothing and one round was already lost
   to each of them:

     · `.sc__space` and `.sc__cam` above are COLUMN flex containers, so the
       horizontal axis is the CROSS axis. `min-width: 0` does not gate the
       cross axis, so the usual overflow guard is inert here.
     · A plate carrying an auto inline margin — /apps gives every even plate
       `margin-inline-start: auto` to alternate the column — is disqualified
       from stretching, because `stretch` requires that neither cross-axis
       margin be auto. Forcing `align-self: stretch` therefore changes
       nothing: verified in the browser, still 543.
     · Left un-stretched, the plate is sized intrinsically, and an
       `overflow: hidden` strip inside it reports the whole rail laid end to
       end as its preferred width. `width: 100%` on the strip cannot help:
       it resolves against the plate that is already too wide.

   So the fix has to be an explicit width on the PLATE, which is the box that
   had no definite size. Scoped to plates that actually hold a strip — a
   blanket `width: 100%` on every `.sc__body` is already known to be wrong
   (it blew /newsroom's deliberately narrow portrait plate from 332px to
   810px; see css/storm-tier1.css). `max-width` still caps the result, so no
   plate ends up wider than its page allows, and the `min-width` floor in
   storm-tier1.css still holds the collapsed ones up.

   The `:has()` rules are deliberately SEPARATE declarations rather than one
   selector list: an engine that does not understand `:has()` throws away the
   whole list it appears in, which would take the plain attribute selector
   down with it. */
html:not(.sc-live) .sc__body[data-car] { width: 100%; box-sizing: border-box; }
html:not(.sc-live) .sc__body:has(> [data-car]) { width: 100%; box-sizing: border-box; }
html:not(.sc-live) .sc__body:has(> .carou) { width: 100%; box-sizing: border-box; }

/* ── LIVE: THE STAGE ──────────────────────────────────────────────────── */
/* Sticky, not fixed. A fixed stage is on screen from the first frame and would
   sit over the page header; a sticky one cannot appear before its own place in
   the flow. The track's height IS the scroll length — one number, --sc-len. */
.sc-live .sc { height: calc(var(--sc-len, 11) * 100svh); }
.sc-live .sc__stage {
  position: sticky;
  top: 0;
  height: 100svh;
  overflow: clip;
  background: var(--sc-ground, #0a0a0a);
}
.sc-live .sc__gl {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  pointer-events: none;
}
.sc-live .sc__space {
  position: absolute;
  inset: 0;
  display: block;
  max-width: none;
  margin: 0;
  padding: 0;
  gap: 0;
  /* perspective + perspective-origin are written by js/camera.js. */
}
.sc-live .sc__cam {
  position: absolute;
  inset: 0;
  display: block;
  gap: 0;
  transform-style: preserve-3d;
  transform-origin: 50% 50%;
  pointer-events: none;
}
.sc-live .sc__body {
  position: absolute;
  inset: 0;
  margin: auto;
  width: min(var(--sc-w, 46rem), 88vw);
  max-width: none;
  height: max-content;
  visibility: hidden;
  opacity: 0;
  transform-origin: 50% 50%;
  backface-visibility: hidden;
  will-change: transform, opacity;
  -webkit-font-smoothing: antialiased;
}

/* ── REDUCED MOTION ───────────────────────────────────────────────────── */
/* js/camera.js is never started under reduce, so `sc-live` is never added and
   these rules never match. They are belt and braces for a stylesheet loaded
   without the script: reduced motion is not something that should need a
   script to be running in order to be honoured. */
@media (prefers-reduced-motion: reduce) {
  .sc-live .sc { height: auto; }
  .sc-live .sc__stage { position: relative; height: auto; overflow: visible; }
  .sc-live .sc__space { position: relative; inset: auto; display: flex; padding: clamp(2.25rem, 4.6vw, 4rem) 1.5rem; max-width: 68rem; margin-inline: auto; }
  .sc-live .sc__cam { position: relative; inset: auto; display: flex; gap: clamp(2.25rem, 4.6vw, 4rem); transform: none !important; transform-style: flat; pointer-events: auto; }
  .sc-live .sc__body { position: relative; inset: auto; margin: 0; width: auto; max-width: 46rem; height: auto; visibility: visible !important; opacity: 1 !important; transform: none !important; will-change: auto; }
  .sc-live .sc__gl { display: none; }
}
