/* O&Z (Ones & Zeros): a hero, then one world per screen. The worlds are the picture: the scroll hands the screen from one
   to the next and puts each one together, and a visitor can take hold of one and turn it (site.js).
   The page is the night they hang in. Beside a world stands its invitation; behind it hangs its
   name, as tall as a poster's, in the world's own colours.

   The islands alternate: one stands on the right with its words on the left, the next on the left
   with its words on the right (data-side on the section; site.js reads where the words are and gives
   the island the rest). The page colour is also the colour the worlds' edges fade into (site.js
   reads --page).

   Two layouts, chosen by the shape of the window (site.js reads --layout); in both a world and its
   words are one screen, held in place while the scroll works on it:
   side     a window wider than it is tall: the words beside the world, the 01 to 05 index at the right edge
   stacked  a window taller than it is wide (and any very narrow one): the name, the world under it, the words under that

   Type: three small sizes and no others (--t-cap for labels, --t-sm for the links, --t-md for
   sentences and buttons), the headline, and the names. Everything is set in rem, and the
   root grows with a wide screen, so the words keep their weight beside a larger island. */
@font-face {
  font-family: "Space Grotesk";
  src: url("fonts/space-grotesk.woff2") format("woff2");
  font-weight: 300 700;
  font-display: swap;
}
/* the names behind the worlds: RedLine's own display face (fonts/OFL-barlow.txt) */
@font-face {
  font-family: "Barlow Condensed";
  src: url("fonts/barlow-condensed-900.ttf") format("truetype");
  font-weight: 900;
  font-display: swap;
}

:root {
  --page: #06070a;
  /* the same colours in plain form, for an engine without oklch (the links must stay readable there) */
  --ink: #f5f3ed;
  --ink-soft: #babec6;
  --ink-mute: #858a94;
  --line: rgba(255, 255, 255, 0.16);
  --rule: rgba(255, 255, 255, 0.1);
  --hair: rgba(245, 243, 237, 0.46);   /* the pointers' hairlines */
  --accent: #f5f3ed;            /* a section sets its world's own colour */
  --accent-ink: #06070a;        /* and the colour of text standing on it */
  --t-cap: 0.6875rem;
  --t-sm: 0.8125rem;
  --t-md: 0.9375rem;
  --gutter: clamp(20px, 4.2vw, 72px);
  --bar: 4.25rem;               /* the top bar's height */
  --index-room: clamp(34px, 3.4vw, 56px);   /* what the 01 to 05 index takes of the right edge */
  --space-xs: 4px;
  --space-sm: 8px;
  --space-md: 16px;
  --space-lg: 24px;
  --space-xl: 48px;
  --space-2xl: 96px;
  --ease-out: cubic-bezier(0.22, 1, 0.36, 1);
  --layout: side;
  color-scheme: dark;
}
@supports (color: oklch(0 0 0)) {
  :root {
    --ink: oklch(0.965 0.008 90);
    --ink-soft: oklch(0.80 0.012 265);
    --ink-mute: oklch(0.635 0.016 265);
    --line: oklch(1 0 0 / 0.16);
    --rule: oklch(1 0 0 / 0.1);
  }
}
/* a screen wider than a laptop's: the root grows with it (17.7 px at 1920, 20 px at 2560) */
@media (orientation: landscape) and (min-width: 1441px) {
  html { font-size: clamp(16px, calc(16px + (100vw - 1440px) * 0.0036), 22px); }
}

* { box-sizing: border-box; }

html { background: var(--page); }
/* Nothing snaps: a world's assembly is the visitor's to scrub, slowly and with small steps, and to
   hold anywhere (a snap sprang such steps back to the last resting place). */
/* a jump to another world is a cut, softened: the page dips to its own colour and comes back (site.js) */
main { transition: opacity 0.4s var(--ease-out); }
html.cut main { opacity: 0; transition: opacity 0.16s linear; }

body {
  margin: 0;
  background: var(--page);
  color: var(--ink);
  font: 400 1rem/1.5 "Space Grotesk", "Helvetica Neue", Arial, sans-serif;
  -webkit-font-smoothing: antialiased;
  text-rendering: optimizeLegibility;
  overflow-x: hidden;
}

a { color: inherit; text-decoration: none; }

:focus-visible { outline: 2px solid var(--ink); outline-offset: 4px; border-radius: 4px; }

.sr-only {
  position: absolute; width: 1px; height: 1px; margin: -1px; padding: 0; overflow: hidden;
  clip-path: inset(50%); white-space: nowrap; border: 0;
}

.skip {
  position: fixed; left: var(--gutter); top: var(--space-sm); z-index: 30; padding: var(--space-sm) var(--space-md);
  background: var(--ink); color: var(--page); border-radius: 999px; font-weight: 600; font-size: var(--t-sm);
  transform: translateY(-200%);
}
.skip:focus { transform: none; }

/* ---------------------------------------------------------------- top bar
   Its words stand on the page colour itself, which runs out below them in a fade: a section's
   world passes under the bar unseen instead of colliding with its words, and no box is drawn (a
   stage's own top is the page colour too, so at rest there is nothing to see of it). The fade is
   over before the names' capitals begin, so it takes nothing from them. */
.top {
  position: fixed; inset: 0 0 auto 0; z-index: 20; height: var(--bar); padding: 0 var(--gutter);
  display: flex; align-items: center; justify-content: space-between;
  pointer-events: none;
}
/* (the fade is a mask on the solid colour: a gradient of so dark a colour at part alpha comes out tinted) */
.top::before {
  content: ""; position: absolute; inset: 0 0 auto 0; height: calc(var(--bar) + 2rem); z-index: -1; pointer-events: none;
  background: var(--page);
  -webkit-mask-image: linear-gradient(to bottom, #000 0, #000 36%, rgba(0, 0, 0, 0.5) 54%, transparent 76%);
  mask-image: linear-gradient(to bottom, #000 0, #000 36%, rgba(0, 0, 0, 0.5) 54%, transparent 76%);
}
.top > * { pointer-events: auto; }
/* the wordmark, small; while the hero has the screen the large one is there, so this one waits (site.js: html.at-hero) */
.brand { display: block; padding: var(--space-sm) 0; transition: opacity 0.4s var(--ease-out); }
.brand img { display: block; height: 1.25rem; width: auto; }
html.at-hero .top .brand { opacity: 0; pointer-events: none; }
.top nav { display: flex; gap: clamp(20px, 2.8vw, 44px); font-size: 1.0625rem; font-weight: 600; letter-spacing: 0.005em; color: var(--ink); }
.top nav a { position: relative; padding: var(--space-sm) 0; transition: color 0.2s var(--ease-out); }
.top nav a::after {
  content: ""; position: absolute; left: 0; right: 0; bottom: 3px; height: 1px; background: currentColor;
  transform: scaleX(0); transform-origin: left; transition: transform 0.3s var(--ease-out);
}
.top nav a[aria-current="page"]::after { transform: scaleX(1); }
.top nav a:hover::after { transform: scaleX(1); }

/* ---------------------------------------------------------------- 01 to 05
   Each number wears a breath of its world's colour, so the five show from the first screen. It is
   also a gauge: its marker travels down the track with the scroll (site.js sets --at, 0 to 4) in the
   colour of the world on screen (--now). At the foot of the page it steps back (html.at-foot). */
.index {
  --step: 2.125rem; --at: 0; --now: var(--ink);
  position: fixed; right: clamp(12px, 1.7vw, 30px); top: 50%; z-index: 20; transform: translateY(-50%);
  display: flex; flex-direction: column;
  font-size: var(--t-cap); font-weight: 500; letter-spacing: 0.08em; color: var(--ink-mute);
  font-variant-numeric: tabular-nums;
  transition: opacity 0.4s var(--ease-out);
}
html.at-foot .index, html.at-hero .index { opacity: 0; pointer-events: none; }
.index::before {
  content: ""; position: absolute; left: 1px; top: calc(var(--step) / 2); bottom: calc(var(--step) / 2); width: 1px;
  background: var(--rule);
}
.index::after {
  content: ""; position: absolute; left: 0; top: calc(var(--step) / 2 - 8px); width: 3px; height: 16px; border-radius: 2px;
  background: var(--now); transform: translateY(calc(var(--at) * var(--step)));
  transition: background-color 0.5s var(--ease-out);
}
.index a {
  position: relative; display: flex; align-items: center; justify-content: flex-end; height: var(--step); padding: 0 2px 0 18px;
  transition: color 0.25s var(--ease-out);
}
@supports (color: color-mix(in srgb, red 50%, blue)) {
  .index a { color: color-mix(in srgb, var(--accent) 46%, var(--ink-mute)); }
}
.index a:hover { color: var(--ink-soft); }
.index a[aria-current="true"] { color: var(--accent); font-weight: 700; }
.index .name {
  position: absolute; right: 100%; margin-right: 10px; padding: 2px 0; white-space: nowrap; pointer-events: none;
  font-weight: 500; letter-spacing: 0.04em; color: var(--ink); text-shadow: 0 0 8px var(--page), 0 0 2px var(--page);
  opacity: 0; transform: translateX(6px); transition: opacity 0.2s var(--ease-out), transform 0.3s var(--ease-out);
}
.index a:hover .name, .index a:focus-visible .name { opacity: 1; transform: none; }

/* ---------------------------------------------------------------- the hero: the page's first screen
   The wordmark, large, with the five islands round it, small; under it the name spelt out and the
   slogan. Everything is placed here, for a wide and for a tall screen (the custom properties on
   .hero and on each island); .hero-flat is that picture in plain elements, and where the 3D can run
   hero.js builds the same picture in depth on the canvas, exactly over it, and takes over (.live:
   the flat pictures go, their links stay as the islands' hit areas). --w is an island's width,
   --x and --y its middle; --mark-w and --mark-y the wordmark's. */
.hero {
  position: relative; height: 100vh; height: 100svh; min-height: 340px;
  --mark-w: min(46vw, 104vh); --mark-y: 39%; --copy-y: 55.5%; --w: min(17.5vw, 33vh);
  --orbit-x: 50%; --orbit-y: 52%; --orbit-rx: 39%; --orbit-ry: 34%;   /* the ellipse the islands go round on (hero.js); its top passes behind and above the wordmark, its foot before and below the slogan */
}
.hero-canvas { position: absolute; inset: 0; z-index: 1; width: 100%; height: 100%; display: block; opacity: 0; transition: opacity 0.6s var(--ease-out); pointer-events: none; }
.hero.live .hero-canvas { opacity: 1; }
.hero-flat { position: absolute; inset: 0; }
.hero-mark {
  position: absolute; left: 50%; top: var(--mark-y); width: var(--mark-w); height: auto; transform: translate(-50%, -50%);
  transition: opacity 0.5s var(--ease-out); user-select: none; pointer-events: none;
}
.hero-isles { list-style: none; margin: 0; padding: 0; }
.hero-isles li { position: absolute; left: var(--x); top: var(--y); width: calc(var(--w) * var(--k, 1)); aspect-ratio: 3 / 2; transform: translate(-50%, -50%); }
.hero-isles a { display: block; width: 100%; height: 100%; border-radius: 14px; }
.hero-isles img { display: block; width: 100%; height: 100%; object-fit: contain; transition: opacity 0.5s var(--ease-out); }
/* Where the 3D will run (html.pinning) the wordmark is not shown flat first: hero.js builds it out of the ones and
   zeros of its background, and a wordmark already standing there would spoil that. The flat wordmark is shown after all (.flat) if the
   3D is long in coming (a slow line: the page's first script, after four seconds; the 3D then takes it over as it
   stands, without the build), and where the 3D has failed or lost its canvas (site.js, hero.js). */
html.pinning .hero-mark { opacity: 0; }
.hero.flat .hero-mark { opacity: 1; }
.hero.live .hero-mark, .hero.live .hero-isles img { opacity: 0; }
/* (once the 3D has taken over, hero.js moves each island's link with its island: a transform from the screen's corner) */
.hero.live .hero-isles li { --k: 1; left: 0; top: 0; transform-origin: 0 0; will-change: transform; }
/* (each island's first place: the orbit's ellipse at its --a, and its size there, --k: the far ones at the ellipse's
   top are smaller, the near ones at its foot larger, as hero.js will draw them; it takes them round from there) */
.hero-isles li:nth-child(1) { --a: 90deg; --k: 0.800; --x: 50.0%; --y: 18.0%; }
.hero-isles li:nth-child(2) { --a: 18deg; --k: 0.902; --x: 87.1%; --y: 41.5%; }
.hero-isles li:nth-child(3) { --a: 306deg; --k: 1.137; --x: 72.9%; --y: 79.5%; }
.hero-isles li:nth-child(4) { --a: 234deg; --k: 1.137; --x: 27.1%; --y: 79.5%; }
.hero-isles li:nth-child(5) { --a: 162deg; --k: 0.902; --x: 12.9%; --y: 41.5%; }
.hero-copy { position: absolute; left: var(--gutter); right: var(--gutter); top: var(--copy-y); z-index: 2; text-align: center; pointer-events: none; }
.hero-name { margin: 0; font-size: var(--t-cap); font-weight: 500; letter-spacing: 0.42em; text-indent: 0.42em; text-transform: uppercase; color: var(--ink-mute); }
.hero-copy h1 { margin: clamp(10px, 1.8vh, 20px) 0 0; font-size: clamp(1.9rem, 1rem + 2.7vw, 3.9rem); font-weight: 700; line-height: 1.05; letter-spacing: -0.035em; }
/* (the scroll cue of the first screen: it goes once the page has been scrolled) */
.hero .cue { opacity: 1; z-index: 0; }   /* (under the canvas: an island on its round passes in front of it) */
html.scrolled .hero .cue { opacity: 0; }

/* ---------------------------------------------------------------- a world
   Its section is one screen tall, or taller by --stretch screens where its world assembles with the
   scroll; everything of it is in the pin, one screen in size.
   Where the worlds can be drawn and motion is welcome (site.js marks the page html.staged) every pin
   is held on the screen, one over the other, and the sections are only the length of the scroll:
   site.js shows each pin by where the scroll is (its opacity; .on while it is the one in front), so
   one world leaves in place and the next arrives in place. Elsewhere the pins are simply the
   sections' own screens and the page scrolls through them. */
.world { position: relative; height: calc(100vh * (1 + var(--stretch, 0))); height: calc(100svh * (1 + var(--stretch, 0))); min-height: 340px; }
/* where the worlds assemble with the scroll (the page's first script says so: html.pinning), every
   world after the first has a screen's length more for it (site.js: PIN.stretch) */
html.pinning .world ~ .world, html.pinning .hero ~ .world { --stretch: 1; }
.pin { position: sticky; top: 0; height: 100vh; height: 100svh; min-height: 340px; overflow: hidden; isolation: isolate; background: var(--page); }
html.staged .pin { position: fixed; left: 0; top: 0; width: 100%; opacity: 0; pointer-events: none; }
html.staged .pin.on { pointer-events: auto; }
/* An island can be taken hold of and turned. A finger turns it sideways; up and down stays the page's scroll. */
.stage { position: absolute; inset: 0; touch-action: pan-y pinch-zoom; -webkit-user-select: none; user-select: none; overflow-anchor: none; }   /* (its contents are placed by site.js after the first layout: a browser that puts a reloaded page back by an anchor must not take one of them) */
.world.holdable .stage { cursor: grab; }
.world.held .stage { cursor: grabbing; }
.stage canvas {
  position: absolute; inset: 0; width: 100%; height: 100%; display: block;
  opacity: 0; transition: opacity 0.7s var(--ease-out);
}
.world.ready .stage canvas { opacity: 1; }

/* the painting while the 3D loads, and for visitors without WebGL: placed on the frame by site.js */
.poster {
  position: absolute; left: 0; top: 0; width: 0; height: 0; object-fit: cover; opacity: 0;
  transition: opacity 0.5s var(--ease-out);
  -webkit-mask-image: linear-gradient(to right, transparent, #000 9%, #000 91%, transparent), linear-gradient(to bottom, transparent, #000 8%, #000 92%, transparent);
  -webkit-mask-composite: source-in;
  mask-image: linear-gradient(to right, transparent, #000 9%, #000 91%, transparent), linear-gradient(to bottom, transparent, #000 8%, #000 92%, transparent);
  mask-composite: intersect;
}
.poster.shown { opacity: 1; }
/* the first world's still before its 3D arrives: its island at an early point of its assembly, cut
   out with the island's own curtain in its alpha, so it needs no mask and the name shows behind it */
.poster.cutout { -webkit-mask-image: none; mask-image: none; }

/* The world's name, behind it: as wide as the screen allows and as tall as a poster's, each run of
   letters in the world's own colour (the spans' --c and --c2: light at the letters' heads, deep at
   their feet, like a sign lit from above). site.js sets its size (--sign) so that it fits the stage
   and stays readable above the island, and in WebGL redraws it behind the island, where the game
   lights it: an outline while the world is apart, on when it stands, brighter with a win and red
   with a loss. This DOM copy is what shows with the painting, before the 3D arrives and where it
   cannot run: the unlit sign. Its first stem is drawn in a hair from the column's edge, so that it
   stands in line with the words under it. */
.ghost {
  position: absolute; top: calc(var(--bar) + 0.4vh - 0.06em); left: calc(var(--gutter) - 0.017em);   /* (its capitals start just under the bar: the line box has 0.09 em of air above them) */
  font-family: "Barlow Condensed", "Arial Narrow", Impact, sans-serif; font-weight: 900;
  font-size: var(--sign, clamp(6rem, 24vw, 30rem)); line-height: 0.78; letter-spacing: 0.012em;
  white-space: nowrap; text-transform: uppercase;
  user-select: none; pointer-events: none;
}
.ghost span { color: var(--c, var(--accent)); opacity: 0.85; -webkit-text-stroke: max(2px, 0.012em) currentColor; -webkit-text-fill-color: transparent; }
@supports (color: color-mix(in srgb, red 10%, transparent)) {
  .ghost span { -webkit-text-fill-color: color-mix(in srgb, currentColor 14%, transparent); }
}
.world[data-side="left"] .ghost { left: auto; right: calc(var(--gutter) + var(--index-room) - 0.017em); }

/* Where the painting is all there is of a world (site.js: paint()), the name is raised over it,
   lit, and the island is laid over the name once more (the painting again, masked by its cut-out). */
.world.painting .ghost { z-index: 1; }
.world.painting .ghost span { opacity: 0.82; -webkit-text-fill-color: currentColor; }
.poster-cut {
  position: absolute; left: 0; top: 0; width: 0; height: 0; z-index: 2; object-fit: cover; pointer-events: none;
  -webkit-mask-size: 100% 100%; mask-size: 100% 100%; -webkit-mask-repeat: no-repeat; mask-repeat: no-repeat;
}
.world.ready .poster-cut { display: none; }

/* ---------------------------------------------------------------- pointers: captions tied to points of an island
   site.js moves each label and draws its hairline to the point it names; here is only how they
   look and arrive. They are as the page first had them (the user, 2026-10-06: "do exactly like
   previously"): light text standing on a hairline shelf, a hairline to a small ring on the point.
   They come in, one after the other, when the world stands (site.js marks the section .noted),
   step back while a round's result is on show (.hushed), and go when the world begins to leave. */
.notes, .leads { position: absolute; inset: 0; width: 100%; height: 100%; margin: 0; padding: 0; pointer-events: none; overflow: visible; }
.notes { list-style: none; opacity: 0; transition: opacity 0.6s var(--ease-out); }
.leads { opacity: 0; transition: opacity 0.6s var(--ease-out); }
.world.noted .notes, .world.noted .leads { opacity: 1; }
.world.noted.hushed .notes, .world.noted.hushed .leads { opacity: 0.4; }
.notes li {
  position: absolute; left: 0; top: 0; white-space: nowrap;
  font-size: clamp(0.6875rem, 0.3rem + 0.45vw, 0.8125rem); line-height: 1; font-weight: 400; letter-spacing: 0.03em; color: var(--ink-soft);
  font-variant-numeric: tabular-nums;
  text-shadow: 0 0 6px var(--page), 0 0 2px var(--page);
}
/* the label stands on a hairline shelf whose near end is the place site.js gives it */
.notes li > span {
  display: block; padding: 0 2px 6px 0; border-bottom: 1px solid var(--hair);
  transform: translate(0, -100%);
  opacity: 0; clip-path: inset(0 100% 0 0);
  transition: opacity 0.2s linear, clip-path 0.2s linear;
}
.notes li.to-left > span { padding: 0 0 6px 2px; transform: translate(-100%, -100%); clip-path: inset(0 0 0 100%); }
.world.noted .notes li > span {
  opacity: 1; clip-path: inset(0 0 0 0);
  transition: opacity 0.3s linear calc(0.34s + var(--i) * 0.11s), clip-path 0.6s var(--ease-out) calc(0.34s + var(--i) * 0.11s);
}
.notes b { font-weight: 600; color: var(--ink); margin-right: 0.45em; }
.leads path {
  fill: none; stroke: var(--hair); stroke-width: 1; vector-effect: non-scaling-stroke;
  stroke-dasharray: 1; stroke-dashoffset: 1; transition: stroke-dashoffset 0.2s linear;
}
.world.noted .leads path { stroke-dashoffset: 0; transition: stroke-dashoffset 0.5s var(--ease-out) calc(var(--i) * 0.11s); }
.leads .dot { fill: var(--ink); }
.leads .ring { fill: none; stroke: var(--hair); stroke-width: 1; }
.leads .dot, .leads .ring { opacity: 0; transition: opacity 0.2s linear; }
.world.noted .leads .dot, .world.noted .leads .ring { opacity: 1; transition: opacity 0.3s linear calc(var(--i) * 0.11s); }

/* ---------------------------------------------------------------- the words: an invitation */
.copy {
  position: absolute; left: var(--gutter); top: var(--copy-top, calc(var(--bar) + 30vh + 34px)); z-index: 2;
  width: min(23.5rem, 28vw);
}
.world[data-side="left"] .copy { left: auto; right: calc(var(--gutter) + var(--index-room)); }
/* where the world stands in the five: the name itself hangs behind the island, and the index has the rest */
.label {
  margin: 0 0 var(--space-md); font-size: var(--t-cap); font-weight: 500; letter-spacing: 0.2em;
  color: var(--ink-mute); font-variant-numeric: tabular-nums;
}
.label .no { color: var(--accent); font-weight: 700; }
.label .of { margin-left: 0.5em; }
/* two lines, always: site.js keeps the world clear of the longest one */
.copy h2 {
  margin: 0; font-size: clamp(2rem, 1.05rem + 2.55vw, 3.7rem); font-weight: 700; line-height: 1.03;
  letter-spacing: -0.035em; white-space: nowrap;
}
.how { margin: var(--space-lg) 0 0; max-width: 36ch; font-size: var(--t-md); line-height: 1.55; color: var(--ink-soft); text-wrap: pretty; }
.actions { margin: var(--space-lg) 0 0; display: flex; align-items: center; gap: var(--space-lg); }
html.tight .how { display: none; }   /* (site.js: a screen too low for the sentence under the headline) */
.btn {
  display: inline-flex; align-items: center; gap: 10px; height: 2.875rem; padding: 0 1.5rem;
  border-radius: 999px; border: 1px solid transparent; font: inherit; font-size: var(--t-md); font-weight: 700;
  letter-spacing: -0.005em; cursor: pointer; white-space: nowrap;
  transition: background-color 0.2s var(--ease-out), color 0.2s var(--ease-out), border-color 0.2s var(--ease-out), transform 0.2s var(--ease-out);
}
.btn:focus-visible { outline-offset: 3px; border-radius: 999px; }
/* the one place a world's colour is spent on the page besides its name: its way in */
.btn.play { background: var(--accent); color: var(--accent-ink); }
.btn span[aria-hidden] { transition: transform 0.25s var(--ease-out); }
.btn.play:hover { background: var(--ink); color: var(--page); }
.btn:hover span[aria-hidden] { transform: translateX(4px); }
.btn:active { transform: scale(0.98); }
.btn.line { height: 3.25rem; padding: 0 1.75rem; font-weight: 600; color: var(--ink); border-color: var(--line); }
.btn.line:hover { background: var(--ink); color: var(--page); border-color: var(--ink); }

/* The words come with the world: while the scroll has its assembly unfinished, and when the world
   begins to leave, they are out (site.js sets .apart); they come in, one after the other, as the
   assembly completes. */
.copy > * { transition: opacity 0.6s var(--ease-out), transform 0.8s var(--ease-out); }
.copy h2 { transition-delay: 0.05s; }
.copy .how { transition-delay: 0.11s; }
.copy .actions { transition-delay: 0.17s; }
.world.apart .copy > * {
  opacity: 0; transform: translateY(16px); pointer-events: none;
  transition: opacity 0.22s linear, transform 0.22s linear; transition-delay: 0s;
}

/* the first screen says that there is more below, until the page has been scrolled (html.scrolled) */
.cue {
  position: absolute; left: 50%; bottom: clamp(8px, 1.6vh, 18px); z-index: 2; margin: 0; transform: translateX(-50%);
  display: flex; flex-direction: column; align-items: center; gap: 6px;
  font-size: var(--t-cap); font-weight: 500; letter-spacing: 0.26em; text-transform: uppercase; color: var(--ink-mute);
  transition: opacity 0.5s var(--ease-out);
}
.cue::after {
  content: ""; width: 1px; height: 20px; background: linear-gradient(to bottom, var(--ink-soft), transparent);
  transform-origin: top; animation: cue 2.2s var(--ease-out) infinite;
}
@keyframes cue { 0% { transform: scaleY(0); opacity: 1; } 55% { transform: scaleY(1); opacity: 1; } 100% { transform: scaleY(1); opacity: 0; } }
html.scrolled .cue, .world:not(.standing) .cue { opacity: 0; }

/* ---------------------------------------------------------------- the foot
   The studio's own name as a sixth sign, unlit until it is on screen, then lit letter by letter in
   the five worlds' colours (site.js marks the foot .lit); the shelf stands in front of its feet,
   as every island stands in front of its name. */
.foot {
  position: relative; z-index: 3; display: grid; justify-items: center; gap: var(--space-xl);
  padding: var(--space-2xl) var(--gutter) calc(var(--space-xl) + env(safe-area-inset-bottom));   /* (the same on both sides: the index has stepped back here, and everything of the foot stands in the middle) */
  font-size: var(--t-sm); color: var(--ink-soft);
}
/* The foot comes up over the last world, which stays in its place and goes dark (site.js): the foot
   has the page's colour for its ground, and its upper edge is a rising dusk, not a line (a mask on
   the solid colour: a gradient of so dark a colour at part alpha comes out tinted). */
.foot::before {
  content: ""; position: absolute; inset: 0; z-index: -1; pointer-events: none; background: var(--page);
}
html.staged .foot { padding-top: calc(var(--space-2xl) + 8vh); }
html.staged .foot::before {
  -webkit-mask-image: linear-gradient(to bottom, transparent 0, rgba(0, 0, 0, 0.55) 9vh, #000 20vh);
  mask-image: linear-gradient(to bottom, transparent 0, rgba(0, 0, 0, 0.55) 9vh, #000 20vh);
}
.mark {
  --mark-w: min(100vw - 2 * var(--gutter), 60rem); --mark-h: calc(var(--mark-w) / 3.385);
  margin: 0 0 calc(var(--mark-h) * -0.24 - var(--space-xl)); width: var(--mark-w);   /* (the shelf's islands stand in front of its feet: the grid's gap is taken back too) */
  user-select: none; pointer-events: none;
}
.mark svg { display: block; width: 100%; height: auto; overflow: visible; }
.mark path {
  fill: var(--ink); fill-opacity: 0; stroke: var(--ink); stroke-width: 1.5px; vector-effect: non-scaling-stroke; opacity: 0.55;
  transition: fill-opacity 0.5s var(--ease-out), opacity 0.5s var(--ease-out);
  transition-delay: calc(var(--i) * 120ms);
}
.foot.lit .mark path { fill-opacity: 1; opacity: 0.92; }

.shelf { position: relative; width: 100%; }
/* the islands stand in a row on one hairline, the shelf's edge, with their names under it */
.shelf ul {
  list-style: none; margin: 0; padding: 0;
  display: grid; grid-template-columns: repeat(5, minmax(0, 1fr)); gap: clamp(8px, 1.6vw, 28px);
}
.shelf li { min-width: 0; }
.shelf a {
  display: grid; grid-template-columns: auto minmax(0, 1fr) auto; align-items: baseline; column-gap: 10px; row-gap: 12px;
  color: var(--ink-soft);
}
.shelf img {
  grid-column: 1 / -1; align-self: end; display: block; width: 100%; height: auto; aspect-ratio: 3 / 2; object-fit: contain;
  box-sizing: content-box; padding-bottom: 12px; border-bottom: 1px solid var(--line);
  transition: transform 0.5s var(--ease-out);
  animation: bob calc(7s + var(--i) * 0.5s) ease-in-out calc(var(--i) * -1.3s) infinite alternate;
}
@keyframes bob { from { translate: 0 3px; } to { translate: 0 -4px; } }
.shelf .no { font-size: var(--t-cap); font-weight: 700; letter-spacing: 0.1em; color: var(--accent); font-variant-numeric: tabular-nums; }
.shelf .name { font-size: var(--t-md); font-weight: 600; letter-spacing: -0.01em; color: var(--ink); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.shelf .go { font-size: var(--t-sm); font-weight: 600; white-space: nowrap; color: var(--accent); }
.shelf .go span[aria-hidden] { display: inline-block; transition: transform 0.25s var(--ease-out); }
.shelf a:hover img, .shelf a:focus-visible img { transform: translateY(-10px) scale(1.03); }
.shelf a:hover .go span[aria-hidden] { transform: translateX(4px); }
.shelf a:focus-visible { outline-offset: 6px; }

/* the ways on, in the middle under the shelf: all the games, how to reach the studio; under them the small print */
.out { margin: 0; display: flex; flex-wrap: wrap; justify-content: center; gap: var(--space-md); }
.foot .small {
  margin: 0; display: flex; flex-wrap: wrap; justify-content: center; gap: var(--space-xs) var(--space-lg);
  font-size: var(--t-cap); letter-spacing: 0.04em; color: var(--ink-mute);
}
.foot .small a { color: var(--ink-soft); text-decoration: underline; text-decoration-color: var(--line); text-underline-offset: 3px; transition: color 0.2s var(--ease-out); }
.foot .small a:hover { color: var(--ink); }

/* ---------------------------------------------------------------- the side layout at other heights
   (what a laptop's browser really leaves of its screen is 650 to 800 px) */
/* a low window: the sentence under the headline goes */
@media (orientation: landscape) and (min-width: 561px) and (max-height: 619px) {
  .how { display: none; }
}
/* a short, wide window (a phone on its side): the same screen, closer set */
@media (orientation: landscape) and (max-height: 480px) {
  .label { margin-bottom: var(--space-sm); }
  .actions { margin-top: var(--space-md); }
  .btn { height: 2.5rem; padding: 0 1.25rem; }
  .cue { display: none; }
}
@media (orientation: landscape) and (max-height: 599px) {
  :root { --bar: 3.5rem; }
}

/* ---------------------------------------------------------------- a tall or very narrow window
   One screen still: the name under the bar, the island under the name as wide as the screen, the
   words under the island (site.js places the three: --drop, each island's frame, --copy-top). */
@media (max-width: 560px), (orientation: portrait) {
  :root { --layout: stacked; --index-room: 0px; --bar: 3.5rem; }
  .index { display: none; }
  /* The hero on a tall screen (the user, 2026-10-07: "on phone O Z should be in middle"): the wordmark in the
     middle of the screen with the slogan right under it, and the islands going round both: two small ones above,
     the near ones below, and at the sides they swing out to the screen's edges. (--copy-y hangs the words a fixed
     step under the wordmark, whose height is --mark-w / 3.385; the two together are centred on the screen.) */
  .hero {
    --mark-w: 78vw; --mark-y: calc(50% - 36px); --copy-y: calc(50% - 36px + 78vw / 6.77 + 16px); --w: 33vw;
    --orbit-x: 50%; --orbit-y: 50%; --orbit-rx: 46%; --orbit-ry: 30%;
  }
  .hero-isles li:nth-child(1) { --a: 126deg; --k: 0.826; --x: 23.0%; --y: 25.7%; }
  .hero-isles li:nth-child(2) { --a: 54deg; --k: 0.826; --x: 77.0%; --y: 25.7%; }
  .hero-isles li:nth-child(3) { --a: 198deg; --k: 1.019; --x: 6.3%; --y: 59.3%; }
  .hero-isles li:nth-child(4) { --a: 342deg; --k: 1.019; --x: 93.7%; --y: 59.3%; }
  .hero-isles li:nth-child(5) { --a: 270deg; --k: 1.190; --x: 50.0%; --y: 80.0%; }
  .hero-copy h1 { font-size: clamp(1.6rem, 7.6vw, 3.2rem); }
  .ghost, .world[data-side="left"] .ghost { top: calc(var(--bar) + var(--drop, 0px) - 0.06em); left: calc(var(--gutter) - 0.017em); right: auto; }
  .copy, .world[data-side="left"] .copy { left: var(--gutter); right: var(--gutter); top: var(--copy-top, 62%); width: auto; }
  .label { margin-bottom: 10px; }
  .copy h2 { font-size: clamp(1.75rem, 8.2vw, 3.4rem); }
  .how { margin-top: 14px; max-width: 46ch; }
  .actions { margin-top: 18px; }
  .cue { display: none; }
  .foot { padding: var(--space-xl) var(--gutter) calc(var(--space-xl) + env(safe-area-inset-bottom)); }
  html.staged .foot { padding-top: calc(var(--space-xl) + 10vh); }
  .mark { --mark-w: calc(100vw - 2 * var(--gutter)); }
  /* the shelf in rows, and every row in the middle: on a phone two, two, and the fifth island under the middle
     of the four (the user, 2026-10-07, of the foot: "continue here in middle") */
  .shelf ul { --gap: clamp(8px, 1.6vw, 28px); display: flex; flex-wrap: wrap; justify-content: center; gap: var(--space-lg) var(--gap); }
  .shelf li { flex: 0 0 calc((100% - (var(--cols, 2) - 1) * var(--gap)) / var(--cols, 2) - 0.5px); }
}
/* (how many stand in a row of that shelf: read by the tall layout only) */
@media (min-width: 480px) { .shelf ul { --cols: 3; } }
@media (min-width: 1100px) { .shelf ul { --cols: 5; } }   /* (five abreast only where each still has room for its name) */

/* a finger needs more room than a pointer */
@media (pointer: coarse) {
  .index { --step: 2.75rem; }
  .brand, .top nav a { padding: 12px 0; }
}

@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after { transition-duration: 0.01ms !important; transition-delay: 0s !important; animation: none !important; }
}
