/* ============================================================================
 *  base.css — RESET, STAGE, LOADER, AND THE SHARED SMALL PARTS
 * ========================================================================== */

*, *::before, *::after { box-sizing: border-box; }

html {
  background: var(--field);
  /* The stage is fixed and the spacer supplies the scroll length; smooth
   * behaviour is handled by the damped follower in stage.js, not the browser. */
  scroll-behavior: auto;
  -webkit-text-size-adjust: 100%;
}

body {
  margin: 0;
  background: var(--field);
  color: var(--ink);
  font-family: var(--mono);
  font-size: 16px;
  line-height: 1.4;
  overflow-x: hidden;
  /* the field is the ground for everything; the canvas paints over it, and if
   * WebGL is unavailable this is what remains */
}

h1, h2, h3, p, ul, figure, figcaption, blockquote { margin: 0; }
ul { padding: 0; list-style: none; }
img { display: block; max-width: 100%; height: auto; }
a { color: inherit; }
button { font: inherit; color: inherit; background: none; border: 0; padding: 0; cursor: pointer; }

/* ---- focus: never removed, always visible on both grounds ---------------- */
:focus { outline: none; }
:focus-visible {
  outline: 3px solid var(--ink);
  outline-offset: 3px;
  border-radius: 1px;
}

.sr-only {
  position: absolute;
  width: 1px; height: 1px;
  margin: -1px; padding: 0;
  overflow: hidden;
  clip: rect(0 0 0 0);
  clip-path: inset(50%);
  white-space: nowrap;
  border: 0;
}

.skip {
  position: fixed;
  top: 0; left: 0;
  z-index: 200;
  transform: translateY(-120%);
  background: var(--ink);
  color: var(--field);
  padding: 0.75rem 1.25rem;
  font-family: var(--mono);
  font-size: 12px;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  text-decoration: none;
  transition: transform var(--t-fast) var(--ease-out);
}
.skip:focus { transform: translateY(0); }

/* ==========================================================================
 *  THE STAGE
 *  Everything visible lives in one fixed viewport-sized box. The document
 *  scrolls behind it; the stage never moves.
 * ======================================================================== */
/* THE PAGE DOES NOT SCROLL. The site is one screen; there is nothing below
   the fold to reach, so scrolling to it was scrolling to dead space.

   BOTH HALVES ARE NEEDED. `TOTAL_VH` is 1.0 in config.js, so stage.js writes
   the spacer to exactly one viewport — but `100vh` on a phone is the viewport
   WITH the browser chrome retracted, which is taller than what you are
   actually looking at, and that difference is scrollable. Locking overflow on
   the root is what closes it on every device. Nothing is lost by clipping:
   the stage is `position: fixed` and sizes itself to the viewport, so there
   has never been anything outside it to see. */
html, body {
  height: 100%;
  overflow: hidden;
  overscroll-behavior: none;
}

.spacer {
  /* stage.js overwrites this from the SCENES table on the first frame; this is
     what the document has before the script lands, and it must already be the
     final value or a slow connection gets a scrollbar for a moment. Kept in
     step with `TOTAL_VH`, which is 1.0. */
  height: 100vh;
  pointer-events: none;
}

.stage {
  position: fixed;
  inset: 0;
  overflow: hidden;
  /* the stage itself must not eat pointer events meant for the page; each
   * interactive child opts back in */
  z-index: 1;
}

/* A fine registration lattice and a hard black vignette sit between the
 * shader and the content. The world remains black first, with the detail only
 * revealing itself when it moves or catches an accent. */
.stage::after {
  content: "";
  position: absolute;
  inset: 0;
  pointer-events: none;
}
.stage::after {
  z-index: 4;
  background:
    radial-gradient(ellipse at 50% 43%, transparent 32%, rgba(0, 0, 10, 0.12) 66%, rgba(0, 0, 5, 0.62) 100%),
    linear-gradient(to bottom, rgba(0, 0, 8, 0.14), transparent 19%, transparent 77%, rgba(0, 0, 10, 0.28));
}

.world-art {
  position: absolute;
  inset: -24%;
  z-index: 0;
  background:
    radial-gradient(ellipse at 18% 32%, rgba(122, 67, 35, 0.11), transparent 25%),
    radial-gradient(ellipse at 82% 58%, rgba(91, 105, 99, 0.07), transparent 24%),
    radial-gradient(ellipse at 54% 84%, rgba(112, 44, 25, 0.07), transparent 28%);
  filter: saturate(0.72);
  opacity: 0.34;
  transform: translate3d(var(--art-x, 0), var(--art-y, 0), 0) scale(var(--art-s, 1.02));
  transform-origin: center;
  will-change: transform;
}

.world-art::after {
  content: "";
  position: absolute;
  inset: 0;
  background:
    linear-gradient(to bottom, rgba(0, 0, 12, 0.04), transparent 42%, rgba(0, 0, 10, 0.24)),
    radial-gradient(circle at 72% 42%, rgba(168, 126, 79, 0.055), transparent 31%);
  pointer-events: none;
}

@keyframes star-twinkle {
  0%, 100% { opacity: 0.26; filter: brightness(0.72); }
  44%, 58% { opacity: 1; filter: brightness(1.45); }
}
@keyframes satellite-float { to { transform: translate3d(8px, -5px, 0) rotate(4deg); } }
@keyframes ship-glide { to { transform: translate3d(10px, -3px, 0); } }
@keyframes planet-breathe { 50% { filter: brightness(1.16) saturate(1.15); } }

.world {
  display: block;
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  z-index: 0;
}

/* the hairline that frames the viewport */
.frame {
  position: absolute;
  inset: var(--edge);
  border: var(--hair) solid var(--ink-soft);
  pointer-events: none;
  z-index: 12;
  opacity: 0.85;
}

/* ---- the shared ruler rule ---------------------------------------------- */
.rule {
  position: relative;
  height: var(--hair);
  background: var(--ink-soft);
  width: 100%;
}

/* ==========================================================================
 *  THE GLYPH LOADER
 * ======================================================================== */
.loader {
  position: fixed;
  inset: 0;
  z-index: 100;
  background: var(--field);
  display: grid;
  place-items: center;
  transition: opacity 320ms linear, visibility 0s linear 320ms;
}
.loader[hidden] {
  opacity: 0;
  visibility: hidden;
  pointer-events: none;
  display: grid; /* keep the grid so the fade has something to fade */
}
.loader__glyph {
  display: block;
  width: 26px;
  height: 26px;
  color: var(--ink-soft);
}
.loader__glyph svg { display: block; width: 100%; height: 100%; }

/* ==========================================================================
 *  TARGET SIZE — WCAG 2.2 AA, 2.5.8
 *  --------------------------------------------------------------------------
 *  Measured at 375×812, nine of the site's controls were between 19px and 22px
 *  tall: every "Learn more", "Follow", "Company" link, and all four items in
 *  the fixed nav. The criterion asks for 24×24 CSS pixels, and none of these
 *  qualify for the inline exception — they are standalone links in their own
 *  row, not words inside a sentence, so nothing about the surrounding text
 *  constrains them. Width was never the problem; every one is 41px or wider.
 *
 *  THE HIT AREA GROWS, THE INK DOES NOT. A transparent centred overlay is what
 *  enlarges the target, rather than padding — padding on these would push the
 *  underline away from the word it belongs to, and the nav is on the protected
 *  list, so its box must not move at all. The overlay changes no layout and no
 *  pixel; it only makes the link as easy to hit as it is to see.
 *
 *  IT USES `::before`, AND THAT IS NOT ARBITRARY. The first attempt used
 *  `::after`, which is the nav's own underline indicator — declared in
 *  chrome.css, which loads after this file, so the hit area silently lost the
 *  cascade and resolved to the underline's 1px. Had it won instead, it would
 *  have deleted the underline from all four nav items. `::before` is free on
 *  every one of these three.
 * ======================================================================== */
.chrome__nav a,
.brandfront__links a,
.connect__channels a {
  position: relative;
}
.chrome__nav a::before,
.brandfront__links a::before,
.connect__channels a::before {
  content: "";
  position: absolute;
  left: 0;
  right: 0;
  top: 50%;
  translate: 0 -50%;
  height: 24px;
}

/* ==========================================================================
 *  THE REDUCE PREFERENCE
 *  --------------------------------------------------------------------------
 *  THE SCROLL IS NOT WHAT THIS TURNS OFF. Almost everything on this page is a
 *  pure function of scroll position — the visitor moves the camera, and motion
 *  that only happens because someone is driving it is not the motion a reduce
 *  preference is about. Stopping that would leave a site that could not be read.
 *
 *  What it turns off is everything running on a WALL CLOCK, because that is the
 *  motion nobody asked for and nobody can stop: the loader's cycling glyph, the
 *  scroll cue's fall, the statement's float and its auto-stepping word reel, and
 *  the shader's own flow (frozen in world.js, not here).
 *
 *  `html.is-reduced` is set from JavaScript in stage.js and tracks the query
 *  live. Both selectors are kept because they answer different questions: the
 *  media query covers the no-JavaScript document, the class covers the moment
 *  the preference changes mid-visit.
 * ======================================================================== */
@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after {
    animation-duration: 0.001ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 0.001ms !important;
    scroll-behavior: auto !important;
  }
}
html.is-reduced *,
html.is-reduced *::before,
html.is-reduced *::after {
  animation-duration: 0.001ms !important;
  animation-iteration-count: 1 !important;
  transition-duration: 0.001ms !important;
}

/* ==========================================================================
 *  NO COLOPHON
 *  There was a fixed line pinned to the bottom edge — name, chapter counter,
 *  location — with a scroll-progress rule drawn across it. It is gone by
 *  request: a bar that never leaves reads as browser furniture, and the whole
 *  point of this page is that the stage is uninterrupted. If a sign-off is
 *  wanted again it belongs at the END of the document, in flow, not welded to
 *  the viewport.
 * ======================================================================== */
