/* NIX — the document
   ══════════════════════════════════════════════════════════════════════════
   The reset, the two boxes that never scroll, the one that does, and the
   grain over all of it. Nothing here has a class of its own except .page,
   which is the scroller every standalone document hangs its content in.

   The long note under the reset is the load-bearing one: this site's <html>
   and <body> are locked and the story scrolls INSIDE .page. It was measured
   on an iPhone before it was believed, and every "why is this fixed rather
   than sticky" further down the set answers to it.
   ══════════════════════════════════════════════════════════════════════════ */

* {
  margin: 0;
  padding: 0;
  box-sizing: border-box;
}

/* THE DOCUMENT DOES NOT SCROLL. The story scrolls inside .page.
   Safari puts the page in a scroll view whose top inset is the status bar. The
   inset moves the LAYOUT VIEWPORT down, but not the content: whatever the
   document scrolls keeps riding up UNDER the bar, and stays visible there. And
   everything anchored to the layout viewport — fixed and sticky alike — is
   clipped to it, so no bar, no overpaint and no shield can ever be painted over
   that content. Measured on an iPhone, three times, before believing it.
   The only cure is to leave no content that CAN ride up there. A scroll
   container lives inside the viewport and clips its own content to its own box,
   so the band above it can only ever show the root canvas — which is paper.
   Costs Safari's toolbar auto-collapse and tap-status-bar-to-top. Worth it. */
/* AND EVERY VIEWPORT UNIT IN THESE SHEETS IS svh, BECAUSE OF THIS RULE. A
   locked document cannot collapse a mobile browser's toolbar, so the layout
   viewport stays the SMALL one for the life of the page while `vh` goes on
   measuring the large one — anything sized in vh is then taller than the box
   that clips it, and the tail of it is unreachable. Two pages had already
   lost a submit button to it. */
html {
  height: 100%;
  overflow: hidden;
  /* the band under the status bar shows this, and nothing else, forever */
  background: var(--nix-paper);
}

body {
  height: 100%;
  overflow: hidden;
  display: flex;
  flex-direction: column;
  background: var(--nix-paper);
  color: var(--nix-text);
  font-family: var(--font-sans);
  font-weight: 400;
  -webkit-font-smoothing: antialiased;
  text-rendering: optimizeLegibility;
}

/* nothing moves behind the preloader — on the scroller, since the body no
   longer scrolls and cannot be locked */
body.is-loading .page {
  overflow: hidden;
}

/* White surfaces keep ink type — the shadow belongs to the grey ground only */
.nav-mode__option,
.plan,
.voice,
.btn {
  text-shadow: none;
}

.plan,
.voice {
  color: var(--nix-ink);
}

/* ---- Page ------------------------------------------------------------ */

/* The scroller. Everything that scrolls, scrolls in here — see the note on
   html above. min-height:0 is what lets a flex item shrink enough to scroll at
   all; without it this box grows to its content and the page never moves. */
.page {
  flex: 1;
  min-height: 0;
  overflow-y: auto;
  /* hidden, not clip: paired with a scrollable axis, `clip` computes to `hidden`
     anyway (checked in both engines), so say so. The old warning about
     overflow-x:hidden was about the BODY, where it conferred scroll-container
     semantics and broke a sticky header. This is a scroll container already, and
     nothing here is sticky. The ribbon runs 100vw and needs the clip. */
  overflow-x: hidden;
  scroll-behavior: smooth;
  overscroll-behavior-y: none;
  -webkit-overflow-scrolling: touch;
  display: flex;
  /* column, NOT row. The footer lives in here now (it has to — outside the
     scroller a body that cannot scroll pins it to the foot of the screen for the
     whole story). Beside the chooser it stole its width and sat in a column of
     its own down the right-hand edge. */
  flex-direction: column;
  align-items: center;
  padding: var(--space-6) var(--space-3);
}

@media (prefers-reduced-motion: reduce) {
  .page {
    scroll-behavior: auto;
  }

  /* THE FLOOR, AND IT IS A FLOOR RATHER THAN A CEILING. Seven of the ten
     sheets carry hand-written reduced-motion blocks naming the animations
     they own; three do not, and the blog's card lift, the notification
     panel's slide and the store's hovers played for a reader who had asked
     the operating system for none of it. A sheet can still name a specific
     animation to switch off — those blocks are more precise than this and
     they still win — but nothing gets to move by default any more.

     .01ms rather than 0 because that is the safe idiom: a transition of
     exactly zero fires no transitionend, and a script that waits on one would
     wait forever. Nothing here currently does — both preloaders return before
     they attach that listener when reduced motion matches — so this is
     insurance rather than a dependency. */
  *,
  *::before,
  *::after {
    animation-duration: 0.01ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 0.01ms !important;
    /* THE DELAYS GO TOO, and without this the floor made things worse rather
       than better: the orbit reveals element by element over 1980ms and the
       menu cards wait 330, 440 and 550 — all of them from opacity 0. Zeroing
       only the durations turned a two-second choreography into two seconds of
       missing content arriving in pops. */
    animation-delay: 0.01ms !important;
    transition-delay: 0.01ms !important;
    scroll-behavior: auto !important;
  }
}

/* ==== Design life pass ================================================= */

/* Ink selection */
::selection {
  background: var(--focus-ink);
  color: var(--nix-paper);
}

/* Paper grain — barely-there print texture over everything */
body::after {
  content: "";
  position: fixed;
  inset: 0;
  z-index: var(--z-grain);
  pointer-events: none;
  opacity: 0.028;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='160' height='160'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.9' numOctaves='2'/%3E%3C/filter%3E%3Crect width='160' height='160' filter='url(%23n)'/%3E%3C/svg%3E");
}
