/* arete-foundation.css — the shared design foundation.
 *
 * WHY THIS FILE EXISTS
 *
 * Every page on this estate carried its own `:root` block, its own breakpoints
 * and its own type sizes, so they disagreed with each other and nobody could
 * see it by reading one file. Measured at 390px on current main:
 *
 *     paid landing page   h1 50.7px   body 14px   gutter 18px
 *     front door          h1 49px     body 14px   gutter 18px
 *     privacy             h1 44px     body 14px   gutter 18px
 *     partners            h1 34px     body 14.5px gutter 18px
 *     refunds             h1 42px     body 15px   gutter 18px
 *
 * Three separate body sizes, an h1 range of 34–50.7px at one viewport, and
 * every page shrinking its body text BELOW its desktop size on the device most
 * readers use. `p, li { font-size: 14px }` under `max-width: 1024px` was
 * copied from page to page; desktop kept 16px. The estate was making its text
 * smaller on phones.
 *
 * None of that showed up as a defect because nothing overflowed. The old bar
 * was "does it break at 320px". This file is the new one.
 *
 * WHAT A TOKEN IS FOR
 *
 * A page may compose these freely. It should not restate their values — a
 * literal `font-size: 14px` on a paragraph is how the estate drifted the first
 * time. `test/e2e/test-marketing-foundation.mjs` measures the RENDERED result at
 * 320/390/768/1280 rather than trusting the declarations here, because clamp()
 * cannot be checked by reading it.
 *
 * NO BUILD STEP, BY DESIGN. Same-origin, cacheable, and allowed by the
 * marketing site's `style-src 'self'` CSP without loosening anything.
 */

:root {
  /* ── Brand, unchanged ────────────────────────────────────────────────── */
  --bronze: #CC8A3D;
  --bronze-deep: #92602A;      /* accessible accent TEXT on light grounds */
  --bronze-dim: rgba(204, 138, 61, 0.12);
  --ink: #1A1A1A;
  --ink-mid: #48423B;
  --ink-light: #6F6960;
  --surface: #FAFAF8;          /* Soft Paper */
  --surface-alt: #F1EEE8;
  --paper: #FFFFFF;
  --border: #E0D9CF;
  --border-strong: #C9BFB1;

  /* Restrained semantic colours for judgment states. Deliberately desaturated:
     a report is a document, not a dashboard. */
  --state-act: #8C3A21;        /* ready to act */
  --state-assess: #7A5A1E;     /* assess first */
  --state-watch: #4A5A6B;      /* watch for change */
  --state-unknown: #5E5A54;    /* not enough evidence */
  --state-clear: #3D6B4A;      /* evidence-backed all clear */
  --state-act-bg: #F6E5E1;
  --state-assess-bg: #F5EEDC;
  --state-watch-bg: #E4EAEF;
  --state-unknown-bg: #EDEBE7;
  --state-clear-bg: #E3EDE5;

  /* ── Type scale ──────────────────────────────────────────────────────────
     Mobile-first. The phone values ARE the defaults; desktop is the override.
     That ordering is the fix — the old estate treated desktop as the real
     design and mobile as a shrink. */
  --font-display: "Cormorant Garamond", Georgia, "Times New Roman", serif;
  --font-text: "DM Sans", system-ui, -apple-system, "Segoe UI", Helvetica, Arial, sans-serif;

  /* 36px at 320, ~39px at 390, 42px at 420+. Within the 36–42px band at every
     phone width instead of the 50.7px the paid page was rendering. */
  --fs-h1: clamp(36px, 10vw, 42px);
  --fs-h2: clamp(27px, 7.2vw, 32px);
  --fs-h3: 21px;
  --fs-lead: 19px;
  --fs-body: 17px;             /* was 14px on every page below 1024px */
  --fs-evidence: 15px;         /* supporting detail, captions, meta */
  --fs-small: 13px;            /* legal, footer — meant to recede */
  --fs-eyebrow: 12px;

  --lh-display: 1.08;
  --lh-body: 1.6;
  --lh-tight: 1.45;

  /* ── Spacing ─────────────────────────────────────────────────────────────
     One scale. Sections and components step through it rather than inventing
     values. */
  --sp-1: 4px;
  --sp-2: 8px;
  --sp-3: 12px;
  --sp-4: 16px;
  --sp-5: 24px;
  --sp-6: 32px;
  --sp-7: 48px;
  --sp-8: 64px;

  /* 20px at 320, ~23px at 390, capped at 42px. The old clamp(18px, 4vw, 42px)
     never cleared 18px below 450px. */
  --gutter: clamp(20px, 6vw, 42px);
  --section-y: clamp(40px, 8vw, 72px);

  /* Comfortable measure. Long-form prose wants a bound; a heading does not. */
  --measure: 34em;
  --measure-tight: 28em;

  /* ── Surface treatment ───────────────────────────────────────────────────
     Thin borders, restrained shadows, one radius family. */
  --radius-sm: 6px;
  --radius: 8px;
  --radius-lg: 12px;
  --hairline: 1px solid var(--border);
  --shadow-sm: 0 1px 2px rgba(26, 26, 26, 0.05);
  --shadow: 0 8px 24px rgba(26, 26, 26, 0.08);
  --shadow-lg: 0 22px 60px rgba(26, 26, 26, 0.12);

  --tap: 44px;                 /* Apple HIG minimum, used as a floor */
  --t: 160ms ease;
}

@media (min-width: 700px) {
  :root {
    --fs-h1: clamp(42px, 5.2vw, 56px);
    --fs-h2: clamp(32px, 3.6vw, 40px);
    --fs-h3: 24px;
    --fs-lead: 21px;
    --fs-body: 18px;
    --fs-evidence: 16px;
  }
}

/* Respect the reader's own text-size preference: everything above is in px so
   it is predictable, but nothing here prevents browser zoom or user stylesheets,
   and no viewport tag in this estate disables pinch-zoom. */

/* ── Base ────────────────────────────────────────────────────────────────── */

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

.af {
  font-family: var(--font-text);
  font-size: var(--fs-body);
  line-height: var(--lh-body);
  color: var(--ink);
  background: var(--surface);
  /* Stop iOS inflating text in landscape, which silently breaks a measured
     type scale on exactly the orientation nobody checks. */
  -webkit-text-size-adjust: 100%;
  text-size-adjust: 100%;
}

.af h1, .af h2, .af h3 {
  font-family: var(--font-display);
  font-weight: 700;
  line-height: var(--lh-display);
  color: var(--ink);
  text-wrap: balance;
}
.af h1 { font-size: var(--fs-h1); margin-bottom: var(--sp-4); }
.af h2 { font-size: var(--fs-h2); margin-bottom: var(--sp-4); }
.af h3 { font-size: var(--fs-h3); line-height: var(--lh-tight); margin-bottom: var(--sp-2); }

.af p, .af li { font-size: var(--fs-body); max-width: var(--measure); }
.af .lead { font-size: var(--fs-lead); line-height: var(--lh-tight); color: var(--ink-mid); }
.af .evidence, .af .meta, .af figcaption { font-size: var(--fs-evidence); color: var(--ink-light); }
.af .small, .af footer, .af .legal { font-size: var(--fs-small); }
.af .eyebrow {
  font-size: var(--fs-eyebrow);
  font-weight: 700;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--bronze-deep);
}

.af .wrap { width: 100%; max-width: 1080px; margin-inline: auto; padding-inline: var(--gutter); }
.af section { padding-block: var(--section-y); }

/* Wide content scrolls inside its own port. The page body must never scroll
   horizontally — a table is allowed to. */
.af .scroll-x { overflow-x: auto; -webkit-overflow-scrolling: touch; }

/* ── Interactive states ──────────────────────────────────────────────────
   Selected, hover, focus, pressed, loading, empty and error are specified
   once, here, so they cannot be half-implemented per page. */

.af .btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: var(--sp-2);
  min-height: var(--tap);
  padding: var(--sp-3) var(--sp-5);
  border: 1px solid transparent;
  border-radius: var(--radius-sm);
  background: var(--ink);
  color: var(--paper);
  font: inherit;
  font-weight: 700;
  text-decoration: none;
  cursor: pointer;
  transition: background var(--t), border-color var(--t), transform var(--t);
}
.af .btn:hover { background: #000; }
.af .btn:active { transform: translateY(1px); }
.af .btn[aria-busy="true"] { opacity: 0.72; pointer-events: none; }

.af .btn-secondary {
  background: var(--paper);
  color: var(--ink);
  border-color: var(--border);
  font-weight: 600;
}
.af .btn-secondary:hover { border-color: var(--border-strong); background: var(--paper); }

/* One focus treatment everywhere, visible on both light and dark grounds. */
.af :focus-visible {
  outline: 2px solid var(--bronze-deep);
  outline-offset: 2px;
  border-radius: var(--radius-sm);
}

/* A selected control announces itself with more than colour — the border
   weight and the background both move, so it survives a colour-vision
   difference and a greyscale screenshot. */
.af [aria-pressed="true"], .af [aria-selected="true"], .af .is-selected {
  background: var(--ink);
  color: var(--paper);
  border-color: var(--ink);
}

.af .is-empty { color: var(--ink-light); font-size: var(--fs-evidence); font-style: italic; }
.af .is-error {
  padding: var(--sp-3) var(--sp-4);
  border: 1px solid #E2C3BA;
  border-radius: var(--radius-sm);
  background: var(--state-act-bg);
  color: #7E2C18;
  font-size: var(--fs-evidence);
}

/* Judgment-state pills. Colour is the accent, never the only signal: each pill
   carries its own words. */
.af .state {
  display: inline-block;
  padding: 2px var(--sp-2);
  border-radius: 999px;
  font-size: var(--fs-eyebrow);
  font-weight: 700;
  letter-spacing: 0.04em;
  text-transform: uppercase;
}
.af .state-act { background: var(--state-act-bg); color: var(--state-act); }
.af .state-assess { background: var(--state-assess-bg); color: var(--state-assess); }
.af .state-watch { background: var(--state-watch-bg); color: var(--state-watch); }
.af .state-unknown { background: var(--state-unknown-bg); color: var(--state-unknown); }
.af .state-clear { background: var(--state-clear-bg); color: var(--state-clear); }

/* ── Motion ──────────────────────────────────────────────────────────────
   Brief feedback transitions only. No autoplay, no parallax, no content that
   waits for a scroll animation to become readable. */
@media (prefers-reduced-motion: reduce) {
  .af *, .af *::before, .af *::after {
    animation-duration: 0.01ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 0.01ms !important;
  }
  /* SCROLLING IS SET ON THE ROOT, AND THE ROOT IS NOT INSIDE `.af`.
     Pages here declare `html { scroll-behavior: smooth }`, and the reset above
     is scoped to `.af *` — with `.af` on <body>, `html` is an ANCESTOR and can
     never match it. So a reader who had asked for less motion still got every
     anchor jump animated, and the rule that was supposed to stop it read as
     though it did. It is declared unscoped here because the root is the only
     place this property works. */
  html { scroll-behavior: auto !important; }
}

/* ══════════════════════════════════════════════════════════════════════════
   ARETE COMPONENTS — the parts more than one product uses.
   ══════════════════════════════════════════════════════════════════════════

   WHY THESE AND NOT MORE. The estate is one product to a customer and three
   stylesheets to us: the dashboard carried its own palette (--ink-mid #48423B
   against the report's #444, radius 12 against 8, body 16px against 17px), and
   a customer moving from the dashboard to their report crossed a seam. These
   are the pieces that seam ran through, and nothing else — extracted because
   this implementation needs them, not because a component library is nice to
   have.

   `.ar-` prefixed and UNSCOPED, on purpose. A page adopts one component at a
   time without taking on a whole framework, and no existing class name is
   captured by accident. `.af` above remains the marketing estate's full-page
   base; these stand alone.

   WHAT IS NOT HERE. Anything that means something only in a home review — tier
   colours, decision-state vocabulary, hazard treatment, the shape of a finding.
   A component here must be true for a review of something that is not a house.
   `--ar-accent` is the hook: a product sets it, the component uses it, and the
   component never learns why it is red. */

:root {
  --ar-accent: var(--bronze-deep);
  --ar-control-h: var(--tap);
  --ar-card-pad: 18px;
}

/* ── Surface ───────────────────────────────────────────────────────────────
   A card is a thing you read or act on. Depth says which: an actionable card
   takes `.ar-act` and gets its accent edge and a faint lift; a reading panel
   stays flat. Nothing here decides which one a given card is. */
.ar-card {
  background: var(--paper);
  border: 1px solid var(--border);
  border-radius: var(--radius-lg);
  padding: var(--ar-card-pad);
}
.ar-card.ar-act {
  border-left: 3px solid var(--ar-accent);
  box-shadow: var(--shadow-sm);
}

/* ── Section header ────────────────────────────────────────────────────────
   Eyebrow over title over one line saying what the section is for. The same
   three-part rhythm on the dashboard and in a report is most of why they read
   as one product. */
.ar-eyebrow {
  font-size: var(--fs-eyebrow);
  font-weight: 700;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--ink-light);
}
.ar-sec-head { margin: var(--sp-5) 0 var(--sp-3); }
.ar-sec-head h2 { font-size: var(--fs-h3); margin: 2px 0 0; }
.ar-sec-head .ar-what { font-size: var(--fs-evidence); color: var(--ink-light); line-height: var(--lh-tight); margin-top: 2px; }

/* ── State ─────────────────────────────────────────────────────────────────
   One pill for every state any Arete product reports — a job that is running,
   a thing that is ready, a judgment that is made, a window that has closed.
   The TONE is the vocabulary: act / assess / watch / unknown / clear / live.
   A product maps its own words onto a tone; the pill never learns the words.
   Colour is never the only signal — every pill carries its own text. */
.ar-state {
  display: inline-block;
  padding: 3px 9px;
  border-radius: 999px;
  font-size: var(--fs-eyebrow);
  font-weight: 700;
  letter-spacing: 0.04em;
  text-transform: uppercase;
  /* Not nowrap: at 200% browser zoom a 195px effective layout cannot hold a
     three-word pill on one line, and a pill that cannot shrink pushes the
     document sideways. Measured — see the report's own note.
     `anywhere` rather than `break-word`, because break-word cannot break a
     SINGLE long word and a pill is often exactly one: "Demonstration" is 13
     characters and 142px, which overflowed a 160px layout at 200% zoom. The
     two behave identically except that `anywhere` also reduces min-content
     width, which is the property a shrink-to-fit pill needs. */
  overflow-wrap: anywhere;
}
.ar-state-act { background: var(--state-act-bg); color: var(--state-act); }
.ar-state-assess { background: var(--state-assess-bg); color: var(--state-assess); }
.ar-state-watch { background: var(--state-watch-bg); color: var(--state-watch); }
.ar-state-unknown { background: var(--state-unknown-bg); color: var(--state-unknown); }
.ar-state-clear { background: var(--state-clear-bg); color: var(--state-clear); }
.ar-state-live { background: var(--bronze-dim); color: var(--bronze-deep); }

/* ── Evidence display ──────────────────────────────────────────────────────
   A small label over the thing it names. Every Arete product shows the reader
   what a claim rests on, and it has looked different on every surface. */
/* A DISPLAY THAT IS HANDED DATA MUST SURVIVE THE DATA.
   This component exists to show a reader what a claim rests on, and what it is
   handed is not prose — an address, an identifier, a model number, a URL. One
   unbreakable token is wider than a phone at large text sizes, and a token
   that cannot wrap takes the whole document sideways with it. Measured: the
   completion screen printed a customer's email address at 320px with the
   browser at 200%, an effective 160px layout, and the page scrolled to 191px
   on a 21-character address. Real addresses are longer.
   `anywhere` rather than `break-word`: it breaks only when there is no other
   option, and unlike `break-all` it does not chop ordinary words mid-syllable. */
.ar-kv { font-size: var(--fs-evidence); line-height: var(--lh-tight); margin-top: 10px; overflow-wrap: anywhere; }
.ar-kv > .ar-k {
  display: block;
  font-size: var(--fs-eyebrow);
  font-weight: 700;
  letter-spacing: 0.07em;
  text-transform: uppercase;
  color: var(--ar-accent);
  margin-bottom: 2px;
}

/* ── Disclosure ────────────────────────────────────────────────────────────
   The third reading depth. <details>, so it is keyboard operable, announced,
   findable by in-page search where the browser expands on find, and alive when
   a script is not. The summary must name what is inside it — a reader should
   never open a fold to learn whether it was worth opening. */
.ar-disclosure { margin-top: 12px; border-top: 1px dashed var(--border); }
.ar-disclosure > summary {
  list-style: none;
  cursor: pointer;
  display: flex;
  align-items: center;
  gap: var(--sp-2);
  min-height: var(--ar-control-h);
  font-size: var(--fs-evidence);
  font-weight: 700;
  color: var(--ar-accent);
}
.ar-disclosure > summary::-webkit-details-marker { display: none; }
.ar-disclosure > summary::after {
  content: '';
  flex: 0 0 auto;
  width: 6px; height: 6px;
  border-right: 2px solid currentColor;
  border-bottom: 2px solid currentColor;
  transform: rotate(45deg) translate(-1px, -1px);
  transition: transform var(--t);
}
.ar-disclosure[open] > summary::after { transform: rotate(-135deg) translate(-2px, -2px); }
.ar-disclosure > summary:focus-visible { outline: 2px solid var(--ar-accent); outline-offset: 2px; border-radius: var(--radius-sm); }

/* ── Navigation: where you are, and how to get back ────────────────────────
   Two pieces, and no more. `.ar-back` is a labelled return to a named place —
   never "Back", which tells a reader nothing about where they will land.
   `.ar-crumb` is the header's own return. No tab bar: a tab with nowhere
   useful to go is an app costume, and this product has three places. */
.ar-back {
  display: inline-flex;
  align-items: center;
  gap: var(--sp-2);
  min-height: var(--ar-control-h);
  font-size: var(--fs-evidence);
  font-weight: 600;
  color: var(--ink-light);
  text-decoration: none;
}
.ar-back::before {
  content: '';
  width: 6px; height: 6px;
  border-left: 2px solid currentColor;
  border-bottom: 2px solid currentColor;
  transform: rotate(45deg);
  transition: transform var(--t);
}
.ar-back:hover { color: var(--ar-accent); }
.ar-back:hover::before { transform: rotate(45deg) translate(2px, -2px); }
.ar-back:focus-visible { outline: 2px solid var(--ar-accent); outline-offset: 2px; border-radius: var(--radius-sm); }

/* ── Control ───────────────────────────────────────────────────────────────
   Substantial, obviously pressable, 44px floor, and a press that answers. */
.ar-btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: var(--sp-2);
  min-height: var(--ar-control-h);
  padding: 10px 18px;
  border: 1px solid transparent;
  border-radius: 9px;
  background: var(--ink);
  color: var(--paper);
  font: inherit;
  font-weight: 700;
  font-size: var(--fs-evidence);
  text-decoration: none;
  cursor: pointer;
  transition: background var(--t), border-color var(--t), transform var(--t);
}
.ar-btn:hover { background: #000; }
.ar-btn:active { transform: translateY(1px); }
.ar-btn-secondary {
  background: var(--paper);
  color: var(--ink);
  border-color: var(--border);
  font-weight: 600;
}
.ar-btn-secondary:hover { background: var(--paper); border-color: var(--border-strong); }
.ar-btn:focus-visible, .ar-btn-secondary:focus-visible { outline: 2px solid var(--ar-accent); outline-offset: 2px; }

/* Motion here decorates state changes that colour and content already carry,
   so removing it removes nothing. Unscoped for the same reason the components
   are: a page that adopts one component adopts its reduced-motion behaviour
   with it, rather than having to remember. */
@media (prefers-reduced-motion: reduce) {
  .ar-btn, .ar-back, .ar-back::before, .ar-disclosure > summary::after {
    transition: none !important;
  }
  .ar-btn:active { transform: none; }
  .ar-back:hover::before { transform: rotate(45deg); }
}
