/* ==========================================================================
   BASE — reset, page, typography primitives, layout, utilities, nav, grain.
   Reads only tokens from tokens.css.
   ========================================================================== */

/* ---------- reset ------------------------------------------------------- */
*, *::before, *::after { box-sizing: border-box; }
* { margin: 0; }
html {
  -webkit-text-size-adjust: 100%;
  text-size-adjust: 100%;
  scroll-padding-top: 5rem;
}
html:not(.lenis) { scroll-behavior: smooth; }
@media (prefers-reduced-motion: reduce) { html { scroll-behavior: auto !important; } }
body {
  min-height: 100dvh;
  background: var(--c-dawn-100);
  color: var(--color-text);
  font-family: var(--font-text);
  font-size: var(--text-body);
  font-weight: var(--weight-text);
  line-height: var(--leading-body);
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
  text-rendering: optimizeLegibility;
  overflow-x: clip;
}
img, svg, video { display: block; max-width: 100%; }
button, input, select, textarea { font: inherit; color: inherit; }
button { cursor: pointer; background: none; border: 0; padding: 0; }
a { color: inherit; text-decoration-thickness: 1px; text-underline-offset: .2em; }
ul, ol { list-style: none; padding: 0; }
summary { list-style: none; }
summary::-webkit-details-marker { display: none; }
::selection { background: var(--color-selection); color: var(--color-text); }

/* Lenis (smooth scroll) — required rules from lenis.css */
html.lenis, html.lenis body { height: auto; }
.lenis.lenis-smooth { scroll-behavior: auto !important; }
.lenis.lenis-smooth [data-lenis-prevent] { overscroll-behavior: contain; }
.lenis.lenis-stopped { overflow: clip; }

/* ---------- sections pick up their theme -------------------------------- */
[data-theme] { background: var(--color-bg); color: var(--color-text); }

/* ---------- layout ------------------------------------------------------ */
.container {
  width: 100%;
  max-width: var(--max-width);
  margin-inline: auto;
  padding-inline: var(--margin);
}

/* ---------- type primitives -------------------------------------------- */
/* Display (Montserrat). <em> inside a heading = the Lora italic accent. */
.display, .title, .hero__title, .cta__title, .pitch__text {
  font-family: var(--font-display);
  font-optical-sizing: auto;
  font-weight: var(--weight-display);
  text-wrap: balance;
}
.display {
  font-size: var(--text-display);
  line-height: var(--leading-tight);
  letter-spacing: var(--tracking-display);
}
.title {
  font-size: var(--text-title);
  line-height: var(--leading-snug);
  letter-spacing: var(--tracking-title);
  font-weight: var(--weight-title);
}
.display em, .title em, .hero__title em, .cta__title em {
  font-style: italic;
  font-weight: 340;
  color: var(--color-text-soft);
}
.lead {
  font-size: var(--text-lead);
  line-height: 1.45;
  color: var(--color-text-soft);
  max-width: var(--measure-narrow);
  text-wrap: pretty;
}
p { text-wrap: pretty; }

/* Mono label: tiny, uppercase, tracked. Numerals (01, 1920s) live here. */
.label {
  font-family: var(--font-mono);
  font-size: var(--text-label);
  font-weight: 400;
  line-height: 1.4;
  letter-spacing: var(--tracking-label);
  color: var(--color-text-muted);
}

/* Kicker: the small line above a heading. Words, not data, so it is set in
   the text face, sentence case, no tracking. */
.kicker {
  font-family: var(--font-ui);
  font-size: var(--text-kicker);
  font-weight: 500;
  line-height: 1.4;
  color: var(--color-text-muted);
}

/* The brand's small square, as an inline glyph. */
.sq {
  display: inline-block;
  width: .7em; height: .7em;
  border: 1.5px solid var(--color-accent);
  margin-right: .75em;
  vertical-align: -.05em;
}

/* ---------- icons ------------------------------------------------------- */
.sprite { position: absolute; width: 0; height: 0; overflow: hidden; }
.icon {
  width: 1.1em; height: 1.1em;
  flex: none;
  fill: none;
  stroke: currentColor;
  stroke-width: 2;
  stroke-linecap: square;
  stroke-linejoin: miter;
}

/* ---------- accessibility utilities ------------------------------------ */
.visually-hidden {
  position: absolute !important;
  width: 1px; height: 1px;
  padding: 0; margin: -1px;
  overflow: hidden; clip: rect(0 0 0 0);
  white-space: nowrap; border: 0;
}
.skip-link {
  position: fixed;
  left: var(--space-4); top: var(--space-4);
  z-index: var(--z-skip);
  padding: var(--space-3) var(--space-4);
  background: var(--c-ink-900); color: var(--c-dawn-100);
  border-radius: var(--radius-sm);
  font-size: var(--text-small);
  transform: translateY(-200%);
  transition: transform var(--dur-fast) var(--ease-out);
}
.skip-link:focus { transform: none; }

:focus-visible {
  outline: 2px solid var(--color-focus);
  outline-offset: 3px;
  border-radius: var(--radius-xs);
}

/* ---------- NAV ---------------------------------------------------------
   Fixed. Transparent over the hero, a paper bar once scrolled, and it
   flips to the dark treatment over dark sections (JS sets data-tone). */
.nav {
  position: fixed;
  inset: 0 0 auto 0;
  z-index: var(--z-nav);
  color: var(--c-ink-900);
  transition: background-color var(--dur-base) var(--ease-out), color var(--dur-base) var(--ease-out),
              transform var(--dur-slow) var(--ease-expo), box-shadow var(--dur-base) var(--ease-out);
}
.nav__inner {
  display: flex;
  align-items: center;
  gap: var(--space-6);
  max-width: var(--max-width);
  margin-inline: auto;
  padding: var(--space-4) var(--margin);
}
.nav.is-scrolled {
  background: rgb(247 245 239 / .95);
  -webkit-backdrop-filter: blur(14px) saturate(1.2);
  backdrop-filter: blur(14px) saturate(1.2);
  box-shadow: 0 1px 0 var(--c-dawn-400);
}
.nav[data-tone="dark"] { color: var(--c-dawn-100); }
.nav[data-tone="dark"].is-scrolled {
  background: rgb(23 20 21 / .94);
  box-shadow: 0 1px 0 var(--c-ink-700);
}
.nav.is-hidden { transform: translateY(-110%); }
.nav__brand { display: block; margin-right: auto; border-radius: var(--radius-xs); }
.nav__logo { width: clamp(150px, 12vw, 184px); height: auto; overflow: visible; }
.nav__logo-nodes { stroke: var(--c-blue-500); }
.nav[data-tone="dark"] .nav__logo-nodes { stroke: var(--c-blue-300); }
.nav__links { display: none; gap: var(--space-6); font-size: var(--text-small); font-weight: 500; }
.nav__links a { text-decoration: none; position: relative; padding-block: var(--space-2); }
.nav__links a::after {
  content: ""; position: absolute; left: 0; right: 0; bottom: 2px; height: 1px;
  background: currentColor; transform: scaleX(0); transform-origin: right;
  transition: transform var(--dur-base) var(--ease-expo);
}
.nav__links a:hover::after { transform: scaleX(1); transform-origin: left; }
@media (min-width: 800px) { .nav__links { display: flex; } }
/* Client login (round 7): a secondary button beside the primary "Talk to us".
   Outlined in the text colour so it reads as a button, never filled, so the
   blue CTA stays the main action. On phones it shortens to "Login". */
.nav__login.btn--ghost { border-color: currentColor; color: var(--color-text); white-space: nowrap; }
.nav__login.btn--ghost:hover { background: var(--color-text); border-color: var(--color-text); color: var(--color-bg); }
.nav[data-tone="dark"] .nav__login.btn--ghost { color: var(--c-dawn-100); border-color: rgb(247 245 239 / .6); }
.nav[data-tone="dark"] .nav__login.btn--ghost:hover { background: var(--c-dawn-100); border-color: var(--c-dawn-100); color: var(--c-ink-950); }
.nav__login-short { display: none; }
/* The two nav buttons sit as a pair, with the same gap as the hero's two
   buttons (--space-3), not the wider gap between the text links. */
.nav__login + .nav__cta { margin-left: calc(var(--space-3) - var(--space-6)); }
@media (max-width: 480px) {
  .nav__login-long { display: none; }
  .nav__login-short { display: inline; }
  .nav__inner { gap: .625rem; }
  .nav__login + .nav__cta { margin-left: 0; } /* the phone gap is already tight */
  .nav .btn--small { --btn-h: 2.25rem; padding: 0 .75rem; font-size: .8125rem; }
}
@media (max-width: 380px) { .nav__logo { width: 118px; } }
@media (max-width: 420px) { .nav__logo { width: 136px; } }

/* ---------- GRAIN (finishing layer) ------------------------------------ */
.grain {
  position: fixed; inset: 0;
  z-index: var(--z-grain);
  pointer-events: none;
  background-image: url("../img/grain.png");
  background-size: 160px 160px;
  opacity: var(--grain-opacity);
  mix-blend-mode: overlay;
}

/* ---------- REVEAL BASELINE ---------------------------------------------
   Elements marked data-reveal / data-split start hidden ONLY when JS is on
   and motion is allowed. A CSS fallback animation shows them after 2.5s in
   case the scripts never arrive; main.js cancels it (.motion-ready). */
@keyframes reveal-fallback { to { opacity: 1; visibility: visible; } }
.js:not(.reduced-motion) [data-reveal] {
  opacity: 0;
  animation: reveal-fallback 0s 2.5s forwards;
}
.js:not(.reduced-motion) [data-split] {
  visibility: hidden;
  animation: reveal-fallback 0s 2.5s forwards;
}
.js.motion-ready [data-reveal],
.js.motion-ready [data-split] { animation: none; }
.js.motion-ready [data-split].is-split { visibility: visible; }
/* SplitText line masks */
/* Room for descenders inside each line's mask. .08em clipped the tail of the
   Lora italic "g" in "We'll get the phone" (Corey, 26 Sept): an italic serif
   descends far further than Montserrat. .25em clears it; the negative margin
   keeps the line spacing exactly as designed. And once a line has risen into
   place the mask is switched off entirely (reveal.js, .is-revealed), so
   nothing can be clipped at rest. */
.split-line { overflow: clip; padding-bottom: .25em; margin-bottom: -.25em; }
.is-revealed .split-line, .is-revealed .split-line-mask { overflow: visible !important; }
/* No motion (reduced motion, or the scripts failed): everything is simply there. */
.js.static [data-reveal] { opacity: 1; animation: none; }
.js.static [data-split] { visibility: visible; animation: none; }
/* While the intro gate is open the page underneath does not scroll. */
html.gate-open, html.gate-open body { overflow: hidden; }

/* No orphaned words in headings: every heading-sized line balances. */
h1, h2, h3, summary, .explainer__line, .phones__name, .price__title, .kpi__foot { text-wrap: balance; }
.nw { white-space: nowrap; }

/* ---------- SIGNATURE: the section kicker connects the dots --------------
   A small square (the call) and a short line that runs to the words. As a
   section arrives, the square draws in from its centre and the line draws
   across to the kicker: the logo's idea, at the size of a label. */
.kicker--mark { display: inline-flex; align-items: center; gap: .7em; }
.kicker--mark::before,
.kicker--mark::after { content: ""; flex: none; }
.kicker--mark::before { order: -2; width: 7px; height: 7px; border: 1.5px solid var(--color-accent); }
.kicker--mark::after { order: -1; width: 1.75rem; height: 1.5px; margin-left: -.35em; background: var(--color-accent); opacity: .55; transform-origin: left center; }
.js.motion-ready .kicker--mark:not(.is-in)::before { clip-path: inset(50% 50% 50% 50%); }
.js.motion-ready .kicker--mark:not(.is-in)::after { transform: scaleX(0); }
.js.motion-ready .kicker--mark::before { transition: clip-path var(--dur-draw) var(--ease-in-out); }
.js.motion-ready .kicker--mark::after { transition: transform var(--dur-draw) var(--ease-in-out) calc(var(--dur-draw) * .6); }
