/* =============================================================================
   BimmerExpert — base.css
   Reset, page frame, the shared utility contract, header, mobile menu, footer.

   Load order is fixed:  tokens.css  →  base.css  →  sections.css
   Every colour here is a var() from tokens.css. There is no raw hex in this file.

   The utilities between the SHARED UTILITIES banners are the contract with the
   section builders. Section stylesheets USE them and never redefine them.

   Utilities that were published here and that no section ever adopted have been
   removed: .grid-2/-3/-4, .container--wide, .section--tight, .rule--strong,
   .icon--lg/--xl, .bg-layer--pcb. Every section wrote its own grid instead,
   which is most of why sections.css is the larger file. If a shared layer is
   published again, check that it was actually taken up before shipping it.
   ========================================================================== */


/* ═══ 1. RESET ══════════════════════════════════════════════════════════════ */

*, *::before, *::after { box-sizing: border-box; }

/* Kill every UA margin; vertical rhythm is set deliberately, never inherited. */
html, body, h1, h2, h3, h4, h5, h6, p, figure, blockquote, dl, dd, ol, ul {
  margin: 0;
}
ul[class], ol[class] { list-style: none; padding: 0; }

/* The UA sheet's `[hidden] { display: none }` loses to ANY author rule that sets
   display, at any specificity — .cnt-form{display:grid} and
   .to-top{display:inline-flex} both did, so `el.hidden = true` from app.js was a
   no-op on exactly the two elements it is called on. One guarded rule here
   instead of a per-component patch, and every future component inherits it. */
[hidden] { display: none !important; }

html {
  /* Anchor jumps land below the sticky header instead of under it. */
  scroll-padding-top: calc(var(--header-h) + var(--space-6));
  -webkit-text-size-adjust: 100%;
  text-size-adjust: 100%;
}

/* Smooth scrolling is opt-in and dies the moment the user asks for less motion. */
@media (prefers-reduced-motion: no-preference) {
  html { scroll-behavior: smooth; }
}

body {
  margin: 0;
  min-height: 100%;
  background: var(--surface-0);
  color: var(--text-secondary);
  font-family: var(--ff-body);
  font-size: var(--fs-body);
  font-weight: var(--fw-regular);
  line-height: var(--lh-body);
  letter-spacing: var(--ls-body);
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
  text-rendering: optimizeLegibility;
  font-synthesis-weight: none;
  /* Guards against a wide child ever producing a horizontal scrollbar. */
  overflow-x: clip;
}

/* Replaced elements behave like blocks and never overflow their column. */
img, svg, video, canvas, picture { display: block; max-width: 100%; }
img, video { height: auto; }

input, button, textarea, select { font: inherit; color: inherit; letter-spacing: inherit; }
button { background: none; border: 0; padding: 0; cursor: pointer; }
textarea { resize: vertical; }

a { color: var(--text-brand); text-decoration-thickness: 1px; text-underline-offset: 0.22em; }
a:hover { color: var(--text-cyan); }

hr { border: 0; border-top: var(--hairline) solid var(--border-hairline); margin: 0; }

table { border-collapse: collapse; }

::selection { background: var(--brand); color: var(--text-inverse); }

/* Every focusable element gets the outline ring from tokens.css. Keyboard only
   — :focus-visible, so a mouse click never draws it.
   This selector is (0,1,0). It MUST NOT use box-shadow: .btn--primary sets
   box-shadow at the same specificity later in this file and silently won every
   primary button's focus indicator. outline is contested by nothing. */
:where(a, button, input, select, textarea, summary, details, [tabindex]):focus-visible {
  outline: var(--focus-width) solid var(--focus);
  outline-offset: var(--focus-gap);
  border-radius: var(--radius-sm);
}
/* <main> takes programmatic focus from the skip link; never draw a ring on it. */
main:focus { outline: none; }

/* Firefox draws its own dotted ring inside buttons — remove it, we have our own. */
::-moz-focus-inner { border: 0; padding: 0; }


/* ═══ 2. TYPE DEFAULTS ══════════════════════════════════════════════════════ */

h1, h2, h3, h4 {
  font-family: var(--ff-display);
  font-weight: var(--fw-bold);
  color: var(--text-primary);
  text-wrap: balance;
}
h1 { font-size: var(--fs-display); line-height: var(--lh-display); letter-spacing: var(--ls-display); }
h2 { font-size: var(--fs-h2);      line-height: var(--lh-heading); letter-spacing: var(--ls-h2); }
h3 { font-size: var(--fs-h3);      line-height: var(--lh-snug);    letter-spacing: var(--ls-h3);
     font-weight: var(--fw-semibold); }
h4 { font-size: var(--fs-body);    line-height: var(--lh-snug);    letter-spacing: var(--ls-h3);
     font-weight: var(--fw-semibold); }

p { max-width: var(--measure); text-wrap: pretty; }

strong, b { font-weight: var(--fw-semibold); color: var(--text-primary); }

code, kbd, samp, pre {
  font-family: var(--ff-mono);
  font-size: 0.92em;
  letter-spacing: var(--ls-mono);
}

address { font-style: normal; }


/* ═══════════════════════════════════════════════════════════════════════════
   3. SHARED UTILITIES — the contract with every section builder.
      Do not redefine any of these in a section stylesheet.
   ═══════════════════════════════════════════════════════════════════════════ */

/* .container — the content column: max-width plus a fluid gutter, centred. */
.container {
  width: 100%;
  max-width: calc(var(--container) + var(--gutter) * 2);
  margin-inline: auto;
  padding-inline: var(--gutter);
}
/* Narrow variant for prose, FAQ and legal copy. */
.container--narrow { max-width: calc(var(--container-narrow) + var(--gutter) * 2); }

/* .section — the standard section wrapper: fluid vertical rhythm, isolated so a
   section's own background layers can never escape into a neighbour. */
.section {
  position: relative;
  isolation: isolate;
  padding-block: var(--section-y);
}

/* .section-head — eyebrow + h2 + lead, grouped with the standard gaps. */
.section-head {
  display: flex;
  flex-direction: column;
  gap: var(--space-4);
  margin-bottom: var(--block-gap);
}
.section-head > .lead { margin-top: var(--space-1); }

/* .eyebrow — mono, uppercase, letter-spaced, a leading index number and a
   hairline that runs to the container edge.
   Markup is tolerant, both of these work:
     <p class="eyebrow" data-index="03">Services</p>
     <p class="eyebrow"><span class="eyebrow__num">03</span>Services</p> */
.eyebrow {
  display: flex;
  align-items: center;
  gap: var(--space-3);
  margin: 0;
  max-width: none;
  font-family: var(--ff-mono);
  font-size: var(--fs-eyebrow);
  font-weight: var(--fw-medium);
  line-height: var(--lh-flat);
  letter-spacing: var(--ls-eyebrow);
  text-transform: uppercase;
  color: var(--text-cyan);
}
/* The index number, from data-index. Hidden entirely when the attribute is absent. */
.eyebrow::before {
  content: attr(data-index);
  flex: none;
  color: var(--text-muted);
  font-variant-numeric: tabular-nums;
}
.eyebrow:not([data-index])::before { content: none; }
/* Same treatment when the builder writes the number as a span instead. */
.eyebrow__num { flex: none; color: var(--text-muted); font-variant-numeric: tabular-nums; }
/* The hairline, running out to the container edge. */
.eyebrow::after {
  content: "";
  flex: 1 1 auto;
  min-width: var(--space-6);
  height: var(--hairline);
  background: linear-gradient(90deg, var(--border-cyan), var(--border-hairline) 40%, transparent);
}
/* On a phone two of the labels wrap to a second line, which strands the rule on
   line 1 as a 24px dash in the top-right corner — it reads as a rendering
   glitch. Let it drop to its own full-width line instead. */
@media (max-width: 40em) {
  .eyebrow { flex-wrap: wrap; }
  .eyebrow::after { flex-basis: 100%; margin-top: var(--space-2); }
}

/* .lead — the larger intro paragraph under a heading. */
.lead {
  max-width: 62ch;
  font-size: var(--fs-lead);
  line-height: var(--lh-lead);
  letter-spacing: var(--ls-lead);
  color: var(--text-secondary);
}

/* .btn — shared button geometry. Modifiers below supply the skin. */
.btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: var(--space-2);
  padding: var(--space-3) var(--space-5);
  /* Measured 43px tall at every width — one pixel under the 44px touch
     minimum, on the controls that are every conversion point this page has.
     Costs nothing at desktop, where the padding already clears it. */
  min-block-size: 44px;
  border: var(--hairline) solid transparent;
  border-radius: var(--radius-md);
  font-family: var(--ff-body);
  font-size: var(--fs-small);
  font-weight: var(--fw-medium);
  line-height: var(--lh-tight);
  letter-spacing: var(--ls-body);
  text-decoration: none;
  white-space: nowrap;
  cursor: pointer;
  transition:
    background-color var(--dur-fast) var(--ease-out),
    border-color     var(--dur-fast) var(--ease-out),
    color            var(--dur-fast) var(--ease-out),
    box-shadow       var(--dur-fast) var(--ease-out),
    transform        var(--dur-fast) var(--ease-out);
}
.btn:active { transform: translateY(1px); }
/* 0.55 composited the whole button and dropped the label to 2.45:1 — and the
   word being rendered illegible was "Sending". 0.72 holds the ink label over
   4.5:1 on --brand while still reading as inactive. */
.btn[disabled], .btn[aria-disabled="true"] { opacity: 0.72; pointer-events: none; }

/* .btn--primary — the one large flat blue on the page. Ink label, never white. */
.btn--primary {
  background: var(--brand);
  color: var(--text-inverse);
  box-shadow: var(--glow-brand);
}
.btn--primary:hover { background: var(--brand-hover); color: var(--text-inverse); }
.btn--primary:active { background: var(--brand-active); }

/* .btn--ghost — hairline outline on the page ground. */
.btn--ghost {
  background: transparent;
  color: var(--text-primary);
  border-color: var(--border-control);
}
.btn--ghost:hover {
  color: var(--text-primary);
  border-color: var(--border-control-hover);
  background: color-mix(in srgb, var(--surface-2) 70%, transparent);
}

/* .btn--lg — the hero and contact scale. */
.btn--lg {
  padding: var(--space-4) var(--space-8);
  font-size: var(--fs-body);
  border-radius: var(--radius-md);
}

/* .rule — a 1px hairline divider. */
.rule {
  height: var(--hairline);
  border: 0;
  margin: 0;
  background: var(--border-hairline);
}

/* .reveal — entrance animation.
   DEFAULT IS VISIBLE. If JS never runs, or IntersectionObserver is missing, or
   the user asked for reduced motion, the content simply shows. app.js sets
   .js-reveal on <html>, which arms the pre-animation state; it never hides
   anything on its own. */
.js-reveal .reveal {
  opacity: 0;
  transform: translateY(14px);
  transition:
    opacity   var(--dur-slow) cubic-bezier(.16, 1, .3, 1) var(--reveal-delay, 0ms),
    transform var(--dur-slow) cubic-bezier(.16, 1, .3, 1) var(--reveal-delay, 0ms);
  will-change: opacity, transform;
}
.js-reveal .reveal.is-revealed {
  opacity: 1;
  transform: none;
  will-change: auto;
}
@media (prefers-reduced-motion: reduce) {
  .js-reveal .reveal { opacity: 1; transform: none; transition: none; }
}

/* .sr-only — removed from view, still read by a screen reader. Clip, not
   display:none, so the honeypot input and the skip link stay focusable. */
.sr-only {
  position: absolute;
  width: 1px; height: 1px;
  padding: 0; margin: -1px;
  overflow: hidden;
  clip: rect(0 0 0 0);
  clip-path: inset(50%);
  white-space: nowrap;
  border: 0;
}

/* .icon — svg sizing helper. Scales with the type, inherits the text colour. */
.icon {
  display: inline-block;
  width: 1em;
  height: 1em;
  flex: none;
  fill: none;
  stroke: currentColor;
  vertical-align: -0.125em;
}
.icon--md { width: 1.5rem;  height: 1.5rem;  }

/* ── Background art layers. Sections opt in; the art never sits under body copy.
      Usage:  <div class="bg-layer bg-layer--grid" aria-hidden="true"></div>
      inside a .section (which is already position:relative + isolate). ── */
.bg-layer {
  position: absolute;
  inset: 0;
  z-index: -1;
  pointer-events: none;
  background-repeat: repeat;
}
.bg-layer--grid  { background-image: url("../img/bg-grid.svg");       background-size: 128px 128px; opacity: 0.07; }
.bg-layer--noise { background-image: url("../img/noise.svg");         background-size: 240px 240px; opacity: 0.035; mix-blend-mode: overlay; }
/* Fades the art out before it reaches the copy below it. */
.bg-layer--fade-b { mask-image: linear-gradient(180deg, var(--surface-0) 0%, transparent 88%);
                    -webkit-mask-image: linear-gradient(180deg, var(--surface-0) 0%, transparent 88%); }

/* ═══════════════════════════════════════════════════════════════════════════
   END SHARED UTILITIES
   ═══════════════════════════════════════════════════════════════════════════ */


/* ── Form error slot. app.js creates one of these per invalid field when the
      contact builder has not supplied a [data-error-for="…"] element, so it
      must have a guaranteed style even though the form itself is section-owned.
      The contact builder is free to ship its own slots and style them. ── */
.form-error {
  max-width: var(--measure);
  margin-top: var(--space-2);
  font-size: var(--fs-small);
  line-height: var(--lh-snug);
  color: var(--text-danger);
}
.form-error:empty { display: none; }


/* ═══ 4. SKIP LINK ══════════════════════════════════════════════════════════ */

/* Hidden until focused, then pinned top-left on a raised plate. */
.skip-link {
  position: fixed;
  top: var(--space-3);
  left: var(--space-3);
  z-index: var(--z-toast);
  padding: var(--space-3) var(--space-5);
  background: var(--surface-3);
  color: var(--text-primary);
  border: var(--hairline) solid var(--border-strong);
  border-radius: var(--radius-sm);
  font-size: var(--fs-small);
  font-weight: var(--fw-medium);
  text-decoration: none;
  box-shadow: var(--shadow-3);
  transform: translateY(calc(-100% - var(--space-6)));
  transition: transform var(--dur-base) var(--ease-out);
}
/* :focus as well as :focus-visible — a programmatic .focus() or a pointer
   activation may not match the heuristic, and this is the one control whose
   entire job is to appear when it receives focus. */
.skip-link:focus,
.skip-link:focus-visible { transform: none; color: var(--text-primary); }


/* ═══ 5. SITE HEADER ════════════════════════════════════════════════════════ */

.site-header {
  position: sticky;
  top: 0;
  z-index: var(--z-sticky);
  /* Transparent over the hero: no background, no border. */
  background: transparent;
  border-bottom: var(--hairline) solid transparent;
  transition:
    background-color var(--dur-base) var(--ease-out),
    border-color     var(--dur-base) var(--ease-out),
    backdrop-filter  var(--dur-base) var(--ease-out);
}
/* app.js adds .is-scrolled past ~40px. */
.site-header.is-scrolled {
  background: var(--surface-glass);
  border-bottom-color: var(--border-hairline);
  -webkit-backdrop-filter: blur(var(--blur-glass)) saturate(140%);
  backdrop-filter: blur(var(--blur-glass)) saturate(140%);
}
/* No backdrop-filter support: fall back to the opaque sticky-header surface. */
@supports not ((backdrop-filter: blur(1px)) or (-webkit-backdrop-filter: blur(1px))) {
  .site-header.is-scrolled { background: var(--surface-3); }
}

.site-header__inner {
  display: flex;
  align-items: center;
  gap: var(--space-6);
  height: var(--header-h);
}

/* ── Logo lockup ── */
.brand-link {
  display: inline-flex;
  align-items: center;
  flex: none;
  color: var(--text-primary);
  text-decoration: none;
  border-radius: var(--radius-sm);
}
.brand-link:hover { color: var(--text-primary); }
/* Sized by HEIGHT, never width — brand/logo-icon.svg is explicit about that.
   28px in the 72px bar leaves well over 1X clear space (brand-guide §9.1).
   Header: the monogram below 38em, the full lockup above it. The full lockup is
   223px wide at 28px tall and cannot share a phone header with the CTA and the
   hamburger; the monogram is 52px, over its 45px floor (brand-guide §3.2). */
.brand-logo { width: auto; height: 28px; display: block; }
.site-header .brand-logo--full { display: none; }
@media (min-width: 38em) {
  .site-header .brand-logo--full { display: block; }
  .site-header .brand-logo--mark { display: none; }
}
/* The footer column is full width, so it always carries the full lockup. */
.site-footer .brand-logo { height: 30px; }

/* ── Desktop nav ── */
.site-nav { display: none; }
.site-nav__list {
  display: flex;
  align-items: center;
  gap: clamp(var(--space-4), 1.6vw, var(--space-8));
  margin: 0; padding: 0; list-style: none;
}
.site-nav__link {
  position: relative;
  display: block;
  padding-block: var(--space-2);
  font-size: var(--fs-small);
  font-weight: var(--fw-medium);
  color: var(--text-secondary);
  text-decoration: none;
  transition: color var(--dur-fast) var(--ease-out);
}
.site-nav__link:hover { color: var(--text-primary); }
/* Scroll-spy marker: a 1px cyan underline, not a pill. */
.site-nav__link::after {
  content: "";
  position: absolute;
  left: 0; right: 0; bottom: 0;
  height: var(--hairline);
  background: var(--cyan);
  opacity: 0;
  transform: scaleX(0.4);
  transform-origin: left;
  transition: opacity var(--dur-fast) var(--ease-out), transform var(--dur-fast) var(--ease-out);
}
.site-nav__link[aria-current="true"] { color: var(--text-primary); }
.site-nav__link[aria-current="true"]::after { opacity: 1; transform: none; }

/* ── Header actions ── */
.site-header__actions { display: flex; align-items: center; gap: var(--space-3); margin-left: auto; }
.site-header__cta .btn-label-full  { display: none; }
.site-header__cta .btn-label-short { display: inline; }

/* ── Hamburger ── */
.nav-toggle {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 2.75rem; height: 2.75rem;
  flex: none;
  color: var(--text-primary);
  border: var(--hairline) solid var(--border-default);
  border-radius: var(--radius-sm);
  background: color-mix(in srgb, var(--surface-2) 60%, transparent);
  transition: border-color var(--dur-fast) var(--ease-out), background-color var(--dur-fast) var(--ease-out);
}
.nav-toggle:hover { border-color: var(--border-control); }
.nav-toggle .icon { width: 1.25rem; height: 1.25rem; }
.nav-toggle__close { display: none; }
.nav-toggle[aria-expanded="true"] .nav-toggle__open  { display: none; }
.nav-toggle[aria-expanded="true"] .nav-toggle__close { display: block; }

/* ── The full-screen mobile menu ── */
.site-menu {
  position: fixed;
  inset: 0;
  /* One below the sticky header on purpose: the panel is opaque and full-bleed,
     so the header — and with it the logo and the close button — stays drawn on
     top of it. Above every page layer (z-raised is 10), below the skip link. */
  z-index: calc(var(--z-sticky) - 1);
  display: flex;
  flex-direction: column;
  padding: calc(var(--header-h) + var(--space-6)) var(--gutter) var(--space-10);
  background: var(--surface-0);
  overflow-y: auto;
  overscroll-behavior: contain;
  opacity: 0;
  visibility: hidden;
  transform: translateY(-0.5rem);
  transition:
    opacity    var(--dur-base) var(--ease-out),
    transform  var(--dur-base) var(--ease-out),
    visibility 0s linear var(--dur-base);
}
.site-menu.is-open {
  opacity: 1;
  visibility: visible;
  transform: none;
  transition:
    opacity    var(--dur-base) var(--ease-out),
    transform  var(--dur-base) var(--ease-out),
    visibility 0s;
}
/* A faint grid behind the panel, so the menu reads as part of the instrument. */
.site-menu::before {
  content: "";
  position: absolute;
  inset: 0;
  z-index: -1;
  background: url("../img/bg-grid.svg") repeat 0 0 / 128px 128px;
  opacity: 0.06;
  pointer-events: none;
}
.site-menu__list { display: flex; flex-direction: column; margin: 0; padding: 0; list-style: none; }
.site-menu__item { border-top: var(--hairline) solid var(--border-hairline); }
.site-menu__item:last-child { border-bottom: var(--hairline) solid var(--border-hairline); }
.site-menu__link {
  display: flex;
  align-items: baseline;
  gap: var(--space-4);
  padding-block: var(--space-5);
  font-family: var(--ff-display);
  font-size: var(--fs-h3);
  font-weight: var(--fw-semibold);
  letter-spacing: var(--ls-h3);
  color: var(--text-primary);
  text-decoration: none;
}
.site-menu__link::before {
  content: attr(data-index);
  font-family: var(--ff-mono);
  font-size: var(--fs-micro);
  font-weight: var(--fw-regular);
  letter-spacing: var(--ls-eyebrow);
  color: var(--text-muted);
}
.site-menu__link:hover { color: var(--text-cyan); }
.site-menu__link[aria-current="true"] { color: var(--text-cyan); }
.site-menu__foot { margin-top: var(--space-10); display: flex; flex-direction: column; gap: var(--space-4); }
.site-menu__foot .btn { width: 100%; }
.site-menu__contact {
  font-family: var(--ff-mono);
  font-size: var(--fs-small);
  letter-spacing: var(--ls-mono);
  color: var(--text-muted);
}
/* 18px tall as plain inline text — the one control in the panel under the
   touch minimum. The panel is never shown above 60em, so this needs no query. */
.site-menu__contact a {
  display: inline-block;
  padding-block: var(--space-3);
  color: var(--text-brand);
  text-decoration: none;
}
.site-menu__contact a:hover { text-decoration: underline; }

/* Body scroll lock while the menu is open. */
/* The dialog's own close control. aria-modal hides everything outside
   #site-menu from assistive tech, so the header's hamburger cannot be the only
   way out. Sits above the list, right-aligned, and is the first tab stop. */
.site-menu__close {
  align-self: flex-end;
  display: inline-flex;
  align-items: center;
  gap: var(--space-2);
  margin-bottom: var(--space-6);
  padding: var(--space-3) var(--space-4);
  min-block-size: 44px;
  font-size: var(--fs-small);
  font-weight: var(--fw-medium);
  color: var(--text-secondary);
  background: color-mix(in srgb, var(--surface-2) 60%, transparent);
  border: var(--hairline) solid var(--border-default);
  border-radius: var(--radius-sm);
  transition: color var(--dur-fast) var(--ease-out), border-color var(--dur-fast) var(--ease-out);
}
.site-menu__close:hover { color: var(--text-primary); border-color: var(--border-control); }
.site-menu__close .icon { width: 1rem; height: 1rem; }

body.has-menu-open { overflow: hidden; }

/* ── Header at tablet and up ── */
@media (min-width: 60em) {
  .site-nav { display: block; }
  .nav-toggle { display: none; }
  .site-menu { display: none; }
  .site-header__cta .btn-label-full  { display: inline; }
  .site-header__cta .btn-label-short { display: none; }
  .site-header__inner { gap: var(--space-10); }
  .site-header__actions { margin-left: 0; }
  .site-nav { margin-left: auto; }
}
@media (min-width: 26.25em) {
  .site-header__cta .btn-label-full  { display: inline; }
  .site-header__cta .btn-label-short { display: none; }
}


/* ═══ 6. FOOTER ═════════════════════════════════════════════════════════════ */

.site-footer {
  position: relative;
  isolation: isolate;
  border-top: var(--hairline) solid var(--border-hairline);
  background: var(--surface-1);
  padding-block: var(--section-y-tight);
  color: var(--text-muted);
  font-size: var(--fs-small);
}
.site-footer::before {
  content: "";
  position: absolute;
  inset: 0;
  z-index: -1;
  background: url("../img/bg-pcb-traces.svg") repeat 0 0 / 320px 320px;
  opacity: 0.05;
  mask-image: linear-gradient(180deg, var(--surface-0), transparent 70%);
  -webkit-mask-image: linear-gradient(180deg, var(--surface-0), transparent 70%);
  pointer-events: none;
}

.site-footer__top {
  display: grid;
  gap: var(--block-gap) var(--grid-gap);
  grid-template-columns: 1fr;
}
@media (min-width: 40em) { .site-footer__top { grid-template-columns: repeat(2, 1fr); } }
@media (min-width: 60em) { .site-footer__top { grid-template-columns: 1.6fr 1fr 1fr 1fr; } }

.site-footer__brand { display: flex; flex-direction: column; gap: var(--space-4); align-items: flex-start; }
@media (min-width: 40em) { .site-footer__brand { grid-column: 1 / -1; } }
@media (min-width: 60em) { .site-footer__brand { grid-column: auto; } }

.site-footer__tagline {
  max-width: 34ch;
  font-size: var(--fs-body);
  color: var(--text-secondary);
}

.site-footer__col h3 {
  margin-bottom: var(--space-4);
  font-family: var(--ff-mono);
  font-size: var(--fs-small);
  font-weight: var(--fw-medium);
  line-height: var(--lh-flat);
  letter-spacing: var(--ls-eyebrow);
  text-transform: uppercase;
  color: var(--text-muted);
}
/* A second heading in the same column (Contact + Legal share one column). */
.site-footer__col h3 ~ h3 { margin-top: var(--space-8); }
.site-footer__list { display: flex; flex-direction: column; gap: var(--space-3); margin: 0; padding: 0; list-style: none; }
.site-footer__list a { color: var(--text-secondary); text-decoration: none; }
.site-footer__list a:hover { color: var(--text-cyan); text-decoration: underline; }
.site-footer__list li { line-height: var(--lh-snug); }

/* Phone: the footer links are the only navigation on privacy.html, and at 360
   each was a 17px-tall target — under the 24px WCAG 2.5.8 minimum. The line box
   is unchanged; the padding is what the finger gets. This lives here rather
   than in sections.css because privacy.html loads tokens + base only, and the
   shared footer must render identically on both pages. The disclaimer bumps a
   step for the same reason: at 360 it is a four-line sentence, not a label. */
@media (max-width: 48em) {
  .site-footer__list a { display: inline-block; padding-block: var(--space-2); }
  .site-footer__disclaimer { font-size: var(--fs-small); }
}
.site-footer__note { color: var(--text-muted); }

/* The border-top is the footer's structural rule and must run the full
   container width; the reading measure belongs on the text inside, not on the
   element that carries the border. With both on one element the rule stopped at
   536px inside a 1200px footer and read as a stray dash. */
.site-footer__disclaimer {
  max-width: none;
  margin-top: var(--space-10);
  padding-top: var(--space-6);
  border-top: var(--hairline) solid var(--border-hairline);
  font-size: var(--fs-micro);
  line-height: var(--lh-lead);
  color: var(--text-muted);
}
.site-footer__disclaimer > span { display: block; max-width: var(--measure); }

.site-footer__bottom {
  /* Matching full-width rule, so the footer closes on two consistent hairlines
     instead of one partial one. */
  padding-top: var(--space-6);
  border-top: var(--hairline) solid var(--border-hairline);
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--space-3) var(--space-6);
  margin-top: var(--space-6);
  font-size: var(--fs-micro);
  color: var(--text-muted);
}
.site-footer__bottom p { margin: 0; max-width: none; }
/* Specificity has to clear `.site-footer__bottom p { margin: 0 }` above, or the
   auto margin never applies and the legal links sit mid-row. */
.site-footer__bottom .site-footer__legal {
  display: flex; flex-wrap: wrap;
  gap: var(--space-3) var(--space-5);
  margin-left: auto;
}
.site-footer__legal a { color: var(--text-muted); text-decoration: none; }
.site-footer__legal a:hover { color: var(--text-cyan); text-decoration: underline; }


/* ═══ 7. BACK TO TOP ════════════════════════════════════════════════════════ */

.to-top {
  position: fixed;
  right: var(--gutter);
  bottom: var(--gutter);
  z-index: var(--z-raised);
  display: inline-flex;
  align-items: center;
  gap: var(--space-2);
  padding: var(--space-3) var(--space-4);
  font-size: var(--fs-small);
  font-weight: var(--fw-medium);
  color: var(--text-secondary);
  background: var(--surface-2);
  border: var(--hairline) solid var(--border-default);
  border-radius: var(--radius-sm);
  box-shadow: var(--shadow-2);
  opacity: 0;
  visibility: hidden;
  transform: translateY(var(--space-2));
  transition:
    opacity    var(--dur-base) var(--ease-out),
    transform  var(--dur-base) var(--ease-out),
    visibility 0s linear var(--dur-base),
    color      var(--dur-fast) var(--ease-out),
    border-color var(--dur-fast) var(--ease-out);
}
.to-top.is-visible {
  opacity: 1;
  visibility: visible;
  transform: none;
  transition:
    opacity    var(--dur-base) var(--ease-out),
    transform  var(--dur-base) var(--ease-out),
    visibility 0s,
    color      var(--dur-fast) var(--ease-out),
    border-color var(--dur-fast) var(--ease-out);
}
.to-top:hover { color: var(--text-primary); border-color: var(--border-control); }
.to-top .icon { width: 1rem; height: 1rem; }
@media (max-width: 26.25em) {
  .to-top span { display: none; }
  /* Icon-only, but never under the 44px touch minimum, and clear of a phone's
     home indicator. */
  .to-top {
    padding: var(--space-3);
    min-inline-size: 44px;
    min-block-size: 44px;
    justify-content: center;
    bottom: calc(var(--gutter) + env(safe-area-inset-bottom, 0px));
  }
}


/* ═══ 8. STANDALONE PROSE PAGE (privacy, terms) ═════════════════════════════ */

.page-head { padding-block: calc(var(--section-y-tight) + var(--header-h) * 0.4) var(--space-10); }
.page-head h1 { font-size: var(--fs-h1); line-height: var(--lh-heading); letter-spacing: var(--ls-h1); }
.page-head .page-meta {
  margin-top: var(--space-3);
  font-family: var(--ff-mono);
  font-size: var(--fs-micro);
  letter-spacing: var(--ls-mono);
  color: var(--text-muted);
}
.page-head .lead { margin-top: var(--space-6); }

/* The template warning banner. Warning tokens, not the reserved red. */
.notice {
  display: flex;
  gap: var(--space-4);
  padding: var(--space-5) var(--space-6);
  margin-bottom: var(--block-gap);
  background: var(--warning-subtle);
  border: var(--hairline) solid var(--border-default);
  border-left: 3px solid var(--warning);
  border-radius: var(--radius-sm);
  color: var(--text-secondary);
  font-size: var(--fs-small);
  line-height: var(--lh-lead);
}
.notice strong { color: var(--text-primary); }
.notice p { max-width: none; }
.notice p + p { margin-top: var(--space-3); }

.prose { padding-bottom: var(--section-y); }
.prose h2 {
  margin-top: var(--space-12);
  margin-bottom: var(--space-4);
  font-size: var(--fs-h3);
  display: flex;
  align-items: baseline;
  gap: var(--space-4);
}
.prose h2::before {
  content: counter(prose-clause);
  counter-increment: prose-clause;
  font-family: var(--ff-mono);
  font-size: var(--fs-micro);
  font-weight: var(--fw-regular);
  letter-spacing: var(--ls-eyebrow);
  color: var(--text-muted);
}
.prose { counter-reset: prose-clause; }
.prose p { color: var(--text-secondary); }
.prose p + p { margin-top: var(--space-4); }
.prose .rule { margin-block: var(--space-12); }
.prose .rule + p { color: var(--text-muted); font-size: var(--fs-small); }
.prose .placeholder {
  font-family: var(--ff-mono);
  font-size: 0.9em;
  color: var(--text-cyan);
  background: var(--cyan-subtle);
  border: var(--hairline) solid var(--border-cyan);
  border-radius: var(--radius-xs);
  padding: 0.05em 0.35em;
  white-space: nowrap;
}


/* ═══ 9. PRINT ══════════════════════════════════════════════════════════════ */

@media print {
  .site-header, .site-menu, .to-top, .skip-link, .bg-layer { display: none !important; }
  body { background: none; color: black; }
}
