/* =========================================================================
   CortexCommerce — /scan (public AI Visibility Scanner) styles (#567, Plan 3
   Task 2). Loaded after styles.css — reuses every existing design token and
   component class it can (.hero, .hero-grid, .wrap, .eyebrow, .chip, .btn,
   .btn-grad, .btn-ghost, .reveal, section.block, .pulse, the modal,
   .form-note) and adds only what's genuinely new: the terminal, the score
   ring, category bars, the page table, the "How it works" step-card
   treatment (.steps/.step/.n — this page never loads product.css, the
   stylesheet that styles that markup elsewhere on the site, so it needs its
   own copy here), and the animations that drive them. Every new selector is
   prefixed `scan-`/`.sr-only` so nothing here can collide with the shared
   stylesheet.

   Per CLAUDE.md's "no new colors" rule, every color below is either an
   existing design token (var(--...)) or one of the exact hex values already
   present in styles.css (#1a7f4b/#e7f7ee success, #c0392b error, #f5a623
   star/amber, #c3c9e8 dark-surface body text, #ff5f57/#febc2e/#28c840
   traffic-light dots) — `color-mix()` is used only to LIGHTEN those existing
   values for contrast on a dark surface (the same technique styles.css
   already uses for --accent-soft etc.), never to introduce a new hue.

   Animation: every @keyframes / animation rule lives inside
   `@media (prefers-reduced-motion: no-preference)` so the unprefixed,
   no-animation state (no ring sweep, no count-up, no stagger, no shine) is
   always the default — reduced-motion users see the final layout instantly,
   matching styles.css's own `@media (prefers-reduced-motion: reduce)` block.
   ========================================================================= */

:root {
  /* Semantic status tones for light surfaces (grade label, category rows,
     table chips, fix cards) — derived from the exact success/error hex
     already used elsewhere in styles.css (.status.live/.schema-valid use
     #1a7f4b+#e7f7ee; .form-note.err uses #c0392b) plus the existing amber
     from .sc-stars (#f5a623). Backgrounds are computed tints of the same
     value, not new colors. */
  --scan-good: #1a7f4b;
  --scan-good-bg: #e7f7ee;
  --scan-warn: #f5a623;
  --scan-warn-bg: color-mix(in oklab, #f5a623 14%, white);
  /* The star-amber above is a chip/terminal accent — as small bold TEXT on a
     white surface it lands around 2:1 contrast, a WCAG AA failure. This is
     the same amber DARKENED with the file's existing color-mix technique
     (~#8c5d0f, ≈5.7:1 on white) for anywhere warn-toned text sits directly
     on a light background — no new hue, per the header's "no new colors"
     rule. */
  --scan-warn-ink: color-mix(in oklab, #f5a623 66%, black);
  --scan-bad: #c0392b;
  --scan-bad-bg: color-mix(in oklab, #c0392b 10%, white);
}

/* #567 layout-fix: the hero/progress/results swap (below) collapses one of
   these three sections to zero height while another expands from zero, in
   the same tick as scan.js's own scrollIntoView call. Live testing found
   Chrome's CSS Scroll Anchoring reacting to that swap at the VIEWPORT
   (document) scroller level — `overflow-anchor: none` on the individual
   sections alone did not suppress it, only disabling it for the actual
   scroll container (the document itself, scoped to this page only — this
   rule lives in scan.css, never loaded by any other page) reliably does. See
   the matching, more narrowly-scoped comments on .scan-hero/
   .scan-progress-hero/.scan-results-section below for why those exist too
   (defense in depth once anchoring's ancestor-selection is disabled here). */
body {
  overflow-anchor: none;
}

/* =========================================================================
   Accessibility utility
   ========================================================================= */
.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;
}

/* =========================================================================
   Hero — URL form + demo terminal (#scan-hero)
   ========================================================================= */
.scan-hero {
  padding-bottom: 86px;
  /* #567 layout-fix: this stage collapses to zero height the instant a scan
     starts (same swap as .scan-progress-hero/.scan-results-section below) —
     opt out of scroll anchoring for the same reason. */
  overflow-anchor: none;
}

.scan-form {
  margin-top: 26px;
  max-width: 460px;
}
.scan-input-row {
  display: flex;
  gap: 10px;
}
.scan-input {
  flex: 1;
  min-width: 0;
  border: 1px solid var(--line);
  border-radius: 11px;
  padding: 14px 16px;
  font-size: 16px; /* 16px avoids iOS auto-zoom on focus */
  font-family: inherit;
  color: var(--ink);
  background: var(--card);
  box-shadow: var(--shadow-sm);
}
.scan-input:focus {
  outline: none;
  border-color: var(--indigo-1);
  box-shadow: 0 0 0 3px color-mix(in oklab, var(--indigo-1) 15%, transparent);
}
.scan-input::placeholder {
  color: var(--muted);
}
.scan-input-row .btn {
  flex: 0 0 auto;
}
.scan-input-row .btn[disabled] {
  opacity: 0.65;
  cursor: default;
  transform: none;
}

.scan-error {
  margin: 12px 0 0;
  padding: 11px 14px;
  border-radius: 10px;
  background: var(--scan-bad-bg);
  color: var(--scan-bad);
  font-size: 14px;
  font-weight: 600;
  display: flex;
  align-items: flex-start;
  justify-content: space-between;
  gap: 12px;
}
.scan-error[hidden] {
  display: none;
}
.scan-error .scan-error-dismiss {
  background: none;
  border: none;
  color: inherit;
  font-size: 16px;
  line-height: 1;
  cursor: pointer;
  padding: 0;
  flex: 0 0 auto;
}
.scan-error .scan-error-retry {
  background: none;
  border: none;
  color: inherit;
  font-weight: 800;
  text-decoration: underline;
  cursor: pointer;
  padding: 0;
  font: inherit;
}

.scan-trust {
  font-size: 13.5px;
  color: var(--muted);
  margin: 16px 0 0;
}

.scan-demo-col {
  display: flex;
  flex-direction: column;
  gap: 10px;
}
.scan-demo-caption {
  text-align: center;
  font-size: 12px;
  color: var(--muted);
  margin: 0;
}

/* =========================================================================
   Terminal (shared: hero demo, live progress, full report log)
   ========================================================================= */
.scan-terminal {
  position: relative;
  background: var(--ink);
  color: #c3c9e8; /* existing dark-surface body color, styles.css --hero-body dark value */
  border-radius: 16px;
  padding: 16px 18px 18px;
  font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
  font-size: 13px;
  line-height: 1.85;
  box-shadow: var(--shadow-md);
  overflow: hidden;
}
/* NOTE: there is deliberately no standalone `.scan-terminal-lg { ... }` rule
   (#567 follow-up cleanup). The class only ever appears compounded with
   .scan-terminal-live (the report's live terminal in scan.html), and the
   compound rule further down overrode both of the standalone rule's
   properties anyway — the standalone block was dead weight left over from
   the removed collapsed-strip design. */
/* Persistent "live" terminals (#567) — the enlarged progress-hero terminal
   and the report's while-still-crawling panel. In the current single-live-
   terminal design these are the ONLY scan-log surfaces: once the scan
   reaches a terminal status the report's one simply hides (scan.js's
   renderReportLog) — there is no collapsed archive strip. A subtle top fade
   is a purely decorative echo of "there's more above" — scan.js's
   tail-trimming (#567 liveness fix) is what actually guarantees the newest
   line is visible, this no longer depends on/represents real scroll
   position. */
.scan-terminal-live::before {
  content: "";
  position: absolute;
  top: 0;
  left: 0;
  right: 0;
  height: 28px;
  background: linear-gradient(to bottom, var(--ink), transparent);
  pointer-events: none;
}
/* The hero progress terminal — the visual centerpiece of the enlarged
   running-scan stage — and the results screen's own live-crawl terminal
   (#567 liveness fix: scan.js keeps only the tail LIVE_TERMINAL_MAX_LINES
   lines of both in the DOM instead of relying on scroll — scrolling proved
   unreliable there per the product owner's own testing — so both need the
   SAME roomier height to fit that many lines without ever needing to
   scroll). Both: tall enough for ~12-16 lines so the work looks real
   throughout quick checks and sampling, not just once a hop finishes.
   `overflow-y: auto` stays as a belt-and-suspenders fallback (a single very
   long wrapped line could still exceed the fixed height even within the
   line-count budget). */
.scan-terminal-hero,
.scan-terminal-lg.scan-terminal-live {
  height: 380px;
  overflow-y: auto;
}
@media (max-width: 720px) {
  .scan-terminal-hero,
  .scan-terminal-lg.scan-terminal-live {
    height: 260px;
  }
}
.scan-term-bar {
  display: flex;
  align-items: center;
  gap: 6px;
  margin-bottom: 10px;
}
.scan-term-dot {
  width: 10px;
  height: 10px;
  border-radius: 50%;
  display: block;
}
.scan-term-dot:nth-child(1) {
  background: #ff5f57;
}
.scan-term-dot:nth-child(2) {
  background: #febc2e;
}
.scan-term-dot:nth-child(3) {
  background: #28c840;
}
.scan-term-badge {
  margin-left: auto;
  font-size: 10px;
  font-weight: 800;
  letter-spacing: 0.6px;
  color: #c3c9e8;
  background: rgba(255, 255, 255, 0.1);
  border-radius: 999px;
  padding: 3px 9px;
}
.scan-term-body {
  display: flex;
  flex-direction: column;
  gap: 2px;
}
.scan-term-line {
  word-break: break-word;
}
.scan-term-empty {
  color: var(--muted);
}

.scan-tone-dim {
  color: var(--muted);
}
/* #567 bot-verdict fix: a bot we couldn't verify (a security service
   blocked our probe) is neither allowed nor genuinely blocked — it must
   read as neutral/unknown, never the same alarm color as a real block. */
.scan-tone-muted {
  color: var(--muted);
}
.scan-tone-ok {
  color: color-mix(in oklab, #1a7f4b 58%, white);
}
.scan-tone-warn {
  color: #f5a623;
}
.scan-tone-err {
  color: color-mix(in oklab, #c0392b 64%, white);
}
.scan-tone-cyan {
  color: var(--accent);
}

.scan-term-cursor {
  display: inline-block;
  width: 8px;
  height: 1em;
  background: var(--accent);
  vertical-align: -2px;
}

/* =========================================================================
   Progress (#scan-progress)
   ========================================================================= */
.scan-progress-hero {
  /* #567: while a scan runs, this stage is the whole show — occupy real
     screen space (roughly 60-70vh on desktop) instead of a small band, with
     the terminal below as the centerpiece. Shorter on mobile, where the
     enlarged terminal alone already uses proportionally more of the
     viewport. */
  /* #567 layout-fix: this section starts flow at the very top of <main> (the
     idle hero above it is `[hidden]`/zero-height at this point), so at the
     top-most scroll position its own top coincides with the viewport's top —
     exactly where the fixed nav (styles.css --header-height) sits. padding-
     top of at least --header-height guarantees the eyebrow/heading/terminal
     can never render under/behind the nav, regardless of scroll position;
     align-items:center below can only ADD extra clearance on top of this
     floor, never subtract from it. scroll-margin-top additionally makes
     scan.js's `scrollIntoView` (and any future anchor/fragment navigation)
     land exactly below the header instead of flush with the viewport top. */
  padding-top: calc(var(--header-height) + 24px);
  padding-bottom: 70px;
  min-height: 68vh;
  display: flex;
  align-items: center;
  scroll-margin-top: var(--header-height);
  /* #567 layout-fix: this stage (and .scan-results-section below) collapses
     to zero height the instant the scan reaches a results-type state, while
     the other one simultaneously expands from zero — a live-tested browser
     behavior, not a bug in scan.js's own scroll call: Chrome's CSS Scroll
     Anchoring tries to preserve the reader's position across that shift by
     picking an anchor node near the (about to vanish) top of the viewport,
     and can "correct" scrollY to somewhere thousands of pixels into the
     freshly-revealed report instead of its top. Opting out tells the browser
     this element's own size changes are an intentional swap, not a shift to
     compensate for. */
  overflow-anchor: none;
}
/* An author-stylesheet `display` declaration on this class would otherwise
   beat the browser's own `[hidden] { display: none }` UA rule (author styles
   win over user-agent styles regardless of specificity) — without this, the
   flex layout above keeps the whole progress stage visibly rendered even
   after scan.js sets `els.progress.hidden = true` on completion. */
.scan-progress-hero[hidden] {
  display: none;
}
.scan-progress-hero .wrap {
  width: 100%;
}
@media (max-width: 720px) {
  .scan-progress-hero {
    min-height: 0;
  }
}
.scan-progress-card {
  max-width: 820px;
  margin: 0 auto;
  position: relative;
  z-index: 2;
}
.scan-progress-head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 16px;
  flex-wrap: wrap;
  margin-bottom: 16px;
}
.scan-progress-domain {
  font-size: clamp(22px, 3.2vw, 30px);
  font-weight: 800;
  letter-spacing: -0.5px;
  margin: 4px 0 0;
  color: var(--ink);
  word-break: break-word;
}
/* #594: store domain pinned at the top of the report (live crawl + completed). */
.scan-report-domain {
  font-size: clamp(20px, 3vw, 28px);
  font-weight: 800;
  letter-spacing: -0.5px;
  margin: 0 0 14px;
  color: var(--ink);
  word-break: break-word;
}
.scan-live-chip {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  background: var(--indigo-soft);
  color: var(--indigo-2);
  border-radius: 999px;
  padding: 7px 14px;
  font-size: 12.5px;
  font-weight: 700;
  white-space: nowrap;
}
.scan-live-chip .pulse {
  width: 7px;
  height: 7px;
  border-radius: 50%;
  background: var(--indigo-2);
}

.scan-progress-bar {
  height: 8px;
  border-radius: 999px;
  background: var(--bg-soft-2);
  overflow: hidden;
  margin-top: 16px;
}
.scan-progress-bar > i {
  display: block;
  height: 100%;
  width: 4%;
  border-radius: 999px;
  background: var(--grad-bright);
  transition: width 0.5s ease;
}
.scan-progress-bar.is-indeterminate > i {
  width: 32%;
}
.scan-progress-meta {
  font-size: 13px;
  color: var(--muted);
  margin: 8px 0 0;
}
.scan-progress-fact {
  text-align: center;
  font-size: 14px;
  color: var(--body);
  margin: 20px 0 0;
}
.scan-progress-fact strong {
  color: var(--ink);
}

/* =========================================================================
   Results (#scan-results)
   ========================================================================= */
.scan-results-section {
  /* #567 layout-fix: this section shares the same mutually-exclusive-
     visibility slot as .scan-progress-hero above (both start flow right
     after <main>'s zero-height hidden siblings), so it's exposed to the
     exact same "top coincides with the viewport top" case — same fix, same
     reasoning: a padding-top floor of --header-height that scroll-centering
     elsewhere can only add to, plus scroll-margin-top for scrollIntoView. */
  padding-top: calc(var(--header-height) + 56px);
  scroll-margin-top: var(--header-height);
  /* See .scan-progress-hero's matching comment — same collapse/expand swap,
     same scroll-anchoring correction to opt out of. */
  overflow-anchor: none;
}
/* #567 UI polish: styles.css's shared `section.block { padding: 100px 0; }`
   (a type+class selector) outranks a plain class selector like
   `.scan-results-section` on specificity alone, regardless of stylesheet
   load order — so this section's inherited 100px bottom padding was still
   winning here, stacking with #how-it-works's own inherited 100px top
   padding (below) into a ~200px dead zone between the page-by-page table
   and the "How it works" heading. Note that because a box's own
   padding-top never shifts its own getBoundingClientRect().top (only its
   *previous* sibling's trailing padding/height does), the visible-gap
   contribution of #scan-results's OWN bottom padding is the only half of
   that 200px a `#how-it-works.top - .scan-table-section.bottom` probe can
   ever see — hence tuning it alone into the 56–72px target range here. An
   id+class compound selector (higher specificity than the shared rule,
   still scoped to just this page's results section — never touches
   `section.block` spacing anywhere else on the site) is what makes the
   override stick. */
#scan-results.scan-results-section {
  padding-bottom: 64px;
}

/* ---- Honest terminal-state panel (password_protected / blocked_robots /
   unreachable / failed) — shown INSTEAD of a score, never alongside one. */
.scan-honest {
  max-width: 620px;
  margin: 0 auto;
  text-align: center;
  background: var(--card);
  border: 1px solid var(--line);
  border-radius: var(--radius);
  padding: 44px 36px;
  box-shadow: var(--shadow-sm);
}
.scan-honest-icon {
  font-size: 34px;
  margin-bottom: 6px;
}
.scan-honest h2 {
  font-size: 24px;
  margin: 0 0 12px;
  letter-spacing: -0.4px;
}
.scan-honest p {
  font-size: 16px;
  color: var(--body);
  margin: 0 auto;
  max-width: 480px;
}
.scan-honest-actions {
  display: flex;
  justify-content: center;
  gap: 12px;
  margin-top: 26px;
  flex-wrap: wrap;
}

/* ---- Persistent live scan log (#567) — shown above the score while the
   crawl continues past the sample reveal (scanClientLogic.ts's
   resolveReportLogView), with its own "live" indicator up top, and simply
   hidden once the scan reaches a terminal status. It is the report's only
   scan-log surface — nothing replaces it after it hides. ---- */
.scan-report-live-log {
  margin-bottom: 22px;
}
.scan-report-live-log-head {
  display: flex;
  margin-bottom: 8px;
}
/* #567 liveness fix: the results-screen-while-crawling progress indicator —
   same `.scan-progress-bar`/`.is-indeterminate` component as the
   progress-hero's, just sitting between the "live" chip and the terminal
   instead of below it, so it needs its own top/bottom spacing rather than
   the hero's `margin-top: 16px` (which assumes a terminal directly above). */
.scan-report-live-log .scan-progress-bar {
  margin: 0 0 14px;
}

/* ---- Two-column report layout (#567): score/fixes/table in the main
   column, a sticky conversion-CTA rail on the right, instead of the CTAs
   sitting below everything at the page bottom. `.scan-report-head` /
   `.scan-table-section` / `.scan-report-email` no longer carry their own
   margin-top/bottom — the grid's own `gap` supplies that spacing now that
   all three (plus the rail) are siblings in the same grid, so the old
   margins would otherwise stack on top of `gap` and double the space. ---- */
.scan-report-columns {
  display: grid;
  grid-template-columns: minmax(0, 1fr) 320px;
  grid-template-areas:
    "head rail"
    "email rail"
    "table rail";
  align-items: start;
  gap: 40px;
}
@media (max-width: 720px) {
  /* Mobile: single column, NOT sticky, and the rail restacks right after
     the score head and before the page-by-page table (product owner's ask
     — seen early without scrolling, since there's no room for a side rail
     on a narrow viewport). */
  .scan-report-columns {
    /* minmax(0, 1fr), not a bare 1fr: a bare `1fr` track (`minmax(auto,
       1fr)`) sizes to its item's min-content width, which for
       .scan-table-section bubbles up from the table's own `min-width: 480px`
       and blew out the page with a ~1400px-wide horizontal scrollbar on a
       375px viewport (confirmed live) — the classic CSS Grid implicit
       min-width:auto overflow trap. The `0` floor is what lets the table's
       own `.scan-table-wrap { overflow-x: auto }` do its job instead. */
    grid-template-columns: minmax(0, 1fr);
    grid-template-areas:
      "head"
      "rail"
      "email"
      "table";
  }
}

/* Sticky CTA rail — sticks below the fixed header (same --header-height
   pattern used by .scan-progress-hero/.scan-results-section above):
   `align-self: start` on the grid item (plus `align-items: start` on the
   grid container above) keeps the rail only as tall as its own content
   instead of stretching to match the 3-row-tall "rail" area, which is what
   lets it actually stick while the main column scrolls past it.
   `overflow: visible` (#567 UI polish) — an earlier version paired
   `overflow-y: auto` with a `max-height` viewport clamp as an internal-
   scroll safety net, but per the CSS overflow spec setting only `overflow-y`
   forces `overflow-x` to compute to `auto` too. On the rail's actual
   content (cards ~600px tall, well under any real viewport) that meant
   `scrollWidth === clientWidth` with nothing to scroll, and macOS still
   painted a stray overlay scrollbar along the bottom edge — a fake "the
   cards can scroll" affordance with no real overflow behind it. The rail's
   cards are short enough that internal-scroll machinery was never needed
   in practice; on the rare tenant whose rail legitimately outgrows the
   viewport, it now just extends past the fold like any other sticky
   element rather than clipping itself into a scroll box. No background/
   border of its own — every card inside (.scan-fullcatalog/.scan-cta)
   supplies its own chrome, so when only one child is visible (or, pre-
   eligibility, none of the full-catalog card but still exactly one
   platform CTA) the rail never renders as an empty or oversized box, it
   just hugs whatever is shown. */
.scan-report-rail {
  grid-area: rail;
  align-self: start;
  display: flex;
  flex-direction: column;
  gap: 24px;
  position: sticky;
  top: calc(var(--header-height) + 24px);
  overflow: visible;
}
@media (max-width: 720px) {
  .scan-report-rail {
    position: static;
    top: auto;
  }
}

/* ---- Header: ring + meta (left) / bars + fixes (right) ---- */
.scan-report-head {
  grid-area: head;
  display: flex;
  gap: 40px;
  align-items: flex-start;
  flex-wrap: wrap;
}
.scan-ring-col {
  flex: 0 0 auto;
  width: 220px;
  text-align: center;
}
.scan-bars-col {
  flex: 1 1 380px;
  min-width: 280px;
}

.scan-ring-wrap {
  position: relative;
  width: 200px;
  height: 200px;
  margin: 0 auto;
}
.scan-ring {
  transform: rotate(-90deg);
  width: 200px;
  height: 200px;
}
.scan-ring-track {
  stroke: var(--bg-soft-2);
}
.scan-ring-val {
  stroke: url(#scanRingGrad);
  stroke-linecap: round;
}
.scan-ring-num {
  position: absolute;
  inset: 0;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
}
.scan-score-num {
  font-size: 44px;
  font-weight: 800;
  color: var(--ink);
  font-variant-numeric: tabular-nums;
  line-height: 1;
}
.scan-ring-num small {
  font-size: 13px;
  color: var(--muted);
  font-weight: 700;
  margin-top: 2px;
}

.scan-grade-label {
  font-size: 13px;
  font-weight: 800;
  letter-spacing: 0.3px;
  text-transform: uppercase;
  margin-top: 10px;
}
.scan-grade-label.scan-tone-good {
  color: var(--scan-good);
}
.scan-grade-label.scan-tone-warn {
  color: var(--scan-warn);
}
.scan-grade-label.scan-tone-bad {
  color: var(--scan-bad);
}
.scan-live-note {
  font-size: 11.5px;
  color: var(--indigo-2);
  font-weight: 700;
  margin: 6px 0 0;
}
.scan-live-note[hidden] {
  display: none;
}

/* #611 score-over-time trend: delta chip + prior-scores strip. */
.scan-score-trend {
  margin-top: 12px;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 6px;
}
.scan-score-trend[hidden] {
  display: none;
}
.scan-trend-chip {
  display: inline-block;
  padding: 3px 12px;
  border-radius: 999px;
  font-size: 12px;
  font-weight: 800;
  letter-spacing: 0.2px;
}
.scan-trend-chip.scan-trend-up {
  background: var(--scan-good-bg);
  color: var(--scan-good);
}
.scan-trend-chip.scan-trend-down {
  background: var(--scan-bad-bg);
  color: var(--scan-bad);
}
.scan-trend-chip.scan-trend-flat {
  background: color-mix(in oklab, var(--muted) 12%, white);
  color: var(--muted);
}
.scan-trend-history {
  font-size: 11.5px;
  color: var(--muted);
  margin: 0;
}

.scan-report-meta {
  font-size: 12.5px;
  color: var(--muted);
  margin-top: 14px;
  line-height: 1.6;
}
.scan-report-actions {
  display: flex;
  flex-direction: column;
  gap: 8px;
  margin-top: 16px;
}
.scan-ghost-sm {
  padding: 9px 14px !important;
  font-size: 13.5px !important;
}
.scan-inline-note {
  font-size: 12.5px;
  font-weight: 600;
  margin-top: 10px;
}
.scan-inline-note[hidden] {
  display: none;
}
.scan-inline-note.scan-tone-good {
  color: var(--scan-good);
}
.scan-inline-note.scan-tone-bad {
  color: var(--scan-bad);
}

/* ---- Category bars ---- */
/* #590 AI store summary card */
.scan-ai-summary {
  background: var(--card);
  border: 1px solid var(--line);
  border-radius: var(--radius);
  padding: 18px 20px;
  margin-bottom: 20px;
  box-shadow: var(--shadow-sm);
}
.scan-ai-summary h2 {
  font-size: 13px;
  text-transform: uppercase;
  letter-spacing: 0.5px;
  color: color-mix(in oklab, var(--body) 62%, transparent);
  margin: 0 0 8px;
}
.scan-ai-summary p {
  font-size: 15.5px;
  line-height: 1.55;
  color: var(--body);
  margin: 0;
}
.scan-cat-bars {
  display: flex;
  flex-direction: column;
  gap: 11px;
}
.scan-cat-row {
  display: grid;
  grid-template-columns: 132px 1fr 56px 20px;
  gap: 10px;
  align-items: center;
  font-size: 13.5px;
  color: var(--body);
}
.scan-cat-track {
  height: 9px;
  border-radius: 5px;
  background: var(--bg-soft-2);
  overflow: hidden;
}
.scan-cat-fill {
  display: block;
  height: 100%;
  border-radius: 5px;
  background: var(--indigo-1);
  width: 0;
}
.scan-cat-pts {
  text-align: right;
  font-variant-numeric: tabular-nums;
  color: var(--ink);
  font-weight: 700;
  font-size: 13px;
}
.scan-cat-icon {
  font-weight: 800;
  text-align: center;
}
.scan-cat-icon.scan-tone-good {
  color: var(--scan-good);
}
.scan-cat-icon.scan-tone-warn {
  color: var(--scan-warn);
}
.scan-cat-icon.scan-tone-bad {
  color: var(--scan-bad);
}
@media (max-width: 560px) {
  .scan-cat-row {
    grid-template-columns: 96px 1fr 40px 16px;
    font-size: 12.5px;
  }
}

/* ---- Fixes ---- */
.scan-fixes {
  margin-top: 26px;
}
/* Both levels share one visual size: the FULL report's fixes heading is an
   <h2> — the report view's first visible heading (its h1/h2 ancestors live
   in the hidden hero/progress stages, so an h3 here would break heading
   contiguity for AT users) — while the limited panel's fixes heading sits
   under that panel's own <h2> and stays an <h3>. */
.scan-fixes h2,
.scan-fixes h3 {
  font-size: 16px;
  margin: 0 0 12px;
}
.scan-fixcard {
  display: flex;
  gap: 12px;
  align-items: flex-start;
  border: 1px solid var(--line-soft);
  border-radius: 12px;
  padding: 13px 15px;
  background: var(--card);
  font-size: 13.5px;
  color: var(--body);
  margin-bottom: 8px;
}
.scan-fixcard b {
  color: var(--ink);
}
.scan-fixcard .scan-fix-pts {
  color: var(--indigo-2);
  font-weight: 700;
}
.scan-fixnum {
  width: 22px;
  height: 22px;
  border-radius: 50%;
  background: var(--indigo-soft);
  color: var(--indigo-2);
  font-size: 12px;
  font-weight: 800;
  display: flex;
  align-items: center;
  justify-content: center;
  flex: 0 0 auto;
  margin-top: 1px;
}

/* ---- Page-by-page table ---- */
.scan-table-section {
  grid-area: table;
}
.scan-table-section h3 {
  font-size: 16px;
  margin: 0 0 14px;
}
.scan-table-wrap {
  overflow-x: auto;
  border: 1px solid var(--line-soft);
  border-radius: 12px;
}
.scan-table {
  width: 100%;
  min-width: 480px;
  border-collapse: collapse;
  font-size: 13.5px;
  color: var(--body);
}
.scan-table th {
  text-align: left;
  color: var(--muted);
  font-weight: 700;
  font-size: 12px;
  text-transform: uppercase;
  letter-spacing: 0.3px;
  padding: 10px 14px;
  border-bottom: 1px solid var(--line-soft);
  background: var(--bg-soft);
}
.scan-table td {
  padding: 10px 14px;
  border-bottom: 1px solid var(--line-soft);
  white-space: nowrap;
}
.scan-table td.scan-table-url {
  white-space: normal;
  word-break: break-word;
  color: var(--ink);
  font-weight: 600;
}
.scan-table tr:last-child td {
  border-bottom: none;
}
.scan-scorechip {
  font-weight: 800;
  border-radius: 7px;
  padding: 2px 8px;
  font-size: 12.5px;
  font-variant-numeric: tabular-nums;
}
.scan-scorechip.scan-tone-good {
  background: var(--scan-good-bg);
  color: var(--scan-good);
}
.scan-scorechip.scan-tone-warn {
  background: var(--scan-warn-bg);
  color: var(--scan-warn);
}
.scan-scorechip.scan-tone-bad {
  background: var(--scan-bad-bg);
  color: var(--scan-bad);
}
#load-more-pages {
  margin-top: 14px;
}
/* Same class of bug already fixed for .scan-progress-hero[hidden] and
   .scan-cta[hidden] elsewhere in this file: this button carries the shared
   `.btn` class, whose author-origin `display: inline-flex` (styles.css)
   beats the browser's own `[hidden] { display: none }` UA rule regardless
   of specificity. Without this, scan.js's `els.loadMoreBtn.hidden = true`
   never actually hid the button when there was no next cursor — and
   clicking the still-visible button re-fetched page 1 and appended every
   row a second time. */
#load-more-pages[hidden] {
  display: none;
}

/* ---- Optional email row ---- */
.scan-report-email {
  grid-area: email;
}
.scan-email-row {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 10px;
  border: 1.5px dashed var(--line);
  border-radius: 12px;
  padding: 14px 16px;
  background: var(--bg-soft);
}
.scan-email-row label {
  font-size: 13.5px;
  color: var(--body);
  font-weight: 600;
}
.scan-email-row input {
  flex: 1 1 200px;
  min-width: 160px;
  border: 1px solid var(--line);
  background: var(--card);
  border-radius: 8px;
  padding: 9px 12px;
  font-size: 14px;
  font-family: inherit;
  color: var(--ink);
}
.scan-email-row .mut {
  font-size: 12px;
  color: var(--muted);
  flex-basis: 100%;
}

/* ---- Platform-aware CTA band (never both visible) ---- */
.scan-cta {
  position: relative;
  overflow: hidden;
  border-radius: var(--radius);
  padding: 26px 30px;
  color: #fff;
  background: linear-gradient(120deg, var(--accent), var(--indigo-1) 60%, var(--indigo-2));
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 20px;
  flex-wrap: wrap;
  box-shadow: var(--shadow-glow);
}
/* #567 rail fix: this element sets its own `display` (flex, above), which —
   per the CSS cascade's origin ordering — beats the browser's default
   `[hidden] { display: none }` UA rule regardless of selector specificity
   (confirmed live; same class of bug already fixed for .scan-progress-hero
   elsewhere in this file). Without this, scan.js's `els.ctaShopify.hidden =
   true` / `els.ctaOther.hidden = true` toggling never actually hid the
   inactive card — both CTA bands rendered stacked at once. Now that they
   share the rail with the full-catalog card, that would show as a visibly
   broken/duplicated box, so it's fixed here. */
.scan-cta[hidden] {
  display: none;
}
.scan-cta-copy strong {
  font-size: 17px;
  display: block;
}
.scan-cta-copy p {
  font-size: 13.5px;
  margin: 6px 0 0;
  opacity: 0.9;
  max-width: 480px;
}
.scan-cta-btn {
  background: #fff;
  color: var(--indigo-2);
  border: none;
  border-radius: 10px;
  padding: 12px 20px;
  font-size: 14.5px;
  font-weight: 800;
  white-space: nowrap;
  cursor: pointer;
  font-family: inherit;
  text-decoration: none;
  display: inline-block;
  position: relative;
  z-index: 1;
}
.scan-cta-btn:hover {
  filter: brightness(0.98);
}

/* ---- Full-catalog card ---- */
.scan-fullcatalog {
  border: 1.5px solid var(--indigo-1);
  border-radius: var(--radius);
  padding: 22px 26px;
  background: linear-gradient(180deg, var(--indigo-soft), #fff);
  /* Clip the ::before shine sweep to the card. Without this the beam escapes
     the card (the rail is overflow:visible) and streaks across the results
     table, parking there for part of its loop (#567 UI polish). Matches
     .scan-cta, which already sets overflow:hidden for the same reason. */
  position: relative;
  overflow: hidden;
}
.scan-fullcatalog-pitch strong {
  font-size: 16px;
  color: var(--ink);
  display: block;
  margin-bottom: 6px;
}
.scan-fullcatalog-pitch p {
  font-size: 14px;
  color: var(--body);
  margin: 0 0 14px;
}
#full-catalog-form {
  display: flex;
  flex-wrap: wrap;
  gap: 10px;
}
#full-catalog-form input {
  flex: 1 1 160px;
  border: 1px solid var(--line);
  background: var(--card);
  border-radius: 8px;
  padding: 10px 12px;
  font-size: 14px;
  font-family: inherit;
  color: var(--ink);
}
.scan-fullcatalog-done {
  font-size: 15px;
  font-weight: 700;
  color: var(--scan-good);
}

/* Muted ("not measured") category-row tone — visually distinct from a real
   "bad" score, never red, never implying a filled bar (#567 Plan 5). Shared
   by the report's category bars: any `not_measured` category renders with
   this muted tone via categoryRowView/setCategoryRowValues. */
.scan-cat-pts.scan-tone-muted {
  color: var(--muted);
  font-weight: 600;
  font-size: 12px;
}
.scan-cat-icon.scan-tone-muted {
  color: var(--muted);
}

/* =========================================================================
   Below-the-fold: How it works / What we check
   ========================================================================= */
/* #567 UI polish: `#how-it-works` inherited styles.css's shared
   `section.block { padding: 100px 0; }` top padding untouched — the other
   half of the dead zone described above `.scan-results-section`'s own
   override. Its own padding-top doesn't move ITS OWN box position (so it's
   invisible to a getBoundingClientRect probe rooted there), but it's still
   real, visible space between the section boundary and its "How it works"
   heading — trimming it too keeps the eyebrow from feeling like it's
   floating in its own separate dead zone right after the first one. An id
   selector (higher specificity than the shared type+class rule, scoped to
   just this one page) makes the override stick. */
#how-it-works {
  padding-top: 32px;
}

/* #567 UI polish: the "How it works" 3-step markup (`.steps`/`.step`/`.n`,
   shared with cortexguard.html/cortexpersona.html) was only ever given real
   card CSS in product.css — a stylesheet this page never loads — so here it
   rendered as a bare number + heading + paragraph with no card chrome.
   Gives it the same visual language as `.scan-checkcard` below (existing
   tokens only: --card/--line-soft/--radius/--indigo-soft/--indigo-2) plus a
   small circular number badge, matching the approved mockup. Scoped to
   this stylesheet only, so cortexguard.html/cortexpersona.html's own
   product.css treatment is untouched. */
.steps {
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: 20px;
}
.step {
  background: var(--card);
  border: 1px solid var(--line-soft);
  border-radius: var(--radius);
  padding: 22px 22px 24px;
  box-shadow: var(--shadow-sm);
}
.step .n {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 28px;
  height: 28px;
  border-radius: 50%;
  background: var(--indigo-soft);
  color: var(--indigo-2);
  font-size: 13px;
  font-weight: 800;
  margin-bottom: 14px;
}
.step h3 {
  font-size: 17px;
  letter-spacing: -0.2px;
  margin: 0 0 8px;
  color: var(--ink);
}
.step p {
  font-size: 14px;
  color: var(--body);
  margin: 0;
  line-height: 1.55;
}
@media (max-width: 720px) {
  .steps {
    grid-template-columns: 1fr;
    gap: 16px;
  }
}

.scan-checkgrid {
  display: grid;
  grid-template-columns: repeat(4, minmax(0, 1fr));
  gap: 16px;
}
.scan-checkcard {
  border: 1px solid var(--line-soft);
  border-radius: 12px;
  padding: 18px 18px 20px;
  background: var(--card);
  box-shadow: var(--shadow-sm);
  transition:
    transform 0.25s,
    box-shadow 0.25s;
}
.scan-checkcard:hover {
  transform: translateY(-3px);
  box-shadow: var(--shadow-md);
}
.scan-checkcard b {
  display: block;
  font-size: 15px;
  color: var(--ink);
  margin-bottom: 6px;
}
.scan-checkcard p {
  font-size: 13.5px;
  color: var(--body);
  margin: 0;
  line-height: 1.5;
}
@media (max-width: 900px) {
  .scan-checkgrid {
    grid-template-columns: repeat(2, minmax(0, 1fr));
  }
}
@media (max-width: 560px) {
  .scan-checkgrid {
    grid-template-columns: 1fr;
  }
  .scan-report-head {
    gap: 26px;
  }
  .scan-ring-col {
    width: 100%;
  }
}

/* =========================================================================
   Toast (Share-report confirmation, etc.)
   ========================================================================= */
.scan-toast {
  position: fixed;
  left: 50%;
  bottom: 28px;
  transform: translateX(-50%);
  background: var(--ink);
  color: #fff;
  padding: 12px 20px;
  border-radius: 999px;
  font-size: 14px;
  font-weight: 600;
  box-shadow: var(--shadow-lg);
  z-index: 80;
}
.scan-toast[hidden] {
  display: none;
}

/* =========================================================================
   Animation — ring sweep, count-up scaffold, staggered fade-up, terminal
   line-in, CTA shine. ALL wrapped in the no-preference guard: the rule-free
   default above is already the correct reduced-motion end state (full ring,
   static bars, no shine, lines simply present).
   ========================================================================= */
@media (prefers-reduced-motion: no-preference) {
  .scan-ring-val {
    transition: stroke-dashoffset 1.4s cubic-bezier(0.16, 0.84, 0.44, 1);
  }
  .scan-score-num {
    transition: none; /* the rAF count-up (scan.js) writes textContent every frame */
  }

  .scan-cat-fill {
    transition: width 0.9s cubic-bezier(0.16, 0.84, 0.44, 1);
  }

  .scan-stagger {
    opacity: 0;
    transform: translateY(12px);
    transition:
      opacity 0.5s ease,
      transform 0.5s ease;
  }
  .scan-stagger.is-in {
    opacity: 1;
    transform: none;
  }

  .scan-term-line {
    animation: scan-term-line-in 0.32s ease both;
  }
  @keyframes scan-term-line-in {
    from {
      opacity: 0;
      transform: translateY(4px);
    }
    to {
      opacity: 1;
      transform: none;
    }
  }

  .scan-term-cursor {
    animation: scan-caret-blink 1s steps(1) infinite;
  }
  @keyframes scan-caret-blink {
    50% {
      opacity: 0;
    }
  }

  /* Hero demo loop — a fixed, pre-written transcript that fades in line by
     line, holds, then resets. Pure CSS: no JS required until a real scan
     starts (scan.js then simply stops/replaces this terminal). */
  .scan-demo-line {
    opacity: 0;
    transform: translateY(6px);
    animation: scan-demo-cycle 9s ease-in-out infinite;
  }
  @keyframes scan-demo-cycle {
    0%,
    4% {
      opacity: 0;
      transform: translateY(6px);
    }
    9%,
    88% {
      opacity: 1;
      transform: none;
    }
    100% {
      opacity: 0;
    }
  }

  .scan-progress-bar.is-indeterminate > i {
    animation: scan-indeterminate 1.7s ease-in-out infinite;
  }
  @keyframes scan-indeterminate {
    0% {
      margin-left: -32%;
    }
    100% {
      margin-left: 100%;
    }
  }

  /* CTA shine — a soft diagonal highlight sweep, matching the approved
     mockup's "gate" shine but slowed down and dimmed so it reads as a subtle
     accent rather than a distracting loop. */
  .scan-cta::before,
  .scan-fullcatalog::before {
    content: "";
    position: absolute;
    top: 0;
    left: -60%;
    width: 34%;
    height: 100%;
    transform: skewX(-20deg);
    background: linear-gradient(90deg, transparent, rgba(255, 255, 255, 0.35), transparent);
    animation: scan-shine 9s ease-in-out infinite;
    pointer-events: none;
  }
  .scan-fullcatalog::before {
    background: linear-gradient(
      90deg,
      transparent,
      color-mix(in oklab, var(--indigo-1) 16%, transparent),
      transparent
    );
  }
  @keyframes scan-shine {
    0%,
    60% {
      left: -60%;
    }
    85%,
    100% {
      left: 130%;
    }
  }
}
