/* ==========================================================================
   SONARAE: the 10-act freefall
   1 tokens + base   2 loader   3 pin container + plates   4 typography   5 icarus
   6 solar flare (mask gate)   7 dust + blast   8 call to action (act 10)   9 overlays + HUD
   10 cookie banner, bottom controls   11 responsive + reduced motion

   The site header, colour tokens and the .btn component come from header.css (loaded first).
   ========================================================================== */

/* Gloock, served from this website (SIL Open Font License, fonts/Gloock-OFL.txt): the same two files as global.css. The latin file is
   preloaded by the page; latin-ext is only fetched if a character from that range ever appears in the film's type. */
@font-face {
  font-family: 'Gloock';
  src: url('./fonts/gloock-v8-latin.woff2') format('woff2');
  font-weight: 400;
  font-style: normal;
  font-display: swap;
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
@font-face {
  font-family: 'Gloock';
  src: url('./fonts/gloock-v8-latin-ext.woff2') format('woff2');
  font-weight: 400;
  font-style: normal;
  font-display: swap;
  unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}

/* ---------- 1. tokens + base ---------- */
:root {
  --ink: #06080d;
  --ivory: #fff6e6;
  --gold: #e5b05c;
  --serif: 'Gloock', 'Iowan Old Style', 'Times New Roman', serif;
  --sans: 'Inter', system-ui, -apple-system, 'Segoe UI', sans-serif;
  --mono: ui-monospace, 'SFMono-Regular', Menlo, Consolas, 'Liberation Mono', monospace;
  --L: 150vmax;                          /* side of the oversized, rotatable background square (JS sets the exact hypot) */
  --logo: min(44vmin, 500px);
  --sun-size: min(38vmin, 460px);
  --ic-h: clamp(300px, 58vh, 700px);
  --ease-blade: cubic-bezier(.76, 0, .24, 1);
  --vh-l: 100vh;                         /* large viewport (a phone's toolbar hidden): the picture is sized to this, so it never leaves a gap when the toolbar slides away */
  --vh-s: 100vh;                         /* small viewport (toolbar showing): anything that must stay in sight is laid out against this */
  --dock: 0px;                           /* height of the cookie notice while it is open; the bottom controls ride above it (set by app.js) */
  --muted-ink: #4d4d4d;                  /* the static screen's secondary type and inner rules, the same values global.css gives every other page */
  --hairline: #cfcfcf;
  --fs-script: clamp(1.625rem, 1.2rem + 1.6vw, 2.5rem);   /* DESIGN.md Script: every word of the film, written out on the static screen */
}
[data-theme="dark"] { --muted-ink: #b3b3b3; --hairline: #333333; }
@supports (height: 100lvh) { :root { --vh-l: 100lvh; --vh-s: 100svh; } }

*, *::before, *::after { box-sizing: border-box; }
html { background: var(--ink); -webkit-text-size-adjust: 100%; }
/* Locked under the loader. The scrollbar's gutter stays reserved (and painted in the loader's colour) the whole time, so the page is laid out
   at its final width from the first frame: on a system with classic scrollbars, unlocking used to add a 15px scrollbar at the very moment
   the loader lifted, and the fixed header and everything under it jumped sideways by that much. */
html.is-locked { overflow: hidden; overscroll-behavior: none; scrollbar-gutter: stable; background: var(--bg-color); }
html.is-locked body { touch-action: none; }
body {
  margin: 0; min-height: 100vh; background: var(--ink); color: var(--ivory);
  font-family: var(--sans); font-weight: 400; -webkit-font-smoothing: antialiased; text-rendering: optimizeLegibility;
}
img { display: block; max-width: none; }
button { font: inherit; color: inherit; cursor: pointer; }
/* #FFDE00 (and the near-identical --accent-yellow-light) reads at 1.34:1 against white, far short of the 3:1
   a non-text focus indicator needs under WCAG 2.1 SC 1.4.11; --focus-color is Rule Gold, the same token the
   rest of the site already verified at 3.82:1 on paper and 9.26:1 on ink. Bolder now: 3px and a 4px offset. */
:focus-visible { outline: 3px solid var(--focus-color); outline-offset: 4px; }
.sprite { position: absolute; width: 0; height: 0; overflow: hidden; }

.skip-link {
  position: absolute;
  left: var(--gutter);
  top: -100px;
  z-index: 9200;
  padding: 12px 16px;
  background: var(--accent-yellow-light);
  color: #000000;
  font-family: var(--font-ui);
  font-weight: 600;
  text-decoration: none;
  border: 1px solid #000000;
}
.skip-link:focus { top: 8px; outline-color: #000000; }

.link {
  color: inherit;
  text-decoration: underline;
  text-decoration-thickness: 1px;
  text-underline-offset: 0.2em;
}

.site-footer { position: relative; z-index: 1; background: var(--bg-color); border-top: 1px solid var(--border-color); }
.site-footer__inner {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-2) var(--space-6);
  max-width: var(--max);
  margin-inline: auto;
  padding: var(--space-5) var(--gutter);
  color: var(--text-color);
  font-family: var(--font-ui);
  font-size: 0.8125rem;
}
.site-footer__brand { display: inline-flex; align-items: center; gap: 10px; margin: 0; }
.site-footer__brand img { display: block; }
.site-footer__links { display: flex; flex-wrap: wrap; gap: var(--space-2) var(--space-5); }

/* The static screen: JavaScript off, reduced motion, or the film did not load. The film is gone and the page becomes a title page in the site's
   own colours: the welcome title, one note that says why the film is not playing and what to do, the two buttons, a still of the opening shot,
   and the film written out act by act (the typography layer itself, restyled: the words are never kept twice). Keyed to the attribute, so
   nothing flashes. The still's two pictures are named only by static rules, so a visitor who gets the film never downloads them a second time. */
.static-note, .still, .script__title, .typo--note { display: none; }
html[data-intro='static'], html[data-intro='static'] body { background: var(--bg-color); }
html[data-intro='static'] .plate, html[data-intro='static'] .streaks, html[data-intro='static'] .feathers,
html[data-intro='static'] #icarus, html[data-intro='static'] #solar-flare,
html[data-intro='static'] #dust-particles, html[data-intro='static'] .hud, html[data-intro='static'] .grain,
html[data-intro='static'] .vignette { display: none; }
html[data-intro='static'] #pin-container { height: auto; min-height: var(--vh-l); background: var(--bg-color); color: var(--text-color); }
html[data-intro='static'] .stage {
  position: relative; display: grid; grid-template-columns: minmax(0, 1fr); align-content: start; row-gap: clamp(40px, 6vw, 72px);
  max-width: var(--max); min-height: var(--vh-l); margin-inline: auto;
  padding: calc(var(--header-h) + clamp(24px, 6vh, 72px)) var(--gutter) clamp(64px, 10vh, 128px);
  font-family: var(--font-ui); row-gap: 0;
}
html[data-intro='static'] #dossier-card { margin-top: clamp(24px, 3.5vh, 36px); }
html[data-intro='static'] .typography-layer { margin-top: clamp(72px, 9vw, 128px); }
html[data-intro='static'] .welcome { position: static; padding: 0; overflow: visible; background: none; color: var(--text-color); pointer-events: auto; }
html[data-intro='static'] #dossier-card { position: static; height: auto; padding: 0; display: block; }
html[data-intro='static'] .finale { max-width: 880px; }

/* The note: one quiet line under the title that says why the film is not playing, with the next step as a plain link (the only two buttons
   on the page stay Register and Home). Only the note for the actual reason shows; the head script and app.js set data-static-reason. */
html[data-intro='static'][data-static-reason='motion'] .static-note--motion,
html[data-intro='static'][data-static-reason='failed'] .static-note--failed,
html[data-intro='static'] .static-note--noscript { display: block; }
.static-note {
  max-width: 60ch; margin: clamp(20px, 3vh, 32px) 0 0; font-family: var(--font-ui); font-size: 1rem; line-height: 1.6;
  color: var(--muted-ink); text-wrap: pretty;
}
.static-note__tag {
  display: inline-block; margin-right: var(--space-2); padding: 1px 7px 2px; background: var(--accent-yellow-light); color: #000000;
  font-size: .75rem; font-weight: 600; line-height: 1.45; letter-spacing: .06em; text-transform: uppercase; vertical-align: 1px;
}
.static-note__action { color: var(--text-color); font-weight: 600; white-space: nowrap; text-decoration-color: var(--accent-yellow-dark); text-decoration-thickness: 2px; }
html[data-lib='local'] .static-note--failed .static-note__action { display: none; }   /* the site's own copy was already tried: no loop */

/* The two buttons, at the site's ordinary size here: the film's finale sizes them for a full screen of picture, a title page does not. */
html[data-intro='static'] .finale { display: flex; flex-wrap: wrap; gap: var(--space-3); max-width: none; }
html[data-intro='static'] .btn--cta { width: auto; min-height: 56px; padding: 0 22px 0 26px; font-size: 1.0625rem; gap: var(--space-4); white-space: nowrap; }

/* Figure 1: the film's opening as a flat print that moves with CSS alone. The same drawing language as the ranges on Our Team: posterized
   sky bands, hard-edged rings for the sun, stepped clouds, lit and halftone-shaded faces. Icarus is a two-tone print cut from the film's own
   engraving. Every coordinate is a share of the 4:5 frame, so the fall lands behind the far range at any size. One authored moment: he falls
   out of the sun in a long arc (the drift across eases out, the drop eases in, like a thrown body), tumbling and shrinking with distance,
   sheds three feathers, and slips behind the far range. Three times, then the print rests. Nothing here moves a layout property. */
html[data-intro='static'] .still { display: grid; gap: var(--space-3); margin: clamp(40px, 6vw, 64px) 0 0; align-content: start; }
.poster {
  position: relative; aspect-ratio: 4 / 5; overflow: hidden; isolation: isolate;
  background: linear-gradient(to bottom, #a9d3ea 0 17%, #b9dcef 17% 31%, #c9e4f1 31% 45%, #d9ecf2 45% 58%, #e7f2f0 58%);   /* five flat bands, hard stops: no visible gradient */
}
.poster > * { position: absolute; }
.poster__rays {                                                              /* flat wedges round the sun, turning once every three minutes */
  left: 68%; top: 21%; width: 136%; aspect-ratio: 1; translate: -50% -50%; clip-path: circle(50%);
  background: repeating-conic-gradient(from 6deg, #e6efe0 0 4deg, transparent 4deg 22.5deg);
  animation: ps-rays 180s linear infinite;
}
.poster__sun { left: 68%; top: 21%; width: 30%; height: auto; translate: -50% -50%; overflow: visible; }
.ps-halo-2 { fill: #edf1d8; } .ps-halo-1 { fill: #f8efc0; } .ps-disc { fill: #ffd400; }   /* the site's one yellow is the sun itself */
.poster__cloud { height: auto; }
.pc { fill: #ffffff; } .pc-under { fill: #d5e7f1; }
.poster__cloud--a { left: 5%; top: 8%; width: 36%; animation: ps-cloud-a 70s ease-in-out infinite alternate; }
.poster__cloud--b { left: 58%; top: 37%; width: 32%; animation: ps-cloud-b 85s ease-in-out infinite alternate; }
.poster__cloud--c { left: 12%; top: 46%; width: 25%; animation: ps-cloud-a 60s ease-in-out -20s infinite alternate; }
.poster__fall, .poster__drop, .poster__drift { inset: 0; }                  /* full-frame layers, so their translate percentages are shares of the frame */
.poster__drop { position: absolute; }
.poster__icarus { position: absolute; left: 68%; top: 21%; width: 18%; aspect-ratio: 309 / 520; transform: translate(-50%, -50%); }
html[data-intro='static'] .poster__icarus { background: url('./assets/icarus-print.png') center / contain no-repeat; }
.poster__feather { position: absolute; width: 1.9%; height: auto; color: #fff6e6; stroke: #10141b; stroke-width: 1.4; overflow: visible; }
.poster__drift--1 .poster__feather { left: 61%; top: 30%; }
.poster__drift--2 .poster__feather { left: 53%; top: 40%; }
.poster__drift--3 .poster__feather { left: 46%; top: 50%; }
.poster__ranges { left: 0; right: 0; bottom: 0; width: 100%; height: 56%; }  /* its viewBox has this same 10:7 shape, so nothing is cropped or stretched */
.poster__ranges pattern rect { shape-rendering: crispEdges; }
.ps-far-lit { fill: #a9bdd0; } .ps-far-shade { fill: #8ea4ba; } .ps-far-dot { fill: #6a839d; }
.ps-mid-lit { fill: #5d6e80; } .ps-mid-shade { fill: #475769; } .ps-mid-dot { fill: #26313f; }
.ps-near-lit { fill: #222933; } .ps-near-shade { fill: #10141b; } .ps-near-dot { fill: #000000; }
.ps-snow { fill: #ffffff; } .ps-snow-shade { fill: #d6e3ee; } .ps-snow-dot { fill: #a8c2d6; }

html[data-intro='static'] .poster__fall { animation: ps-fall-x 16s linear .6s 3 both; }
html[data-intro='static'] .poster__drop { animation: ps-fall-y 16s linear .6s 3 both; }
html[data-intro='static'] .poster__icarus { animation: ps-tumble 16s linear .6s 3 both; }
html[data-intro='static'] .poster__drift--1 { animation: ps-drift-1 16s linear .6s 3 both; }
html[data-intro='static'] .poster__drift--2 { animation: ps-drift-2 16s linear .6s 3 both; }
html[data-intro='static'] .poster__drift--3 { animation: ps-drift-3 16s linear .6s 3 both; }
html[data-intro='static'] .poster__feather { animation: ps-spin 16s linear .6s 3 both; }
@keyframes ps-rays { to { rotate: 360deg; } }
@keyframes ps-cloud-a { to { transform: translateX(8%); } }
@keyframes ps-cloud-b { to { transform: translateX(-7%); } }
@keyframes ps-fall-x {
  0%, 5% { transform: translateX(0); animation-timing-function: cubic-bezier(.22, .62, .36, 1); }
  44%, 100% { transform: translateX(-31%); }
}
@keyframes ps-fall-y {
  0%, 5% { transform: translateY(0); animation-timing-function: cubic-bezier(.55, 0, .85, .45); }
  44%, 100% { transform: translateY(52%); }
}
@keyframes ps-tumble {
  0% { opacity: 0; transform: translate(-50%, -50%) rotate(-24deg) scale(.55); }
  5% { opacity: 1; transform: translate(-50%, -50%) rotate(-14deg) scale(.9); animation-timing-function: cubic-bezier(.45, 0, .8, .5); }
  43% { opacity: 1; }
  44%, 100% { opacity: 0; transform: translate(-50%, -50%) rotate(118deg) scale(.5); }
}
@keyframes ps-drift-1 {
  0%, 11% { opacity: 0; transform: translate(0, 0); }
  13% { opacity: 1; transform: translate(.4%, 1%); }
  26% { transform: translate(2.4%, 8%); }
  38% { transform: translate(-1.2%, 15%); }
  48% { opacity: 1; transform: translate(1.6%, 21%); }
  54%, 100% { opacity: 0; transform: translate(0, 25%); }
}
@keyframes ps-drift-2 {
  0%, 18% { opacity: 0; transform: translate(0, 0); }
  20% { opacity: 1; transform: translate(-.4%, 1%); }
  32% { transform: translate(-2.2%, 7%); }
  44% { transform: translate(1%, 13%); }
  54% { opacity: 1; transform: translate(-1.4%, 18%); }
  60%, 100% { opacity: 0; transform: translate(0, 21%); }
}
@keyframes ps-drift-3 {
  0%, 25% { opacity: 0; transform: translate(0, 0); }
  27% { opacity: 1; transform: translate(.3%, .8%); }
  38% { transform: translate(1.8%, 5%); }
  50% { opacity: 1; transform: translate(-.8%, 9%); }
  56%, 100% { opacity: 0; transform: translate(0, 11%); }
}
@keyframes ps-spin { 0%, 10% { rotate: -20deg; } 60%, 100% { rotate: 320deg; } }

/* The film, written out: the typography layer becomes a ruled ledger, one row per act, the act's numeral in a left column. The film's own
   words stay in Gloock; the explanation under each title and the three acts that are pictures only are set as reading text. */
html[data-intro='static'] .typography-layer { position: static; display: grid; align-content: start; pointer-events: auto; }
html[data-intro='static'] .script__title {
  display: block; margin: 0 0 var(--space-5); font-family: var(--serif); font-size: clamp(2rem, 1.2rem + 3.8vw, 4rem); font-weight: 400;
  line-height: 1.14; letter-spacing: -.02em; color: var(--text-color); text-wrap: balance;
}
html[data-intro='static'] .typo {
  position: static; inset: auto; z-index: auto; visibility: visible; display: grid; grid-template-columns: 5.5rem minmax(0, 1fr); gap: var(--space-5);
  padding-block: var(--space-5); border-top: 1px solid var(--hairline); transform: none; will-change: auto;
}
html[data-intro='static'] .typo--one { border-top-color: var(--border-color); }
html[data-intro='static'] .typo:last-child { border-bottom: 1px solid var(--border-color); }
html[data-intro='static'] .typo::before {
  content: attr(data-num); position: static; transform: none; padding-top: .5em;
  font: 600 .9375rem/1.4 var(--font-ui); letter-spacing: .06em; color: var(--text-color); -webkit-text-stroke: 0;
}
html[data-intro='static'] .typo__box { position: static; transform: none; max-width: none; max-height: none; overflow: visible; text-align: left; }
html[data-intro='static'] .line {
  position: static; display: block; max-width: 30ch; clip-path: none; visibility: visible;
  font-size: var(--fs-script); line-height: 1.14; letter-spacing: -.012em; color: var(--text-color); text-wrap: balance;
}
html[data-intro='static'] .line__mark {                                            /* the marked number on the yellow block, in the line's own face, as in the film */
  display: inline-block; padding: .05em .12em .03em; background: var(--accent-yellow-light); color: #000000; line-height: .9;
}
html[data-intro='static'] .title, html[data-intro='static'] .typo--xl .title {
  font-size: var(--fs-script); line-height: 1.14; color: var(--text-color); text-wrap: balance;
}
html[data-intro='static'] .rule { display: none; }
html[data-intro='static'] .sub, html[data-intro='static'] .note {
  max-width: 60ch; margin: var(--space-3) 0 0; font: 400 1.0625rem/1.65 var(--font-ui); color: var(--muted-ink);
}
html[data-intro='static'] .note { margin: 0; padding-top: .3em; }
html[data-intro='static'] .typo--note { display: grid; }

/* Wide screens: a title page. The title, the note and the buttons on the left, the still beside them, the film written out underneath. */
@media (min-width: 1100px) {
  html[data-intro='static'] .stage {
    grid-template-columns: minmax(0, 7fr) minmax(0, 5fr); grid-template-rows: auto 1fr auto;
    grid-template-areas: 'welcome still' 'card still' 'script script'; column-gap: clamp(40px, 4vw, 72px);
  }
  html[data-intro='static'] .welcome { grid-area: welcome; }
  html[data-intro='static'] #dossier-card { grid-area: card; }
  html[data-intro='static'] .still { grid-area: still; margin-top: 0; }
  html[data-intro='static'] .typography-layer { grid-area: script; max-width: 1100px; }
  html[data-intro='static'] .welcome__title { font-size: clamp(3rem, min(10vw, 15vh), 8.5rem); }   /* fills the left seven columns; the cap holds past the 1440px container */
}
@media (min-width: 600px) and (max-width: 1099px) { html[data-intro='static'] .still { max-width: 560px; } }
@media (max-width: 719px) {
  html[data-intro='static'] .btn--cta { width: 100%; justify-content: space-between; }
  html[data-intro='static'] .typo { grid-template-columns: minmax(0, 1fr); gap: var(--space-2); }
  html[data-intro='static'] .typo::before { padding-top: 0; }
}
/* Without JavaScript the header's links wrap under the brand (header.css), so it can be taller than the 64px the film reserves: here it sits
   in the page and scrolls away with it, instead of being fixed over the title. */
html:not(.js) .site-header--overlay { position: relative; }
html:not(.js)[data-intro='static'] .stage { padding-top: clamp(24px, 6vh, 72px); }

/* ---------- 2. loader ---------- */
/* A flat panel under the header: one big tabular counter and a yellow bar. preload.js counts real bytes into it; app.js lifts it. */
#preloader {
  position: fixed; inset: 0; z-index: 8000; display: flex; flex-direction: column; justify-content: flex-end;
  background: var(--bg-color); color: var(--text-color); font-family: var(--font-ui);
  transition: transform .9s var(--ease-blade);
}
#preloader.is-done { transform: translateY(-100%); pointer-events: none; }
#preloader[hidden] { display: none; }
html[data-intro='static'] #preloader, html:not(.js) #preloader { display: none; }
.preload__count {
  display: flex; align-items: baseline; gap: .1em; padding: 0 var(--gutter) clamp(20px, 4vw, 44px);
  font-size: clamp(5rem, 22vw, 14rem); font-weight: 600; line-height: .8; letter-spacing: -.04em; font-variant-numeric: tabular-nums;
}
.preload__pct { font-size: .26em; font-weight: 500; letter-spacing: 0; }
.preload__rule { position: relative; height: 12px; border-top: 1px solid var(--border-color); }
.preload__rule i { position: absolute; inset: 0; background: var(--accent-yellow-light); transform-origin: 0 50%; transform: scaleX(0); }

/* ---------- 3. pin container + plates ---------- */
#pin-container {
  position: relative; height: 100vh; height: var(--vh-l); overflow: hidden; isolation: isolate; background: var(--ink);
  will-change: transform;                                            /* ScrollTrigger parks the pin with a transform once the fall is over */
}
/* Pre-stage guard. While the film is pending (JavaScript on, motion allowed: the bootstrap in welcome.html), the picture stays hidden
   until app.js has set its starting states, built the timeline and rendered beat 0, then adds html.is-staged. The loader covers this
   moment too; the guard makes sure nothing under it can paint in an unstaged position, whatever order the scripts, fonts and pictures
   arrive in. visibility keeps the layout, so every measurement still reads real sizes. No path leaves it hidden: without JavaScript
   or with reduced motion the page never becomes pending, and the 30 s failsafe and app.js's own fallback both switch to static. */
html[data-intro='pending']:not(.is-staged) #pin-container { visibility: hidden; }
.stage {                                                             /* the whole picture: the crash shake moves and overscans this, never the pin itself */
  position: absolute; inset: 0; isolation: isolate; will-change: transform;
}

#background-plate {                                                  /* oversized square so a 90 degree tumble never shows a corner */
  position: absolute; left: 50%; top: 50%; z-index: 0; width: var(--L); height: var(--L);
  margin: calc(var(--L) / -2) 0 0 calc(var(--L) / -2); transform-origin: 50% 50%; will-change: transform;
}
.plate { position: absolute; visibility: hidden; }

.plate--sky {                                                        /* tall painted sky: width = max(100vw, 1.58 x the pin height), centred on the viewport */
  visibility: visible; width: max(100vw, calc(var(--vh-l) * 1.58));
  left: calc((var(--L) - max(100vw, calc(var(--vh-l) * 1.58))) / 2); top: calc((var(--L) - var(--vh-l)) / 2);
  background: #a5834c; will-change: transform;
}
.plate--sky::before {                                                /* soft top edge, moves with the plate */
  content: ''; position: absolute; left: 0; right: 0; top: 0; height: 6%; z-index: 1; pointer-events: none;
  background: linear-gradient(to bottom, #a5834c, rgba(165, 131, 76, 0));
}
.plate--sky .plate__img { display: block; width: 100%; height: auto; aspect-ratio: 1422 / 2340; }   /* fixed ratio: the placeholder gif must never decide the plate's height */

.plate--space, .plate--clouds { inset: 0; overflow: hidden; opacity: 0; }
.strip { position: absolute; left: 0; top: 0; width: 100%; height: 200%; will-change: transform; }   /* two stacked square tiles, wrapped in JS */
.strip i { display: block; width: 100%; height: 50%; background-position: center; background-size: 100% 100%; background-repeat: no-repeat; }
.plate--space .strip i { background-image: var(--nebula, none); }    /* --nebula and --clouds are set by preload.js once the picture has downloaded (the small variant on phones) */
.plate--clouds .strip i { background-image: var(--clouds, none); }
.plate--clouds { mix-blend-mode: normal; }

.plate--palace {                                                     /* viewport-sized and centred inside the oversized square */
  opacity: 0; width: 100vw; height: var(--vh-l);
  left: calc((var(--L) - 100vw) / 2); top: calc((var(--L) - var(--vh-l)) / 2); will-change: transform, opacity;
}
.plate--palace .plate__img { width: 100%; height: 100%; object-fit: cover; object-position: 62% 50%; }

.streaks { position: absolute; inset: 0; opacity: 0; pointer-events: none; }
.streaks i {
  position: absolute; left: 0; top: 0; width: 2px; will-change: transform;
  background: linear-gradient(to bottom, rgba(255, 236, 200, 0), rgba(255, 236, 200, .85), rgba(255, 236, 200, 0));
}

.feathers { position: absolute; inset: 0; z-index: 1; overflow: hidden; pointer-events: none; }
.feather { position: absolute; left: 0; top: 0; width: 14px; height: 46px; color: rgba(255, 246, 225, .8); opacity: 0; will-change: transform, opacity; }

/* ---------- 4. typography: monumental, floating, fallen past ---------- */
.typography-layer { position: absolute; inset: 0; pointer-events: none; }      /* no z-index on purpose: each block sorts against Icarus */
.typo { position: absolute; inset: 0; z-index: 2; visibility: hidden; transform-origin: 50% 50%; will-change: transform, filter, opacity; }

.typo--line { z-index: 4; }
.line {                                                                      /* flat black type straight on the painted sky: no shadow, no glow */
  position: absolute; left: 7vw; max-width: 58vw; margin: 0; display: flex; flex-wrap: wrap; column-gap: .26em; row-gap: .04em;
  font-family: var(--serif); font-weight: 400; font-size: clamp(1.9rem, min(4.9vw, 9.4vh), 6rem); line-height: 1.04; letter-spacing: -.012em;
  color: #000000;
  /* one blade cut for the whole line, not one per word: app.js wipes this in and out as a single shape, so a fast
     scroll flick that skips past several frames while ScrollTrigger's scrub catches up always shows one clean
     diagonal cut, never a scatter of some words shown and others not. */
  clip-path: polygon(-4% -10%, -4% -10%, -4% 110%, -4% 110%);
}
.line:not(.is-split) { visibility: hidden; }
.typo--one .line, .typo--two .line { top: max(15vh, calc(var(--header-h) + 32px)); }   /* high in the frame: clear of the fixed header above, and of the cookie notice and the controls below */
.word { display: inline-block; }   /* the blade clip lives on .line itself now: see the note there */
.word__in { display: inline-block; will-change: transform; }
.word--mark {                                                               /* the figure in Act 2 sits on the yellow marker, set in the line's own Gloock like every marked figure on the site */
  padding: .05em .12em .03em; background: var(--accent-yellow-light); color: #000000; line-height: .9;   /* the block stays inside the 1.04 line pitch */
}

.typo--fly::before {                                                 /* the act numeral, engraved and huge, behind the words */
  content: attr(data-num); position: absolute; top: 50%; transform: translateY(-50%); pointer-events: none;
  font-family: var(--serif); font-size: clamp(14rem, 40vw, 54rem); line-height: .8; color: transparent;
  -webkit-text-stroke: 1.5px rgba(229, 176, 92, .24);
}
.typo--left::before { right: -6vw; }
.typo--right::before { left: -6vw; }
.typo__box { position: absolute; top: 50%; transform: translateY(-50%); max-width: min(64vw, 1100px); max-height: 82vh; overflow: hidden; }   /* a centred box never grows past the viewport it is centred in, so a wide, short window cannot push it under the header or the cookie notice */
.typo--left .typo__box { left: 9vw; text-align: left; }        /* margins leave room for the 1.07 reading scale and the 3D tilt */
.typo--right .typo__box { right: 9vw; text-align: right; }
.title {
  margin: 0; font-family: var(--serif); font-weight: 400; font-size: clamp(2.6rem, min(8vw, 15vh), 10rem); line-height: .96; letter-spacing: -.02em;
  color: #f9f3e6;
}
.typo--xl .title { font-size: clamp(4rem, min(16vw, 26vh), 21rem); }
.rule {                                                              /* a Greek key band */
  display: block; width: min(300px, 42vw); height: 14px; margin: clamp(14px, 2vw, 26px) 0; opacity: .92; transform-origin: 0 50%;
  background: url("data:image/svg+xml;utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='28' height='14' viewBox='0 0 28 14'%3E%3Cpath d='M0 13H28M3 13V2H25V10H10V6H18' fill='none' stroke='%23e5b05c' stroke-width='1.6'/%3E%3C/svg%3E") repeat-x left center / auto 100%;
}
.typo--right .rule { margin-left: auto; transform-origin: 100% 50%; background-position: right center; }
.sub {
  margin: 0; max-width: 44ch; font: 400 clamp(1rem, 1.5vw, 1.6rem)/1.5 var(--sans); color: #fff6e6;
}
.typo--right .sub { margin-left: auto; }

/* ---------- 5. icarus ---------- */
#icarus {
  position: absolute; left: 0; top: 0; z-index: 3; height: var(--ic-h); width: auto; aspect-ratio: 460 / 775; opacity: 0;
  transform-origin: 50% 50%; will-change: transform, filter;
}

/* ---------- 6. solar flare: sun, logo, and the mask gate ---------- */
/* The sheet is blinding white with a moving hole (mask inversion): alpha 0 inside radius --h, ramping to 1 across --f.
   GSAP tweens --h from -f (fully opaque) to Rmax + f (fully transparent everywhere), so the camera punches through into deep space. */
#solar-flare {
  position: absolute; inset: 0; z-index: 6; pointer-events: none;
  --fx: 50%; --fy: 50%; --h: -160; --f: 160;
  --hole: calc(var(--h) * 1px); --feather: calc(var(--f) * 1px);
}
.flare__sun {
  position: absolute; left: 0; top: 0; width: var(--sun-size); height: var(--sun-size);
  margin: calc(var(--sun-size) / -2) 0 0 calc(var(--sun-size) / -2);        /* centred on (0,0); GSAP x/y moves it to the focal point */
  visibility: hidden; will-change: transform, filter;
}
.flare__disc {
  position: absolute; inset: 0; border-radius: 50%;
  background: radial-gradient(circle closest-side,                          /* white plateau = first 20% of the radius (SUN_PLATEAU) */
    #fffdf6 0 20%, #fff0bd 26%, #ffc45e 38%, rgba(255, 138, 44, .65) 52%, rgba(255, 96, 20, .22) 72%, rgba(255, 96, 20, 0) 100%);
}
.flare__rays {
  position: absolute; inset: -30%; border-radius: 50%; mix-blend-mode: screen;
  background: repeating-conic-gradient(from 0deg, rgba(255, 226, 160, .5) 0 1.1deg, transparent 1.1deg 7.5deg);
  -webkit-mask-image: radial-gradient(circle closest-side, transparent 22%, #000 34%, transparent 100%);
          mask-image: radial-gradient(circle closest-side, transparent 22%, #000 34%, transparent 100%);
  animation: spin 90s linear infinite;
}
@keyframes spin { to { transform: rotate(360deg); } }

.logo-anchor {                                                              /* never transformed: JS measures the focal point from here */
  position: absolute; width: var(--logo); height: var(--logo);
  left: calc(50% - var(--logo) / 2); top: calc(50% - var(--logo) / 2 - 5vmin);
}
.flare__logo { width: 100%; height: 100%; clip-path: circle(0% at 50% 50%); will-change: transform, filter; }

.flare__sheet {
  position: absolute; inset: 0; background: #fffdf6; visibility: hidden;      /* = the sun's plateau colour, so the hand-off is invisible */
  -webkit-mask-image: radial-gradient(circle at var(--fx) var(--fy), transparent 0, transparent var(--hole), #000 calc(var(--hole) + var(--feather)));
          mask-image: radial-gradient(circle at var(--fx) var(--fy), transparent 0, transparent var(--hole), #000 calc(var(--hole) + var(--feather)));
}
.flare__corona {                                                            /* burning limb; at --h = -f every stop is <= 0, so one flat #fffdf6 */
  position: absolute; inset: 0; visibility: hidden;
  background: radial-gradient(circle at var(--fx) var(--fy),
    rgba(255, 120, 30, 0) calc(var(--hole) - var(--feather) * .9),
    rgba(255, 150, 50, .55) calc(var(--hole) - var(--feather) * .25),
    rgba(255, 196, 110, .95) var(--hole),
    rgba(255, 238, 196, 1) calc(var(--hole) + var(--feather) * .35),
    rgba(255, 253, 246, 1) calc(var(--hole) + var(--feather)));
}

/* ---------- 7. dust + blast ---------- */
/* Layer order inside #dust-particles: flash, fireball and smoke (.blast--back), ground waves, dust, then debris and embers (.blast--front). */
#dust-particles { position: absolute; inset: 0; z-index: 5; overflow: hidden; pointer-events: none; }
.dust {
  position: absolute; left: 0; top: 0; opacity: 0; border-radius: var(--br, 50%); filter: blur(4px); will-change: transform, opacity;
  background: radial-gradient(circle at 42% 40%, rgba(238, 222, 194, .94), rgba(172, 142, 106, .62) 52%, rgba(120, 96, 70, 0) 72%);
}
.impact__flash {                                                             /* the one white flash: hot at the impact point, wide enough to take the whole frame */
  position: absolute; inset: 0; opacity: 0;
  background: radial-gradient(circle at var(--ix, 50%) var(--iy, 80%), #fffdf6 0, rgba(255, 250, 235, .96) 34%, rgba(255, 226, 170, .62) 66%, rgba(255, 226, 170, .2) 100%);
}
.impact__waves { position: absolute; inset: 0; width: 100%; height: 100%; overflow: visible; fill: none; }
.impact__wave { stroke: #17110c; opacity: 0; }                               /* ground shockwaves in ink, so they read over the white core and the stone alike; rx, ry, cx, cy and stroke width are driven from app.js */

.blast { position: absolute; inset: 0; pointer-events: none; }
.blast__disc { position: absolute; inset: 0; opacity: 0; }                  /* the fireball: a jagged burst whose clip-path polygon is written by app.js and scales with --r (px) */
.blast__disc--orange { background: #ff7a1a; }
.blast__disc--yellow { background: #ffd400; }
.blast__disc--white { background: #fffdf6; }
/* No will-change on the pieces that swell: a promoted layer keeps its first raster and upscales it, which turns flat edges into stair-steps. */
.blast__smoke, .blast__shard, .blast__ember { position: absolute; left: 0; top: 0; opacity: 0; }
.blast__ember { will-change: transform, opacity; }                           /* embers never grow, so they can keep their layer */
.blast__smoke { background: #17110c; border-radius: var(--br, 50%); }
.blast__shard { background: linear-gradient(var(--cut, 120deg), var(--lit) calc(46% - 1px), var(--shade) calc(46% + 1px)); }   /* one flat lit face, one flat shade face; the 2px seam is only there to antialias the edge */

/* ---------- 8. call to action (act 10) ---------- */
#dossier-card { position: absolute; inset: 0 0 auto 0; height: var(--vh-s); z-index: 7; display: grid; place-items: center; padding: calc(var(--header-h) + 24px) 24px 24px; pointer-events: none; }   /* centred in the part of the screen a phone's toolbar leaves visible */
.finale { position: relative; display: grid; gap: var(--space-4); width: 100%; max-width: 880px; pointer-events: auto; will-change: transform; }
#dossier-card.is-armed .finale { pointer-events: none; }                    /* clickable only once fully surfaced */
@media (min-width: 720px) { .finale { grid-template-columns: repeat(2, minmax(0, 1fr)); } }
.btn--cta {                                                                 /* flat, high contrast: a yellow block and a black block, each with an ink-to-white wipe on hover */
  --btn-hover-bg: #ffffff; --btn-hover-fg: #000000;
  justify-content: space-between; gap: clamp(16px, 2.4vw, 32px); width: 100%; min-height: 84px; padding: 22px clamp(20px, 3vw, 36px);
  font-size: clamp(1.1rem, .85rem + 1vw, 1.6rem); line-height: 1.3; letter-spacing: -.005em; text-align: left; text-wrap: balance;
}
.btn--cta.btn--ink { --btn-hover-bg: var(--accent-yellow-light); --btn-hover-fg: #000000; }   /* the black block wipes to yellow, so the two never wipe to the same colour */
.btn--cta .btn__icon { width: clamp(20px, 2.2vw, 28px); height: clamp(20px, 2.2vw, 28px); stroke-width: 1.5; }

/* The welcome screen: one flat field of clear sky (the scenery palette's fourth band), a sheet rather than a photograph. The busy
   painted texture and the sunburst are held back for the moment the fall actually needs them (the gate and the
   drop); putting the title straight on that dramatic, detailed art made the very first thing a visitor sees look
   cluttered. This sheet lifts away on the first scroll exactly as before. */
.welcome {
  position: absolute; inset: 0; z-index: 9; overflow: hidden; display: grid; align-content: start; pointer-events: none;
  padding: calc(var(--header-h) + clamp(20px, 5vh, 64px)) var(--gutter) 0;
  background: #d9ecf2; color: #000000; will-change: transform;
}
/* The sheet is part of the picture, not the chrome: it is the same clear day sky in either theme, like every painted scene after it.
   So whatever sits on it (the Skip Cinematic button, its hint, their focus ring) takes the light theme's values from here and stays
   black on the pale sky, even while the header, the loader, the notice and the controls around the film follow a saved dark theme.
   The static screen is not the picture: there the sheet has no background of its own and follows the theme like any other page. */
html:not([data-intro='static']) .welcome { --bg-color: #ffffff; --text-color: #000000; --border-color: #000000; --focus-color: #a67c00; }
.welcome__logo { display: block; width: clamp(48px, 8vw, 110px); height: auto; margin-bottom: clamp(12px, 2vh, 28px); }
.welcome__title { margin: 0; font-family: var(--serif); font-weight: 400; font-size: clamp(3rem, min(14.5vw, 24vh), 16rem); line-height: 1.04; letter-spacing: -.025em; }   /* "Welcome to" is about 5.2em wide in Gloock, so two lines fill three quarters of a landscape screen; the vh cap keeps both lines and the scroll cue on a short one */
.welcome__line { display: block; }
.welcome__mark { display: inline-block; margin-top: .04em; padding: .03em .12em; background: var(--accent-yellow-light); color: #000000; }   /* even padding: the block leaves the same room above the capital as below the baseline */

/* Skip cinematic: an escape hatch for a returning visitor only, on the title sheet only. A first-time visitor
   (no html.is-returning, set by welcome.html's own pre-paint bootstrap from the sonarae-skip-intro cookie)
   never sees this at all, default display: none; app.js sets that cookie once the film reaches its own end,
   or the moment Skip Cinematic itself is pressed, so it always plays through once before the shortcut appears.
   The wrapper is fixed under the header at top right, clear of every other fixed control (the sound button
   and the cookie notice both live at the bottom, the act arrows and .hud__act share the bottom right corner
   but stay invisible, opacity 0, for as long as this is shown, so the slot is never doubly occupied on
   screen). Bottom right was tried first and rejected: at a short viewport the cookie notice (.consent, fixed,
   full width, z-index 110) can grow tall enough to sit on top of anything docked there. Fixed rather than
   flowing under the headline so it can never drift regardless of how tall the headline renders at a given
   viewport. The sheet itself is pointer-events: none (it just sits over the scene), so this wrapper opts back
   in. A transformed ancestor becomes the containing block for a fixed descendant, so this still rides the
   sheet's own lift-away tween (see app.js) and still fades to nothing and goes inert the instant scroll
   starts, hint text included. flex-wrap lets the hint drop to its own line above the button rather than
   overflow, on a narrow screen with html.is-returning set. */
.welcome__return {
  position: fixed;
  top: calc(var(--header-h) + 24px);
  right: var(--gutter);
  z-index: 100;
  display: none;
  align-items: center;
  flex-wrap: wrap;
  justify-content: flex-end;
  gap: var(--space-3);
  max-width: min(70vw, 360px);
  pointer-events: auto;
}
/* Only over the film. The static screen (reduced motion, a blocked animation library) has no cinematic to skip, and its own
   "Go to the homepage" button already does what Skip Cinematic would; there this fixed pair only sat on top of the title. */
html.is-returning:not([data-intro='static']) .welcome__return { display: flex; }
.welcome__return-hint {
  margin: 0;
  font-family: var(--font-ui);
  font-size: .75rem;
  line-height: 1.35;
  color: var(--text-color);
  text-align: right;
}

/* The interaction is deliberately brutalist, not the site's usual smooth ink wipe: a thick 2px border (the
   one other exception to the site's 1px rule, alongside the active-page underline, the selected provider path
   and focus rings, see DESIGN.md), colors invert with no transition at all, and hovering lifts the whole
   block 2px up and left with a fast, unsoftened linear snap, not an eased glide. Pressing it drives the same
   block straight back to (0, 0), reading as the button being pushed back into the page under the pointer. */
.welcome__skip {
  flex: none;
  padding: 10px 18px;
  background: var(--bg-color);
  color: var(--text-color);
  border: 2px solid var(--border-color);
  font-family: var(--font-ui);
  font-size: .8125rem;
  font-weight: 600;
  letter-spacing: .04em;
  text-transform: uppercase;
  text-decoration: none;
  transform: translate(0, 0);
  transition: transform .08s linear;
}
.welcome__skip:active { background: var(--text-color); color: var(--bg-color); }   /* a tap on a touch device still flips color even with no hover to lift from */
@media (hover: hover) and (pointer: fine) {
  .welcome__skip:hover { background: var(--text-color); color: var(--bg-color); transform: translate(-2px, -2px); }
  .welcome__skip:active { transform: translate(0, 0); }                            /* pressed back into the page; wins over :hover's lift since it is declared after it */
}

/* ---------- 9. overlays + HUD ---------- */
.grain {
  position: absolute; inset: -50%; z-index: 8; pointer-events: none; opacity: .09; mix-blend-mode: overlay;
  background-image: url("data:image/svg+xml;utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='220' height='220'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='.85' numOctaves='2' stitchTiles='stitch'/%3E%3CfeColorMatrix values='0 0 0 0 1 0 0 0 0 1 0 0 0 0 1 0 0 0 1.2 -.2'/%3E%3C/filter%3E%3Crect width='100%25' height='100%25' filter='url(%23n)'/%3E%3C/svg%3E");
  animation: grain 1s steps(6) infinite;
}
@keyframes grain {
  0% { transform: translate(0, 0); } 20% { transform: translate(-3%, 2%); } 40% { transform: translate(2%, -3%); }
  60% { transform: translate(-2%, -1%); } 80% { transform: translate(3%, 3%); } 100% { transform: translate(0, 0); }
}
.vignette { position: absolute; inset: 0; z-index: 8; pointer-events: none; background: radial-gradient(120% 95% at 50% 50%, transparent 52%, rgba(3, 6, 16, .6) 100%); }

.hud {
  position: absolute; inset: 0 0 auto 0; height: var(--vh-s); z-index: 9; pointer-events: none;
  font-family: var(--font-ui); font-size: .6875rem; font-weight: 600; letter-spacing: .14em; text-transform: uppercase; font-variant-numeric: tabular-nums;
  color: var(--text-color);
}
.hud > * { position: absolute; }
.hud__act, .hud__pct { padding: 7px 10px 6px; line-height: 1; background: var(--bg-color); border: 1px solid var(--border-color); }   /* every readout is a flat plate, so it reads on the gold sky and on the dark scenes alike */
.hud__act { right: var(--gutter); top: calc(var(--header-h) + 24px); }        /* below the fixed header, which already carries the brand */
.hud__act em { font-style: normal; }
.hud__act, .hud__pct, .hud__rail { transition: opacity .6s ease; }
.hud:not(.is-scrolled) .hud__act, .hud:not(.is-scrolled) .hud__pct, .hud:not(.is-scrolled) .hud__rail { opacity: 0; }   /* on the welcome screen only the "Scroll to begin" cue shows */
.hud.is-finale .hud__pct { opacity: 0; }                                         /* the percent leaves with the sound button once the buttons arrive */
.hud__pct { left: var(--gutter); bottom: 96px; transform: translateY(calc(var(--dock) * -1)); transition: transform .5s cubic-bezier(.16, 1, .3, 1), opacity .6s ease; }   /* above the sound button, and above the cookie notice while it is open */
.hud__rail { left: var(--gutter); top: 50%; width: 8px; height: 22vh; margin-top: -11vh; background: var(--bg-color); border: 1px solid var(--border-color); }
.hud__rail i { position: absolute; inset: 0; background: var(--text-color); transform-origin: top; transform: scaleY(0); }
.hud__pct, .hud__rail { display: none; }

/* ---------- 10. cookie banner, bottom controls ---------- */
.consent {                                                                   /* a flat bar on the bottom edge, in the site's own colours (they follow the theme toggle) */
  position: fixed; left: 0; right: 0; bottom: 0; z-index: 110; padding-bottom: env(safe-area-inset-bottom);
  background: var(--bg-color); color: var(--text-color); border-top: 1px solid var(--border-color);
  font-family: var(--font-ui); will-change: transform;
}
.consent[hidden] { display: none; }
.consent__inner { display: flex; align-items: center; justify-content: space-between; gap: var(--space-6); max-width: var(--max); margin-inline: auto; padding: var(--space-4) var(--gutter); }
.consent__text { flex: 1 1 auto; min-width: 0; max-width: 56ch; }
.consent__title { margin: 0; font-size: 1rem; font-weight: 600; line-height: 1.3; }
.consent__fold { display: grid; grid-template-rows: 1fr; transition: grid-template-rows .5s var(--ease-out); }
.consent__copy { min-height: 0; margin: 0; overflow: hidden; font-size: .9375rem; line-height: 1.55; text-wrap: pretty; }
.consent__accept { flex: none; min-height: 48px; }
/* Once the visitor starts scrolling the notice folds to one line, so an ignored banner never sits on the film. */
.consent.is-compact .consent__fold { grid-template-rows: 0fr; }

/* Bottom controls: sound bottom left, act arrows bottom right. Both ride above the cookie notice (--dock). */
#sound-btn {
  position: fixed; left: var(--gutter); bottom: max(var(--space-5), env(safe-area-inset-bottom)); z-index: 100; display: inline-flex; align-items: center; gap: 8px;
  min-height: 44px; padding: 0 16px 0 12px; white-space: nowrap;
  background: var(--bg-color); color: var(--text-color); border: 1px solid var(--border-color); border-radius: 0;
  font: 500 .8125rem/1 var(--font-ui); letter-spacing: .02em;
  transform: translateY(calc(var(--dock) * -1));
  transition: transform .5s var(--ease-out), background-color .2s ease, color .2s ease;
}
#sound-btn.is-on { background: var(--accent-yellow-light); color: #000000; }
#sound-btn[hidden] { display: none; }
.sound__icon { flex: none; width: 18px; height: 18px; fill: none; stroke: currentColor; stroke-width: 1.6; stroke-linecap: square; stroke-linejoin: miter; }
.sound__waves { opacity: 0; transition: opacity .2s ease; }                  /* the waves show only while the sound is on */
#sound-btn.is-on .sound__waves { opacity: 1; }

.acts {
  position: fixed; right: var(--gutter); bottom: max(var(--space-5), env(safe-area-inset-bottom)); z-index: 100; display: grid;
  transform: translateY(calc(var(--dock) * -1));
  transition: transform .5s var(--ease-out), opacity .4s ease, visibility 0s linear 0s;
}
.acts:not(.is-on) { opacity: 0; visibility: hidden; transition: transform .5s var(--ease-out), opacity .4s ease, visibility 0s linear .4s; }
html.is-footer-visible .acts, html.is-footer-visible #sound-btn { opacity: 0; visibility: hidden; pointer-events: none; }
html[data-intro='static'] .acts { display: none; }
.acts__btn {
  display: grid; place-items: center; width: 44px; height: 44px; padding: 0; cursor: pointer;
  background: var(--bg-color); color: var(--text-color); border: 1px solid var(--border-color); border-radius: 0;
  transition: background-color .2s ease, color .2s ease, opacity .2s ease;
}
.acts__btn + .acts__btn { margin-top: -1px; }                                /* the two squares share one rule */
.acts__btn svg { width: 20px; height: 20px; fill: none; stroke: currentColor; stroke-width: 1.75; stroke-linecap: square; stroke-linejoin: miter; }
.acts__btn[aria-disabled='true'] { opacity: .35; cursor: default; }
@media (hover: hover) and (pointer: fine) {
  #sound-btn:hover, .acts__btn:not([aria-disabled='true']):hover { background: var(--text-color); color: var(--bg-color); }
}

/* ---------- 11. responsive + reduced motion ---------- */
@media (max-width: 900px) {
  .hud__rail { display: none; }
  .typo__box { max-width: 88vw; }
  .typo--left .typo__box { left: 7vw; }
  .typo--right .typo__box { right: 7vw; }
}
/* Screens squarer than 3:2 (a 4:3 monitor, a tablet on its side): Icarus is sized by the height and the type by the width, so on these
   his wing reaches under the explanations. Each explanation's column is held to the room beside him (he stands at 0.34 or 0.64 of the
   width, about 0.69 of the height across at his 1.5 scale, and the reading drift scales the block by 1.07), so its words never lie on
   the figure. The titles still fly past him, as designed. A square screen takes the portrait layout below instead. */
@media (min-aspect-ratio: 1/1) and (max-aspect-ratio: 3/2) and (min-height: 521px) {
  .sub { max-width: min(44ch, calc((55vw - 34.3vh - 24px) / 1.07)); }
}
@media (max-aspect-ratio: 1/1) {                                             /* portrait: full-width type */
  :root { --ic-h: clamp(260px, 44vh, 520px); }
  .welcome__title { font-size: clamp(3rem, 16.4vw, 9rem); }                   /* "Welcome to" on one line, filling the width, "Sonarae" under it */
  .line { left: 6vw; max-width: 88vw; font-size: clamp(1.6rem, 7.4vw, 3.4rem); }
  .sub { max-width: 100%; }
  .typo--one .line, .typo--two .line { top: max(14vh, calc(var(--header-h) + 56px)); }   /* both sky acts sit high, clear of the act counter under the header and far above the notice and the controls */
  /* Acts 4 to 8 read in the top of the frame too, so the type never lies across Icarus: a phone held upright is too narrow for the wide
     layout's type on one side and him on the other, so app.js puts him in the room under each block instead, sized to fit it. */
  .typo__box { top: max(14vh, calc(var(--header-h) + 56px)); transform: none; }
  .typo--fly::before { top: calc(var(--header-h) + 8px); transform: none; }
  .title { font-size: clamp(2.3rem, 12.5vw, 5.4rem); }
  .typo--xl .title { font-size: clamp(3.6rem, 27vw, 10rem); }
  .typo--fly::before { font-size: clamp(11rem, 70vw, 30rem); }
  .plate--palace .plate__img { object-position: 64% 50%; }
}
@media (max-aspect-ratio: 1/1), (max-height: 520px) and (min-aspect-ratio: 1/1) {
  /* On a phone, upright or on its side, "Welcome to" runs across the Skip pair's corner, so a returning visitor's pair cannot float over
     it: the pair takes the logo's row instead (on its own, on a phone on its side, where the logo is not shown), and the title starts under it. */
  html.is-returning:not([data-intro='static']) .welcome { grid-template-columns: auto minmax(0, 1fr); column-gap: var(--space-4); }
  html.is-returning:not([data-intro='static']) .welcome__return { position: static; grid-column: 2; grid-row: 1; justify-self: end; align-self: start; margin-bottom: clamp(12px, 2vh, 28px); }
  html.is-returning:not([data-intro='static']) .welcome__title { grid-column: 1 / -1; }
}
@media (max-width: 720px) {                                                  /* the notice stacks (title, copy, full-width button); folded keeps the same stack, just with the copy row collapsed */
  .consent__inner { display: grid; grid-template-columns: minmax(0, 1fr) auto; grid-template-areas: 'title title' 'copy copy' 'btn btn'; align-items: center; gap: 0 var(--space-4); padding-block: var(--space-4); }
  .consent__text { display: contents; }
  .consent__title { grid-area: title; }
  .consent__fold { grid-area: copy; }
  .consent__accept { grid-area: btn; margin-top: var(--space-4); }
  /* Folded used to force title and button onto one row here ('title btn'). At phone widths the title ("Cookies
     and local memory") and the button ("Accept cookies and memory") both run long, and squeezing them side by
     side left the title a sliver column wrapping across four lines. Folded now stays on the same full-width
     stack as open, with only the copy paragraph collapsed away by .consent__fold above, so it is still shorter
     than open, just two clean full-width lines instead of one cramped one. */
  .acts { grid-auto-flow: column; }                                           /* the arrows sit side by side, one control row with the sound button, and out of the type's way */
  .acts__btn + .acts__btn { margin-top: 0; margin-left: -1px; }
}
@media (max-width: 720px) { .hud__pct { display: none; } }                  /* phones: the percent would sit in the copy's column, so only the act label stays */
@media (max-width: 520px) {
  #sound-btn, .acts { bottom: max(16px, env(safe-area-inset-bottom)); }
  .btn--cta { min-height: 76px; padding: 18px 20px; }
}
/* Landscape phones (about 390px tall): the header and the folded notice take a third of the screen, so the type is sized to the height that is left,
   the controls shrink to squares at the screen edges, and the button clears them. */
@media (max-height: 520px) and (min-aspect-ratio: 1/1) {
  :root { --header-h: 56px; }
  .title { font-size: clamp(2rem, 11.5vh, 4.6rem); }
  .typo--xl .title { font-size: clamp(3rem, 23vh, 9rem); }
  .line { font-size: clamp(1.4rem, 8.6vh, 3rem); }
  .typo__box { max-width: 50vw; }                                              /* half the width: Icarus falls through the other half (app.js) */
  .typo--one .line, .typo--two .line { top: calc(var(--header-h) + 20px); }
  .sub { font-size: clamp(.8125rem, 4.2vh, 1.05rem); line-height: 1.4; max-width: 52ch; }
  .rule { margin: 8px 0; }
  #dossier-card { padding-bottom: 120px; }
  .btn--cta { min-height: 64px; padding: 14px 24px; }
  .consent__inner { padding-block: var(--space-2); }
  #sound-btn { left: 12px; width: 44px; padding: 0; justify-content: center; }
  .sound__label { display: none; }
  .welcome__return { top: calc(var(--header-h) + 12px); max-width: 78vw; }   /* a shorter header leaves real clearance above the act arrows here */
  .welcome__return-hint { font-size: .6875rem; }
  .welcome__skip { padding: 8px 14px; font-size: .75rem; }
  /* The title fits the height that is left: under the header's 20px gap, and 8px clear of the sound square along the bottom (24px up,
     44px tall), which is 96px in all; the title itself is two lines at 1.04 plus the block's 0.1em, 2.18em. The sheet's own mark is not
     shown here: the header carries the same mark right above it. A returning visitor's Skip pair takes a 48px row over the title. On a
     first visit the notice takes the bottom edge too, open (131px) and then folded under the controls (66px more), so the title is sized
     for it from the first frame, never resized later: 162px here, more below where the notice's text wraps to more lines. */
  html:not([data-intro='static']) .welcome__logo { display: none; }
  html:not([data-intro='static']) .welcome__title { font-size: clamp(2rem, min(14.5vw, 22vh, calc((var(--vh-s) - var(--header-h) - 96px) / 2.18)), 16rem); }
  html.is-returning:not([data-intro='static']) .welcome__title { font-size: clamp(2rem, min(14.5vw, 22vh, calc((var(--vh-s) - var(--header-h) - 144px) / 2.18)), 16rem); }
  html.is-notice-due:not([data-intro='static']) .welcome__title { font-size: clamp(2rem, min(14.5vw, 22vh, calc((var(--vh-s) - var(--header-h) - 162px) / 2.18)), 16rem); }
  /* While the notice is still open it covers the bottom edge: the controls wait for it to fold instead of riding above it onto the title. */
  #consent:not([hidden]):not(.is-compact) ~ #sound-btn,
  #consent:not([hidden]):not(.is-compact) ~ .acts { opacity: 0; visibility: hidden; }
  .acts { right: 12px; grid-auto-flow: row; }
  .acts__btn + .acts__btn { margin-top: -1px; margin-left: 0; }
}
@media (max-height: 520px) and (min-aspect-ratio: 1/1) and (min-width: 721px) and (max-width: 840px) {
  html.is-notice-due:not([data-intro='static']) .welcome__title { font-size: clamp(2rem, min(14.5vw, 22vh, calc((var(--vh-s) - var(--header-h) - 184px) / 2.18)), 16rem); }   /* the open notice's text wraps to one more line here: 154px */
}
@media (max-height: 520px) and (min-aspect-ratio: 1/1) and (max-width: 720px) {
  html.is-notice-due:not([data-intro='static']) .welcome__title { font-size: clamp(2rem, min(14.5vw, 22vh, calc((var(--vh-s) - var(--header-h) - 200px) / 2.18)), 16rem); }   /* under 721px the notice stacks: 172px open, 104px folded */
}
html.is-lite .grain { display: none; }                                       /* low-end devices: no animated full-screen blend layer */
@media (prefers-reduced-motion: reduce) {
  .flare__rays, .grain { animation: none; }
  .poster *, html[data-intro='static'] .poster * { animation: none !important; }
  .poster__fall { transform: translateX(-9%); }
  .poster__drop { transform: translateY(10%); }
  .poster__icarus { opacity: 1; transform: translate(-50%, -50%) rotate(4deg) scale(.82); }
  .poster__drift--1 { transform: translate(1.4%, 3%); }
  .poster__drift--2 { transform: translate(-1%, -2%); }
  .poster__drift--3 { opacity: 0; }
  .poster__feather { rotate: 40deg; }
}
@media (forced-colors: active) {
  html[data-intro='static'] .still { display: none; }                         /* high contrast needs the words, not the scenery (as on Our Team) */
  .welcome__title { line-height: 1.2; }                                       /* room for the plate high contrast paints behind each line */
}
