/* ==========================================================================
   BASE — reset, typography, layout primitives
   References theme tokens only (PRINCIPLES §2). No component styles here.
   ========================================================================== */

/* --- Derived spacing scale (from --space-unit) ---------------------- */
:root {
  --space-1: calc(var(--space-unit) * 1);   /*  4px */
  --space-2: calc(var(--space-unit) * 2);   /*  8px */
  --space-3: calc(var(--space-unit) * 3);   /* 12px */
  --space-4: calc(var(--space-unit) * 4);   /* 16px */
  --space-5: calc(var(--space-unit) * 5);   /* 20px */
  --space-6: calc(var(--space-unit) * 6);   /* 24px */
  --space-8: calc(var(--space-unit) * 8);   /* 32px */
  --space-10: calc(var(--space-unit) * 10); /* 40px */
  --space-12: calc(var(--space-unit) * 12); /* 48px */
  --space-16: calc(var(--space-unit) * 16); /* 64px */

  /* The reading measure, in one place. Long-form text and the article
     head both use it; when they each carried their own 65ch they could
     drift apart without anyone noticing. `ch` resolves against whichever
     element uses it, which is the point — the measure follows the font.
     (PRINCIPLES §6 relative-measure exception.) */
  --measure: 65ch;

  /* The page gutter, in one place. Anything that sets its own edge —
     the container, the floating header, the menu panel it drops — reads
     it from here, or they drift apart and the header ends up flush with
     the screen while the text below it is inset. Both ends land on the
     4px grid; the interpolation between them cannot, by definition
     (PRINCIPLES §6, fluid-value exception). */
  --gutter: clamp(var(--space-4), 4vw, var(--space-8));

  /* The bar's height, named because more than the bar needs it: the
     fullscreen menu's wash has to start below a full-bleed header, and
     a fixed element cannot ask its parent how tall it is — a percentage
     there resolves against the viewport, which put the wash entirely
     off-screen the first time round. */
  --header-height: 4rem;

  /* --- Layer scale -------------------------------------------------
     Every stacked thing takes its value from here. Ad-hoc z-index
     numbers scattered across files are how overlap bugs are born. */
  --z-base: 1;
  --z-sticky: 40;      /* sticky section headers, floating labels */
  --z-header: 50;      /* site header */
  --z-nav: 55;         /* mobile menu panel — above the page, below tools */
  --z-overlay: 70;     /* modals, lightboxes */
  --z-preview: 90;     /* preview-only tooling (gear panel) */

  /* The width at which the header stops using the burger and lays the
     full nav out in the bar. Declared here because `header.js` reads it:
     a media query cannot be shared with script any other way, and the
     two drifting apart is exactly how the burger ends up showing while
     the script thinks it is on desktop. Changing it changes both. */
  --bp-nav: 64rem;
}

/* --- Reset ----------------------------------------------------------- */
*,
*::before,
*::after {
  box-sizing: border-box;
  margin: 0;
}

/* The browser hides `[hidden]` with a rule of the lowest possible weight,
   so any component that sets `display` on a class — a flex bar, a grid
   panel — silently outranks it and stays on screen with the attribute
   dutifully set. Hidden is not a suggestion; this is the one place the
   `!important` is the correct tool rather than a shortcut. */
[hidden] {
  display: none !important;
}

html {
  -webkit-text-size-adjust: 100%;
  /* No sideways scroll, ever. A reveal with a horizontal offset —
     `data-reveal="left"` / `"right"` — sits translated to one side until
     it scrolls into view, and any such element below the fold widens the
     page by its offset. On a desktop the reveal fires almost immediately
     and the classic scrollbar hides the seam; on an iPhone the element
     waits off-screen and Safari lets you drag the whole page sideways to
     find it. The contact aside (`data-reveal="left"`) was doing exactly
     this — 8px of drift, the width of nothing in particular.

     `clip`, not `hidden`: it clips the overflow without becoming a scroll
     container, so `position: sticky` (header variant B) still sticks and
     `position: fixed` (the floating pill) is untouched. The vertical axis
     stays `visible` and scrolls as normal — `clip` on one axis does not
     drag the other into `auto`. */
  overflow-x: clip;
  /* Reserve the scrollbar's width whether or not it is showing. Freezing
     the page behind the fullscreen menu removes the scrollbar, and
     everything measured against the viewport — the header pill included —
     jumped sideways by exactly its 8px the moment the menu opened. The
     burger looked like it was hopping; it was the whole bar moving.
     No cost where scrollbars overlay the content, which is every touch
     device. */
  scrollbar-gutter: stable;
  /* In-page anchors glide instead of teleporting. Neutralised for
     reduced-motion in motion.css. */
  scroll-behavior: smooth;
  /* Keep an anchored heading clear of the header instead of tucked under it. */
  scroll-padding-block-start: var(--space-16);
}

/* --- Scrollbar ------------------------------------------------------
   Themed rather than left to the OS. The default bar is the one piece of
   grey system chrome on an otherwise designed page, and on a warm palette
   it reads as a seam. Tinted from the theme, it disappears into the
   design instead.
   Two syntaxes because they cover different browsers; both are needed. */
/* --- What the browser draws for us ----------------------------------
   Everything else in this file styles what we draw. `color-scheme` is the
   one declaration that reaches the parts we cannot: the list a native
   <select> opens, the caret and autofill inside a field, the spinners on a
   number input, the default scrollbar. Unset, a browser draws all of them
   light — so a dark theme's select opens a white sheet over a black page,
   and the fault is invisible until somebody clicks the one control the
   stylesheet does not own.

   Found by the second theme, which is what a second theme is for. Default
   `light`, which is what the other two are; a dark theme sets `dark` and
   every one of those parts follows. */
:root {
  color-scheme: var(--color-scheme, light);
  scrollbar-width: thin;                                   /* Firefox */
  scrollbar-color: var(--color-primary-200) transparent;
}

::-webkit-scrollbar {                                       /* Chromium, Safari */
  width: 8px;
  height: 8px;
}

::-webkit-scrollbar-track {
  background: transparent;
}

::-webkit-scrollbar-thumb {
  background: var(--color-primary-200);
  border-radius: var(--radius-full);
}

::-webkit-scrollbar-thumb:hover {
  background: var(--color-primary-400);
}

/* Dark surfaces need the opposite treatment or the thumb disappears */
.surface-inverse::-webkit-scrollbar-thumb,
.site-footer::-webkit-scrollbar-thumb {
  background: color-mix(in srgb, currentColor 25%, transparent);
}

body {
  font-family: var(--font-body);
  font-weight: var(--font-weight-body);
  font-size: var(--text-base);
  line-height: var(--leading-normal);
  /* Running text has never had a tracking token, because every body face
     so far was drawn for running text and its own spacing was right. A
     brand that sets its display face as the body face changes that: a
     squarish, engineered letter is drawn to be seen at size, and at 16px
     across a paragraph its own fit reads as cramped. Default `normal`
     hands the decision back to the typeface, which is where it belongs
     for a face designed for the job. */
  letter-spacing: var(--tracking-body, normal);
  color: var(--color-text);
  background: var(--color-surface);
}

img,
svg,
video {
  display: block;
  max-width: 100%;
}

button,
input,
select,
textarea {
  font: inherit;
  color: inherit;
}

/* --- Typography ------------------------------------------------------ */
h1,
h2,
h3,
h4 {
  font-family: var(--font-heading);
  font-weight: var(--font-weight-heading);
  line-height: var(--leading-tight);
  overflow-wrap: break-word;
}

h1 { font-size: var(--text-3xl); }
h2 { font-size: var(--text-2xl); }
h3 { font-size: var(--text-xl); }
h4 { font-size: var(--text-lg); }

/* Display — hero headings. Tighter leading and negative tracking are what
   separate "big text" from "display type".

   The family and weight are stated here rather than inherited from the
   `h1…h4` rule above, because this is a type ROLE and a role that only
   works on some elements is a trap. Every real page happens to put it on a
   heading, so the gap was invisible until the design-system page put it on
   a `<p>` — the demo, and the `reveal-lines` demo with it, rendered the
   display face in the body font at 400. Anything a class promises, the
   class carries. */
.display {
  font-family: var(--font-heading);
  font-weight: var(--font-weight-heading);
  font-size: var(--text-4xl);
  line-height: var(--leading-display);
  letter-spacing: var(--tracking-display);
  overflow-wrap: break-word;
  text-wrap: balance;
}

h1, h2 {
  letter-spacing: var(--tracking-tight);
  text-wrap: balance;
}

p,
li {
  overflow-wrap: break-word;
}

a {
  color: var(--color-primary);
  transition: color var(--motion-duration) var(--motion-easing);
}

a:hover {
  color: var(--color-primary-700);
}

/* The sentence under a heading. Slotted for the same reason `--stat-size`
   is: how loudly the sub-claim speaks is a theme's voice, not a constant.
   `--text-lg` is the default and stays what every approved page renders;
   a theme whose base text already runs large (vitarent sets 17px for an
   older audience) can find the step above it shouting, and drops the
   lead without dragging `--text-lg` down for the testimonial quote, the
   process title and the dialog, which all read it too. */
.lead {
  font-size: var(--lead-size, var(--text-lg));
  color: var(--color-text-muted);
}

.text-small {
  font-size: var(--text-sm);
}

.text-muted {
  color: var(--color-text-muted);
}

/* --- Focus (PRINCIPLES §6: keyboard focus is part of "done") --------
   A focus ring is a non-text indicator, so it owes 3:1 against whatever it
   sits on — and until the second theme nobody had measured it. `primary-400`
   was the ring for every theme and cleared that on neither of the light
   ones: 2.87 on drga's page, 2.98 on neutral's, 2.59 and 2.75 on their alt
   panels. A ring nobody can see is the whole keyboard experience, so this
   is not a rounding error.

   There is no single step that fixes it. 500 clears every light surface in
   every theme (3.55–5.12) and failed at 2.88 on the dark slab of the theme
   that exposed this; 400 does the reverse. The step is the wrong tool: the
   ring has to answer the surface, the same way the statistics do in
   blocks.css.

   So: 500 on light, and on a brand or dark slab the surface's own text
   colour — already required, already verified, and the only value guaranteed
   to be readable there in every theme. Measured after the change, the gold
   theme reads 11.81 / 10.87 / 11.37 / 17.57 across its four surfaces.

   The offset matters to the measurement: the ring sits 2px outside the
   element, on the page rather than on the button, so what it is measured
   against is the surface behind it. Change the offset to 0 and this whole
   note is about the wrong pair of colours. */
:focus-visible {
  outline: 2px solid var(--color-primary-500);
  outline-offset: 2px;
}

.surface-brand :focus-visible {
  outline-color: var(--color-text-on-primary);
}

.surface-inverse :focus-visible {
  outline-color: var(--color-text-on-inverse);
}

/* --- Layout primitives ----------------------------------------------- */
.container {
  /* `width: 100%` is redundant in normal flow and load-bearing in flex.
     A flex item with auto inline margins stops stretching and sizes to
     its content instead, so a .container placed straight into the flex
     body below collapsed to the width of its longest line and sat
     centred. Stating the width makes the max-width the thing that
     decides, in either context. */
  width: 100%;
  max-width: var(--container-max);
  margin-inline: auto;
  /* Grows with the viewport so the side gap does not vanish the moment
     the container stops having any room around it. */
  padding-inline: var(--gutter);
}

/* Short pages must still push the footer to the bottom of the viewport.
   404 and the thank-you page are the first templates with too little
   content to do it themselves, and a footer floating halfway up the
   screen reads as a page that failed to load rather than one that is
   simply short. Costs nothing on long pages. */
body {
  display: flex;
  flex-direction: column;
  min-height: 100svh;
}

main {
  flex-grow: 1;
}

/* A page built from a single short block centres it in the leftover
   space instead of hugging the header. Opt-in per page, because on a
   normal page the first block must start at the top like every other
   one. */
.main--fill {
  display: flex;
  flex-direction: column;
}

.main--fill > .section {
  flex-grow: 1;
  display: grid;
  align-content: center;
}

/* A label for the screen reader that the layout has no room for — the
   standard clip-rect recipe. `display: none` would silence it; this only
   hides it. First user: the newsletter form, whose visible "label" is the
   heading above the whole form rather than one attached to the control. */
.visually-hidden {
  position: absolute;
  width: 1px;
  height: 1px;
  margin: -1px;
  padding: 0;
  overflow: hidden;
  clip-path: inset(50%);
  white-space: nowrap;
  border: 0;
}

/* Vertical rhythm inside a text column */
.flow > * + * {
  margin-block-start: var(--space-4);
}

.flow > h2 + *,
.flow > h3 + * {
  margin-block-start: var(--space-3);
}

/* --- Long-form typography (article detail, text page) ---------------- */
.prose {
  max-width: var(--measure);
}

.prose > * + * {
  margin-block-start: var(--space-4);
}

.prose h2 {
  margin-block-start: var(--space-8);
}

.prose h3 {
  margin-block-start: var(--space-6);
}

.prose ul,
.prose ol {
  padding-inline-start: 1.5em;
}

.prose blockquote {
  border-inline-start: 2px solid var(--color-primary-300);
  padding-inline-start: var(--space-4);
  color: var(--color-text-muted);
}

.prose img {
  border-radius: var(--radius-lg);
}

.prose h2,
.prose h3 {
  max-width: none;
}

.prose hr {
  border: 0;
  border-block-start: var(--border-width) solid var(--color-border);
  margin-block: var(--space-8);
}

/* Tables and code widen past the reading column, so they get their own
   scroll container rather than forcing the page sideways (a long price
   table or a CSS selector must never make the whole page pan). */
.prose__scroll {
  overflow-x: auto;
  margin-block-start: var(--space-4);
}

.prose table {
  width: 100%;
  border-collapse: collapse;
  font-size: var(--text-sm);
}

.prose th,
.prose td {
  text-align: start;
  padding: var(--space-3);
  border-block-end: var(--border-width) solid var(--color-border);
  vertical-align: top;
}

.prose th {
  font-weight: var(--font-weight-bold);
  white-space: nowrap;
}

/* Inline code sits inside a line of text, so its padding is em-based to
   scale with the surrounding size (PRINCIPLES §6 relative-measure
   exception) — a fixed px inset would crowd small text and float in
   large. */
.prose code {
  padding: 0.1em 0.35em;
  border-radius: var(--radius-sm);
  background: var(--color-surface-alt);
  font-size: 0.9em;
}

.prose pre {
  padding: var(--space-4);
  border-radius: var(--radius-md);
  background: var(--color-surface-alt);
  font-size: var(--text-sm);
  line-height: var(--leading-normal);
}

.prose pre code {
  padding: 0;
  background: none;
  font-size: inherit;
}
