/* ===========================================================================
 * STRIP ROOM — CSS 3D grid parallax backdrop
 *
 * Sits behind both .strip-header and .strip-content inside any
 * .accordion-strip.has-room. Five perspective-projected walls painted
 * with two stacked linear-gradients (no images, no SVG). The
 * perspective-origin shifts vertically via --room-viewer-y (per-strip
 * pixel value written by js/stripRoom.js's bindViewerYTracker so the
 * viewer tracks viewport center in absolute screen space). Falls back
 * to a sane default so the effect is static if JS never loads.
 *
 * Paired with js/stripRoom.js which (a) projects ROOM config values
 * onto the :root custom props below, and (b) writes --room-viewer-y
 * on each .strip-room element as its containing strip scrolls.
 * ===========================================================================
 */

/* Room-wide defaults live on :root so js/stripRoom.js can override them
 * globally by writing inline custom properties to documentElement. */
:root {
  --room-grid-rgb: 61, 42, 30;    /* base hue, tune via CSS if other palettes needed */
  --room-grid-alpha: 0.9;
  --room-cell: 72px;
  --room-line: 1px;
  --room-depth: 480px;            /* back wall distance from camera */
  --room-perspective: 1100px;     /* lens focal length; larger = flatter */

  /* Live parallax signal. --room-viewer-y is per-strip (js/stripRoom.js
     writes it on every scroll) and carries the viewer's Y position in
     the strip's LOCAL coordinate frame, in pixels. It's computed as
     (vh/2 - strip top) so the viewer tracks viewport center in absolute
     screen space — which makes the top-wall reveal depend only on where
     the strip sits on the screen, not on how tall the strip is. */
  --room-viewer-y: 50%;
}

.strip-room {
  position: absolute;
  inset: 0;
  pointer-events: none;
  perspective: var(--room-perspective);
  /* Perspective-origin Y is in LOCAL strip coords (px), not a percentage
     of strip height. That's the whole point: the viewer is pinned at
     viewport center (vh/2 in absolute screen space) and JS projects
     that into each strip's local frame as (vh/2 - strip top). Two
     expanded strips of different content heights, both pinned at the
     top of the viewport, get identical --room-viewer-y values → the
     top wall reveals the same number of pixels on both. Perspective-
     origin X stays centered since all strips are full-width. */
  perspective-origin: 50% var(--room-viewer-y);
  /* The strip itself has overflow:hidden so the walls clip cleanly. */

  /* Per-strip scale for --room-depth (0..1).
     0 = back wall flush with viewport plane, side walls zero-sized → flat.
     1 = full depth, full 3D room.
     Desktop: accordion.js tweens this on its own from/target (0 or 1),
     sharing the height tween's eased progress but NOT its current value.
     The independent from/target means the synchronous off-screen-trim
     that chops an active strip's height doesn't cause a visible pop in
     depth — depth keeps gliding toward its goal regardless.
     Also an optimization: collapsed strips have zero-area sidewalls
     (gradient paints on zero-sized boxes are free) and the back wall
     alone reads as a flat tiled backdrop.
     Mobile: body.accordion-static pins this to 1 (all strips always
     fully opened — static accordion never collapses anything). */
  --room-depth-scale: 0;
}

/* Static accordion body class (touch-primary devices) — added by
   js/accordion/static.js when `(pointer: coarse)` matches. Pins
   --room-depth-scale to 1 since mobile strips are always expanded. */
body.accordion-static .strip-room {
  --room-depth-scale: 1;
}

/* Profiling flag: `?noParallax` applied by js/flags.js strips the room
   visuals entirely so no compositor layers get promoted for the 3D
   faces. Useful for isolating perf cost. */
body.no-parallax .strip-room { display: none !important; }

.strip-room-scene {
  position: absolute;
  inset: 0;
  transform-style: preserve-3d;
  /* Promote the whole 3D scene to its own compositor layer so when the
     parent .strip-room's perspective-origin changes (driven per-frame
     by the accordion tick), the browser re-projects this single layer
     on the GPU instead of repainting the 5 gradient walls. This is the
     key to making room scroll-parallax effectively free. */
  will-change: transform;
  /* Scene origin sits at the screen plane (z = 0). The back wall pushes
     itself back by --room-depth; the side walls attach at z = 0 and rotate
     backward to meet it. This keeps the side walls' *front* edges flush
     with the strip's left/right/top/bottom — which is what reads as an
     inset room instead of a floating box. */
}

/* Shared face painting — two gradients make the grid, no image assets. */
.strip-room-face {
  position: absolute;
  background:
    linear-gradient(to right,
      rgba(var(--room-grid-rgb), var(--room-grid-alpha)) var(--room-line),
      transparent var(--room-line)) 0 0 / var(--room-cell) var(--room-cell),
    linear-gradient(to bottom,
      rgba(var(--room-grid-rgb), var(--room-grid-alpha)) var(--room-line),
      transparent var(--room-line)) 0 0 / var(--room-cell) var(--room-cell);
  backface-visibility: hidden;
}

/* A/B flag (?facePromo → body.face-promo, see js/flags.js): re-adds the
   old per-face layer promotion. It was originally a hairline-seam fix
   for wall corners, but it permanently promoted ~20 gradient-painted
   layers (5 faces × every .has-room strip) on top of the scene layers —
   real GPU memory for a fix the scene-level will-change (above) plus
   backface-visibility already largely covers. If seams reappear without
   it, prefer a 0.01px translateZ nudge on each face transform or a 1px
   corner overdraw before re-promoting. */
body.face-promo .strip-room-face {
  will-change: transform;
}

/* All face positions/sizes use calc(var(--room-depth) * var(--room-depth-scale))
   rather than var(--room-depth) directly. When --room-depth-scale = 0
   (collapsed strip), the back wall sits at z=0 and the side/top/bottom
   walls collapse to zero size — the whole room reads as a flat
   grid-tiled rectangle. When --room-depth-scale = 1, the full configured
   --room-depth applies and the 3D room is open. */

/* Back wall — pushed away from the viewer so the side walls can run
   from the strip's edge (z=0) back to meet it (z=-depth). Perspective
   foreshortening naturally shrinks the back wall grid as it recedes,
   which reads as the room "pushing back" into depth. */
.face-back {
  left: 0; top: 0;
  width: 100%;
  height: 100%;
  transform: translateZ(calc(var(--room-depth) * var(--room-depth-scale) * -1));
}

/* Side walls — rotated 90° around their inner edge, extending toward camera
   by scaled --room-depth so they meet the back wall. */
.face-left {
  left: 0; top: 0;
  width: calc(var(--room-depth) * var(--room-depth-scale));
  height: 100%;
  transform-origin: left center;
  transform: rotateY(90deg);
}

.face-right {
  right: 0; top: 0;
  width: calc(var(--room-depth) * var(--room-depth-scale));
  height: 100%;
  transform-origin: right center;
  transform: rotateY(-90deg);
}

/* Floor and ceiling — same idea, rotated around X. */
.face-top {
  left: 0; top: 0;
  width: 100%;
  height: calc(var(--room-depth) * var(--room-depth-scale));
  transform-origin: center top;
  transform: rotateX(-90deg);
}

.face-bottom {
  left: 0; bottom: 0;
  width: 100%;
  height: calc(var(--room-depth) * var(--room-depth-scale));
  transform-origin: center bottom;
  transform: rotateX(90deg);
}

/* Content must sit above the room. Stacking context inside the strip is
   already solid because .strip-room is first in source order; these are
   belt-and-suspenders in case z-index shuffles happen. */
.accordion-strip.has-room > .strip-header,
.accordion-strip.has-room > .strip-content {
  position: relative;
  z-index: 1;
}
