/* myloankit stylesheet. First-party only, no framework.
   Mobile-first: base rules target small screens, min-width queries layer on
   the wider layout.

   Palette blocks are delimited by the "palette:" marker comments below. A test
   parses those three blocks and fails if any token is defined in one and not
   the others, so a colour can never exist in one theme only. Structural tokens
   (type, space, radius, measure) are theme-independent and live in their own
   :root block, which the test ignores by design. */

/* Structural tokens. Identical in both themes. */
:root {
  --font-sans: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;

  --text-body: 17px;
  --text-h1: 2.5rem;
  --text-h2: 1.5rem;
  --text-small: 0.875rem;
  --leading-body: 1.6;
  --leading-heading: 1.1;
  --tracking-heading: -0.022em;

  --space-1: 0.25rem;
  --space-2: 0.5rem;
  --space-3: 1rem;
  --space-4: 1.5rem;
  --space-5: 2.5rem;
  --space-6: 4rem;
  --space-7: 6rem;

  /* The gap between the header and the hero heading, below 640px.
     Half of --space-7, which is what the gap is from 640px up.

     Narrow viewports are short as well as narrow, and 96px of empty space
     above the first heading is a quarter of a 390px-tall phone viewport spent
     before a word is read. There is no 3rem step in the space scale and this
     is the only measure that wants one, so it is named for what it is rather
     than added to a scale nothing else would use.

     640px is the header's own breakpoint -- two rows below it, one row above
     -- so the seam is one that already exists rather than a second one
     invented for this. TestHeroTopIsHalvedOnNarrowViewports pins the
     arithmetic against --space-7, and the layout gate measures the rendered
     pixels at all seven widths. */
  --hero-top-narrow: 3rem;

  --radius-sm: 8px;
  --radius-md: 14px;
  --radius-pill: 980px;

  --measure-prose: 720px;
  --measure-wide: 1100px;
  --header-h: 56px;
}

/* palette: light */
:root {
  color-scheme: light;
  --surface: #ffffff;
  --surface-alt: #f5f5f7;
  --ink: #1d1d1f;
  --ink-muted: #6e6e73;
  --hairline: #d2d2d7;
  --accent: #0071e3;
  --accent-hover: #0077ed;
  --on-accent: #ffffff;
  --focus-ring: #0071e3;
  --header-bg: var(--surface);
  /* 5.38:1 on --surface, 4.94:1 on --surface-alt. Both clear the 4.5:1 text
     threshold; the border use only needs 3:1. */
  --error: #d70015;
  /* 5.38:1 on --surface, 4.94:1 on --surface-alt -- the same pair as --error,
     which is deliberate: the affirming and denying states of the verification
     card must read with equal weight, and a green that sat lighter than the
     red would make a match look like the softer answer. */
  --affirm: #0f7b33;
}

/* palette: dark (system)
   The :not([data-theme="light"]) qualifier is load-bearing: the head script
   always writes data-theme, so without it an explicit light choice on a
   dark-preferring machine would lose to this block. */
@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) {
    color-scheme: dark;
    --surface: #000000;
    --surface-alt: #1c1c1e;
    --ink: #f5f5f7;
    --ink-muted: #a1a1a6;
    --hairline: #48484a;
    --accent: #0a84ff;
    --accent-hover: #409cff;
    --on-accent: #ffffff;
    --focus-ring: #0a84ff;
    --header-bg: var(--surface);
    /* 6.16:1 on --surface, 4.99:1 on --surface-alt. */
    --error: #ff453a;
    /* 10.39:1 on --surface, 8.42:1 on --surface-alt. */
    --affirm: #30d158;
  }
}

/* palette: dark (explicit) */
:root[data-theme="dark"] {
  color-scheme: dark;
  --surface: #000000;
  --surface-alt: #1c1c1e;
  --ink: #f5f5f7;
  --ink-muted: #a1a1a6;
  --hairline: #48484a;
  --accent: #0a84ff;
  --accent-hover: #409cff;
  --on-accent: #ffffff;
  --focus-ring: #0a84ff;
  --header-bg: var(--surface);
  /* 6.16:1 on --surface, 4.99:1 on --surface-alt. */
  --error: #ff453a;
  /* 10.39:1 on --surface, 8.42:1 on --surface-alt. */
  --affirm: #30d158;
}

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

html { -webkit-text-size-adjust: 100%; }

body {
  margin: 0;
  min-height: 100vh;
  display: flex;
  flex-direction: column;
  font-family: var(--font-sans);
  font-size: var(--text-body);
  line-height: var(--leading-body);
  color: var(--ink);
  background: var(--surface);
  -webkit-font-smoothing: antialiased;
}

h1, h2, h3 {
  margin: 0 0 var(--space-3);
  font-weight: 600;
  line-height: var(--leading-heading);
  letter-spacing: var(--tracking-heading);
}

h1 { font-size: var(--text-h1); }
h2 { font-size: var(--text-h2); }
h3 { font-size: 1.125rem; }

p { margin: 0 0 var(--space-3); }

a { color: var(--accent); text-decoration: none; }
a:hover { color: var(--accent-hover); text-decoration: underline; }

:where(a, button, input, select, textarea):focus-visible {
  /* The focus ring is the accent, never --error, so it stays legible against
     an error border: a blue outline outside a red border reads as two
     separate things, which is the point. */
  outline: 3px solid var(--focus-ring);
  outline-offset: 2px;
  border-radius: var(--radius-sm);
}

img { max-width: 100%; height: auto; }

/* The walkthrough video. width/height on the element give the intrinsic
   1280x900 so the box is reserved before the poster arrives; these two rules
   then let it shrink to the column without distorting, which is what keeps it
   inside the measure at every width the layout check reports. Capping by
   max-width rather than setting width:100% means it is never blown up past
   its own resolution on a wide viewport. */
video { max-width: 100%; height: auto; }

.demo { margin: 0; }

.demo__video {
  display: block;
  width: 100%;
  border-radius: var(--radius-md);
  background: var(--surface-alt);
}

/* Long unbroken identifiers -- the test names cited on /compliance -- are
   wider than a phone and have no break opportunity, so they push the page
   sideways. anywhere lets the browser break them mid-token. */
code {
  overflow-wrap: anywhere;
  word-break: break-word;
  font-size: 0.9375em;
}

.skip-link {
  position: absolute;
  left: -9999px;
  top: 0;
  padding: var(--space-2) var(--space-3);
  background: var(--surface);
  color: var(--ink);
  z-index: 2;
}
.skip-link:focus { left: var(--space-3); }

/* Header: one hairline, no shadow. */
/* Header. Opaque, never translucent: it is sticky, so anything less than a
   solid background lets body text scroll visibly through it. --header-bg
   resolves to the surface token, so the two can never drift apart. */
.site-header {
  position: sticky;
  top: 0;
  z-index: 10;
  border-bottom: 1px solid var(--hairline);
  background: var(--header-bg);
}

/* Two rows below 640px: brand and toggle on the first, nav on the second.
   One row above. The wrap is what puts nav on its own line -- flex-basis 100%
   on an order-3 child forces the break without a second container. */
.site-header__inner {
  max-width: var(--measure-wide);
  margin: 0 auto;
  padding: var(--space-2) var(--space-3);
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  row-gap: var(--space-1);
}

.brand {
  order: 1;
  flex-shrink: 0;
  display: inline-flex;
  align-items: center;
  gap: var(--space-2);
  color: var(--ink);
  font-weight: 600;
  letter-spacing: var(--tracking-heading);
}
.brand:hover { color: var(--ink); text-decoration: none; }
/* overflow:hidden is what makes the radius bite. It was a no-op while the mark
   was an <img> -- a replaced element is clipped by its own border box -- and it
   is load-bearing now that the mark is inline SVG, whose children border-radius
   alone does not clip. Without it the black plate behind the artwork renders
   with square corners and nothing measures the difference. */
.brand__mark { width: 28px; height: 28px; border-radius: var(--radius-sm); overflow: hidden; }

/* Below 640px the nav is a panel under the header: full bleed via negative
   margins that cancel the inner padding, opaque so it never shows the page
   through itself. It renders OPEN by default -- only the .js class, set by
   the head script, allows it to be closed. With scripting off the links stay
   reachable and the button never appears. */
.site-nav {
  order: 4;
  /* 100% resolves against the inner's CONTENT box, so the negative margins
     below would shift the panel without widening it. Adding the gutters back
     into the basis is what makes it reach both viewport edges. */
  flex-basis: calc(100% + var(--space-3) * 2);
  display: flex;
  flex-direction: column;
  align-items: stretch;
  background: var(--surface);
  border-top: 1px solid var(--hairline);
  margin: var(--space-2) calc(-1 * var(--space-3)) calc(-1 * var(--space-2));
  padding: 0 var(--space-3) var(--space-2);
}
.js .site-nav[data-open="false"] { display: none; }

.nav-toggle { display: none; }
.js .nav-toggle {
  order: 3;
  flex-shrink: 0;
  margin-left: var(--space-2);
  display: inline-flex;
  /* column, or the three bars lay out side by side and squash into one line */
  flex-direction: column;
  align-items: center;
  justify-content: center;
  width: 44px;
  height: 44px;
  padding: 0;
  border: 1px solid var(--hairline);
  border-radius: var(--radius-pill);
  background: transparent;
  color: var(--ink-muted);
  cursor: pointer;
}
.nav-toggle:hover { color: var(--ink); }
.nav-toggle__bar {
  display: block;
  width: 18px;
  height: 2px;
  margin: 2px 0;
  background: currentColor;
  border-radius: 2px;
}

/* nowrap keeps "How it works" on one line; 44px is the tap target.
   In the panel the link stretches to the full content width (align-items:
   stretch on the column), so justify-content moves only the glyphs to the
   right while the hit area still spans the row. Horizontal padding is zero
   so the text's right edge lands on the panel's content edge, which is the
   same gutter the hamburger button sits on. */
.site-nav__link {
  display: inline-flex;
  align-items: center;
  justify-content: flex-end;
  min-height: 44px;
  /* Negative margin cancels the panel's gutter so the row is tappable edge
     to edge; the matching padding puts the glyphs back on that gutter. */
  margin: 0 calc(-1 * var(--space-3));
  padding: 0 var(--space-3);
  white-space: nowrap;
  color: var(--ink-muted);
  font-size: var(--text-small);
}
.site-nav__link:hover { color: var(--ink); }
.site-nav__link.is-current { color: var(--ink); }

.theme-toggle {
  order: 2;
  flex-shrink: 0;
  margin-left: auto;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 44px;
  height: 44px;
  padding: 0;
  border: 1px solid var(--hairline);
  border-radius: var(--radius-pill);
  background: transparent;
  color: var(--ink-muted);
  cursor: pointer;
}
.theme-toggle:hover { color: var(--ink); }
.theme-toggle__icon { display: block; }
.theme-toggle__icon--sun { display: none; }
:root[data-theme="dark"] .theme-toggle__icon--sun { display: block; }
:root[data-theme="dark"] .theme-toggle__icon--moon { display: none; }

main { flex: 1 0 auto; }

.wrap {
  max-width: var(--measure-prose);
  margin: 0 auto;
  padding: var(--space-6) var(--space-3);
}
.wrap--wide { max-width: var(--measure-wide); }

/* padding-bottom: 0 for the same reason .section carries it -- .wrap sets 4rem
   on both edges, every section zeroes the bottom one, and the hero was the
   only block that did not. It therefore stacked its own 4rem on top of the
   next section's 4rem, putting 144px between the hero copy and the first
   heading on every page that has one.

   It read as deliberate breathing room on the marketing pages and as a fault
   on /verify, where the heading below it is the answer the visitor came for
   and 144px pushes it toward the fold. It was the same 144px in both places,
   and the same oversight. TestHeroDoesNotDoublePad measures it.

   padding-top is the narrow value here and the full one from 640px up. The
   file is mobile-first -- every other width override in it is a min-width
   block -- so the base rule carries the narrow measure and the breakpoint
   restores the wide one, rather than a max-width block undoing the base.
   Written the other way round, a viewport that matched neither query would
   get the desktop value, and the one that does that is the narrowest. */
.hero { padding-top: var(--hero-top-narrow); padding-bottom: 0; }
.hero p { font-size: 1.1875rem; color: var(--ink-muted); max-width: 34em; }

/* padding-top/bottom, never the shorthand: `padding: x 0 0` here would
   reset the horizontal padding .wrap sets, and .section wins on order at
   equal specificity. That is what pushed every card to the viewport edge
   while hero text kept its gutter. */
.section { padding-top: var(--space-6); padding-bottom: 0; }
.section__lead { color: var(--ink-muted); }

.grid {
  display: grid;
  grid-template-columns: 1fr;
  gap: var(--space-4);
  padding: 0;
  margin: var(--space-4) 0 0;
  list-style: none;
}

.card {
  padding: var(--space-4);
  border: 1px solid var(--hairline);
  border-radius: var(--radius-md);
  background: var(--surface-alt);
}
.card h3 { margin-bottom: var(--space-2); }
.card p { margin: 0; color: var(--ink-muted); }

/* The verification result card.
   .card generalised to the tinted-border treatment .draft-banner introduced,
   with three tones instead of one. Only the border and the mark carry the
   tone: tinting the body text would cost legibility for no information, since
   the headline already states the outcome in words. */
.result {
  padding: var(--space-4);
  border: 2px solid var(--hairline);
  border-left-width: 6px;
  border-radius: var(--radius-md);
  background: var(--surface-alt);
}
.result__mark {
  margin: 0 0 var(--space-2);
  font-size: var(--text-h1);
  line-height: 1;
  color: var(--ink-muted);
}
.result__headline { margin: 0 0 var(--space-2); }
.result__detail { margin: 0; color: var(--ink-muted); }

/* Colour is never the only carrier: the three marks are a tick, a cross and a
   dash, so the card reads the same in greyscale, and every tone states its
   outcome in the headline above the fields. */
.result--affirm { border-color: var(--affirm); }
.result--affirm .result__mark { color: var(--affirm); }
.result--deny { border-color: var(--error); }
.result--deny .result__mark { color: var(--error); }
.result--neutral { border-color: var(--hairline); }

.result .rows { margin-top: var(--space-3); }
.result code { overflow-wrap: anywhere; user-select: all; }

.steps { margin: var(--space-4) 0 0; padding-left: var(--space-4); }
.steps li { margin-bottom: var(--space-3); }

.plain { list-style: none; padding: 0; margin: var(--space-4) 0 0; }
.plain li {
  padding: var(--space-3) 0;
  border-top: 1px solid var(--hairline);
  color: var(--ink-muted);
}
.plain li strong { color: var(--ink); font-weight: 600; }

/* Draft banner and placeholders on the Terms page.
   These must not read as settled provisions. The banner states the document's
   status once at the top; each gap states its own status where it sits, since
   a reader arriving from a deep link or a search result never sees the banner.
   Loud on purpose: an unreviewed placeholder mistaken for a term is the whole
   failure mode this styling exists to prevent. */
.draft-banner {
  margin: var(--space-4) 0;
  padding: var(--space-4);
  border: 2px solid var(--error);
  border-radius: var(--radius-md);
  background: var(--surface);
}
.draft-banner__label,
.placeholder__label {
  display: inline-block;
  margin-bottom: var(--space-2);
  font-size: var(--text-small);
  font-weight: 700;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--error);
}
.draft-banner p:last-child,
.placeholder p:last-child { margin-bottom: 0; }

.placeholder {
  margin: var(--space-3) 0;
  padding: var(--space-3) var(--space-4);
  border-left: 4px solid var(--error);
  background: var(--surface);
}
.placeholder p { color: var(--ink-muted); }

/* Scope and affiliation note, carried beside every SBA claim. Muted and
   smaller than body copy, but not as quiet as a citation: a reader skipping it
   is the failure this element exists to prevent, so it keeps the body measure
   and gains a rule to separate it from the claim above. */
.note {
  margin-top: var(--space-3);
  padding-top: var(--space-3);
  border-top: 1px solid var(--hairline);
  font-size: var(--text-small);
  color: var(--ink-muted);
}

/* Citation line under a dimension: the source and its effective date. */
.cite {
  display: block;
  margin-top: var(--space-2);
  font-size: var(--text-small);
  color: var(--ink-muted);
}

.site-footer {
  margin-top: var(--space-7);
  border-top: 1px solid var(--hairline);
  background: var(--surface);
}
.site-footer__inner {
  max-width: var(--measure-wide);
  margin: 0 auto;
  padding: var(--space-4) var(--space-3);
  color: var(--ink-muted);
  font-size: var(--text-small);
}
.site-footer__inner p { margin: 0 0 var(--space-2); }

/* Footer links wrap rather than overflowing a phone, and each is a 44px tap
   target with enough separation to be hit individually. */
.site-footer__links {
  display: flex;
  flex-wrap: wrap;
  gap: 0 var(--space-4);
  margin: 0 0 var(--space-2);
}
.site-footer__links a {
  display: inline-flex;
  align-items: center;
  min-height: 44px;
}
.site-footer__inner p:last-child { margin: 0; }

/* --- Form and result ------------------------------------------------- */

.fieldset { border: 0; margin: 0; padding: var(--space-6) 0 0; }
.fieldset legend { padding: 0; }
.fieldset legend h2 { margin-bottom: var(--space-2); }

.field { margin: 0 0 var(--space-4); }
.field__label { display: block; font-weight: 600; margin-bottom: var(--space-1); }
.field__req { color: var(--accent); }
.field__help { margin: 0 0 var(--space-2); color: var(--ink-muted); font-size: var(--text-small); }
.field__error { margin: var(--space-1) 0 0; color: var(--error); font-size: var(--text-small); }

.field input[type="text"], .field input[type="date"], .field select, .field textarea {
  width: 100%;
  min-height: 44px;
  padding: var(--space-2);
  font: inherit;
  color: var(--ink);
  background: var(--surface);
  border: 1px solid var(--hairline);
  border-radius: var(--radius-sm);
}
.field textarea { min-height: 88px; }

/* A failed field is marked, not just described. The border is server-rendered
   via .field--short on the wrapper, so it appears with scripting off.
   These selectors mirror the base rule above exactly: at (0,2,1) each, an
   under-specific .field--short input would lose to .field input[type="text"]
   on specificity and again on source order, which is why the error border
   never appeared even before --error was missing. */
.field--short input[type="text"],
.field--short input[type="date"],
.field--short select,
.field--short textarea {
  border-color: var(--error);
}

/* Focus clears the marker: a field being corrected should not still be shouting.
   :focus rather than :focus-visible so it clears on pointer too, and the border
   returns on blur -- the field is still invalid until it is resubmitted. No
   script is involved in any of that. */
.field--short input[type="text"]:focus,
.field--short input[type="date"]:focus,
.field--short select:focus,
.field--short textarea:focus {
  border-color: var(--hairline);
}

.field__money { display: flex; align-items: stretch; }
.field__prefix {
  display: inline-flex;
  align-items: center;
  padding: 0 var(--space-2);
  color: var(--ink-muted);
  border: 1px solid var(--hairline);
  border-right: 0;
  border-radius: var(--radius-sm) 0 0 var(--radius-sm);
  background: var(--surface-alt);
}
.field__money input { border-radius: 0 var(--radius-sm) var(--radius-sm) 0; }

.field__check { display: flex; align-items: center; gap: var(--space-2); min-height: 44px; }
.field__check input { width: 20px; height: 20px; }

.checklist li { border-top: 0; padding: 0; }

/* The sample document cards. The grid and card shapes already exist; these
   only handle the row the download button shares with its file size, which
   would otherwise sit on two lines and read as two unrelated things. */
.sample__actions {
  display: flex;
  align-items: center;
  gap: var(--space-3);
  flex-wrap: wrap;
}

.sample__size {
  color: var(--ink-muted);
  font-size: var(--text-small);
}

/* The file chooser on /verify.
   Styled through ::file-selector-button rather than the usual trick of hiding
   the input behind a label that acts as the button. That trick costs the
   filename: the browser draws it as part of the control, so a hidden input
   draws nothing and the page needs script to say which file was chosen. The
   pseudo-element keeps the real control -- its keyboard behaviour, its
   accessible name, its filename -- and restyles the only part that looked
   out of place.
   The outline treatment is deliberate. "Check this file" below is the
   dominant action and takes the solid accent; choosing a file is the step
   before it and must not compete. */
.filepick {
  display: block;
  width: 100%;
  font: inherit;
  color: var(--ink);
}

.filepick::file-selector-button {
  min-height: 44px;
  margin-right: var(--space-3);
  padding: var(--space-2) var(--space-4);
  font: inherit;
  font-weight: 600;
  color: var(--accent);
  background: transparent;
  border: 1px solid var(--accent);
  border-radius: var(--radius-pill);
  cursor: pointer;
}

.filepick::file-selector-button:hover {
  background: var(--surface-alt);
}

/* The ring goes on the input, not the pseudo-element: the input is what takes
   focus, and a ring drawn only on the button would leave a keyboard user with
   no indication when the control itself is focused. */
.filepick:focus-visible {
  outline: 3px solid var(--focus-ring);
  outline-offset: 2px;
  border-radius: var(--radius-sm);
}

.btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-height: 44px;
  padding: 0 var(--space-4);
  font: inherit;
  font-weight: 600;
  color: var(--on-accent);
  background: var(--accent);
  border: 1px solid var(--accent);
  border-radius: var(--radius-pill);
  cursor: pointer;
}
.btn:hover { background: var(--accent-hover); border-color: var(--accent-hover); color: var(--on-accent); text-decoration: none; }

.actions { padding-top: var(--space-6); }


.score { font-size: var(--text-h1); font-weight: 600; letter-spacing: var(--tracking-heading); margin: 0 0 var(--space-3); }
.score__max { font-size: var(--text-h2); color: var(--ink-muted); font-weight: 400; }

.status {
  display: inline-block;
  margin-left: var(--space-2);
  padding: 0 var(--space-2);
  border-radius: var(--radius-pill);
  font-size: var(--text-small);
  border: 1px solid var(--hairline);
}
.status--met { color: var(--ink); }
.status--short { color: var(--error); border-color: var(--error); }
.status--pending { color: var(--ink-muted); }

.points { display: inline-block; margin-left: var(--space-2); color: var(--ink-muted); font-size: var(--text-small); }

.rows { margin: var(--space-2) 0 0; }
.rows dt { font-weight: 600; color: var(--ink); font-size: var(--text-small); margin-top: var(--space-2); }
.rows dd { margin: 0; color: var(--ink-muted); }
.signals { margin-top: var(--space-3); }

/* The header is sticky, so a field scrolled to by autofocus or by a fragment
   would sit underneath it. scroll-margin-top reserves the header's height plus
   a little breathing room, and is sized from the same token the header is, so
   the two cannot drift. */
.field,
.field input,
.field select,
.field textarea { scroll-margin-top: calc(var(--header-h) + var(--space-4)); }

.findings li { border-top: 1px solid var(--hairline); }
.findings li:first-child { border-top: 0; }

/* The home page film. Its own class rather than .demo's: the two players sit
   on different pages at different sizes, and sharing a class would make a
   change to one a change to both. The name avoids the identifier ban list,
   which reaches markup as well as copy -- a class called "verdict" would fail
   the rendered-body scan exactly as the word would. */
.intro-film { margin: 0; }

.intro-film__video {
  display: block;
  width: 100%;
  border-radius: var(--radius-md);
  background: var(--surface-alt);
}

/* The disclaimer beside the player carries the same words the film's closing
   segment speaks and burns into the frame, so a visitor who never presses
   play reads them anyway. Sized as help text, not as fine print: it is the
   scope of the product, not a footnote to it. */
.intro-film__note {
  margin-top: var(--space-3);
  color: var(--ink-muted);
  font-size: 0.9375rem;
  max-width: var(--measure-prose);
}


/* The front page hero. LK-S71, revised LK-S72.
   An attorney read the page for about a second, took it for a loan
   application, and never reached the film. The copy was correct; there were
   900 words of it, and the film was a text link below three dense paragraphs.
   What follows sizes a hero whose whole job is to be legible in five seconds
   without scrolling and without reading a sentence.

   LK-S71 also embedded the film here, autoplaying. That went further than the
   request, which was to cut the volume of prose, and it was worse than the
   problem: fourteen seconds into the cut the film shows the application form,
   currency inputs and all, directly under the headline. A reader who lingered
   saw a loan application. The player is back on /intro-video and the front
   page links to it, so .hero__film is gone; everything else here is the
   compression and stays.

   Selectors are .hero-scoped at two classes rather than one, because
   `.hero p` above sets the body measure, colour and size for every paragraph
   in the region and beats a bare single-class rule on specificity. A rule
   named .hero__sub alone would render at the body size and look like a
   fourth paragraph, which is the failure this whole section exists to end. */
.hero .hero__sub {
  font-size: 1.375rem;
  color: var(--ink);
  max-width: 34em;
  margin-top: var(--space-2);
}

/* Four words each, no body copy under them. A separate class from .grid/.card:
   those carry a heading and a paragraph and a heavier box, and reusing them
   here would put a card's padding around three short lines. */
.tiles {
  display: grid;
  grid-template-columns: 1fr;
  gap: var(--space-3);
  padding: 0;
  margin: var(--space-5) 0 0;
  list-style: none;
}

.tile {
  padding: var(--space-3) var(--space-4);
  border: 1px solid var(--hairline);
  border-radius: var(--radius-md);
  background: var(--surface-alt);
  font-weight: 600;
}

/* flex-wrap, not two inline links: at 320px the pair does not fit on one row,
   and a button that wraps mid-label is worse than a second row of buttons. */
.hero .hero__cta {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-3);
  margin-top: var(--space-5);
  max-width: none;
}

/* The second call to action. Same size and tap target as .btn so the pair
   reads as one row, quieter fill so the primary one is still the primary. */
.btn--quiet {
  color: var(--accent);
  background: transparent;
  border-color: var(--accent);
}
.btn--quiet:hover { color: var(--on-accent); background: var(--accent); border-color: var(--accent); }


/* ─────────────────────────────────────────────────────────────────────────
   MEDIA QUERIES ARE LAST. NOTHING MAY BE APPENDED BELOW THIS LINE.

   A rule placed after a media query wins at every width, because at equal
   specificity the later rule takes the cascade. Two hundred lines of form
   and button CSS were appended below the 640px block across several
   sessions; none of it happened to name a header selector, so the bug
   stayed latent rather than absent.

   New rules go ABOVE this boundary. Width overrides go INSIDE the blocks
   below it. TestMediaQueriesAreLastInTheStylesheet fails the build if a
   top-level rule appears down here.
   ───────────────────────────────────────────────────────────────────────── */


@media (min-width: 640px) {
  .site-header__inner {
    flex-wrap: nowrap;
    padding: 0 var(--space-3);
    min-height: var(--header-h);
    column-gap: var(--space-4);
  }
  .brand { order: 1; }
  .site-nav, .js .site-nav[data-open="false"] {
    order: 2;
    display: flex;
    flex-direction: row;
    align-items: center;
    justify-content: flex-end;
    flex-basis: auto;
    margin: 0 0 0 auto;
    padding: 0;
    gap: var(--space-4);
    background: none;
    border-top: 0;
  }
  .theme-toggle { order: 3; }
  .nav-toggle, .js .nav-toggle { display: none; }
  .site-nav__link { min-height: 0; margin: 0; padding: 0; }
  .theme-toggle {
    margin-left: var(--space-3);
    width: 36px;
    height: 36px;
  }

  /* The full nav-to-hero gap, restored from the header's own breakpoint up.
     Below it the base rule's --hero-top-narrow applies, which is half this.
     Only padding-top: padding-bottom stays 0 at every width, and respelling
     it here would be a second place for the double-pad fix to be undone. */
  .hero { padding-top: var(--space-7); }
}


@media (min-width: 768px) {
  :root { --text-h1: 3rem; }
  .grid { grid-template-columns: repeat(3, 1fr); }
  .tiles { grid-template-columns: repeat(3, 1fr); }
  .site-header__inner { padding: 0 var(--space-4); }
  .wrap { padding-left: var(--space-4); padding-right: var(--space-4); }
  .site-footer__inner { padding-left: var(--space-4); padding-right: var(--space-4); }
}


@media (prefers-reduced-motion: reduce) {
  * { animation: none !important; transition: none !important; }
}
