@import url('https://fonts.googleapis.com/css2?family=Noto+Sans:wght@400;500;600;700;800&display=swap');

/* Hyperspeed AI Readiness Scanner — brand styles.
 *
 * Tokens and component rules come from docs/design.md. The layout language — full-bleed
 * alternating bands, the lavender highlight behind heading words, pill eyebrow badges, flat
 * hairline cards, and the dark footer — is extracted from hyperspeed.me so the tool reads as
 * part of the same product.
 *
 * Two rules worth not breaking:
 *   1. The highlights (--hl/--hl2) are FILLS, never text color and never buttons (design.md).
 *   2. Score color is never the only signal — always paired with the numeral and letter grade.
 */

:root {
  color-scheme: light;
  --ink:#212529; --mut:#6C757D; --faint:#ADB5BD;
  --bg:#ffffff; --surface:#F1F3F5; --band:#F5F5F8; --line:#E4E6EA;
  --hl:#dfe7ff; --hl2:#d0c7f5;
  --ink-dark:#17181A; --on-dark:#FFFFFF; --on-dark-mut:#A7ABB2;
  --btn:#212529; --btn-press:#343A40;
  --ok:#198754; --warn:#E8910C; --bad:#DC3545;
  /* design.md specifies two elevations. Cards get the hairline; --sh2 is reserved for the
     email gate, which is the one surface meant to lift off the page. */
  --sh1:0 1px 2px rgba(33,37,41,.06);
  --sh2:0 14px 40px rgba(33,37,41,.12);
  --f:'Noto Sans', system-ui, -apple-system, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, 'Liberation Sans', sans-serif;
}

* { box-sizing:border-box; }

body {
  margin:0; min-height:100vh; color:var(--ink); background:var(--bg);
  font-family:var(--f); font-size:16px; line-height:1.6; -webkit-font-smoothing:antialiased;
}

/* ----------------------------- layout ----------------------------- */

/* Full-bleed sections that alternate white and near-white grey. The landing page has no
   page-wide gradient — background contrast alone carries the rhythm. */
.band { padding:clamp(34px,6vw,80px) 0; }
.band--gray { background:var(--band); }
.band--tight { padding-top:clamp(22px,3vw,40px); }
.shell { width:min(1120px, calc(100% - 40px)); margin:0 auto; }

.hidden { display:none; }

/* ----------------------------- type ----------------------------- */

h1,h2,h3,h4,p { margin-top:0; }
h1 { max-width:24ch; margin-bottom:16px; font-size:clamp(2rem,4.4vw,3.1rem); font-weight:800; line-height:1.08; letter-spacing:-.022em; }
h2 { margin-bottom:10px; font-size:clamp(1.35rem,2.6vw,1.95rem); font-weight:800; line-height:1.15; letter-spacing:-.015em; }
h3 { margin-bottom:0; font-size:1.02rem; font-weight:700; letter-spacing:-.005em; }
.lede { max-width:54ch; margin-bottom:0; color:var(--mut); font-size:1.05rem; line-height:1.6; }

/* The signature move: a lavender fill behind two or three words INSIDE a heading.
   Held on one line — a highlight split across a line break reads as a rendering fault rather
   than emphasis. Released below 640px, where the phrase would otherwise overflow. */
mark.hl {
  background:var(--hl2); color:inherit; padding:.02em .14em; border-radius:3px;
  white-space:nowrap;
  -webkit-box-decoration-break:clone; box-decoration-break:clone;
}
@media (max-width:640px) { mark.hl { white-space:normal; } }

/* Pill eyebrow badge — replaces the old uppercase micro-text .eyebrow. */
.pill-badge {
  display:inline-flex; align-items:center; gap:7px; min-height:30px; margin:0 0 18px;
  padding:0 13px; border:1px solid var(--line); border-radius:999px; background:#fff;
  color:var(--ink); font-size:.78rem; font-weight:700; line-height:1; white-space:nowrap;
}
.pill-badge::before { content:""; width:6px; height:6px; border-radius:999px; background:var(--hl2); }

/* ----------------------------- hero ----------------------------- */

/* Centred: the landing page balances a left-aligned hero against a product image on the
   right, and this tool has no such image — left-aligning alone just leaves dead space. */
.hero { text-align:center; }
.hero h1, .hero .lede, .hero .scan-form { margin-left:auto; margin-right:auto; }
.hero .scan-form { width:min(560px,100%); margin-top:28px; text-align:left; }
.hero .trust { justify-content:center; }

/* Hairline-divided strip echoing the landing page's under-hero feature row. */
.feature-strip { display:grid; grid-template-columns:repeat(4,1fr); gap:1px; margin-top:clamp(28px,4vw,46px); border:1px solid var(--line); border-radius:14px; background:var(--line); overflow:hidden; text-align:left; }
.feat { padding:17px 20px; background:#fff; }
.feat b { display:block; margin-bottom:3px; font-size:.9rem; font-weight:700; }
.feat span { display:block; color:var(--mut); font-size:.82rem; line-height:1.5; }

/* ----------------------------- nav ----------------------------- */

.nav { position:sticky; top:0; z-index:30; border-bottom:1px solid var(--line); background:rgba(255,255,255,.86); backdrop-filter:saturate(1.4) blur(8px); }
.nav-inner { display:flex; align-items:center; justify-content:space-between; gap:16px; min-height:66px; }
.nav-links { display:flex; align-items:center; gap:24px; }
.nav-links a { color:var(--ink); font-size:.9rem; font-weight:600; text-decoration:none; }
.nav-links a:hover { color:var(--mut); }
/* `.nav-links a` outranks `.primary-button`, which left the CTA as ink-on-ink — invisible. */
.nav-links a.link-button, .nav-links a.link-button:hover { color:#fff; }

/* The real wordmark, self-hosted from hyperspeed.me. This used to be an inline base64 PNG
   carried since the initial commit, which read "hypersped" — a low-res recreation missing
   the second e.
   The endorsed lockup ("by rvere" on a second line, 1000x295) is used everywhere: the
   endorsement is part of the brand, so the nav keeps it rather than dropping to the shorter
   unendorsed mark. That makes the lockup the taller ratio (3.39), so the nav is sized by
   width and lets aspect-ratio do the rest — 140px lands at 41px, well clear of the 66px bar,
   where the original 196px filled 88% of it. */
.mark { flex:0 0 auto; color:transparent; font-size:0; overflow:hidden; background-position:left center; background-size:contain; background-repeat:no-repeat; }

.nav .mark {
  width:140px; aspect-ratio:1000 / 295;
  background-image:url('/assets/hyperspeed-logo-endorsed-dark.webp');
}

/* ----------------------------- controls ----------------------------- */

label { display:block; margin-bottom:8px; color:var(--mut); font-size:.86rem; font-weight:700; }

input[type="url"], input[type="email"], input[type="text"] {
  width:100%; min-height:54px; border:1px solid var(--line); border-radius:12px; padding:0 16px;
  color:var(--ink); background:#fff; font:inherit; font-weight:600;
}
input::placeholder { color:var(--faint); font-weight:500; }
input:focus { border-color:var(--ink); box-shadow:0 0 0 4px var(--hl); outline:none; }

button, .link-button {
  min-height:54px; border:0; border-radius:12px; padding:0 22px; font:inherit; font-size:.98rem;
  font-weight:700; cursor:pointer; text-decoration:none; white-space:nowrap;
}
.primary-button {
  display:inline-flex; align-items:center; justify-content:center; gap:8px;
  background:var(--btn); color:#fff; box-shadow:var(--sh1); transition:background 140ms ease;
}
.primary-button:hover { background:var(--btn-press); }
.secondary-button { display:inline-flex; align-items:center; justify-content:center; gap:8px; border:1px solid var(--line); background:#fff; color:var(--ink); }
.secondary-button:hover { background:var(--surface); }
button:disabled { cursor:wait; opacity:.55; }

.nav .primary-button, .nav .link-button { min-height:42px; padding:0 16px; border-radius:10px; font-size:.88rem; }

.input-row, .lead-grid { display:grid; grid-template-columns:1fr auto; gap:10px; }

/* Trust microcopy sits next to the CTA, as on the landing page. */
.trust { display:flex; flex-wrap:wrap; align-items:center; gap:8px 16px; margin-top:16px; color:var(--mut); font-size:.85rem; font-weight:600; }
.trust strong { color:var(--ink); font-weight:800; }
.trust .sep { color:var(--faint); }

/* ----------------------------- scanning state ----------------------------- */

.status { display:flex; align-items:center; gap:10px; min-height:26px; margin-top:20px; color:var(--mut); font-size:.92rem; font-weight:600; }
.status:empty { margin-top:0; }
/* A neutral spinning ring — design.md forbids any bolt/flash motif. */
.spinner { flex:0 0 auto; width:16px; height:16px; border:2px solid var(--line); border-top-color:var(--ink); border-radius:999px; animation:hs-spin .8s linear infinite; }
@keyframes hs-spin { to { transform:rotate(360deg); } }

/* ----------------------------- score gauge ----------------------------- */

.score-head { display:flex; align-items:center; gap:clamp(20px,4vw,44px); flex-wrap:wrap; }
.gauge { position:relative; flex:0 0 auto; width:clamp(140px,17vw,180px); height:clamp(140px,17vw,180px); }
.gauge svg { display:block; width:100%; height:100%; transform:rotate(-90deg); }
.gauge-track { fill:none; stroke:var(--surface); stroke-width:11; }
.gauge-fill { fill:none; stroke-width:11; stroke-linecap:round; transition:stroke-dashoffset 900ms cubic-bezier(.22,1,.36,1); }
.gauge-face { position:absolute; inset:0; display:grid; place-content:center; text-align:center; gap:2px; }
.gauge-score { font-size:clamp(2.3rem,4.4vw,3rem); font-weight:800; line-height:1; font-variant-numeric:tabular-nums; }
.gauge-cap { color:var(--faint); font-size:.66rem; font-weight:800; letter-spacing:.09em; text-transform:uppercase; }
.score-copy { flex:1 1 340px; min-width:260px; }
.score-copy h2 { margin-bottom:8px; }
.score-copy .headline { max-width:52ch; margin-bottom:0; color:var(--mut); font-size:1.02rem; }
.score-meta { display:flex; flex-wrap:wrap; align-items:center; gap:10px; margin-bottom:12px; }
.score-domain { font-size:.9rem; font-weight:700; color:var(--mut); }

/* ----------------------------- category bars ----------------------------- */

/* Hairline-divided rows rather than a grid of shadowed tiles. The 1px gap over a --line
   background is what draws the dividers. */
.cat-list { display:grid; gap:1px; margin:0; border:1px solid var(--line); border-radius:14px; background:var(--line); overflow:hidden; }
.cat-row { display:grid; grid-template-columns:minmax(150px,1.2fr) minmax(80px,2fr) auto auto; align-items:center; gap:16px; padding:15px 18px; background:#fff; }
.cat-name { font-size:.94rem; font-weight:700; }
.cat-track { height:8px; border-radius:999px; background:var(--surface); overflow:hidden; }
.cat-fill { display:block; height:100%; width:0; border-radius:inherit; transition:width 800ms cubic-bezier(.22,1,.36,1); }
.cat-score { min-width:4ch; color:var(--ink); font-size:.94rem; font-weight:800; text-align:right; font-variant-numeric:tabular-nums; }
.cat-score.na { color:var(--faint); font-size:.8rem; font-weight:700; min-width:9ch; }

/* Grade pill — score color at low alpha, always beside the numeral, never the only signal. */
.grade-pill { display:inline-grid; place-items:center; min-width:30px; height:26px; padding:0 9px; border-radius:999px; font-size:.78rem; font-weight:800; }

.tone-pass { color:#0F6B43; background:rgba(25,135,84,.13); }
.tone-warn { color:#8A5200; background:rgba(232,145,12,.18); }
.tone-bad  { color:#A21B28; background:rgba(220,53,69,.13); }
.tone-na   { color:var(--mut); background:rgba(108,117,125,.12); }
.fill-pass { background:var(--ok); } .fill-warn { background:var(--warn); } .fill-bad { background:var(--bad); } .fill-na { background:var(--faint); }
.stroke-pass { stroke:var(--ok); } .stroke-warn { stroke:var(--warn); } .stroke-bad { stroke:var(--bad); } .stroke-na { stroke:var(--faint); }

/* ----------------------------- cards & sections ----------------------------- */

.section-head { display:flex; flex-wrap:wrap; align-items:center; justify-content:space-between; gap:12px; margin:0 0 14px; }
.section-head h2, .section-head h3 { margin-bottom:0; }
.stack { display:grid; gap:clamp(20px,3vw,32px); }

.pill { display:inline-flex; align-items:center; min-height:28px; border:1px solid var(--line); border-radius:999px; padding:0 11px; color:var(--mut); background:#fff; font-size:.76rem; font-weight:700; white-space:nowrap; }
.pill.green { border-color:rgba(25,135,84,.3); color:#0a6b46; background:rgba(25,135,84,.08); }

/* ----------------------------- findings & checks ----------------------------- */

.finding-list, .check-list { display:grid; gap:10px; }
.finding, .check { border:1px solid var(--line); border-left-width:4px; border-radius:12px; padding:15px 16px; background:#fff; }
.finding.critical { border-left-color:var(--bad); }
.finding.high { border-left-color:var(--warn); }
.finding.medium { border-left-color:var(--hl2); }
.finding.low { border-left-color:var(--ok); }
.finding-title, .check-title { display:flex; flex-wrap:wrap; align-items:center; justify-content:space-between; gap:8px; margin-bottom:6px; font-size:.98rem; font-weight:800; }
.finding p, .check p { margin-bottom:0; color:var(--mut); font-size:.93rem; line-height:1.55; }
.owner { color:var(--ink); font-size:.74rem; font-weight:800; background:var(--hl); border-radius:999px; padding:3px 10px; }

.check { border-left-color:var(--hl2); }
.check.pass { border-left-color:var(--ok); }
.check.attention { border-left-color:var(--warn); }
/* Not assessed is score-neutral — read it as grey, not as a problem. */
.check.na { border-left-color:var(--faint); opacity:.72; }
.compact .finding p { font-size:.9rem; }

/* Single column: the full report is a long read, and two columns meant scanning down
   one side and back up the other. */
.detail-grid { display:grid; grid-template-columns:1fr; gap:clamp(24px,3.5vw,36px); }
.report-actions { display:flex; flex-wrap:wrap; align-items:flex-start; justify-content:space-between; gap:16px; margin-bottom:26px; }
.action-row { display:flex; flex-wrap:wrap; align-items:center; gap:10px; }

/* ----------------------------- email gate ----------------------------- */

/* The one surface that lifts off the page — design.md reserves --sh2 for it. */
.lead-panel { margin-top:clamp(20px,3vw,30px); border:1px solid var(--line); border-radius:18px; padding:clamp(20px,3.4vw,30px); background:#fff; box-shadow:var(--sh2); }
.lead-panel h2 { margin-bottom:6px; }
.lead-panel .lede { margin-bottom:18px; font-size:.98rem; }
.consent { display:flex; align-items:flex-start; gap:10px; margin-top:14px; color:var(--mut); font-size:.87rem; line-height:1.5; }
.consent input { margin-top:4px; flex:0 0 auto; width:16px; height:16px; accent-color:var(--ink); }
.consent a { color:var(--ink); font-weight:700; }
/* Honeypot. Positioned off-canvas rather than display:none or visibility:hidden — the
   simplest scrapers skip anything those hide, and this needs to look reachable to them.
   !important so a future layout rule cannot accidentally reveal it to a real merchant. */
.hp-field {
  position:absolute !important; left:-9999px !important; top:auto !important;
  width:1px !important; height:1px !important; overflow:hidden !important;
}

/* The widget is invisible, so its container must not reserve space. Kept as a hook: if the
   widget mode ever goes back to managed, restore `margin-top:14px` here so the visible
   challenge is not flush against the field above it. */
.turnstile { margin:0; }

/* ----------------------------- dark CTA + footer ----------------------------- */

.site-footer { padding:clamp(40px,6vw,64px) 0 26px; background:var(--ink-dark); color:var(--on-dark-mut); }
.foot-grid { display:grid; grid-template-columns:1.4fr repeat(4,1fr); gap:clamp(20px,2.6vw,32px); }
.foot-brand p { margin:14px 0 16px; max-width:34ch; font-size:.88rem; line-height:1.6; }
.foot-brand a { display:block; padding:5px 0; color:var(--on-dark-mut); font-size:.88rem; text-decoration:none; }
.foot-brand a:hover { color:#fff; }
.foot-col h4 { margin:0 0 12px; color:#fff; font-size:.82rem; font-weight:800; letter-spacing:.02em; }
.foot-col a { display:block; padding:5px 0; color:var(--on-dark-mut); font-size:.88rem; text-decoration:none; }
.foot-col a:hover { color:#fff; }
.foot-base { display:flex; flex-wrap:wrap; align-items:center; justify-content:space-between; gap:10px; margin-top:clamp(30px,4vw,44px); padding-top:22px; border-top:1px solid rgba(255,255,255,.13); font-size:.82rem; }
/* Light variant rather than filter:invert() on the dark logo — inverting turned the ink
   wordmark a washed grey and mangled the rocket's interior strokes. */
.site-footer .mark {
  width:150px; aspect-ratio:1000 / 295;
  background-image:url('/assets/hyperspeed-logo-endorsed-light.webp');
}


/* ----------------------------- view states ----------------------------- */

/* One attribute on <body> drives the whole scan → report transition, so the handlers in
   app.js only set state and CSS owns the motion. */

.view-input, #results { transition: opacity 260ms ease, transform 260ms ease; }

/* input: form and strip visible, results hidden */
body[data-view="input"] #results,
body[data-view="input"] #full-report { display:none; }

/* scanning: the form and the check strip recede so the progress line is the only focus */
body[data-view="scanning"] .scan-form,
body[data-view="scanning"] .feature-strip,
body[data-view="scanning"] .pill-badge,
body[data-view="scanning"] .lede {
  opacity:0; transform:translateY(-6px); pointer-events:none;
  transition: opacity 180ms ease, transform 180ms ease;
}
body[data-view="scanning"] #results,
body[data-view="scanning"] #full-report { display:none; }
body[data-view="scanning"] .status { min-height:64px; font-size:1rem; }

/* report: the hero is gone, results are the page */
body[data-view="report"] .view-input { display:none; }
body[data-view="report"] #results { opacity:1; }

/* Fades results in on arrival rather than snapping. */
#results.entering { opacity:0; transform:translateY(8px); }

.report-head { display:flex; flex-wrap:wrap; align-items:center; justify-content:space-between; gap:12px; }
.report-head .pill-badge { margin-bottom:0; }
#rescan { min-height:42px; padding:0 16px; font-size:.88rem; }

/* ----------------------------- gated preview ----------------------------- */

/* Blurred placeholders standing in for the findings behind the email gate. Deliberately
   skeletons, not real content under a blur filter: nothing gated reaches the DOM, so the
   gate survives someone opening devtools. See ISSUES.md §2.
   The shapes mirror .finding — severity rail, title row with an owner pill, detail lines —
   so the blur reads as a list of real findings rather than as grey slabs. */
.gated-preview { position:relative; }
.skeleton-list {
  display:grid; gap:10px; user-select:none; pointer-events:none;
  filter:blur(4px); opacity:.8;
  /* Fades downward so the stack reads as "more below", not as broken rendering. */
  -webkit-mask-image:linear-gradient(to bottom, #000 0%, #000 45%, transparent 96%);
  mask-image:linear-gradient(to bottom, #000 0%, #000 45%, transparent 96%);
}
.skeleton-row { border:1px solid var(--line); border-left-width:4px; border-radius:12px; padding:15px 16px; background:#fff; }
/* Muted severity rails, cycled, so the stack looks like a real mixed-severity list. */
.skeleton-row:nth-child(3n+1) { border-left-color:rgba(232,145,12,.55); }
.skeleton-row:nth-child(3n+2) { border-left-color:rgba(208,199,245,.9); }
.skeleton-row:nth-child(3n+3) { border-left-color:rgba(25,135,84,.45); }

.skeleton-head { display:flex; align-items:center; justify-content:space-between; gap:12px; margin-bottom:9px; }
.skeleton-title { height:12px; border-radius:999px; background:#DDE0E5; }   /* title weight */
.skeleton-pill  { flex:0 0 auto; width:104px; height:20px; border-radius:999px; background:var(--hl); }
.skeleton-line  { height:9px; border-radius:999px; background:#E7E9ED; }
.skeleton-line + .skeleton-line { margin-top:7px; }

/* The gate sits high over the stack and stays narrow, so the blurred rows read around it
   rather than being pushed off-screen beneath it. Negative margin rather than absolute
   positioning: the container then grows if the panel is taller than the overlap, so a tall
   form on a narrow viewport can never overflow the stack. */
.gated-preview .lead-panel {
  position:relative;
  width:min(470px, 100%);
  /* Sits just below the first blurred row. The stack is ~614px, so this puts the panel top
     around 90px into it — the gate is the thing you land on, with the blurred rows reading
     around and behind it rather than above it. */
  margin:-520px auto 0;
}

@media (max-width:640px) {
  .gated-preview .lead-panel { width:100%; margin-top:-500px; }
  .skeleton-pill { width:76px; }
}
}

/* ----------------------------- responsive ----------------------------- */

@media (max-width:900px) {
  .foot-grid { grid-template-columns:1fr 1fr; }
  .foot-brand { grid-column:1 / -1; }
  .feature-strip { grid-template-columns:1fr 1fr; }
}

@media (max-width:640px) {
  .shell { width:min(100% - 28px, 1120px); }
  .input-row, .lead-grid { grid-template-columns:1fr; }
  .action-row { width:100%; flex-direction:column; align-items:stretch; }
  .action-row button, .action-row .link-button { width:100%; }
  .nav-links { gap:14px; }
  .nav-links a:not(.link-button) { display:none; }
  /* The full-size wordmark plus the CTA overflows a 375px viewport. */
  .nav .mark { width:116px; }
  .nav .primary-button { min-height:38px; padding:0 12px; font-size:.82rem; }
  .score-head { gap:22px; }
  .feature-strip { grid-template-columns:1fr; }
  /* Two rows: name over bar, with score and grade held on the right. */
  .cat-row { grid-template-columns:1fr auto auto; gap:10px 12px; }
  .cat-name { grid-column:1 / -1; }
  .foot-grid { grid-template-columns:1fr; }
  .foot-base { flex-direction:column; align-items:flex-start; }
}

/* Honour a reduced-motion preference: hold the gauge and bars at their final value and stop
   the spinner rather than animating into place. */
@media (prefers-reduced-motion: reduce) {
  .gauge-fill, .cat-fill { transition:none; }
  .spinner { animation:none; border-top-color:var(--line); }
  /* View changes become instant swaps; nothing is hidden that was not hidden before. */
  .view-input, #results,
  body[data-view="scanning"] .scan-form,
  body[data-view="scanning"] .feature-strip,
  body[data-view="scanning"] .pill-badge,
  body[data-view="scanning"] .lede { transition:none; }
  #results.entering { opacity:1; transform:none; }
}
