/* app.css -- Headlands Scorecard, hybrid: paper-reveal (docs/DESIGN-SPEC.md).
   Every color/font/size/spacing/motion value below is a copy of a
   design/TOKENS.yaml entry; node design/check-tokens.mjs enforces that no
   other raw hex literal appears anywhere in this file. Motion lives only in
   the teaser reveal (.result-score count-up in app.mjs, .result-cat-row
   landing below) -- the landing and question views are still. */

/* Self-hosted fonts (site/assets/fonts/, pinned builds; see VENDOR.md) --
   no hex literals; replaces the former Google Fonts stylesheet. */
@font-face{font-family:'DM Serif Display';font-style:normal;font-weight:400;font-display:swap;src:url('/assets/fonts/DMSerifDisplay-400.woff2') format('woff2');}
@font-face{font-family:'DM Serif Display';font-style:italic;font-weight:400;font-display:swap;src:url('/assets/fonts/DMSerifDisplay-400-italic.woff2') format('woff2');}
@font-face{font-family:'Inter';font-style:normal;font-weight:400;font-display:swap;src:url('/assets/fonts/Inter-400.woff2') format('woff2');}
@font-face{font-family:'Inter';font-style:normal;font-weight:500;font-display:swap;src:url('/assets/fonts/Inter-500.woff2') format('woff2');}
@font-face{font-family:'Inter';font-style:normal;font-weight:600;font-display:swap;src:url('/assets/fonts/Inter-600.woff2') format('woff2');}
@font-face{font-family:'JetBrains Mono';font-style:normal;font-weight:400;font-display:swap;src:url('/assets/fonts/JetBrainsMono-400.woff2') format('woff2');}
/*

   Ported into the Headlands marketing site at /scorecard/ (site/scorecard/).
   Canonical source: G:\Dev\_hub\headlands-scorecard\public\css\app.css --
   edits belong there first, then re-port. Only addition made here: the
   `.kicker a` rule for the landing view's "Headlands & Co" link back to the
   main site (source app has no such link, since it has no parent site). */

:root {
  /* The report is paper by locked design language (DESIGN-SPEC). Pinning the
     scheme stops browser auto-dark/dark-mode UA styling from darkening form
     controls (reveal feedback 2026-07-11: gate input rendered dark on a
     dark-mode machine). */
  color-scheme: light;
  --color-bg: #F2F0EB;
  --color-surface: #FBFAF7;
  --color-ink: #1A2438;
  --color-text-dim: #8C8478;
  --color-primary: #1A2438;
  --color-on-primary: #F2F0EB;
  --color-sand: #C2BDB3;
  --color-sand-soft: rgba(194, 189, 179, .45);
  --color-sand-text: #8A8477;
  --color-good: #2E7D32;
  --color-warn: #C7811F;
  --color-bad: #B00020;
  --color-rule: #E3E0D8;
  --color-input-bg: #FFFDF9;

  /* HIGH GROUND brand polish (2026-07-12) -- --color-primary retired from
     umber (#4A3F35) to slate, matching the marketing site's dark-section
     idiom exactly (site/styles.css's --slate); --color-on-primary was
     already limestone, so every element that already reads off these two
     tokens (buttons, .cta-panel, .gate-card once restyled below) picks up
     the site's proven slate/limestone pair for free. See
     artifacts/DECISION-scorecard-brand-polish-2026-07-12.md. */
  --font-display: 'DM Serif Display', Georgia, serif;
  --font-body: Inter, 'Segoe UI', system-ui, sans-serif;
  --font-mono: 'JetBrains Mono', monospace;

  --size-step--1: 0.875rem;
  --size-step-0: 1rem;
  --size-step-1: 1.25rem;
  --size-step-2: 1.75rem;
  --size-step-3: 2.5rem;
  --size-score: 5.5rem;

  --space-1: 0.5rem;
  --space-2: 1rem;
  --space-3: 2rem;
  --space-4: 3.5rem;

  --radius-card: 10px;
  --radius-input: 8px;

  --motion-fast: 150ms;
  --motion-base: 320ms;
  --motion-reveal: 900ms;
  --motion-stagger: 400ms;
  --motion-ease: cubic-bezier(0.2, 0.8, 0.2, 1);
}

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

html, body { margin: 0; padding: 0; }

body {
  background: var(--color-bg);
  color: var(--color-ink);
  font-family: var(--font-body);
  font-size: var(--size-step-0);
  line-height: 1.55;
}

.wrap {
  max-width: 720px;
  margin: 0 auto;
  padding: var(--space-4) var(--space-3);
}

.view[hidden] { display: none; }

.kicker {
  font-family: var(--font-body);
  font-size: var(--size-step--1);
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--color-text-dim);
  margin: 0 0 var(--space-2);
}

/* Landing-view kicker's link back to the main site -- the only interactive
   element ported into an otherwise-static kicker. Keeps the same dim color
   at rest so the kicker line still reads as one quiet unit; underline is
   the only affordance that it's clickable. */
.kicker a {
  color: inherit;
  text-decoration: underline;
  text-decoration-color: var(--color-rule);
  text-underline-offset: 2px;
}
.kicker a:hover,
.kicker a:focus-visible { color: var(--color-primary); text-decoration-color: var(--color-primary); }
.kicker a:focus-visible { outline: 2px solid var(--color-primary); outline-offset: 3px; }

/* ---------- Landing ---------- */

.headline {
  font-family: var(--font-display);
  font-weight: 400;
  font-size: var(--size-step-3);
  line-height: 1.15;
  letter-spacing: -0.01em;
  margin: 0 0 var(--space-2);
  color: var(--color-ink);
}

.sub {
  font-family: var(--font-body);
  font-size: var(--size-step-1);
  color: var(--color-text-dim);
  max-width: 46ch;
  margin: 0 0 var(--space-3);
}

.btn-primary {
  display: inline-block;
  font-family: var(--font-body);
  font-size: var(--size-step-0);
  font-weight: 600;
  color: var(--color-on-primary);
  background: var(--color-primary);
  border: 0;
  border-radius: var(--radius-input);
  padding: var(--space-2) var(--space-3);
  cursor: pointer;
  transition: opacity var(--motion-fast) var(--motion-ease);
}
.btn-primary:hover { opacity: 0.9; }
.btn-primary:focus-visible { outline: 2px solid var(--color-primary); outline-offset: 3px; }
.btn-primary:disabled { opacity: 0.6; cursor: not-allowed; }

/* ---------- Landing: scan form ---------- */

#scan-form {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-1);
  margin: 0 0 var(--space-2);
}
#scan-url {
  flex: 1 1 260px;
  font-family: var(--font-body);
  font-size: var(--size-step-0);
  padding: var(--space-1) var(--space-2);
  border: 1px solid var(--color-rule);
  border-radius: var(--radius-input);
  background: var(--color-input-bg);
  color: var(--color-ink);
}
#scan-url:focus-visible { outline: 2px solid var(--color-primary); outline-offset: 2px; }
#scan-url:disabled { opacity: 0.6; }

/* scan-loading: the signal-rings scan-bloom composition (HIGH GROUND brand
   polish, 2026-07-12) -- the scanned hostname, in mono, centered over the
   shipped /assets/signal-rings.svg. The ring pulse and the exit fade
   (.is-fading, toggled by app.mjs's fadeOutScanBloom) are the one place
   outside the reveal sequence proper where motion is sanctioned: this IS
   the start of the reveal, not decorative wait-time animation. Motion is
   skipped/stilled entirely under reduced motion below. */
.scan-loading {
  display: flex;
  align-items: center;
  justify-content: center;
  position: relative;
  width: 160px;
  height: 160px;
  margin: 0 auto var(--space-2);
  opacity: 1;
  transition: opacity 220ms var(--motion-ease); /* mirrors app.mjs's SCAN_BLOOM_FADE_MS */
}
.scan-loading[hidden] { display: none; }
.scan-loading.is-fading { opacity: 0; }

.scan-bloom-rings {
  display: block;
  width: 100%;
  height: 100%;
  animation: bloom-pulse 2.6s ease-in-out infinite;
}
@keyframes bloom-pulse {
  0%, 100% { opacity: .5; }
  50% { opacity: 1; }
}

.scan-bloom-host {
  position: absolute;
  top: 50%;
  left: 50%;
  transform: translate(-50%, -50%);
  font-family: var(--font-mono);
  font-size: var(--size-step--1);
  color: var(--color-ink);
  text-align: center;
  line-height: 1.3;
  max-width: 128px;
  overflow-wrap: break-word;
  word-break: break-word;
}

.scan-error {
  margin: 0 0 var(--space-2);
}
.scan-error p {
  font-family: var(--font-body);
  font-size: var(--size-step--1);
  color: var(--color-bad);
  margin: 0 0 var(--space-1);
}

.text-link {
  display: inline-block;
  font-family: var(--font-body);
  font-size: var(--size-step--1);
  color: var(--color-text-dim);
  background: none;
  border: 0;
  padding: 0;
  margin: 0;
  cursor: pointer;
  text-decoration: underline;
  text-align: left;
}
.text-link:focus-visible { outline: 2px solid var(--color-primary); outline-offset: 2px; }

.landing-alt { margin: var(--space-2) 0 0; }

/* ---------- Question flow ---------- */

.progress-track {
  height: 4px;
  background: var(--color-rule);
  border-radius: 999px;
  overflow: hidden;
  margin-bottom: var(--space-2);
}
.progress-fill {
  height: 100%;
  width: 0%;
  background: var(--color-primary);
}

.progress-label {
  font-family: var(--font-mono);
  font-size: var(--size-step--1);
  color: var(--color-text-dim);
  margin: 0 0 var(--space-3);
}

.back-link {
  display: inline-block;
  font-family: var(--font-body);
  font-size: var(--size-step--1);
  color: var(--color-text-dim);
  background: none;
  border: 0;
  padding: 0;
  margin: 0 0 var(--space-3);
  cursor: pointer;
  text-decoration: underline;
}
.back-link[hidden] { display: none; }

.q-text {
  font-family: var(--font-display);
  font-weight: 400;
  font-size: var(--size-step-2);
  line-height: 1.3;
  margin: 0 0 var(--space-3);
  color: var(--color-ink);
}

.options {
  display: flex;
  flex-direction: column;
  gap: var(--space-1);
}

.option-row {
  font-family: var(--font-body);
  font-size: var(--size-step-0);
  text-align: left;
  width: 100%;
  padding: var(--space-2);
  background: var(--color-surface);
  border: 1px solid var(--color-rule);
  border-radius: var(--radius-card);
  color: var(--color-ink);
  cursor: pointer;
  transition: border-color var(--motion-fast) var(--motion-ease);
}
.option-row:hover { border-color: var(--color-primary); }
.option-row:focus-visible { outline: 2px solid var(--color-primary); outline-offset: 2px; }

/* ---------- Result / teaser ---------- */

/* score-topo-band: the topo-score centerpiece (HIGH GROUND brand polish,
   2026-07-12). A plain relative-positioning shim around .score-row -- it
   adds no size or margin of its own, so the SVG behind it is clipped to
   exactly .score-row's box and never affects layout. */
.score-topo-band {
  position: relative;
  overflow: hidden;
}
.score-topo {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  z-index: 0;
  pointer-events: none;
}
.score-topo path {
  fill: none;
  stroke: var(--color-sand-soft);
  stroke-width: 1;
}

.score-row {
  position: relative;
  z-index: 1;
  display: flex;
  align-items: baseline;
  gap: var(--space-2);
  margin: var(--space-2) 0 var(--space-1);
}

.result-score {
  font-family: var(--font-mono);
  font-weight: 700;
  font-size: var(--size-score);
  line-height: 1;
  color: var(--color-ink);
  font-variant-numeric: tabular-nums;
}

.score-of {
  font-family: var(--font-mono);
  font-size: var(--size-step-1);
  color: var(--color-text-dim);
}

.result-verdict {
  font-family: var(--font-body);
  font-size: var(--size-step-1);
  max-width: 40ch;
  margin: 0 0 var(--space-1);
}

/* result-elevation: the mono, uppercase flavor line under the verdict --
   flavor only, never primary (app.mjs's report.mjs elevationLine() owns
   the banding logic). */
.result-elevation {
  font-family: var(--font-mono);
  font-size: var(--size-step--1);
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--color-sand-text);
  margin: 0 0 var(--space-3);
}

hr.rule {
  border: 0;
  border-top: 1px solid var(--color-rule);
  margin: var(--space-3) 0;
}

.section-label { margin-top: var(--space-3); }

.sr-only {
  position: absolute;
  width: 1px;
  height: 1px;
  padding: 0;
  margin: -1px;
  overflow: hidden;
  clip: rect(0, 0, 0, 0);
  white-space: nowrap;
  border: 0;
}

/* Motion lives only in the reveal sequence, in two cascades: (1) the free
   teaser -- score count-up, then the 4 teaser rows and the gate card land
   in a stagger behind it; (2) the full report -- once the gate unlocks, the
   report sections, benchmark, fastest fixes, and CTA panel land in a fresh
   stagger of their own. app.mjs sets each element's animation-delay inline
   per its position in whichever cascade it belongs to; every element here
   only declares the shared landing animation and its 0-motion override. */
@keyframes cat-land {
  to { opacity: 1; transform: none; }
}

.teaser-cats {
  display: flex;
  flex-direction: column;
  gap: var(--space-1);
}

.teaser-row {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--space-2);
  padding: var(--space-1) var(--space-2);
  background: var(--color-surface);
  border: 1px solid var(--color-rule);
  border-radius: var(--radius-card);
  opacity: 0;
  transform: translateY(16px);
  animation: cat-land var(--motion-base) var(--motion-ease) forwards;
}
.teaser-row.no-motion { opacity: 1; transform: none; animation: none; }

/* cat-icon: the category's brand icon on a teaser row or full-report
   section (HIGH GROUND brand polish, 2026-07-12) -- the shipped
   /assets/icon-*.svg files, used as-is (fixed-color strokes, not
   currentColor). Decorative: name text sits right next to it. */
.cat-icon {
  width: 26px;
  height: 26px;
  flex: none;
  opacity: .65;
}

.teaser-name {
  font-family: var(--font-body);
  font-weight: 600;
  flex: 1 1 auto;
  min-width: 14ch;
}
.teaser-score {
  font-family: var(--font-mono);
  font-variant-numeric: tabular-nums;
  color: var(--color-text-dim);
}
.teaser-desc {
  flex-basis: 100%;
  font-family: var(--font-body);
  font-size: var(--size-step--1);
  color: var(--color-text-dim);
  margin: 0;
}
/* display:flex on the gate form beats the [hidden] UA rule -- without this
   the form stays visible after unlock (F5.5 advisory A2's root cause). */
.gate-card form[hidden] { display: none; }
.teaser-meter-track {
  flex-basis: 100%;
  height: 4px;
  background: var(--color-rule);
  border-radius: 999px;
  overflow: hidden;
}
.teaser-meter-fill { height: 100%; width: 0%; }
.teaser-row.cat-good .teaser-meter-fill { background: var(--color-good); }
.teaser-row.cat-warn .teaser-meter-fill { background: var(--color-warn); }
.teaser-row.cat-bad .teaser-meter-fill { background: var(--color-bad); }
/* Unchecked (scan path, zero auto-checked questions): a neutral fill, never
   a score hue -- DESIGN-SPEC's do-not rule that color-good/warn/bad always
   mean pass/partial/gap, never decoration. */
.teaser-row.cat-unchecked .teaser-meter-fill { background: var(--color-text-dim); }
.teaser-row.cat-unchecked .teaser-score { font-style: italic; }

/* ---- Email gate ---- */

/* gate-card: restyled to the marketing site's intake idiom -- slate
   background, sand eyebrow, limestone heading/body, limestone-on-slate CTA
   (HIGH GROUND brand polish, 2026-07-12; see
   artifacts/DECISION-scorecard-brand-polish-2026-07-12.md). Radius is
   unchanged; a hairline sand-soft border replaces the light-surface rule
   color so the card still reads as a distinct block against the page's
   limestone background. */
.gate-card {
  margin-top: var(--space-3);
  padding: var(--space-3);
  background: var(--color-primary);
  color: var(--color-on-primary);
  border: 1px solid var(--color-sand-soft);
  border-radius: var(--radius-card);
  opacity: 0;
  transform: translateY(16px);
  animation: cat-land var(--motion-base) var(--motion-ease) forwards;
}
.gate-card.no-motion { opacity: 1; transform: none; animation: none; }

.gate-eyebrow {
  font-family: var(--font-mono);
  font-size: var(--size-step--1);
  letter-spacing: 0.2em;
  text-transform: uppercase;
  color: var(--color-sand);
  margin: 0 0 var(--space-1);
}
.gate-card h3 {
  font-family: var(--font-display);
  font-weight: 400;
  font-size: var(--size-step-1);
  margin: 0 0 var(--space-1);
  color: var(--color-on-primary);
}
.gate-card .gate-intro {
  font-family: var(--font-body);
  font-size: var(--size-step--1);
  color: var(--color-on-primary);
  opacity: 0.75;
  margin: 0 0 var(--space-2);
}
.gate-card form {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-1);
}
/* Email input styled like the site's .intake field idiom: a light field on
   slate -- transparent background, limestone text, a sand-soft underline
   that brightens to full sand on focus (site/styles.css's .field input
   pattern, proven on the marketing page's own dark intake section). */
.gate-card input {
  flex: 1 1 220px;
  font-family: var(--font-body);
  font-size: var(--size-step-0);
  padding: var(--space-1) 0;
  border: 0;
  border-bottom: 1px solid var(--color-sand-soft);
  border-radius: 0;
  background: transparent;
  color: var(--color-on-primary);
}
.gate-card input::placeholder { color: rgba(242, 240, 235, 0.5); }
.gate-card input:focus-visible {
  outline: none;
  border-bottom-color: var(--color-sand);
}
.gate-card button:disabled { opacity: 0.6; cursor: not-allowed; }
/* Submit button on the now-dark card: invert to limestone bg / slate text
   (the site's .intake .cta pattern) -- the plain .btn-primary rule alone
   would render slate-on-slate here since --color-primary is slate. */
.gate-card .btn-primary {
  background: var(--color-on-primary);
  color: var(--color-primary);
}
.gate-card .btn-primary:hover { opacity: 1; background: #fff; }

.gate-error {
  font-family: var(--font-body);
  font-size: var(--size-step--1);
  margin: var(--space-1) 0 0;
}
/* Warm-on-slate tone lifted from the site's own proven dark-section error
   color (site/styles.css .field-error-msg, used inside .intake) rather than
   --color-bad, which is too dark to read reliably against slate. */
.gate-card .gate-error { color: #e3a594; }
.gate-note {
  font-family: var(--font-body);
  font-size: var(--size-step--1);
  color: var(--color-text-dim);
  margin: var(--space-1) 0 0;
}
.gate-card .gate-note { color: var(--color-sand); }

/* ---- Full report (revealed on unlock) ---- */

.full-report[hidden] { display: none; }

.report-sections {
  display: flex;
  flex-direction: column;
  gap: var(--space-1);
}

.report-section {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--space-2);
  padding: var(--space-2);
  background: var(--color-surface);
  border: 1px solid var(--color-rule);
  border-radius: var(--radius-card);
  opacity: 0;
  transform: translateY(16px);
  animation: cat-land var(--motion-base) var(--motion-ease) forwards;
}
.report-section.no-motion { opacity: 1; transform: none; animation: none; }

.cat-name {
  font-family: var(--font-body);
  font-weight: 600;
  flex: 1 1 auto;
  min-width: 14ch;
}
.cat-score {
  font-family: var(--font-mono);
  font-variant-numeric: tabular-nums;
  color: var(--color-text-dim);
}
.cat-status {
  font-family: var(--font-body);
  font-size: var(--size-step--1);
  text-transform: uppercase;
  letter-spacing: 0.08em;
  padding: 2px 8px;
  border-radius: 999px;
}
.report-section.cat-good .cat-status { color: var(--color-good); background: rgba(46, 125, 50, 0.12); }
.report-section.cat-warn .cat-status { color: var(--color-warn); background: rgba(199, 129, 31, 0.12); }
.report-section.cat-bad .cat-status { color: var(--color-bad); background: rgba(176, 0, 32, 0.12); }

.cat-meter-track {
  flex-basis: 100%;
  height: 6px;
  background: var(--color-rule);
  border-radius: 999px;
  overflow: hidden;
}
.cat-meter-fill { height: 100%; width: 0%; }
.report-section.cat-good .cat-meter-fill { background: var(--color-good); }
.report-section.cat-warn .cat-meter-fill { background: var(--color-warn); }
.report-section.cat-bad .cat-meter-fill { background: var(--color-bad); }

.cat-narrative {
  flex-basis: 100%;
  font-family: var(--font-body);
  font-size: var(--size-step--1);
  color: var(--color-ink);
  margin: 0;
}
.cat-recommendation {
  flex-basis: 100%;
  font-family: var(--font-body);
  font-size: var(--size-step--1);
  font-weight: 600;
  color: var(--color-text-dim);
  margin: 0;
}

.benchmark-text {
  font-family: var(--font-body);
  font-size: var(--size-step-0);
  color: var(--color-ink);
  max-width: 60ch;
  margin: 0;
  opacity: 0;
  transform: translateY(16px);
  animation: cat-land var(--motion-base) var(--motion-ease) forwards;
}
.benchmark-text.no-motion { opacity: 1; transform: none; animation: none; }

.fastest-fixes {
  margin: 0;
  padding-left: 1.4em;
  display: flex;
  flex-direction: column;
  gap: var(--space-1);
  opacity: 0;
  transform: translateY(16px);
  animation: cat-land var(--motion-base) var(--motion-ease) forwards;
}
.fastest-fixes.no-motion { opacity: 1; transform: none; animation: none; }
.fastest-fixes li {
  font-family: var(--font-body);
  font-size: var(--size-step-0);
  color: var(--color-ink);
}

.cta-panel {
  margin-top: var(--space-3);
  padding: var(--space-3);
  background: var(--color-primary);
  color: var(--color-on-primary);
  border-radius: var(--radius-card);
  opacity: 0;
  transform: translateY(16px);
  animation: cat-land var(--motion-base) var(--motion-ease) forwards;
}
.cta-panel.no-motion { opacity: 1; transform: none; animation: none; }
.cta-panel p {
  font-family: var(--font-body);
  margin: 0 0 var(--space-1);
  font-size: var(--size-step--1);
  opacity: 0.9;
}
.cta-panel p:last-child { margin-bottom: 0; }
/* Not-yet-checked note (scan path only): folded into the CTA panel per
   DESIGN-SPEC, set off from the CTA copy below it with a hairline rule in
   the panel's own on-primary ink. */
.not-yet-checked-text {
  padding-bottom: var(--space-1);
  margin-bottom: var(--space-1) !important;
  border-bottom: 1px solid rgba(242, 240, 235, 0.25);
}
.not-yet-checked-text[hidden] { display: none; }
.cta-panel a {
  font-family: var(--font-body);
  color: var(--color-on-primary);
  font-weight: 600;
  text-decoration: none;
  border-bottom: 1px solid var(--color-sand);
  padding-bottom: 1px;
}
.cta-panel a:hover { border-bottom-color: var(--color-on-primary); }

@media (prefers-reduced-motion: reduce) {
  .teaser-row,
  .gate-card,
  .report-section,
  .benchmark-text,
  .fastest-fixes,
  .cta-panel {
    animation: none;
    opacity: 1;
    transform: none;
  }
  /* Signal-rings scan bloom: rings hold still, no pulse; app.mjs's
     fadeOutScanBloom() also skips the exit fade entirely under reduced
     motion, so .is-fading never gets added, but this is the belt-and-
     suspenders CSS-only guarantee. */
  .scan-loading { transition: none; }
  .scan-bloom-rings { animation: none; opacity: .85; }
}
