/* il-scenes.css — the rooms behind the pages: where the scenery may be loud
   and where it has to be quiet. Tokens only; every rule repaints under all
   twelve moods.

   The per-family scenes are composed around a centre focal point in a
   1600x900 viewBox and sliced to the window, which is right for a room and
   wrong for a page of text: the landing's copy column is the left third, and
   at 1280px Stacia's banner-sword lands on the headline, Eclipse's gate
   pillars and beam run through it, and Aether's cone falls across the lead.
   Moving the art would move the soul of each family; the art stays where it
   was drawn. What changes is what sits between it and the words. */

/* ── Reading scrims ───────────────────────────────────────────────────────
   A feathered wash of the mood's own background colour behind a block of
   text standing on the room -- the standard way type is set over artwork. It
   is invisible over a plain background and strongest exactly where there is
   something behind the words; the scenery is untouched a hand's width away.
   Drawn on ::before, which none of these blocks use, behind the text through
   isolation so it never paints over its own content. */
.landing .lhero-copy,
.ilviewhead[data-il-head] { position: relative; isolation: isolate; }
.landing .lhero-copy::before,
.ilviewhead[data-il-head]::before {
  content: ""; position: absolute; z-index: -1; pointer-events: none;
  background: radial-gradient(closest-side,
    color-mix(in srgb, var(--plane) 86%, transparent) 0%,
    color-mix(in srgb, var(--plane) 72%, transparent) 55%,
    color-mix(in srgb, var(--plane) 0%, transparent) 100%);
}
/* Larger than the block on every side, so the edge of the wash is never
   visible as an edge. */
.landing .lhero-copy::before { inset: -70px -120px -60px -140px; }
.ilviewhead[data-il-head]::before { inset: -26px -80px -18px -90px; }

/* Section heads further down the landing stand on the room the same way. */
.landing .lsection > .eyebrow + h2 { position: relative; isolation: isolate; }
.landing .lsection > .eyebrow + h2::before {
  content: ""; position: absolute; z-index: -1; pointer-events: none;
  inset: -40px -90px -30px -90px;
  background: radial-gradient(closest-side,
    color-mix(in srgb, var(--plane) 70%, transparent) 0%,
    color-mix(in srgb, var(--plane) 0%, transparent) 100%);
}

/* ── The floor melts into the edge ────────────────────────────────────────
   The market floor's lowest rows (candles, then the volume bars along
   y 840-900 of its viewBox) stopped hard at the bottom of the window, in
   every mood, and in the light readings the grey volume blocks read as a
   footer bolted to the page. A mask fades the band rather than removing it:
   the texture is still there, it just belongs to the room now instead of
   sitting on the screen's edge. */
.atmosphere-motifs .market-floor {
  -webkit-mask-image: linear-gradient(to bottom, #000 0%, #000 72%, rgba(0,0,0,.38) 94%, rgba(0,0,0,.22) 100%);
          mask-image: linear-gradient(to bottom, #000 0%, #000 72%, rgba(0,0,0,.38) 94%, rgba(0,0,0,.22) 100%);
}

/* ── The centrepiece steps into the gap ───────────────────────────────────
   Aegis's disc and banners and Sovereign's rift gate are drawn at x 1188 of
   the 1600-wide room -- the right quarter of the window, which on the
   landing is exactly where the hero card stands. Sovereign is the default
   mood, so the first thing a new visitor saw of the platform's best scene
   was a card-shaped hole in it. On the landing only, the three layers that
   carry each centrepiece (and nothing else) pan left into the open ground
   between the headline and the card: the gate now rises between the words
   and the verdict instead of behind the verdict. The art is untouched; this
   is where the camera stands, not what it sees. Entering the terminal pans
   it back to where it was drawn, over the transition the layers already
   carry. Stacia's east arch and the sun behind it pan the same way (below);
   Aether and Eclipse already compose into the gap. */
/* **The camera fits the gap** (round 13). The distances below were tuned at
   about 1440, where the gap is ~320px. From 1000 to ~1400 -- where most
   laptops are -- the gap is 214-300px and the emblems are 330-520px wide, so
   the fixed pan put Aegis's disc behind the last third of every lead line and
   Sovereign's banner against the headline. il-reveal.js now measures the gap
   and writes each focal layer's own --pan-x/--pan-y/--pan-k/--pan-o: centred
   in the gap when the emblem fits, and otherwise standing on the words'
   margin with at most 35% tucked under the card, scaled down only as far as
   that needs. The emblem's box is declared on its layer (data-focal-box, in
   the 1600x900 room). The vw values are the fallback before it has run.

   **Only while the hero is on screen** (`:not([data-l-past])`): the room is
   fixed and the sections below scroll over it, so once the hero has half
   gone il-reveal.js sets data-l-past and the emblem glides home, behind the
   right-hand column, with the west arch and banner back beside it.

   **It starts at 1240, not 1000.** Measured: the gap is 16px at 1024, 24 at
   1100 and 116 at 1200 -- nothing an emblem can stand in without shrinking
   to a coin -- and first reaches ~170 at 1240. Below that there is no gap,
   so the room is framed instead (next block), exactly as on a phone. */
@media (min-width: 1240px) {
  :root[data-stage="landing"]:not([data-l-past]) .atmosphere-motifs > [data-focal] {
    translate: var(--pan-x, -22vw) var(--pan-y, 0); scale: var(--pan-k, 1); }
  /* Vanguard's planted sword stands at the room's right edge, further out
     than any other centrepiece, so it walks further to reach the gap -- and
     a little down, so its tip meets the market floor's horizon (y 535 of the
     floor's 900) rather than the scene's own (470). It shrinks about the
     point where it enters the ground (data-focal-anchor), so it stays planted. */
  :root[data-stage="landing"]:not([data-l-past]) .atmosphere-motifs > [data-focal="far"] {
    translate: var(--pan-x, -38vw) var(--pan-y, 6vh); scale: var(--pan-k, 1); }
  /* Stacia's cathedral is a pair of arches flanking the centre. Panned, the
     east arch takes the centre and the west one lands half off the left edge,
     directly behind the headline -- a banner cut in two by the window. On the
     landing the centred arch carries the composition alone. */
  :root[data-stage="landing"]:not([data-l-past]) .atmosphere-motifs .rs-west { opacity: 0; }
  /* Aegis the same way: panned, its west banner hung at x 515-579 of a
     1440 window, behind the end of the headline and the first line of the
     lead. The east banner and the arch carry the crest alone. */
  :root[data-stage="landing"]:not([data-l-past]) .atmosphere-motifs .sv-west { opacity: 0; }
}
.atmosphere-motifs :is(.rs-west, .sv-west) { transition: opacity 1.2s ease; }
/* In the terminal Stacia's west arch stands behind the rail and the view
   head -- its banner at x 286-370 of a 1440 window -- and in daylight it is
   at full strength: a white cloth with a red cross behind "Macro", "Compare"
   and the model page's section rail, pink through the shell's veil. After dark
   the same banner is already at 42% and the arch at half. By day in the
   terminal the west arch is quieted to the same order, so it reads as the
   room rather than as a mark on the title; the east arch, behind the panels,
   is untouched, and nothing moves. */
:root[data-stage="app"][data-theme="light"] .atmosphere-motifs .rs-west { opacity: .3; }
/* Not stage-scoped: the scale eases back to 1 on entering the terminal, and
   an origin that jumped at the same moment would swing the art sideways. */
.atmosphere-motifs > [data-focal] { transform-origin: var(--pan-o, 50% 50%); }
@media (prefers-reduced-motion: reduce) {
  .atmosphere-motifs > [data-focal] { transition: opacity 1.2s ease !important; }
}

/* ── Below the pan, the room is framed ────────────────────────────────────
   The pan above needs a gap between the headline and the card, and below
   1240px there is none: under 850 the card drops beneath the copy, and from
   850 to 1239 it stands where the gap was. So the rule this layer keeps --
   every family shows its emblem on the landing, at Still -- failed at every
   width under the pan. Measured at 400, 820 and 960: Sovereign's gate and
   Aegis's disc were cut by the right edge or covered by the card, Vanguard's
   sword was off screen, and on a phone the rest stood behind the copy, where
   the reading scrim hides them.

   So on a narrow landing the scenes stop being a backdrop and become a picture:
   the whole 1600x900 room, in its own 16:9, between the header and the
   headline -- sword, cathedral, disc, gate, moon and blades each exactly as
   drawn, nothing cropped, nothing panned. It is still the camera that moves,
   never the art. The text below it stands on the plain sky, so the copy loses
   its scrims' work to do rather than gaining any. It scrolls away with the
   hero; the terminal keeps its full-window room.

   Held to 44% of the small viewport height, so a short window keeps the
   headline on the first screen: past that the picture narrows rather than
   cropping, because every scene's emblem stands high in its frame and a
   centred crop cut the gate's arch and the sword's hilt first.
   --l-vista-top is the hero's top, written by il-reveal.js because the header
   above it is one row or two depending on the width. */
@media (max-width: 1239.98px) {
  /* Always the room's own 16:9, so nothing is ever cropped: full width on a
     phone or an upright tablet, and on a landscape screen a centred picture
     whose height is held to 44% of the window. */
  :root[data-stage="landing"] {
    --l-vista-w: min(100vw, calc(44svh * 16 / 9));
    --l-vista-h: calc(var(--l-vista-w) * 9 / 16);
  }
  :root[data-stage="landing"] .atmosphere-motifs {
    position: absolute; left: 50%; right: auto; bottom: auto; translate: -50% 0;
    top: var(--l-vista-top, 112px); width: var(--l-vista-w); height: var(--l-vista-h);
    /* Feathered on all four sides, so it is the room seen through a haze
       rather than a rectangle pasted on the page. */
    -webkit-mask-image: linear-gradient(to bottom, transparent 0, #000 16%, #000 72%, transparent 100%),
                        linear-gradient(to right, transparent 0, #000 9%, #000 91%, transparent 100%);
    -webkit-mask-composite: source-in;
            mask-image: linear-gradient(to bottom, transparent 0, #000 16%, #000 72%, transparent 100%),
                        linear-gradient(to right, transparent 0, #000 9%, #000 91%, transparent 100%);
            mask-composite: intersect;
  }
  :root[data-stage="landing"] .landing .lhero { padding-top: calc(var(--l-vista-h) + 10px); }
}

/* ── A change of family is staged ────────────────────────────────────────
   Picking another family cross-faded all 39 layers at once over 1.2s, so
   for most of a second the old emblem and the new one stood in the same
   room at half strength -- a gate showing through a disc, a sword through a
   cathedral. Now the room the reader is leaving sinks a little and goes
   quickly, and the new one rises into place a beat behind it: one family,
   then the other. theme.js already names the family on <html>
   (data-family), and every layer says whose it is (data-scene), so the
   arriving and the leaving layers can be told apart in CSS alone.

   !important because the canvas markup carries its transitions inline and
   they are kept verbatim; the focal layers keep their pan as well. A flip
   between a family's day and night is not a change of family and stays the
   plain cross-fade. The cull in theme.js waits 1.8s, past the arrival. */
.atmosphere-motifs > [data-scene] {
  transform: translateY(1.6vh) scale(.985);
  transition: opacity .55s ease, transform .9s cubic-bezier(.4,0,.6,1) !important;
}
.atmosphere-motifs > [data-scene][data-focal] {
  transition: opacity .55s ease, transform .9s cubic-bezier(.4,0,.6,1),
              translate 1.6s cubic-bezier(.2,.7,.2,1), scale 1.6s cubic-bezier(.2,.7,.2,1) !important;
}
/* The family on screen. Relit (day <-> night) it cross-fades in place, as
   before; arriving after another family it waits the beat and rises. */
:root[data-family="cs"] .atmosphere-motifs > [data-scene="cs"],
:root[data-family="rs"] .atmosphere-motifs > [data-scene="rs"],
:root[data-family="sv"] .atmosphere-motifs > [data-scene="sv"],
:root[data-family="gt"] .atmosphere-motifs > [data-scene="gt"],
:root[data-family="ng"] .atmosphere-motifs > [data-scene="ng"],
:root[data-family="kr"] .atmosphere-motifs > [data-scene="kr"] {
  transform: none;
  transition: opacity 1.2s ease, transform 1.4s cubic-bezier(.16,.84,.3,1) !important;
}
:root[data-family="cs"] .atmosphere-motifs > [data-scene="cs"][data-focal],
:root[data-family="rs"] .atmosphere-motifs > [data-scene="rs"][data-focal],
:root[data-family="sv"] .atmosphere-motifs > [data-scene="sv"][data-focal],
:root[data-family="gt"] .atmosphere-motifs > [data-scene="gt"][data-focal] {
  transition: opacity 1.2s ease, transform 1.4s cubic-bezier(.16,.84,.3,1),
              translate 1.6s cubic-bezier(.2,.7,.2,1), scale 1.6s cubic-bezier(.2,.7,.2,1) !important;
}
:root[data-scene-change="family"][data-family="cs"] .atmosphere-motifs > [data-scene="cs"],
:root[data-scene-change="family"][data-family="rs"] .atmosphere-motifs > [data-scene="rs"],
:root[data-scene-change="family"][data-family="sv"] .atmosphere-motifs > [data-scene="sv"],
:root[data-scene-change="family"][data-family="gt"] .atmosphere-motifs > [data-scene="gt"],
:root[data-scene-change="family"][data-family="ng"] .atmosphere-motifs > [data-scene="ng"],
:root[data-scene-change="family"][data-family="kr"] .atmosphere-motifs > [data-scene="kr"] {
  transition-delay: .32s, .32s !important;
  transition-duration: 1s, 1.4s !important;
}
:root[data-scene-change="family"][data-family="cs"] .atmosphere-motifs > [data-scene="cs"][data-focal],
:root[data-scene-change="family"][data-family="rs"] .atmosphere-motifs > [data-scene="rs"][data-focal],
:root[data-scene-change="family"][data-family="sv"] .atmosphere-motifs > [data-scene="sv"][data-focal],
:root[data-scene-change="family"][data-family="gt"] .atmosphere-motifs > [data-scene="gt"][data-focal] {
  transition-delay: .32s, .32s, 0s, 0s !important;
  transition-duration: 1s, 1.4s, 1.6s, 1.6s !important;
}
/* The OS asking for less motion, with no choice made here: the plain
   cross-fade, nothing sinks or rises, and the pan does not travel. */
@media (prefers-reduced-motion: reduce) {
  :root:not([data-motion-choice="user"]) .atmosphere-motifs > [data-scene],
  :root:not([data-motion-choice="user"]) .atmosphere-motifs > [data-scene][data-focal] {
    transform: none; transition: opacity 1.2s ease !important;
  }
  /* ...and no beat between the two rooms either: as specific as the rules
     that add it, and later. */
  :root:not([data-motion-choice="user"])[data-scene-change] .atmosphere-motifs > [data-scene],
  :root:not([data-motion-choice="user"])[data-scene-change] .atmosphere-motifs > [data-scene][data-focal] {
    transition-delay: 0s !important; transition-duration: 1.2s !important;
  }
}

/* Stacia's night aurora breathes: the curtain lengthens a little and brightens,
   then settles. Animated on the <svg> root, which the browser can composite,
   because every alternative inside the SVG would re-run the blur each frame.
   In theme.js ENV_STRUCTURAL, so Still freezes it rather than hiding it. */
@keyframes env-aurora {
  0%, 100% { transform: scaleY(1);     opacity: .86; }
  50%      { transform: scaleY(1.035); opacity: 1; }
}

/* ── The room pushes in as the landing scrolls ────────────────────────────
   --l-par (il-reveal.js) runs 0 -> 140 over the first 2800px of the landing
   and is held at 0 in the terminal, at Still and under reduced motion. It
   scales the whole room rather than translating its layers: a translate
   opens an empty band at one edge of every scene (Aether's sea and Eclipse's
   floor are drawn to the bottom of the frame), while a push-in of at most 7%
   never shows an edge. One container, one composited layer -- will-change
   keeps the scroll on the compositor instead of re-rasterising the blurs. */
.atmosphere-motifs {
  transform: scale(calc(1 + var(--l-par, 0) * .0005));
  transform-origin: 50% 42%;
  will-change: transform;
  transition: transform .9s cubic-bezier(.2,.7,.2,1);   /* follows the scroll a beat behind, and eases home on leaving */
}
