/* GENERATED by scripts/newsroom/build.mjs — do not edit.
 *
 * Part 1 is src/design/tokens.css, copied byte for byte so the newsroom can
 * never drift from the app's palette.
 * Part 2 is scripts/newsroom/newsroom.css, which may only USE tokens and may
 * only DEFINE --ns-* names.
 */
/*
 * Premiere.Studio design tokens — ported verbatim from the Claude Design
 * handoff (Premiere.Studio.dc.html → computeTokens). Near-monochrome
 * graphite/silver, one accent reserved for the active/primary action.
 *
 * Themes: [data-theme="dark"] (hero) and [data-theme="light"].
 * Page surfaces (the script paper only): [data-surface="paper|cream|slate|carbon"].
 */

/* The @font-face rules are NOT here. They live in public/fonts/fonts.css,
   which index.html links directly, because the standalone legal pages in
   public/ need the same declarations and never load this bundle. Declaring
   them here instead would either duplicate them or leave those pages unstyled.
   An @import would be worse still: it ships as the first line of the bundled
   CSS and serializes HTML → app CSS → fonts.css → woff2 before any styled text
   renders. Keep the families below in sync with public/fonts/fonts.css. */

:root {
  /* Sans: Hanken Grotesk (clean, editorial, pairs with Newsreader).
     Swap candidates: "Onest", "Manrope", "Geist", "Inter Tight". */
  --font-sans: "Hanken Grotesk", "Onest", -apple-system, system-ui, "Helvetica Neue", Arial, sans-serif;
  --font-serif: "Newsreader", Georgia, "Times New Roman", serif;
  --font-mono-ui: "SF Mono", ui-monospace, "SFMono-Regular", Menlo, monospace;
  --font-script: "Courier Prime", "Courier New", Courier, monospace;

  /* Aura primary-button glow gradients (theme-independent). */
  /* Sweeps via --auraSpin (registered @property) so the spectrum FLOWS
     around the button instead of hue-rotating in place. The stops are
     deliberately ASYMMETRIC with a white-hot streak at ~120deg — a
     symmetric ring reads as static no matter how fast it spins; the streak
     is what makes the flow visible. */
  --aura-gradient: conic-gradient(from var(--auraSpin), #6f9bff 0deg, #b06bff 46deg, #ff6bb0 92deg, #ffffff 118deg, #43e0c0 156deg, #ffd36b 216deg, #5fe0ff 288deg, #6f9bff 360deg);
  --aura-gradient-red: conic-gradient(from 0deg, #ff7a6b, #ff5b86, #ff9a6b, #ffb38a, #ff6b6b, #ff7a6b);
  --aura-gradient-green: conic-gradient(from 0deg, #5fd99a, #43e0c0, #9bf0a8, #6bd98a, #58c98f, #5fd99a);

  /* Safe-area insets for notched / Dynamic Island devices. */
  --safe-top: env(safe-area-inset-top, 0px);
  --safe-bottom: env(safe-area-inset-bottom, 0px);
  --safe-left: env(safe-area-inset-left, 0px);
  --safe-right: env(safe-area-inset-right, 0px);
}

/* ============================ DARK (hero) ============================ */
[data-theme="dark"] {
  --bg: #07080a;
  --bgimg: radial-gradient(1100px 620px at 78% -12%, #14161d 0%, transparent 58%),
    radial-gradient(900px 500px at 12% 108%, #0e0f14 0%, transparent 60%);
  --text: #e9eaee;
  --dim: #9b9da6;
  --dim2: #c5c7cf;
  /* Quietest ink that clears WCAG AA body text (4.5:1) on every dark surface
     text is set on directly: 5.61 on --bg, 5.20 on a --panel card, 4.61 on
     the opaque --pop/--neuSurface of modals, popovers and toasts, 4.79 on a
     hovered row, 4.53 on an active segment, 4.67 on a card under the --bgimg
     glow. Nested compositions (a card or hover row inside a modal, --accentDim)
     sit at 3.23+ (the accent tint inside a modal is the floor); only labels
     go there, readable copy is --dim. The old #5d5f69
     measured 2.93 on a card, below even the 3.0 large-text floor, and 523 uses
     sat on it. Ramp order kept: --dim (#9b9da6) is 7.4 on --bg. */
  --faint: #87878d;
  --bar: rgba(12, 13, 17, 0.62);
  --bar2: rgba(10, 11, 15, 0.5);
  --side: rgba(9, 10, 13, 0.4);
  /* Popups & modals use the opaque neumorphic surface (not a translucent glass). */
  --pop: var(--neuSurface);
  --panel: rgba(255, 255, 255, 0.045);
  --panelH: rgba(255, 255, 255, 0.08);
  --seg: rgba(255, 255, 255, 0.045);
  --segactive: rgba(255, 255, 255, 0.1);
  --border: rgba(255, 255, 255, 0.08);
  --hi: rgba(255, 255, 255, 0.1);
  --accent: #e2e8f3;
  --accentDim: rgba(226, 232, 243, 0.13);
  --accentRing: rgba(226, 232, 243, 0.3);
  --accentInk2: #aeb6c6;
  --accentFill: linear-gradient(180deg, #f3f5fa, #cfd6e4);
  --accentInk: #15161b;
  --accentBorder: rgba(226, 232, 243, 0.5);
  --accentGlow: rgba(180, 195, 225, 0.28);
  --logo: linear-gradient(160deg, #2a2c34, #121319);
  --scroll: rgba(150, 150, 160, 0.22);
  --scrollh: rgba(170, 170, 180, 0.4);
  /* Neumorphic extrusion — one artificial LIGHT, cast from the TOP-LEFT:
     every raised element carries a soft light edge up-left and a soft dark
     shadow down-right; inset (pressed) surfaces invert the pair. */
  --neuSurface: #1c1f27;
  --neuRaised: 10px 10px 24px rgba(0, 0, 0, 0.6), -7px -7px 18px rgba(255, 255, 255, 0.045);
  --neuInset: inset 4px 4px 10px #0f1218, inset -4px -4px 10px #2a2f3a;
  --neuBtnRaised: 4px 4px 10px rgba(0, 0, 0, 0.52), -3px -3px 8px rgba(255, 255, 255, 0.05);
  --neuBtnHover: 6px 6px 14px rgba(0, 0, 0, 0.58), -4px -4px 11px rgba(255, 255, 255, 0.065);
  --neuBtnInset: inset 3px 3px 8px rgba(0, 0, 0, 0.6), inset -2px -2px 6px rgba(255, 255, 255, 0.045);
  --auraSurface: rgba(10, 10, 16, 0.82);
  --auraSurfaceHover: rgba(4, 4, 8, 0.92);
  --auraText: #ffffff;
  --auraBorder: rgba(255, 255, 255, 0.24);
}

/* ============================ LIGHT ============================ */
[data-theme="light"] {
  --bg: #e7e8ea;
  --bgimg: radial-gradient(1100px 620px at 78% -12%, #f5f6f8 0%, transparent 60%),
    radial-gradient(900px 520px at 10% 110%, #e9eaec 0%, transparent 62%);
  --text: #191a1d;
  --dim: #52545b;
  --dim2: #34363c;
  /* Same rule, light: 5.02 on --bg (the worst direct case), 5.75 on a
     --panel card, 5.10 on the --pop modal surface, 4.55 on a segment;
     --accentDim compositions 4.36+ (labels only). The old #8a8c93 measured 2.74
     on --bg. --dim (#52545b) is 6.2 on --bg. */
  --faint: #616167;
  --bar: rgba(250, 250, 252, 0.74);
  --bar2: rgba(246, 247, 249, 0.64);
  --side: rgba(242, 243, 245, 0.62);
  /* Popups & modals use the opaque neumorphic surface (not a translucent glass). */
  --pop: var(--neuSurface);
  --panel: rgba(255, 255, 255, 0.66);
  --panelH: rgba(255, 255, 255, 0.96);
  --seg: rgba(20, 22, 28, 0.05);
  --segactive: rgba(255, 255, 255, 0.97);
  --border: rgba(20, 22, 30, 0.11);
  --hi: rgba(255, 255, 255, 0.85);
  --accent: #2b2d34;
  --accentDim: rgba(43, 45, 52, 0.08);
  --accentRing: rgba(43, 45, 52, 0.22);
  --accentInk2: #474a52;
  --accentFill: linear-gradient(180deg, #34363e, #1d1f25);
  --accentInk: #f5f5f4;
  --accentBorder: rgba(20, 20, 25, 0.5);
  --accentGlow: rgba(40, 42, 50, 0.2);
  --logo: linear-gradient(160deg, #3a3c44, #1f2127);
  --scroll: rgba(40, 44, 55, 0.2);
  --scrollh: rgba(40, 44, 55, 0.35);
  --neuSurface: #e6eaf1;
  --neuRaised: 6px 6px 14px rgba(13, 39, 80, 0.14), -6px -6px 14px rgba(255, 255, 255, 0.92);
  --neuInset: inset 4px 4px 9px rgba(13, 39, 80, 0.16), inset -4px -4px 9px rgba(255, 255, 255, 0.92);
  --neuBtnRaised: 4px 4px 10px rgba(13, 39, 80, 0.16), -4px -4px 10px rgba(255, 255, 255, 0.95);
  --neuBtnHover: 6px 6px 14px rgba(13, 39, 80, 0.2), -5px -5px 12px #ffffff;
  --neuBtnInset: inset 3px 3px 8px rgba(13, 39, 80, 0.18), inset -3px -3px 7px rgba(255, 255, 255, 0.9);
  --auraSurface: rgba(255, 255, 255, 0.82);
  --auraSurfaceHover: rgba(232, 233, 238, 0.9);
  --auraText: #1c2238;
  --auraBorder: rgba(255, 255, 255, 0.7);
}

/* ===================== PAGE SURFACES (script paper) ===================== */
[data-surface="paper"] {
  --paper: #fbfbf9;
  --ink: #16130f;
  --paperBorder: rgba(0, 0, 0, 0.06);
  --paperEdge: rgba(255, 255, 255, 0.6);
}
[data-surface="cream"] {
  --paper: #f4eee1;
  --ink: #241c10;
  --paperBorder: rgba(70, 54, 24, 0.12);
  --paperEdge: rgba(255, 255, 255, 0.55);
}
[data-surface="slate"] {
  --paper: #1b1d23;
  --ink: #d7dae2;
  --paperBorder: rgba(255, 255, 255, 0.09);
  --paperEdge: rgba(255, 255, 255, 0.07);
}
[data-surface="carbon"] {
  --paper: #0d0e11;
  --ink: #cdd0d8;
  --paperBorder: rgba(255, 255, 255, 0.07);
  --paperEdge: rgba(255, 255, 255, 0.05);
}
[data-theme] {
  --paperShadow: rgba(0, 0, 0, 0.5);
}

/* ============================ KEYFRAMES ============================ */
@keyframes auraHue {
  0% { filter: blur(6.4px) hue-rotate(0deg); }
  100% { filter: blur(6.4px) hue-rotate(360deg); }
}
/* Houdini-registered angle so a conic-gradient's start angle can ANIMATE
   smoothly — gives the prismatic AI glow a live, flowing sweep (Gemini-style)
   rather than a static pulse. Degrades gracefully (stays at 0deg) where
   @property is unsupported. */
@property --auraSpin {
  syntax: "<angle>";
  inherits: false;
  initial-value: 0deg;
}
@keyframes auraSpin {
  to { --auraSpin: 360deg; }
}
/* Toned glows (red/green) hold their hue — only a gentle brightness breath. */
@keyframes auraSteady {
  0%, 100% { filter: blur(6.4px) brightness(1); }
  50% { filter: blur(6.4px) brightness(1.13); }
}
@keyframes psFadeUp {
  from { opacity: 0; transform: translateY(7px); }
  to { opacity: 1; transform: none; }
}
@keyframes psFade {
  from { opacity: 0; }
  to { opacity: 1; }
}
@keyframes psPop {
  0% { opacity: 0; transform: translateY(4px) scale(0.98); }
  100% { opacity: 1; transform: none; }
}
@keyframes psBlink {
  0%, 45% { opacity: 1; }
  55%, 100% { opacity: 0.15; }
}
@keyframes psDot {
  0%, 80%, 100% { opacity: 0.25; transform: translateY(0); }
  40% { opacity: 1; transform: translateY(-2px); }
}
@keyframes psGlow {
  0%, 100% { opacity: 0.55; }
  50% { opacity: 1; }
}
@keyframes psViewEnter {
  from { opacity: 0; transform: translateY(10px); }
  to { opacity: 1; transform: none; }
}
@keyframes psSlideInLeft {
  from { opacity: 0; transform: translateX(-10px); }
  to { opacity: 1; transform: none; }
}
@keyframes psScaleIn {
  from { opacity: 0; transform: scale(0.96); }
  to { opacity: 1; transform: none; }
}

/* ============================ PRIMITIVES ============================ */
.ps-scroll::-webkit-scrollbar { width: 11px; height: 11px; }
.ps-scroll::-webkit-scrollbar-thumb {
  background: var(--scroll, rgba(150, 150, 160, 0.22));
  border-radius: 9px;
  border: 3px solid transparent;
  background-clip: padding-box;
}
.ps-scroll::-webkit-scrollbar-thumb:hover {
  background: var(--scrollh, rgba(170, 170, 180, 0.4));
  background-clip: padding-box;
}
.ps-scroll::-webkit-scrollbar-track { background: transparent; }

/* Animated glass-aura (primary buttons). Subtle at rest, lively on hover. */
.aura-host { position: relative; border: 0; background: transparent; padding: 0; cursor: pointer; }
.aura-glow {
  position: absolute;
  inset: -1.6px;
  z-index: 0;
  background: var(--aura-gradient);
  filter: blur(6.4px);
  /* The flow: the conic spectrum's start angle sweeps continuously. On hover
     AuraButton accelerates THIS animation via the Web Animations API
     (updatePlaybackRate) — a seamless speed-up, no restart, no jump. */
  animation: auraSpin 4.5s linear infinite;
  opacity: 0.34;
  transition: opacity 0.35s ease;
}
@media (prefers-reduced-motion: reduce) {
  .aura-glow { animation: none; }
}
.aura-host:hover .aura-glow { opacity: 0.88; }
.aura-surface {
  position: relative;
  z-index: 1;
  background: var(--auraSurface, rgba(10, 10, 16, 0.82));
  -webkit-backdrop-filter: blur(16px) saturate(1.5);
  backdrop-filter: blur(16px) saturate(1.5);
  border: 1px solid var(--auraBorder, rgba(255, 255, 255, 0.22));
  color: var(--auraText, #fff);
  box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.18);
  transition: background 0.35s ease, color 0.3s ease;
}
/* Dark: a solid near-black face with no top border (the glow reads above it). */
[data-theme="dark"] .aura-surface {
  background: #060709;
  border-width: 0px 1px 1px 1px;
}

::selection { background: rgba(150, 175, 235, 0.3); }

/* Brand lockups (ui/Brand.tsx, BetaLanding, Splash): the wordmark is an
   image pair — /brand/wordmark-light.png (dark glyphs) for the light theme,
   /brand/wordmark-dark.png (white glyphs) for dark. Both elements render;
   these rules hide the off-theme one, so the swap follows the theme toggle
   with no JS and no flash. display:none also removes the hidden copy from
   the accessibility tree, so only one "Premiere.Studio" alt is exposed.
   Display is owned HERE for both elements — an inline display on the <img>
   would out-rank the theme hide rules and show both lockups at once. */
.brand-for-light, .brand-for-dark { display: block; }
[data-theme="dark"] .brand-for-light { display: none; }
[data-theme="light"] .brand-for-dark { display: none; }

/* ── newsroom overlay ─────────────────────────────────────────────── */
/*
 * Newsroom overlay.
 *
 * This file may only USE tokens (var(--x)) and may only DEFINE --ns-* names.
 * It is concatenated after src/design/tokens.css verbatim by assets.mjs, so
 * the newsroom can never drift from the app's palette. The retiring blog's
 * STYLE const was a hand-copy of the landing's --land-* values that had to be
 * re-synced by hand forever; that is the mistake this arrangement corrects.
 *
 * NO prefers-color-scheme, deliberately. The app has none (design/theme.tsx
 * defaults to dark with no matchMedia anywhere), the landing pins dark, and
 * privacy.html/terms.html hard-set color-scheme: dark. A light-OS visitor
 * seeing a light newsroom and a dark everything-else would be product drift
 * shipped as a side effect of one page's implementation. If the product ever
 * wants system theme, that is a theme.tsx decision applied everywhere and this
 * page should follow it, not lead it. Reversing this is two lines.
 */

:root {
  --ns-measure: 42rem;                 /* the reading column, matching the retiring blog */
  --ns-wide: 1180px;                   /* the landing's content grid */
  --ns-wider: 52rem;                   /* the lead image's "a little wider than the column" */
  --ns-gutter: clamp(20px, 4vw, 40px); /* the landing's section padding */
  /* The sticky bar's height: the row (.ns-nav-in, held to this less its
     1px bottom border) plus that border. Stated rather than measured, so
     the stylesheet can reason about what the bar covers. */
  --ns-nav-h: 60px;
}

/* tokens.css does not set this, and without it native scrollbars and form
   controls keep the OS appearance while the page flips theme. */
[data-theme="dark"] { color-scheme: dark; }
[data-theme="light"] { color-scheme: light; }

* { box-sizing: border-box; }

/* clip, NOT hidden: hidden on a scroll container breaks position: sticky on
   the nav. Required by the full-bleed figure rule below, which uses 100vw. */
html, body { overflow-x: clip; }

body {
  margin: 0;
  background: var(--bg);
  background-image: var(--bgimg);
  background-attachment: fixed;
  color: var(--dim2);
  font-family: var(--font-sans);
  font-size: 16px;
  line-height: 1.74;
  -webkit-font-smoothing: antialiased;
  text-rendering: optimizeLegibility;
  /* The headline's view timeline (declared on .ns-h1) is hoisted here so the
     nav can read it: a named timeline is visible to descendants only, and
     the nav precedes the article. A dashed ident, not a custom property. */
  timeline-scope: --ns-headline;
}

a { color: inherit; }
img, video { max-width: 100%; }

/* ── Chrome ──────────────────────────────────────────────────────── */

.ns-nav {
  position: sticky; top: 0; z-index: 50;
  border-bottom: 1px solid var(--border);
}
/* The bar's material — its tint and the blur of whatever scrolls under it —
   lives on a pseudo-element, not on .ns-nav itself. A backdrop-filter makes
   its element a backdrop root: anything INSIDE it that asks for a backdrop
   filter samples the nav's own, already-filtered box instead of the page, so
   the mobile title bar, a child hung under the nav, tinted the article
   without blurring it. With the filter on ::before the nav is no root, and
   the bar blurs the paragraph it covers exactly as the nav does. z-index -1
   keeps the pseudo under the nav's content and above the page — the nav is a
   stacking context (sticky + z-index), so -1 cannot fall behind the page. */
.ns-nav::before {
  content: ""; position: absolute; inset: 0; z-index: -1;
  background: var(--bar);
  -webkit-backdrop-filter: blur(26px) saturate(1.6);
  backdrop-filter: blur(26px) saturate(1.6);
}
.ns-nav-in {
  max-width: var(--ns-wide); margin: 0 auto;
  padding: 12px var(--ns-gutter);
  display: flex; align-items: center; gap: 18px;
  /* The bar is this row plus the nav's 1px border. The row's tallest item
     used to be the text mark's 35px line box; the lockup image is shorter
     than the 30px toggle, so without this the bar would follow the toggle
     to 55px. */
  min-height: calc(var(--ns-nav-h) - 1px);
}
.ns-nav-sp { flex: 1; }
.ns-mark { display: inline-flex; align-items: center; text-decoration: none; }
.ns-navlink {
  font-family: var(--font-sans); font-weight: 500; font-size: 11px;
  letter-spacing: 0.18em; text-transform: uppercase;
  color: var(--dim); text-decoration: none; transition: color 0.2s ease;
  /* nowrap — when a long article title is in the bar the row is short of
     room, and flex shrinks every item in proportion: "Open the Studio"
     wrapped to two lines at 1024px and three at 720px, and the bar grew to
     82px (measured). The title is the item built to give way (min-width: 0,
     ellipsis); the links hold their line and the bar its height. */
  white-space: nowrap;
}
.ns-navlink:hover { color: var(--text); }
/* The links, one container the burger controls. Inline on wide screens, so
   the bar reads exactly as it did; a stacked panel under the bar on narrow. */
.ns-nav-links { display: flex; align-items: center; gap: 18px; flex: 1; min-width: 0; }

/* The article's title, in the nav once the reader has scrolled past the
   headline. Inline beside "Newsroom" on wide screens; a slim strip under the
   bar on narrow ones, where the inline span has no room. Both are hidden at
   rest and aria-hidden always: the h1 is the accessible name, this is a
   scroll cue.

   Two mechanisms, one at a time. Where the engine has scroll-driven
   animations (the @supports block after the narrow-screen rules) the cue is
   scrubbed by the headline's own view timeline and needs no script — which
   is what makes it show in the console's preview, a sandbox that runs none.
   Elsewhere theme.js watches the headline and adds ns-nav--title, and the
   two class rules below apply. Under the animation those class rules are
   inert (an animation outranks a normal declaration in the cascade), and
   theme.js does not add the class there anyway. */
.ns-nav-title {
  flex: 0 1 auto; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
  font-family: var(--font-serif); font-style: italic; font-weight: 500; font-size: 15px;
  color: var(--text); opacity: 0; transform: translateX(-8px); pointer-events: none;
  transition: opacity 0.35s ease, transform 0.35s ease;
}
.ns-nav--title .ns-nav-title { opacity: 1; transform: none; } /* the script path */
.ns-nav-titlebar {
  /* Out of flow, hung under the bar like the open menu: opening it must not
     grow the sticky nav, or the whole article reflows by the bar's height
     and the sticky-title observer's inset (measured once, from the nav) goes
     stale — in an engine without scroll anchoring that is a feedback loop
     across a ~36px band of scroll. It carries the nav's own material because
     it sits over the article, not inside the nav's box — and that material
     only blurs because .ns-nav keeps its own filter on ::before (above).
     Set in the inline title's face, serif italic, not the links' mono caps:
     it is the article's name, not a link. line-height 1.3 keeps the 15px line
     plus 7+8px of padding inside the 40px the shown state allows. */
  display: none; position: absolute; top: 100%; left: 0; right: 0;
  padding: 0 var(--ns-gutter); max-height: 0; opacity: 0; overflow: hidden;
  background: var(--bar);
  -webkit-backdrop-filter: blur(26px) saturate(1.6);
  backdrop-filter: blur(26px) saturate(1.6);
  border-bottom: 1px solid transparent;
  font-family: var(--font-serif); font-style: italic; font-weight: 500; font-size: 15px;
  line-height: 1.3; color: var(--text);
  white-space: nowrap; text-overflow: ellipsis;
  transition: max-height 0.35s ease, opacity 0.35s ease, padding 0.35s ease;
}
.ns-nav--title .ns-nav-titlebar { max-height: 40px; opacity: 1; padding: 7px var(--ns-gutter) 8px; border-bottom-color: var(--border); } /* the script path */

.ns-toggle, .ns-burger {
  /* flex: none — on a 320px screen the row is short of room and the lockup
     is what gives way (it scales, .ns-brand-for-*); the buttons keep their
     30px or the toggle squeezed to 28 (measured). */
  display: grid; place-items: center; width: 30px; height: 30px; flex: none;
  border-radius: 9px; border: 1px solid var(--border);
  background: var(--panel); color: var(--dim); cursor: pointer;
  transition: background 0.2s ease, color 0.2s ease, border-color 0.2s ease;
}
.ns-toggle:hover, .ns-burger:hover { background: var(--panelH); color: var(--text); border-color: var(--accentRing); }
/* The burger exists only where the links do not fit. Its two icons ship and
   CSS picks one — lines closed, a cross open — the way the toggle's do. */
.ns-burger { display: none; }
.ns-burger .ns-ico-cross { display: none; }
.ns-nav--open .ns-burger .ns-ico-lines { display: none; }
.ns-nav--open .ns-burger .ns-ico-cross { display: block; }
@media (max-width: 700px) {
  .ns-burger { display: grid; }
  .ns-nav-in .ns-toggle { margin-left: auto; }
  .ns-nav-links { display: none; }
  .ns-nav--open .ns-nav-links {
    display: flex; flex-direction: column; align-items: stretch; gap: 0;
    position: absolute; top: 100%; left: 0; right: 0;
    padding: 4px var(--ns-gutter) 10px;
    background: var(--pop); border-bottom: 1px solid var(--border);
    box-shadow: 0 18px 40px -24px rgba(0, 0, 0, 0.6);
  }
  .ns-nav--open .ns-nav-links .ns-navlink { padding: 14px 0; border-top: 1px solid var(--border); }
  .ns-nav--open .ns-nav-links .ns-navlink:first-child { border-top: 0; }
  .ns-nav--open .ns-nav-sp { display: none; }
  .ns-nav-title { display: none; }
  .ns-nav-titlebar { display: block; }
  /* Both hang under the bar at top: 100%; while the menu is open it owns
     that space. */
  .ns-nav--open .ns-nav-titlebar { display: none; }
}
/* The cue, scrubbed by scroll. The headline's view timeline runs over its
   "exit": 0% as its top edge goes behind the bar (the timeline's start inset
   is the bar's height, so "gone" means gone behind the bar, exactly the
   observer's rootMargin), 100% as its bottom edge does. The title slides in
   beside "Newsroom" as the headline slides under, and back out the same way.
   `both` holds the from-state above the range and the to-state below it.
   The longhands follow the `animation` shorthand on purpose: the shorthand
   resets animation-timeline and animation-range to their initial values.
   theme.js tests the same two features and stands down where both hold, so
   the condition here and the CSS.supports() pair there must stay one. */
@supports (animation-timeline: view()) and (timeline-scope: --ns-headline) {
  .ns-nav-title {
    animation: ns-title-in linear both;
    animation-timeline: --ns-headline;
    animation-range: exit 0% exit 100%;
    transition: none;
  }
  .ns-nav-titlebar {
    animation: ns-titlebar-in linear both;
    animation-timeline: --ns-headline;
    animation-range: exit 0% exit 100%;
    transition: none;
  }
  /* The cue still appears — it is information — but fades in place: no
     slide (the transform itself is cleared in the reduced-motion block
     under Motion, for both paths). The bar's growth is a size, not a
     motion, and stays. */
  @media (prefers-reduced-motion: reduce) {
    .ns-nav-title { animation-name: ns-title-in-still; }
  }
}
@keyframes ns-title-in { from { opacity: 0; transform: translateX(-8px); } to { opacity: 1; transform: none; } }
@keyframes ns-title-in-still { from { opacity: 0; } to { opacity: 1; } }
@keyframes ns-titlebar-in {
  from { max-height: 0; opacity: 0; padding: 0 var(--ns-gutter); border-bottom-color: transparent; }
  to { max-height: 40px; opacity: 1; padding: 7px var(--ns-gutter) 8px; border-bottom-color: var(--border); }
}
/* Both icons ship and CSS picks one, the same trick tokens.css uses for the
   wordmark. No JS in the render path, so the icon is right at first paint. */
.ns-toggle .ns-ico-sun { display: none; }
.ns-toggle .ns-ico-moon { display: block; }
[data-theme="light"] .ns-toggle .ns-ico-sun { display: block; }
[data-theme="light"] .ns-toggle .ns-ico-moon { display: none; }

.ns-eyebrow {
  display: inline-flex; align-items: center; gap: 14px;
  font-family: var(--font-sans); font-weight: 600; font-size: 11px;
  letter-spacing: 0.32em; text-transform: uppercase; color: var(--dim);
  margin: 0;
}
.ns-eyebrow::before, .ns-eyebrow::after {
  content: ""; width: 40px; height: 1px; background: var(--border);
}
.ns-eyebrow--plain::before, .ns-eyebrow--plain::after { display: none; }

.ns-kicker {
  font-family: var(--font-sans); font-weight: 700; font-size: 10px;
  letter-spacing: 0.08em; text-transform: uppercase; color: var(--faint);
}
.ns-date { font-family: var(--font-mono-ui); font-size: 11px; color: var(--faint); }

/* ── Motion ──────────────────────────────────────────────────────── */

.ns-reveal { animation: psFadeUp 0.55s cubic-bezier(0.2, 0.7, 0.2, 1) backwards; }
.ns-d0 { animation-delay: 0ms; }   .ns-d1 { animation-delay: 45ms; }
.ns-d2 { animation-delay: 90ms; }  .ns-d3 { animation-delay: 135ms; }
.ns-d4 { animation-delay: 180ms; } .ns-d5 { animation-delay: 225ms; }
.ns-d6 { animation-delay: 270ms; } .ns-d7 { animation-delay: 315ms; }
.ns-d8 { animation-delay: 360ms; }
/* The framework-free equivalent of the landing's IntersectionObserver. */
@supports (animation-timeline: view()) {
  .ns-reveal {
    animation: psFadeUp linear both;
    animation-timeline: view();
    animation-range: entry 0% cover 22%;
    animation-delay: 0s;
  }
}
@media (prefers-reduced-motion: reduce) {
  .ns-reveal { animation: none !important; }
  html { scroll-behavior: auto; }
  /* The nav's title cue fades in place: no 8px slide on either path (the
     scroll-driven keyframes swap to an opacity-only set under the nav
     rules; the script path's transition drops transform here). Without
     this the animated path left the title parked at translateX(-8px). */
  .ns-nav-title { transform: none; transition: opacity 0.35s ease; }
}

/* ── Index ───────────────────────────────────────────────────────── */

.ns-masthead {
  max-width: var(--ns-wide); margin: 0 auto;
  padding: clamp(48px, 7vw, 92px) var(--ns-gutter) 0;
}
.ns-masthead h1 {
  font-family: var(--font-serif); font-style: italic; font-weight: 500;
  font-size: clamp(38px, 6vw, 62px); line-height: 1.04; letter-spacing: -0.015em;
  /* First in the masthead since the brand eyebrow came out (2026-09-05, the
     owner's call: the lockup is already in the bar above it); the masthead's
     own padding sets the distance from the nav. */
  color: var(--text); margin: 0;
}
.ns-masthead p { margin: 14px 0 0; max-width: 52ch; color: var(--dim); font-size: 16.5px; }

.ns-lead {
  display: grid; grid-template-columns: 1.15fr 1fr;
  gap: clamp(24px, 4vw, 54px); align-items: center;
  max-width: var(--ns-wide); margin: 0 auto;
  padding: clamp(36px, 5vw, 64px) var(--ns-gutter) clamp(36px, 5vw, 60px);
  border-bottom: 1px solid var(--border);
}
@media (max-width: 940px) {
  .ns-lead { grid-template-columns: 1fr; gap: 22px; }
  .ns-lead-media { order: -1; }
}
.ns-lead-title {
  font-family: var(--font-serif); font-style: italic; font-weight: 500;
  font-size: clamp(32px, 4.4vw, 56px); line-height: 1.06; letter-spacing: -0.015em;
  color: var(--text); margin: 18px 0 0; text-wrap: balance;
}
.ns-lead-title a { text-decoration: none; }
.ns-lead-title a:hover { color: var(--accent); }
.ns-lead-stand { margin: 16px 0 0; max-width: 46ch; font-size: 16.5px; line-height: 1.66; color: var(--dim); }
.ns-lead-meta { margin: 16px 0 0; display: flex; gap: 12px; align-items: center; }
.ns-lead-media img {
  /* height: auto is load-bearing beside aspect-ratio: the <img> carries
     width="1280" height="720" for layout stability, and the height attribute
     is a presentational hint (height: 720px) that aspect-ratio defers to. On
     the first day a real post was on the index the poster rendered 409px wide
     and 720px tall, cover-cropped to a portrait strip, with the card's text
     centred beside it half a screen down. */
  display: block; width: 100%; height: auto; aspect-ratio: 16 / 9; object-fit: cover;
  border: 1px solid var(--border); border-radius: 12px;
}

.ns-filters {
  display: flex; gap: 8px; flex-wrap: wrap;
  max-width: var(--ns-wide); margin: 0 auto; padding: 26px var(--ns-gutter) 0;
}
.ns-chip {
  font-family: var(--font-sans); font-weight: 600; font-size: 11px;
  letter-spacing: 0.14em; text-transform: uppercase;
  padding: 7px 13px; border-radius: 999px; text-decoration: none;
  border: 1px solid var(--border); background: var(--seg); color: var(--dim);
  transition: background 0.2s ease, color 0.2s ease, border-color 0.2s ease;
}
.ns-chip:hover { background: var(--segactive); color: var(--text); }
.ns-chip[aria-current="page"] {
  background: var(--segactive); color: var(--text); border-color: var(--accentRing);
}

/* The canonical responsive grid — identical to the four in-app uses. The
   min(100%, 300px) inside minmax is what stops overflow on narrow screens. */
.ns-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(min(100%, 300px), 1fr));
  gap: 20px;
  max-width: var(--ns-wide); margin: 0 auto;
  padding: 28px var(--ns-gutter) 0;
}
@media (max-width: 700px) { .ns-grid { gap: 14px; } }

/* GlassCard's material: --side + blur(18px), the card shadow, the top highlight. */
.ns-card {
  display: flex; flex-direction: column;
  border-radius: 16px; border: 1px solid var(--border);
  background: var(--side);
  -webkit-backdrop-filter: blur(18px) saturate(1.4);
  backdrop-filter: blur(18px) saturate(1.4);
  box-shadow: 0 18px 50px -24px rgba(0, 0, 0, 0.55), inset 0 1px 0 var(--hi);
  overflow: hidden; text-decoration: none;
  transition: transform 0.2s ease, border-color 0.2s ease;
}
.ns-card:hover { transform: translateY(-3px); border-color: var(--accentRing); }
.ns-card-media {
  /* height: auto beside aspect-ratio, for the same reason as .ns-lead-media
     img: the card's <img> carries a height attribute the ratio would defer to. */
  width: 100%; height: auto; aspect-ratio: 16 / 9; object-fit: cover; display: block;
  border-bottom: 1px solid var(--border);
}
.ns-card-body { padding: 14px 16px 16px; display: flex; flex-direction: column; gap: 8px; flex: 1; }
.ns-card-title {
  font-family: var(--font-serif); font-style: italic; font-weight: 500;
  font-size: 18px; line-height: 1.24; color: var(--text); margin: 0;
}
.ns-card-stand {
  margin: 0; font-size: 12.5px; line-height: 1.55; color: var(--dim);
  display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; overflow: hidden;
}
.ns-card-meta { margin-top: auto; display: flex; align-items: center; gap: 10px; }

.ns-pager {
  max-width: var(--ns-wide); margin: 0 auto;
  padding: 40px var(--ns-gutter) 0;
  display: flex; gap: 10px; align-items: center; justify-content: center;
}
.ns-pager a, .ns-pager span {
  font-family: var(--font-mono-ui); font-size: 12px;
  padding: 8px 12px; border-radius: 9px; border: 1px solid var(--border);
  color: var(--dim); text-decoration: none; transition: background 0.2s ease, color 0.2s ease;
}
.ns-pager a:hover { background: var(--panelH); color: var(--text); }
.ns-pager [aria-current="page"] { background: var(--segactive); color: var(--text); border-color: var(--accentRing); }

.ns-empty {
  max-width: var(--ns-wide); margin: 0 auto;
  padding: 60px var(--ns-gutter); color: var(--faint);
}

/* ── Article ─────────────────────────────────────────────────────── */

.ns-article { max-width: var(--ns-wide); margin: 0 auto; padding: 0 var(--ns-gutter) 110px; }
.ns-head { max-width: var(--ns-measure); margin: 0 auto; padding: clamp(44px, 6vw, 84px) 0 0; }
.ns-h1 {
  font-family: var(--font-serif); font-style: italic; font-weight: 500;
  font-size: clamp(34px, 7vw, 50px); line-height: 1.06; letter-spacing: -0.012em;
  color: var(--text); margin: 22px 0 0; text-wrap: balance;
  /* The nav's title cue runs on this element's progress through the
     viewport (the @supports block under the nav rules). The start inset is
     the sticky bar's height, so the timeline counts the headline as gone
     only once it is behind the bar, not merely past the window's edge. */
  view-timeline-name: --ns-headline;
  view-timeline-axis: block;
  view-timeline-inset: var(--ns-nav-h) auto;
}
.ns-stand {
  margin: 18px 0 0; font-family: var(--font-serif); font-weight: 400;
  font-size: 19px; line-height: 1.6; color: var(--dim2);
}
.ns-byline {
  margin: 22px 0 0; display: flex; gap: 14px; flex-wrap: wrap;
  font-family: var(--font-mono-ui); font-size: 11.5px; letter-spacing: 0.04em; color: var(--faint);
}
.ns-ribbon {
  display: inline-block; margin: 18px 0 0; padding: 5px 10px; border-radius: 7px;
  font-family: var(--font-mono-ui); font-size: 11px; letter-spacing: 0.08em;
  border: 1px solid var(--accentRing); background: var(--accentDim); color: var(--accentInk2);
}

/* The body's base element rules live inside :where() so they add NO specificity:
   a bare `.ns-body p` is (0,1,1) and silently beat both the rhythm rule below
   (0,1,0) and every component class in this file — paragraphs and lists sat
   flush against each other, and .ns-cta-link lost its colour to `.ns-body a`
   (near-white text on the near-white accent pill). Inside :where(), each rule is
   (0,1,0): the rhythm rule wins on margin by source order, and component classes
   win on everything they declare. Bare `p` only; classed paragraphs style themselves. */
.ns-body { max-width: var(--ns-measure); margin: 34px auto 0; }
/* UA margins off first (the old base paragraph rule zeroed them as a side
   effect); the rhythm rule right after it is the only margin between blocks. */
.ns-body :where(p, ul, ol, h2, h3, h4, pre) { margin: 0; }
/* The body's children are <section class="ns-section"> runs, one per h2
   (article.mjs), so the rhythm is read at BOTH levels: between sections and
   between the blocks inside one. A section has no box of its own, so its
   18px collapses with the first block's margin — an h2's 64px still wins —
   and the page measures exactly as it did with the blocks bare. */
.ns-body > * + * { margin-top: 18px; }
.ns-section > * + * { margin-top: 18px; }
/* Paragraphs breathe twice as much as the base rhythm (0,1,1 beats the rules above). */
.ns-body > * + p { margin-top: 36px; }
.ns-section > * + p { margin-top: 36px; }
.ns-body :where(p:not([class]), p.ns-lead-para) { font-size: 16px; line-height: 1.78; color: var(--dim2); }
.ns-body :where(p.ns-lead-para) { font-size: 17.5px; color: var(--text); }
.ns-body :where(strong) { color: var(--text); font-weight: 600; }
.ns-body :where(a) {
  color: var(--text); text-decoration: underline;
  text-decoration-color: var(--accentRing); text-underline-offset: 3px;
}
.ns-body :where(a:hover) { text-decoration-color: var(--text); }
.ns-body :where(code) { font-family: var(--font-script); font-size: 0.92em; color: var(--dim2); }
.ns-pre {
  font-family: var(--font-script); font-size: 13px; overflow-x: auto;
  background: var(--panel); border: 1px solid var(--border);
  border-radius: 10px; padding: 14px 16px;
}
.ns-h2 {
  font-family: var(--font-serif); font-style: italic; font-weight: 500;
  font-size: 27px; line-height: 1.2; color: var(--text); margin-top: 64px !important;
}
.ns-h3 {
  font-family: var(--font-sans); font-weight: 600; font-size: 11.5px;
  letter-spacing: 0.2em; text-transform: uppercase; color: var(--dim2); margin-top: 52px !important;
}
.ns-h4 {
  font-family: var(--font-sans); font-weight: 600; font-size: 15px;
  line-height: 1.4; color: var(--text); margin-top: 40px !important;
}
/* A sign-off in the house serif at the h2 scale: the name at the end of a letter. */
.ns-signature {
  font-family: var(--font-serif); font-style: italic; font-weight: 500;
  font-size: 27px; line-height: 1.2; color: var(--text); margin-top: 34px !important;
}
.ns-body :where(ul) { list-style: disc; padding-left: 22px; }
.ns-body :where(ol) { list-style: decimal; padding-left: 22px; }
.ns-body :where(li) { margin-bottom: 6px; }
/* The last item's 6px would collapse through the list's bottom edge and act
   as a bottom margin on the list. Every follower's top margin is 18px or
   more, so the page never showed it — but a spacer after the list would add
   it to the sum (measured: 32px spacer after a list opened 74, not 68). */
.ns-body :where(li:last-child) { margin-bottom: 0; }
.ns-rule { border: 0; border-top: 1px solid var(--border); margin: 40px 0 !important; }

/* Figures. Three widths; bleed depends on html,body{overflow-x:clip} above. */
.ns-fig { margin: 34px 0 !important; }
.ns-fig img, .ns-fig video, .ns-fig iframe {
  display: block; width: 100%; height: auto;
  border: 1px solid var(--border); border-radius: 12px;
}
.ns-fig iframe { border-radius: 12px; }
/* <picture> is an inline box. The block <img> inside it still sat on a line
   box, and that line box's strut (the figure inherits body's 1.74 line-height)
   left a sliver of descender space under the image and a matching one above:
   the "small gap between the image and its container". Blocking the picture
   removes the line box; zeroing its line-height removes the strut. The caption
   is a sibling, not a child, so it keeps its own line-height below. */
.ns-fig picture { display: block; line-height: 0; }
.ns-fig--wide {
  width: min(var(--ns-wide), 100vw - 2 * var(--ns-gutter));
  margin-inline: calc(50% - min(var(--ns-wide), 100vw - 2 * var(--ns-gutter)) / 2) !important;
}
.ns-fig--bleed { width: 100vw; margin-inline: calc(50% - 50vw) !important; }
.ns-fig--bleed img, .ns-fig--bleed video { border-radius: 0; border-left: 0; border-right: 0; }
.ns-hero { margin: clamp(30px, 4vw, 48px) 0 0 !important; }
/* The brand is the official lockup, one image per theme (the same pair the
   app's Brand renders), in the footer at 28px and in the nav at 22px. The nav
   carried a text mark in the page's own fonts until 2026-09-04; the owner
   asked for the asset. 22px was chosen against that mark in headless Chrome:
   the mark's italic "Premiere" had a 16px cap height, which the lockup's
   lettering reaches at 18px, but its strokes are lighter and its figure
   needs 22px to resolve at 1x; 24px and up starts to outweigh the 11px links
   beside it. The bar's height does not follow the image (.ns-nav-in). */
.ns-brand-for-dark, .ns-brand-for-light { display: block; height: 28px; width: auto; }
/* object-fit: contain — the lockup is 216px wide at 22px, and a 320px screen
   has 184px for it beside the two buttons. The flex item shrinks the image's
   box (img { max-width: 100% } above) but not its 22px height, and the
   picture squashed to fit (measured: drawn at 8.45:1 against 9.81:1 natural);
   contain scales it down whole instead. */
.ns-nav .ns-brand-for-dark, .ns-nav .ns-brand-for-light { height: 22px; object-fit: contain; }
[data-theme="dark"] .ns-brand-for-light { display: none; }
[data-theme="light"] .ns-brand-for-dark { display: none; }
/* An embed the console cannot play: its preview is a script-less sandbox and
   the canvas is not the page, so both show a static stand-in instead of the
   player, which would only ever say "unable to execute JavaScript". */
.ns-embed--static {
  display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 6px;
  width: 100%; border: 1px dashed var(--border); border-radius: 12px; background: var(--panel);
  font-family: var(--font-sans); color: var(--dim2); text-align: center; padding: 16px;
}
.ns-embed-static-title { font-weight: 600; font-size: 14px; color: var(--text); }
.ns-embed-static-note { font-size: 12px; color: var(--dim); }
/* The lead image's width is the author's choice (hero_width). "grid" is the
   reading column, so it is centred on --ns-measure rather than left to the
   article's full width; "wider" reuses the wide figure's centring maths with a
   narrower box; "full" is the bleed figure as it is. After .ns-fig--wide on
   purpose: equal specificity, so source order decides. */
.ns-hero--grid { max-width: var(--ns-measure); margin-inline: auto !important; }
.ns-hero--wider {
  width: min(var(--ns-wider), 100vw - 2 * var(--ns-gutter));
  margin-inline: calc(50% - min(var(--ns-wider), 100vw - 2 * var(--ns-gutter)) / 2) !important;
}
.ns-cap {
  margin: 10px auto 0; max-width: var(--ns-measure);
  font-size: 12.5px; line-height: 1.6; color: var(--faint);
}
.ns-fig--bleed .ns-cap { padding-inline: var(--ns-gutter); }
.ns-embed { width: 100%; border: 1px solid var(--border); }

.ns-gallery { display: grid; gap: 12px; }
.ns-gallery[data-cols="2"] { grid-template-columns: repeat(2, 1fr); }
.ns-gallery[data-cols="3"] { grid-template-columns: repeat(3, 1fr); }
@media (max-width: 700px) { .ns-gallery { grid-template-columns: 1fr !important; } }

.ns-quote { margin: 42px 0 !important; padding-left: 22px; border-left: 2px solid var(--accentRing); }
.ns-quote p {
  margin: 0; font-family: var(--font-serif); font-style: italic;
  font-size: 24px; line-height: 1.36; color: var(--text);
}
.ns-quote cite {
  display: block; margin-top: 12px; font-style: normal;
  font-family: var(--font-sans); font-weight: 600; font-size: 10.5px;
  letter-spacing: 0.22em; text-transform: uppercase; color: var(--faint);
}

.ns-facts {
  margin: 36px 0 !important; border: 1px solid var(--border);
  border-radius: 14px; overflow: hidden; background: var(--panel);
}
.ns-facts-h {
  margin: 0; padding: 12px 16px; border-bottom: 1px solid var(--border);
  font-family: var(--font-sans); font-weight: 600; font-size: 10.5px;
  letter-spacing: 0.2em; text-transform: uppercase; color: var(--dim2);
}
.ns-facts-row { display: grid; grid-template-columns: minmax(120px, 30%) 1fr; border-top: 1px solid var(--border); }
.ns-facts-h + .ns-facts-row, .ns-facts-row:first-child { border-top: 0; }
.ns-facts-k {
  padding: 12px 16px; border-right: 1px solid var(--border);
  font-family: var(--font-sans); font-weight: 600; font-size: 10.5px;
  letter-spacing: 0.16em; text-transform: uppercase; color: var(--faint);
}
.ns-facts-v { padding: 12px 16px; font-size: 14.5px; color: var(--dim2); }
@media (max-width: 560px) {
  .ns-facts-row { grid-template-columns: 1fr; }
  .ns-facts-k { border-right: 0; padding-bottom: 0; }
}

.ns-callout {
  margin: 34px 0 !important; padding: 18px 20px; border-radius: 14px;
  border: 1px solid var(--border); background: var(--panel);
}
.ns-callout[data-tone="accent"] { border-color: var(--accentRing); background: var(--accentDim); }
.ns-callout-title {
  margin: 0 0 8px; font-family: var(--font-sans); font-weight: 600; font-size: 10.5px;
  letter-spacing: 0.2em; text-transform: uppercase; color: var(--accentInk2);
}
.ns-callout p { margin: 0; }

.ns-cta { margin: 34px 0 !important; }
.ns-cta-link {
  display: inline-block; padding: 11px 18px; border-radius: 11px;
  border: 1px solid var(--accentBorder); background: var(--accentFill); color: var(--accentInk);
  font-family: var(--font-sans); font-weight: 600; font-size: 13px;
  letter-spacing: 0.02em; text-decoration: none;
  transition: background 0.2s ease, border-color 0.2s ease;
}
.ns-cta-link:hover { background: var(--accentDim); border-color: var(--accentRing); color: var(--text); }
.ns-cta-note { margin: 10px 0 0; font-size: 12.5px; color: var(--faint); }

.ns-contacts {
  margin: 56px 0 0 !important; padding-top: 26px;
  border-top: 1px solid var(--border); font-size: 13.5px; color: var(--dim);
}
.ns-contacts ul { list-style: none; margin: 14px 0 0; padding: 0; }
.ns-contacts li { margin-bottom: 8px; }
.ns-contacts li:last-child { margin-bottom: 0; } /* same reason as the body list's last item */

/* Whitespace ADDS its size to the gap the page would have had without it:
   the rhythm between two blocks stays what it is — a paragraph's 36px, an
   h2's 64px — and the spacer opens that much more. (It used to BE the gap:
   every margin around it was zeroed so its height was the whole distance,
   and a 32px spacer before an h2 shrank a 64px gap to 32.)

   The spacer brings no margin of its own, so the 18px rhythm rule cannot add
   above it: the gap is exactly the normal one plus the height. That normal
   gap is the LARGER of the predecessor's bottom margin and the follower's
   top margin, because adjoining margins collapse — and a box between them
   stops the collapse, which would make the gap bottom + size + top. So a
   block before a spacer hands its bottom margin to the spacer instead: the
   spacer's bottom margin collapses with the follower's top margin exactly as
   the block's own did, and the gap is max(bottom, top) + size for every pair
   (measured: quote 42 → p 36 with a 16px spacer opens 58, not 52 or 94).
   Only these six blocks carry a bottom margin; the number beside each is its
   own, from its rule above, and the guard test holds the two equal.

   A RUN of spacers hands over once, on the run's last one: the zeroing rule
   above matches every spacer that another spacer follows (it is "anything
   before a spacer"), and each hand-over reaches a second and a third spacer
   through `.ns-space:where(block + .ns-space, block + .ns-space + .ns-space,
   ...)`. The block sits INSIDE the :where() — `block + :where(.ns-space +
   .ns-space)` would demand a spacer preceded by both the block and a spacer
   at once, and never matches (measured: quote, ws16, ws16, p opened 68, the
   hand-over lost). :where() keeps every hand-over at (0,1,0): below the
   zeroing rule's (0,2,0), which must win on the inner spacers, and equal to
   `.ns-space { margin: 0 }`, which they follow, so the later rule wins. A
   run longer than three loses the predecessor's bottom margin; a paragraph
   follower's own 36px then still applies, so only a quote or a rule before
   four spacers lands short, by 6px or 4px. */
.ns-space { margin: 0 !important; }
.ns-space[data-size="16"] { height: 16px; }
.ns-space[data-size="32"] { height: 32px; }
.ns-space[data-size="64"] { height: 64px; }
.ns-body :has(+ .ns-space) { margin-bottom: 0 !important; }
.ns-space:where(.ns-fig + .ns-space, .ns-fig + .ns-space + .ns-space, .ns-fig + .ns-space + .ns-space + .ns-space) { margin-bottom: 34px !important; }
.ns-space:where(.ns-quote + .ns-space, .ns-quote + .ns-space + .ns-space, .ns-quote + .ns-space + .ns-space + .ns-space) { margin-bottom: 42px !important; }
.ns-space:where(.ns-facts + .ns-space, .ns-facts + .ns-space + .ns-space, .ns-facts + .ns-space + .ns-space + .ns-space) { margin-bottom: 36px !important; }
.ns-space:where(.ns-callout + .ns-space, .ns-callout + .ns-space + .ns-space, .ns-callout + .ns-space + .ns-space + .ns-space) { margin-bottom: 34px !important; }
.ns-space:where(.ns-cta + .ns-space, .ns-cta + .ns-space + .ns-space, .ns-cta + .ns-space + .ns-space + .ns-space) { margin-bottom: 34px !important; }
.ns-space:where(.ns-rule + .ns-space, .ns-rule + .ns-space + .ns-space, .ns-rule + .ns-space + .ns-space + .ns-space) { margin-bottom: 40px !important; }

/* ── Section reveals ─────────────────────────────────────────────── */

/* Gated on html.ns-js, which theme.js adds at startup, so the hiding rule
   exists only where the script that undoes it is running. With JS off, and in
   the console's sandboxed preview (no allow-scripts), no section is ever
   hidden. The first section is marked in by the script immediately; the rest
   as they enter the viewport, once. */
html.ns-js .ns-section {
  opacity: 0; transform: translateY(14px);
  transition: opacity 0.55s cubic-bezier(0.2, 0.7, 0.2, 1), transform 0.55s cubic-bezier(0.2, 0.7, 0.2, 1);
}
html.ns-js .ns-section.is-in { opacity: 1; transform: none; }
@media (prefers-reduced-motion: reduce) {
  html.ns-js .ns-section { opacity: 1; transform: none; transition: none; }
}

/* The share row closes the header, under the byline: a row of icon controls
   in the toggle's material, so it reads as chrome beside the header's text
   rather than as more of it. */
.ns-share { margin: 20px 0 0; display: flex; align-items: center; gap: 8px; flex-wrap: wrap; }
.ns-share .ns-kicker { margin-right: 6px; }
.ns-share a, .ns-share button {
  display: grid; place-items: center; width: 30px; height: 30px; padding: 0;
  border-radius: 9px; border: 1px solid var(--border);
  background: var(--panel); color: var(--dim); cursor: pointer; text-decoration: none;
  transition: background 0.2s ease, color 0.2s ease, border-color 0.2s ease;
}
.ns-share a:hover, .ns-share button:hover { background: var(--panelH); color: var(--text); border-color: var(--accentRing); }
/* Copied: the chain becomes a check for as long as theme.js holds data-copied. */
.ns-share .ns-ico-check { display: none; }
.ns-share button[data-copied] { color: var(--accentInk2); border-color: var(--accentRing); background: var(--accentDim); }
.ns-share button[data-copied] .ns-ico-link { display: none; }
.ns-share button[data-copied] .ns-ico-check { display: block; }

.ns-related { margin-top: clamp(64px, 8vw, 110px); padding-top: 34px; border-top: 1px solid var(--border); }
/* The eyebrow sits over the middle of the cards, and the cards sit in the
   middle of the column. The index grid stretches its cards to fill the row;
   here a row is one to three related stories, so a stretched grid put two
   cards hard left under a heading that was also hard left. Columns are
   capped at a card's width and the row is centred, so any count is centred. */
.ns-related .ns-eyebrow { display: flex; justify-content: center; }
.ns-related .ns-grid {
  padding-left: 0; padding-right: 0;
  grid-template-columns: repeat(auto-fit, minmax(min(100%, 300px), 353px));
  justify-content: center;
}

/* ── Footer ──────────────────────────────────────────────────────── */

.ns-foot { border-top: 1px solid var(--border); margin-top: clamp(70px, 9vw, 120px); }
.ns-foot-in {
  max-width: var(--ns-wide); margin: 0 auto;
  padding: 54px var(--ns-gutter) 60px;
  display: flex; gap: 40px; flex-wrap: wrap; justify-content: space-between;
}
.ns-foot-cols { display: flex; gap: clamp(28px, 5vw, 70px); flex-wrap: wrap; }
.ns-foot-col h2 {
  margin: 0 0 12px; font-family: var(--font-sans); font-weight: 600; font-size: 10px;
  letter-spacing: 0.2em; text-transform: uppercase; color: var(--faint);
}
.ns-foot-col ul { list-style: none; margin: 0; padding: 0; }
.ns-foot-col li { margin-bottom: 8px; }
.ns-foot-col a { font-size: 13px; color: var(--dim); text-decoration: none; transition: color 0.2s ease; }
.ns-foot-col a:hover { color: var(--text); }
.ns-foot-bar {
  max-width: var(--ns-wide); margin: 0 auto; padding: 0 var(--ns-gutter) 44px;
}
.ns-foot-bar span {
  display: block; padding-top: 24px; border-top: 1px solid var(--border);
  font-size: 11px; letter-spacing: 0.16em; color: var(--faint);
}
