/* The Sanctuary — brand surfaces (thesanctuary.dev, legal., provision.)
 *
 * SELF-HOSTED, deliberately. A <link> to fonts.googleapis.com hands every
 * visitor's IP to Google on page load. These sites exist downstream of an audit
 * whose whole point was removing that kind of thing; loading the type from a
 * third party would undo it on the one page carrying the company's name.
 * Two variable files, latin subset, 52 KB total.
 *
 * Geist / Geist Mono — SIL Open Font License 1.1, Vercel with Basement Studio.
 *
 * PALETTE — ALIGNED TO cloudflare/_kit/tokens.js, NOT A FOURTH ONE.
 * Design audit 2026-09-15 (batch B2) counted four different palettes across
 * the estate: `_kit`, this file (three identical copies), muniment's own, and
 * codex's back link. This file's numbers were CLOSE to `_kit`'s sand+acid
 * ramp but not equal (e.g. ground was #232322 here, #191918 there) — two
 * independently hand-picked "warm dark neutral" values that happened to look
 * similar. They are now the SAME hexes: --bg/--surface/--ink/--dim/--rule
 * below are `_kit`'s real `@radix-ui/colors` `sand` steps
 * (--s-ground/--s-panel/--tx-primary/--tx-meta/--s-hair) and --acid is
 * `_kit`'s own `--acid`, pasted rather than imported because these three
 * sites are plain static HTML with no build step and `_kit/tokens.js` is a
 * JS module emitted into a Worker's inline <style> — not something a
 * Workers-Assets-only site can `import`. `_kit/tokens.js` is not edited by
 * this change; this is a value copy, checked against it below.
 *
 * `--dim` carries "labels, furniture" (this file's own words, unchanged) and
 * is mapped to `_kit`'s `--tx-meta`, not `--tx-secondary`: meta's dark
 * contrast (5.95:1) is the closer match to what this file shipped before
 * (6.08:1) than secondary's (8.39:1) is, and `_kit`'s own docstring reserves
 * secondary for a brighter tier than "furniture".
 *
 * Contrast measured against each scheme's own --bg, recomputed for the new
 * hexes rather than carried over from the old ones:
 *   token   dark      light     carries
 *   ink     15.14     14.56     body text
 *   dim      5.95      5.99     labels, furniture
 *   acid    15.31      5.29     accent, links
 *   rule     1.55      1.26     hairlines only, never text
 * Every token that carries text clears 4.5:1 in BOTH schemes. --acid is a
 * DIFFERENT value per scheme for exactly this reason: #d8ff36 measures 1.60 on
 * cream and would fail; #5b6b00 (`_kit`'s own light --acid) is its
 * light-scheme counterpart.
 *
 * --ok / --warn ADDED in this pass. `legal/public/callback.html` (the bank
 * OAuth redirect target, predates this file per its own "legacy aliases"
 * comment) has read `var(--ok)`/`var(--warn)` on its "granted"/"refused"
 * heading since it shipped, and this file never defined either — a var()
 * that resolves to nothing makes `color` invalid at computed-value time,
 * which for an INHERITED property falls back to the inherited value (plain
 * --ink) rather than erroring, so the failure was silent: both headings
 * rendered in body ink, and the one visual cue distinguishing "your bank said
 * yes" from "your bank said no" never painted, in every browser, since the
 * page existed. Grepped: the only two call sites in root/legal/provision.
 * Mapped to `_kit`'s semantic role-text pair (ok=grass, critical=red) rather
 * than a new pick — dark 9.26:1 / 8.35:1, light 10.82:1 / 11.11:1 against
 * --bg, both re-measured against the values below, not assumed.
 */

@font-face {
  font-family: "Geist";
  src: url("/fonts/geist-latin.woff2") format("woff2");
  font-weight: 100 900;           /* one variable file covers the range */
  font-style: normal;
  font-display: swap;
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA,
                 U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122,
                 U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
@font-face {
  font-family: "Geist Mono";
  src: url("/fonts/geist-mono-latin.woff2") format("woff2");
  font-weight: 100 900;
  font-style: normal;
  font-display: swap;
}

:root {
  color-scheme: light dark;
  --bg:#191918; --surface:#222221; --ink:#eeeeec; --dim:#989690;
  --rule:#3b3a37; --acid:#d8ff36; --ok:#71d083; --warn:#ff9592;
  --sans:"Geist", ui-sans-serif, -apple-system, "Segoe UI", system-ui, sans-serif;
  --mono:"Geist Mono", ui-monospace, SFMono-Regular, Menlo, monospace;
}
@media (prefers-color-scheme: light) {
  :root {
    --bg:#f4f2ec; --surface:#f9f9f8; --ink:#21201c; --dim:#5d5c57;
    --rule:#dad9d6; --acid:#5b6b00; --ok:#203c25; --warn:#641723;
  }
}

* { box-sizing: border-box }
body {
  margin:0; background:var(--bg); color:var(--ink);
  font-family:var(--sans); font-size:16px; line-height:1.65;
  -webkit-font-smoothing:antialiased; text-rendering:optimizeLegibility;
  font-feature-settings:"cv11";   /* Geist: disambiguated single-storey shapes */
}
code, pre, .mono { font-family:var(--mono); font-variant-numeric:tabular-nums; }
a { color:inherit; text-decoration-color:var(--acid); text-underline-offset:3px; }
a:focus-visible { outline:2px solid var(--acid); outline-offset:3px; border-radius:2px; }
/* Design audit 2026-09-15 (batch B2): 44px tap targets, for the links this
   CAN apply to. `footer a` and `.hero__foot a` are standalone one- or
   two-word navigation links (the legal/provisioning cross-links, the mailto,
   root's "Legal notices"/"Provisioning") — measured 18-22px tall before this,
   line-height alone. `display:inline-block` + padding grows the hit area; the
   matching negative margin cancels the visual shift so text position and line
   spacing are unchanged. `.dl` (provision's install cards) is already a
   block-level link well over 44px and needs nothing.
   DELIBERATELY NOT applied to a link embedded IN A SENTENCE (privacy.html's
   regulator URLs, provision's "Verify at on.quad9.net") — WCAG 2.5.8 Target
   Size names exactly this as the "inline" exception: an inline text link does
   not have to meet the minimum, because padding a link mid-paragraph would
   overlap the line above and below rather than growing cleanly. */
footer a, .hero__foot a {
  display: inline-block;
  padding-block: .7rem;
  margin-block: -.7rem;
}
@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after { animation-duration:.01ms !important; transition-duration:.01ms !important; }
}

/* ---------------------------------------------------------------------------
 * LAYOUT — mobile first, and it uses the screen.
 *
 * What was here before: a fixed 38rem column, 16px type, zero media queries.
 * On a phone that meant a cramped measure inside fixed 1.5rem padding; on a
 * wide display it meant a narrow ribbon of text with most of the viewport
 * empty. One layout that suited neither end.
 *
 * Base styles below are the PHONE. Everything grows from there via clamp(),
 * so there is no breakpoint where the design snaps — and one min-width query
 * at 64rem introduces the second column, because that is the width at which a
 * single column genuinely starts wasting space rather than the width some
 * device happens to be.
 * ------------------------------------------------------------------------- */
:root{
  --gutter: clamp(1.25rem, 5vw, 4rem);
  --measure: 66ch;                 /* ceiling on line length, not on the page */
  --s--1: clamp(.82rem, .79rem + .15vw, .93rem);
  --s-0 : clamp(1rem,   .96rem + .24vw, 1.18rem);
  --s-1 : clamp(1.15rem, 1.05rem + .5vw, 1.5rem);
  --s-2 : clamp(1.35rem, 1.15rem + 1vw,  2.1rem);
  --s-3 : clamp(1.8rem,  1.3rem + 2.4vw, 3.4rem);
}
body { font-size: var(--s-0); }

main {
  width: 100%;
  padding: clamp(2rem,7vh,5rem) var(--gutter) clamp(3rem,10vh,7rem);
  max-width: none;                 /* overrides the old fixed 38rem */
  margin-inline: auto;
}
/* Only a page BUILT as rail+flow becomes a grid. Applying this to every <main>
   turned the apex — three loose children — into grid items and scattered them:
   mark left, wordmark far right, link orphaned below. A layout rule that
   assumes a structure has to say so in its selector. */
main.split {
  display: grid;
  /* minmax(0,1fr), NOT the implicit `auto`. A grid track defaults to max-content
     and a grid item defaults to min-width:auto, so children capped at 66ch sized
     the single mobile column to ~600px and pushed the page off a 390px screen.
     This is the whole mobile bug: it was never the type or the padding. */
  grid-template-columns: minmax(0, 1fr);
  gap: clamp(1.5rem, 4vw, 3rem);
  /* Safe-area insets, added in this pass, scoped to `main.split` on purpose
     rather than widening the bare `main` rule above: `main{}` is ALSO what a
     bare `<main>` matches, and legal/public/callback.html and
     legal/public/myminfin/callback.html both use one — this pass verified
     (a live computed-style probe, not assumed) that `main{}`'s existing
     `max-width:none; margin-inline:auto` already wins over those two pages'
     own intended `max-width:36rem; margin:0 auto` at equal specificity by
     source order, so they render full-width today. That is a real,
     PRE-EXISTING bug, out of B2's scope (root/legal/provision HTML + headers
     + palette — not those two callback pages' layout) and left untouched
     rather than folded in here; reported separately. `main.split`'s higher
     specificity (class+tag) means this rule cannot make that bug worse or
     better — it only ever applies to the four pages that carry the class:
     legal's index/privacy/terms and provision's index.
     `env(safe-area-inset-*)` is 0 with no notch/Dynamic Island/home
     indicator, so this is a no-op except on an iPhone in landscape (inset-left
     eats into the inline edge) or the home-indicator strip at the bottom —
     needs `viewport-fit=cover` on the page's own <meta viewport> to take
     effect at all; env() resolves to 0 without it regardless of the device.
     `max()`, not `+`: the visual gutter should stay at LEAST --gutter, not
     --gutter-plus-inset stacked on an inset that already clears it. */
  padding-inline: max(var(--gutter), env(safe-area-inset-left, 0px))
                  max(var(--gutter), env(safe-area-inset-right, 0px));
  padding-bottom: calc(clamp(3rem,10vh,7rem) + env(safe-area-inset-bottom, 0px));
}
/* Grid and flex children need this explicitly or they refuse to shrink below
   their content's intrinsic width. */
main.split > .rail, main.split > .flow { min-width: 0; }
main > .flow > * + * { margin-top: 1.1em; }
.rail h1 { font-size: var(--s-3); line-height: 1.04; letter-spacing: -.02em;
           text-wrap: balance; margin: 0 0 .4em; }
.rail .sub { font-size: var(--s-1); color: var(--dim); margin: 0; max-width: min(34ch, 100%); }
.flow h2 { font-size: var(--s-1); margin: 2em 0 .5em; letter-spacing: -.01em; }
.flow p, .flow li, .flow dd { max-width: min(var(--measure), 100%); }

@media (min-width: 64rem) {
  main.split {
    grid-template-columns: minmax(16rem, 24rem) minmax(0, var(--measure));
    gap: clamp(3rem, 7vw, 8rem);
    max-width: 108rem;
    align-items: start;
  }
  /* The rail holds the page's identity, so it stays put while the body scrolls.
     align-self:start is required — a stretched grid item cannot stick. */
  .rail { position: sticky; top: clamp(2rem,7vh,5rem); align-self: start; }
}

/* Checksums, DNS records and URLs are long unbroken strings; without this they
   set the minimum width of their container and reopen the same overflow. */
.flow pre, .flow code { overflow-x: auto; max-width: 100%; }
.flow a { overflow-wrap: anywhere; }
