/*
 * Public scan-funnel styles (COD-235), ported from the
 * dsa-onboarding-revised.html mockup onto design-tokens.css.
 *
 * The wizard is server-rendered: selection controls are native
 * checkboxes/radios hidden inside styled <label> rows, so every step
 * works without JavaScript.
 */

/* ── Page shell ── */
*,
*::before,
*::after { box-sizing: border-box; margin: 0; padding: 0; }

.funnel-body {
  min-height: 100vh;
  font-family: var(--sans);
  color: var(--dark);
  background: var(--white);
  --funnel-grad: linear-gradient(135deg, #0b1a4f 0%, #1a3aad 50%, var(--blue) 100%);
}

.funnel-shell { display: flex; min-height: 100vh; }

.funnel-left {
  width: 55%;
  display: flex;
  flex-direction: column;
  padding: 2.5rem 3.5rem 1.75rem;
}

.funnel-logo {
  font-family: var(--mono);
  font-size: 0.88rem;
  font-weight: 600;
  color: var(--blue);
  letter-spacing: -0.01em;
  margin-bottom: 2.5rem;
}
.funnel-logo a { color: inherit; text-decoration: none; }

.funnel-content {
  flex: 1;
  display: flex;
  flex-direction: column;
  /* Centre the content column in the left pane (the design centres each step
     horizontally; left-aligning made it read as small and off-centre). */
  max-width: 480px;
  width: 100%;
  margin-left: auto;
  margin-right: auto;
  animation: funnel-step-in 0.28s ease both;
}
@keyframes funnel-step-in {
  from { opacity: 0; transform: translateX(22px); }
  to   { opacity: 1; transform: translateX(0); }
}

/* ── Step header: dots + eyebrow ── */
.step-header {
  display: flex;
  align-items: center;
  gap: 0.75rem;
  margin-bottom: 2.5rem;
}
.step-dots { display: flex; align-items: center; gap: 0.45rem; }
.dot {
  width: 8px;
  height: 8px;
  border-radius: 50%;
  background: var(--blue-light);
}
.dot.active { width: 22px; border-radius: 4px; background: var(--blue); }
.dot.done { background: var(--blue); opacity: 0.3; }

.eyebrow {
  font-family: var(--mono);
  font-size: 0.65rem;
  font-weight: 600;
  color: var(--blue);
  text-transform: uppercase;
  letter-spacing: 0.1em;
}

/* ── Typography ── */
.step-h {
  font-size: 1.85rem;
  font-weight: 800;
  line-height: 1.15;
  letter-spacing: -0.025em;
  margin-bottom: 0.6rem;
}
.step-sub {
  font-size: 1rem;
  color: var(--text-muted);
  line-height: 1.5;
  margin-bottom: 1.75rem;
}
.step-lead {
  font-size: 1rem;
  font-weight: 600;
  color: var(--dark);
  margin-bottom: 1.5rem;
  line-height: 1.5;
}

/* ── URL / email input rows ── */
/* Grows with what's typed, between a resting width and a ceiling, so a long host like
   a specimen subdomain is readable instead of scrolling inside a fixed 420px box.
   The resting value is declared here rather than left to a var() fallback: the sizer in
   start.html overrides it inline, so without JS the box simply rests at 420px — the
   behaviour it had before — and the enhancement can fail silently without taking the
   form's primary input with it. `max-width: 100%` keeps the ceiling off a narrow
   viewport. */
.url-box {
  --url-box-w: 420px;
  display: flex;
  align-items: center;
  border: 1px solid var(--blue);
  border-radius: 50px;
  width: var(--url-box-w);
  /* Deliberately NOT `max-width: 100%`. 100% is `.funnel-content`, which is capped at
     480px to centre the text column — so a long host could never fit however much the
     box grew, which is the whole complaint. The box may exceed the text column; the
     sizer bounds it by the left pane. This cap is only the last-resort "never wider than
     the screen" guard for the no-JS case, where the explicit 420px width would otherwise
     scroll a phone sideways. Viewport-based rather than a breakpoint so a tablet is not
     clamped back to the 480px column when its pane has room. */
  max-width: calc(100vw - 2.5rem);
  margin-bottom: 0.75rem;
  background: var(--white);
  /* No transition on `width`, deliberately. The sizer measures by resetting to the
     resting width and reading how far the value overflows — and a transition means the
     reset has not taken effect by the time that read happens, so the deficit is computed
     against an interpolated box and comes out too small. The symptom was a box that grew
     on every keystroke but always stopped short, with the text still scrolling. It also
     lags the caret while typing, so removing it is not only correct but better. */
  transition: box-shadow 0.15s;
  padding: 4px 4px 4px 0;
}
.url-box:focus-within { box-shadow: 0 0 0 3px rgba(11, 69, 255, 0.12); }
.url-prefix {
  padding: 0 0.5rem 0 1.25rem;
  font-family: var(--mono);
  font-size: 0.78rem;
  color: var(--text-muted);
  white-space: nowrap;
}
.url-input {
  flex: 1;
  min-width: 0;
  border: none;
  outline: none;
  font-size: 0.95rem;
  font-weight: 600;
  color: var(--dark);
  padding: 0.7rem 0.25rem;
  background: transparent;
  font-family: var(--sans);
}
.url-input::placeholder { color: var(--text-sub); font-weight: 400; }
.url-box > .url-input:first-child { padding-left: 1.25rem; }
.btn-scan {
  background: var(--blue);
  color: var(--white);
  border: none;
  padding: 0.7rem 1.25rem;
  font-family: var(--sans);
  font-size: 0.88rem;
  font-weight: 700;
  cursor: pointer;
  white-space: nowrap;
  border-radius: 50px;
  transition: background 0.15s;
}
.btn-scan:hover { background: var(--blue-dark); }
/* Loading state for the step-4 email submit, which acknowledges the click at once
   while the lead is captured (see scope_confirmed.html). */
.btn-spinner {
  display: inline-block;
  width: 13px;
  height: 13px;
  margin-right: 0.45rem;
  border: 2px solid rgba(255, 255, 255, 0.45);
  border-top-color: var(--white);
  border-radius: 50%;
  animation: funnel-spin 0.7s linear infinite;
  vertical-align: -1px;
}

/* ── Step 1 "what we check" box ── */
.proof-box {
  max-width: 480px;
  width: 100%;
  padding: 1rem 1.15rem;
  border: 1.5px solid var(--blue);
  border-radius: 10px;
  margin-top: auto;
}
.proof-box-intro {
  font-size: 0.78rem;
  color: var(--blue);
  line-height: 1.65;
  margin-bottom: 0.85rem;
}
.proof-box-label {
  font-size: 0.7rem;
  font-weight: 700;
  color: var(--blue);
  text-transform: uppercase;
  letter-spacing: 0.07em;
  margin-bottom: 0.5rem;
}
.proof-list {
  list-style: none;
  display: flex;
  flex-direction: column;
  gap: 0.3rem;
}
.proof-list li {
  display: flex;
  align-items: baseline;
  gap: 0.55rem;
  font-size: 0.78rem;
  color: var(--blue);
}
.proof-art {
  font-family: var(--mono);
  font-size: 0.65rem;
  font-weight: 700;
  flex-shrink: 0;
}

/* ── Choice rows (service tier + its refinements, step 2) ── */
.choice-list {
  display: flex;
  flex-direction: column;
  gap: 0.6rem;
  width: 100%;
}
/* Same trap as .scan-offer below: the flex display above wins over the [hidden] UA
   rule, so the Chapter III selectors rendered under *no* tier at all — visible before
   the prospect had chosen anything, which is the state they are least meaningful in. */
.choice-list[hidden] { display: none; }
/* The three §3/§4 selectors are refinements *within* the online-platform tier, not four
   more tiers. Indented under it so the containment is visible rather than asserted — the
   same relationship the in-app classification form draws with .pf-tier-extras. */
.choice-subgroup {
  /* `width: auto`, NOT the 100% inherited from .choice-list above. An explicit 100% plus
     a left margin resolves to "parent width, shifted right", so the indent pushed the
     right edge past the tier cards instead of narrowing the block — the rows read as
     wider than the option they belong under, which is the opposite of contained. */
  width: auto;
  margin: 0.75rem 0 0 1.5rem;
  padding-left: 1.1rem;
  border-left: 2px solid var(--border-solid);
}
.choice-subgroup-title {
  font-size: 0.82rem;
  font-weight: 600;
  color: var(--text-muted);
  /* The flex gap already separates it from the first row; this only lifts it off the
     tier card above, whose border sits tight against it otherwise. */
  margin: 0 0 0.15rem;
}
/* The search-engine option is orthogonal to the whole ladder, so it needs air above it or
   it reads as a fourth refinement rather than a separate question. Its own margin rather
   than an adjacent-sibling rule off the subgroup: the subgroup is hidden for two of the
   three tiers, and `display: none` takes its margins with it — so spacing that leant on
   the neighbour would simply vanish exactly when the neighbour did. */
.choice-list--separate { margin-top: 1.1rem; }
.choice-row {
  /* Positioning context for .choice-info-bubble — see the note there. */
  position: relative;
  display: flex;
  align-items: center;
  gap: 0.75rem;
  /* Blockier since the options became terms rather than sentences (Einat, 2026-09-20). A row
     holding three lines of prose had to be tight or the list ran past the fold; a row holding
     "Online platform" can afford the height, and the extra padding is what makes three short
     rows read as a set of blocks instead of a cramped list. */
  padding: 1.05rem 1.1rem;
  background: var(--white);
  border: 1.5px solid var(--border-solid);
  border-radius: 12px;
  cursor: pointer;
  transition: border-color 0.18s, background 0.18s;
}
.choice-row:hover { border-color: var(--blue-light); }
.choice-row:has(input:checked) { border-color: var(--blue); background: var(--blue-pale); }
.choice-row input { position: absolute; opacity: 0; pointer-events: none; }
.choice-icon {
  width: 30px;
  height: 30px;
  border-radius: 8px;
  display: flex;
  align-items: center;
  justify-content: center;
  background: var(--gray-bg);
  color: var(--text-muted);
  flex-shrink: 0;
  transition: background 0.18s, color 0.18s;
}
.choice-row:has(input:checked) .choice-icon { background: var(--blue); color: var(--white); }
.choice-text { flex: 1; min-width: 0; }
.choice-label {
  font-size: 0.98rem;
  font-weight: 700;
  color: var(--dark);
  line-height: 1.3;
}
/* ── Radio vs. toggle: the control has to mean what the input means ─────────────
   A toggle switch says "this one thing is independently on or off". That is true of
   the Chapter III refinements — tick any, in any combination — and false of the
   service tier, which is one ordered choice where picking one *unpicks* the others.
   Dressing a radio as a switch invites exactly the mental model the DSA's nesting
   rules out, so the tier gets a radio mark and only the checkboxes keep toggles. */
.choice-radio {
  width: 20px;
  height: 20px;
  border-radius: 50%;
  border: 2px solid var(--border-solid);
  background: var(--white);
  position: relative;
  flex-shrink: 0;
  transition: border-color 0.18s;
}
.choice-row:has(input:checked) .choice-radio { border-color: var(--blue); }
.choice-row:has(input:checked) .choice-radio::after {
  content: '';
  position: absolute;
  inset: 3px;
  border-radius: 50%;
  background: var(--blue);
}
/* `.choice-sub`, `.choice-cite` and `.choice-row--compact` were removed on 2026-09-20 with the
   long-sentence options they styled: the citation now lives behind the marker with the sentence,
   and the refinements this step used to ask moved to the in-app step on 2026-09-14. Nothing
   rendered them. `check_template_classes` guards the other direction — a class in a template
   with no rule — so dead rules here are found by looking, which is why this note says what went. */

/* ── Small print, moved off the page into a marker ──────────────────────────────
   The muted second line under every option — the DSA citation on a tier, the
   qualifying hint on a refinement — was most of this step's height: seven options,
   one to four extra lines each. It is supporting detail, not what the answer is read
   off, so it lives behind an ⓘ.

   Funnel-local rather than `includes/_tooltip.html`, deliberately: these pages load
   funnel.css and the design tokens but NOT design-system-components.css, so the shared
   component's classes would be undefined here and the bubble would render inline and
   permanently visible — the opposite of the intent.

   `.is-open` is set on tap. Hover alone would put the trader-contracts hint ("your own
   direct sales don't count") out of reach on touch, and that hint is the one that
   changes what a lead answers. */
.choice-info {
  /* Deliberately NOT a positioning context: the bubble anchors to the row instead (see
     below), so it cannot depend on where in a wrapped label the marker happens to land. */
  display: inline-flex;
  align-items: center;
  margin-left: 0.3rem;
  color: var(--text-muted);
  cursor: help;
  vertical-align: middle;
}
.choice-row:has(input:checked) .choice-info { color: var(--blue); }
.choice-info-bubble {
  /* Anchored to the ROW, not the marker (.choice-row is the positioning context). Both
     marker-relative options failed, in opposite directions and for the same reason —
     where the marker lands depends on how its label wrapped. Anchored left it ran past
     the pane and the artwork column clipped it; anchored right, the two tier markers that
     fall at the start of a wrapped second line pushed it off the left edge instead. The
     row is inside the pane by construction, so anchoring there cannot overflow either
     way, and the hint still reads as belonging to its option. */
  position: absolute;
  bottom: calc(100% + 6px);
  left: 0;
  z-index: 20;
  width: max-content;
  max-width: min(340px, 100%);
  padding: 0.45rem 0.6rem;
  border-radius: 8px;
  background: var(--dark);
  color: var(--white);
  font-size: 0.72rem;
  font-weight: 500;
  line-height: 1.4;
  white-space: normal;
  visibility: hidden;
  opacity: 0;
  pointer-events: none;
  transition: opacity 0.12s;
  box-shadow: 0 6px 18px rgba(15, 23, 42, 0.18);
}
.choice-info:hover > .choice-info-bubble,
.choice-info:focus > .choice-info-bubble,
.choice-info.is-open > .choice-info-bubble {
  visibility: visible;
  opacity: 1;
}
.choice-toggle {
  width: 40px;
  height: 22px;
  border-radius: 50px;
  background: #cbd5e1;
  position: relative;
  flex-shrink: 0;
  transition: background 0.2s;
}
.choice-toggle::after {
  content: '';
  position: absolute;
  top: 2px;
  left: 2px;
  width: 18px;
  height: 18px;
  background: var(--white);
  border-radius: 50%;
  transition: transform 0.2s;
  box-shadow: 0 1px 3px rgba(0, 0, 0, 0.18);
}
.choice-row:has(input:checked) .choice-toggle { background: var(--blue); }
.choice-row:has(input:checked) .choice-toggle::after { transform: translateX(18px); }

/* ── Pill options (company size, VLOP yes/no) ── */
.pill-options { display: flex; gap: 0.6rem; flex-wrap: wrap; }
/* Each pill is as wide as its label, as the design draws them (COD-478 1.1), and the three
   sizes share one row. `width: min-content` is what does it: the button sets `white-space:
   nowrap`, so the column's min-content is the whole label, and the description wraps to that
   width under it. Two things were tried before and are worse: `flex: 1` shares the row
   equally, which stretched Yes and No to half the form each and pushed the third size onto a
   row of its own; `flex: 0 1 auto` sizes the column to its max-content, which is the
   description on one unwrapped line, so every option claimed a full row. An explicit
   `min-width` on the column replaces the automatic minimum that holds the pill open, and let
   "None of these" run out through its border; the floor is on the button instead. */
.pill-option { display: flex; flex-direction: column; flex: 0 0 auto; width: min-content; }
.pill-option input { position: absolute; opacity: 0; pointer-events: none; }
.pill-btn {
  display: block;
  min-width: 4.75rem;
  padding: 0.5rem 1.05rem;
  border-radius: 50px;
  border: 1.5px solid var(--border-solid);
  background: var(--white);
  font-size: 0.84rem;
  font-weight: 600;
  color: var(--dark);
  text-align: center;
  cursor: pointer;
  white-space: nowrap;
  transition: all 0.15s;
}
.pill-option:hover .pill-btn { border-color: var(--blue); color: var(--blue); }
.pill-option:has(input:checked) .pill-btn {
  background: var(--blue);
  border-color: var(--blue);
  color: var(--white);
}
.pill-desc {
  font-size: 0.65rem;
  color: var(--text-muted);
  margin-top: 0.55rem;
  line-height: 1.4;
}

.q-big {
  font-size: 1.4rem;
  font-weight: 700;
  line-height: 1.3;
  letter-spacing: -0.02em;
  color: var(--dark);
  margin-bottom: 0.6rem;
}
.q-note {
  font-size: 0.72rem;
  color: var(--text-muted);
  margin-bottom: 1.25rem;
  line-height: 1.5;
}
/* Separates the two questions on the combined scope-questions step. */
.q-block { margin-bottom: 2.25rem; }
/* Inline per-question validation message (step 3), shown by the page's script in
   place of the browser's default bubble when a group is unanswered (COD-263). */
.q-error {
  margin-top: 0.75rem;
  font-size: 0.8rem;
  font-weight: 500;
  color: #dc2626;
}
.q-error[hidden] { display: none; }

/* Respectful informational note on the platform-type step when detection judges the
   domain isn't a DSA-relevant service (COD-263) — muted, not an error. */
.scope-note {
  margin: 0 0 1.5rem;
  padding: 0.85rem 1rem;
  border: 1px solid var(--border);
  border-left: 3px solid var(--blue);
  border-radius: 8px;
  background: var(--blue-pale);
  font-size: 0.85rem;
  line-height: 1.55;
  color: var(--text-muted);
}
.scope-note strong { color: var(--dark); }

/* ── Confirm page (step 4) ──
 *
 * Einat's batch-5 mock (s7) replaced the old dark key/value scope card with a
 * prose recap + an email "hero" card. The card is the step's whole call to
 * action, so it carries the mark, the ask, and the submit together. */
.scope-h { margin-bottom: 0.5rem; }
/* Wider than the mock's 360px: its sample classification is short, ours can carry
   three category labels. At the mock's airy 1.9 line-height a wrapped sentence
   reads as two unrelated lines, so give each one room to stay whole. */
.scope-line {
  font-size: 1rem;
  color: var(--dark);
  line-height: 1.9;
  margin-bottom: 2rem;
  width: 100%;
}
.scope-line-domain { font-weight: 700; color: var(--blue); }
.scope-line-count { color: var(--blue); }

.email-card {
  width: 100%;
  max-width: 360px;
  background: var(--white);
  border: 1.5px solid var(--border);
  border-radius: 18px;
  padding: 2rem 1.75rem 1.75rem;
  box-shadow: 0 4px 20px rgba(0, 0, 0, 0.06);
  text-align: center;
}
.email-card-h {
  font-size: 1.1rem;
  font-weight: 800;
  color: var(--dark);
  line-height: 1.3;
  letter-spacing: -0.02em;
  margin-bottom: 0.6rem;
}
.email-card-p {
  font-size: 0.82rem;
  color: var(--text-muted);
  line-height: 1.6;
  margin-bottom: 1.4rem;
}
.email-card-input {
  width: 100%;
  border: 2px solid var(--border);
  border-radius: 50px;
  padding: 0.7rem 1.1rem;
  font-family: var(--sans);
  font-size: 0.88rem;
  color: var(--dark);
  outline: none;
  margin-bottom: 0.65rem;
  transition: border-color 0.15s, box-shadow 0.15s;
}
.email-card-input:focus {
  border-color: var(--blue);
  box-shadow: 0 0 0 3px rgba(11, 69, 255, 0.1);
}
.email-card-btn {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 0.5rem;
  width: 100%;
  background: var(--blue);
  color: var(--white);
  border: none;
  border-radius: 50px;
  padding: 0.75rem 1rem;
  font-family: var(--sans);
  font-size: 0.9rem;
  font-weight: 700;
  cursor: pointer;
  transition: background 0.15s;
  box-shadow: 0 4px 14px -4px rgba(11, 69, 255, 0.5);
}
.email-card-btn:hover { background: var(--blue-dark); }
.email-card-btn:disabled { opacity: 0.8; cursor: default; }
.email-card-note {
  font-size: 0.7rem;
  color: var(--text-sub);
  margin-top: 1rem;
  line-height: 1.5;
  max-width: 360px;
}

/* Document-with-tick mark: CSS boxes, as the mock builds it. */
.email-card-illo { position: relative; display: inline-block; margin-bottom: 1.5rem; }
.email-card-illo-shadow {
  position: absolute;
  top: 5px;
  left: 5px;
  width: 58px;
  height: 68px;
  background: var(--blue-light);
  border-radius: 8px;
}
.email-card-illo-doc {
  position: relative;
  width: 58px;
  height: 68px;
  background: var(--white);
  border: 1.5px solid var(--border-solid);
  border-radius: 8px;
  display: flex;
  flex-direction: column;
  justify-content: flex-end;
  padding: 10px 10px 12px;
  gap: 5px;
  box-shadow: 0 1px 4px rgba(0, 0, 0, 0.06);
}
.email-card-illo-fold,
.email-card-illo-fold-inner {
  position: absolute;
  width: 0;
  height: 0;
  border-style: solid;
}
.email-card-illo-fold {
  top: 0;
  right: 0;
  border-width: 0 14px 14px 0;
  border-color: transparent var(--border-solid) transparent transparent;
}
.email-card-illo-fold-inner {
  top: 1.5px;
  right: 1.5px;
  border-width: 0 12px 12px 0;
  border-color: transparent var(--gray-bg) transparent transparent;
}
.email-card-illo-line { height: 3.5px; background: var(--blue-light); border-radius: 2px; }
.email-card-illo-line:nth-child(3) { width: 80%; }
.email-card-illo-line:nth-child(4) { width: 65%; }
.email-card-illo-line:nth-child(5) { width: 75%; }
.email-card-illo-badge {
  position: absolute;
  bottom: -8px;
  right: -8px;
  width: 24px;
  height: 24px;
  background: var(--blue);
  border-radius: 50%;
  border: 2.5px solid var(--white);
  display: flex;
  align-items: center;
  justify-content: center;
  box-shadow: 0 2px 6px rgba(11, 69, 255, 0.35);
}

/* ── Errors ── */
.form-errors {
  max-width: 420px;
  background: var(--amber-bg);
  border: 1px solid var(--amber-border);
  border-radius: 10px;
  color: var(--amber-text);
  font-size: 0.78rem;
  line-height: 1.5;
  padding: 0.6rem 0.9rem;
  margin-bottom: 0.9rem;
}
.form-errors ul { list-style: none; }

/* ── Nav row ── */
.nav-row {
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding-top: 1.5rem;
  margin-top: auto;
  max-width: 480px;
  width: 100%;
}
.btn-back {
  font-size: 0.82rem;
  font-weight: 500;
  color: var(--text-muted);
  text-decoration: none;
  display: inline-flex;
  align-items: center;
  gap: 0.4rem;
  padding: 0.5rem 0.75rem;
  border-radius: 8px;
  transition: color 0.15s, background 0.15s;
}
.btn-back:hover { color: var(--dark); background: var(--gray-bg); }
/* Pull the back link flush to the content column's left edge so it sits in the
   lower-left corner (per the design), without losing its symmetric hover padding. */
.nav-row .btn-back { margin-left: -0.75rem; }

.btn-primary {
  display: inline-flex;
  align-items: center;
  gap: 0.5rem;
  background: var(--blue);
  color: var(--white);
  border: none;
  border-radius: 50px;
  padding: 0.9rem 2rem;
  font-family: var(--sans);
  font-size: 0.95rem;
  font-weight: 700;
  cursor: pointer;
  transition: background 0.15s, transform 0.1s, box-shadow 0.15s;
  box-shadow: 0 6px 18px -6px rgba(11, 69, 255, 0.45), 0 2px 4px rgba(11, 69, 255, 0.15);
}
.btn-primary:hover {
  background: var(--blue-dark);
  box-shadow: 0 10px 24px -6px rgba(11, 69, 255, 0.55), 0 3px 6px rgba(11, 69, 255, 0.2);
}
.btn-primary:active { transform: scale(0.97); }

/* ── Right panel ── */
.funnel-right {
  width: 45%;
  background: var(--funnel-grad);
  position: relative;
  overflow: hidden;
  display: flex;
  align-items: center;
  justify-content: center;
}
.glow {
  position: absolute;
  border-radius: 50%;
  filter: blur(60px);
  pointer-events: none;
}
.glow-1 { width: 320px; height: 320px; background: rgba(11, 69, 255, 0.35); top: -80px; right: -80px; }
.glow-2 { width: 200px; height: 200px; background: rgba(221, 231, 254, 0.1); bottom: 30px; left: 10px; }
.glow-3 { width: 140px; height: 140px; background: rgba(245, 232, 74, 0.07); top: 50%; left: 40%; transform: translate(-50%, -50%); }

.doc-frame { position: relative; width: 260px; z-index: 2; }
.doc-card {
  background: rgba(255, 255, 255, 0.06);
  border: 1px solid rgba(255, 255, 255, 0.14);
  border-radius: 16px;
  padding: 1.5rem 1.75rem;
  backdrop-filter: blur(8px);
}
.doc-label {
  font-family: var(--mono);
  font-size: 0.62rem;
  color: rgba(255, 255, 255, 0.45);
  text-transform: uppercase;
  letter-spacing: 0.1em;
  margin-bottom: 1rem;
}
.doc-line {
  height: 6px;
  border-radius: 3px;
  background: rgba(255, 255, 255, 0.1);
  margin-bottom: 0.5rem;
}
.doc-line.acc { background: rgba(11, 69, 255, 0.55); }
.doc-line.w60 { width: 60%; }
.doc-line.w45 { width: 45%; }
.doc-line.w75 { width: 75%; }
.doc-line.w50 { width: 50%; }
.doc-logo {
  font-family: var(--mono);
  font-size: 0.8rem;
  font-weight: 600;
  color: rgba(255, 255, 255, 0.85);
  margin-top: 1.2rem;
  letter-spacing: -0.01em;
}
.float-pill {
  position: absolute;
  /* nowrap is load-bearing: the side pills are positioned past the 260px card via
     calc(100% + gap), which collapses an auto-width box to near-zero (forcing the
     label to wrap) unless it's kept on one line. */
  white-space: nowrap;
  font-family: var(--mono);
  font-size: 0.62rem;
  font-weight: 600;
  color: var(--white);
  background: rgba(11, 69, 255, 0.65);
  border: 1px solid rgba(255, 255, 255, 0.2);
  border-radius: 50px;
  padding: 0.22rem 0.6rem;
  animation: float-y 3s ease-in-out infinite;
}
/* Badges scattered clear of the card (its backdrop-filter stacking context would
   otherwise paint over an overlapping pill): Art. 16 floats above; Art. 20 flanks
   the left at ~1/3 of the card height; Art. 25 flanks the right at ~2/3. The two
   side pills sit fully outside the card via left/right: calc(100% + gap) — measured
   from the card's own edges — and their % tops track the card height. */
.float-pill:nth-child(1) { top: -32px; left: 60px; animation-delay: 0s; }              /* Art. 16 — above, nudged right of the corner */
.float-pill:nth-child(2) { top: 60%; left: calc(100% + 8px); animation-delay: 0.7s; }  /* Art. 25 — right of the card, ~2/3 down */
.float-pill:nth-child(3) { top: 28%; right: calc(100% + 8px); animation-delay: 1.4s; } /* Art. 20 — left of the card, ~1/3 down */
@keyframes float-y {
  0%, 100% { transform: translateY(0); }
  50% { transform: translateY(-6px); }
}

/* ── Submitted page ── */
.submitted-wrap {
  flex: 1;
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  justify-content: center;
  max-width: 480px;
}
.submitted-icon {
  width: 64px;
  height: 64px;
  border-radius: 50%;
  background: var(--blue-pale);
  color: var(--blue);
  display: flex;
  align-items: center;
  justify-content: center;
  margin-bottom: 1.5rem;
}
.submitted-email { font-weight: 700; color: var(--dark); }

/* ── Live scan progress (token report page, pending state) ── */
/* This page shares the wizard's column width so every block on it centres on one
   axis. It previously widened to 620px to keep each step row ("<label> – Art. NN(N)")
   on one line (Einat, COD-263), but the widened column left the narrower prose blocks
   pinned to its left edge with ~160px of dead space on the right — the page read as
   off-centre rather than roomy. The step rows still fit on one line at the shared
   width (they need ~400px of the ~480px available). Everything that reads as a block
   shares the full column; the prose blocks keep their own narrower measure but are
   centred within it (margin-inline: auto) so no edge goes ragged. On narrow screens
   the column falls back to 100% and rows may wrap, which is fine. */
/* Step 2 only. The 480px column is what makes this step tall: at that width every tier
   statement wraps to two lines and every refinement's hint to two more, so the option a
   *majority* of leads pick — "online platform", which reveals three refinements plus the
   search-engine question — pushed Continue below the fold. Widening attacks the cause
   (line count) rather than shaving padding, which attacks the symptom and costs
   legibility. The left pane is 55% of the shell, so 600px still sits inside it with room,
   and below the 900px breakpoint `width: 100%` takes over as before. */
/* `.funnel-content--wide` was the platform-type step's: 660px plus vertical trims, because seven
   full-sentence options had to fit a laptop fold. Both halves went with the sentences on
   2026-09-20 — three short rows fit the standard column with the standard preamble, which is
   what made "narrower" cheap. Nothing else ever used it. */
.funnel-content--scan { max-width: 480px; }
.funnel-content--scan .scan-progress-card,
.funnel-content--scan .scan-tally,
.funnel-content--scan .scan-offer { max-width: 100%; }

/* The card is unpadded and clips (batch-5 mock, s6) so the header rule and each
   row's separator run edge to edge; padding lives on the header and the rows. */
.scan-progress-card {
  border: 1.5px solid var(--border);
  border-radius: 12px;
  overflow: hidden;
  margin: 0.5rem 0 1.25rem;
  max-width: 460px;
}
.scan-progress-head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: 0.6rem 1rem;
  border-bottom: 1px solid var(--border);
}
.scan-progress-title {
  font-size: 0.72rem;
  font-weight: 600;
  color: var(--text-muted);
  text-transform: uppercase;
  letter-spacing: 0.06em;
}
.scan-progress-count { font-size: 0.72rem; color: var(--text-muted); }
.scan-steps { display: flex; flex-direction: column; }
.scan-step {
  display: flex;
  align-items: center;
  gap: 0.75rem;
  padding: 0.7rem 1rem;
  font-size: 0.82rem;
  color: var(--dark);
  border-bottom: 1px solid var(--border);
}
.scan-step:last-child { border-bottom: none; }
.scan-step.queued .scan-step-label { color: var(--text-muted); }
/* Status chip: a tinted circle behind the icon (batch-5 mock, s6), tinted by the
   row's state. The mock's own palette is inlined raw hexes; we use the shared
   status tokens so the chip agrees with the verdict pill beside it. */
.scan-step-icon {
  width: 22px;
  height: 22px;
  flex-shrink: 0;
  display: flex;
  align-items: center;
  justify-content: center;
  border-radius: 50%;
  background: var(--gray-bg);
  transition: background 0.2s;
}
.scan-step.active             .scan-step-icon { background: var(--blue-pale); }
.scan-step.done               .scan-step-icon,
.scan-step.verdict-pass       .scan-step-icon { background: var(--green-bg); }
.scan-step.verdict-needs-attention .scan-step-icon { background: var(--amber-bg); }
.scan-step.failed             .scan-step-icon { background: var(--red-bg); }
/* The glyphs sit inside the 22px chip, so they render smaller than their
   intrinsic size. */
.scan-step-icon svg { width: 13px; height: 13px; }
/* The queued ring and the running spinner are the same circle in two states, so
   they must render identically. The spinner is a <span>, so the svg rule above
   never reached it: the queued circle rendered 9.75px wide with a 1.08px stroke
   against the spinner's 12px/2px, and the icon jumped +23% in size and +85% in
   weight the moment a row started running. Size this one to the spinner instead;
   its viewBox geometry is tuned to match (see report_progress.html). */
.scan-step-icon .ic-queued { width: 12px; height: 12px; }
/* Each row carries all icon variants; the row's status (or, for a resolved
   scenario, its verdict) class reveals exactly one. */
.scan-step-icon > * { display: none; }
.scan-step.done   .ic-done   { display: inline-flex; }
.scan-step.failed .ic-failed { display: inline-flex; }
.scan-step.queued .ic-queued { display: inline-flex; }
.scan-step.active .ic-active { display: inline-block; }
.scan-step.verdict-pass            .ic-done         { display: inline-flex; }
.scan-step.verdict-needs-attention .ic-warn         { display: inline-flex; }
.scan-step.verdict-inconclusive    .ic-inconclusive { display: inline-flex; }
.scan-step.verdict-did-not-complete .ic-inconclusive { display: inline-flex; }

/* Verdict pill — pre-rendered per row, revealed by the row's verdict class as
   each obligation lands (COD-263). */
.scan-step-verdict { margin-left: auto; }
.scan-step-verdict .sv {
  display: none;
  align-items: center;
  font-size: 0.6rem;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.06em;
  padding: 0.22rem 0.6rem;
  border-radius: 50px;
}
.scan-step.verdict-pass            .sv-pass            { display: inline-flex; background: var(--green-bg); color: var(--green); }
.scan-step.verdict-needs-attention .sv-needs-attention { display: inline-flex; background: var(--amber-bg); color: var(--amber); }
.scan-step.verdict-inconclusive    .sv-inconclusive    { display: inline-flex; background: var(--gray-bg); color: var(--text-muted); }
.scan-step.verdict-did-not-complete .sv-did-not-complete { display: inline-flex; background: var(--gray-bg); color: var(--text-muted); }
.scan-step.verdict-needs-attention .scan-step-label { font-weight: 600; }
/* Muted DSA-article tag after each step label (Einat, COD-263), e.g. "(Art. 16(1))". */
.scan-step-art { color: var(--text-muted); font-weight: 400; white-space: nowrap; }
.scan-spinner {
  width: 12px;
  height: 12px;
  border: 2px solid var(--blue-light);
  border-top-color: var(--blue);
  border-radius: 50%;
  animation: funnel-spin 0.7s linear infinite;
}
@keyframes funnel-spin { to { transform: rotate(360deg); } }
/* Full contrast, not muted: this is the page's most important text (it carries the
   prospect's actual next action — wait here, or close the tab and use the email),
   and it is the "you get your report either way" path standing against the sign-up
   card below it. Whispered in grey it left the upsell reading as the only way
   forward (Yoni + Einat, COD-263). */
.scan-note { font-size: 0.85rem; color: var(--dark); line-height: 1.6; max-width: 460px; margin-inline: auto; }
.scan-note strong { font-weight: 600; }

/* ── Dead-end recovery (failed scan: mistyped / content-less domain, COD-263) ── */
.scan-recovery { margin-top: 0.5rem; max-width: 460px; margin-inline: auto; }
.scan-recovery-note { font-size: 0.82rem; color: var(--text-muted); line-height: 1.55; margin-bottom: 1rem; }
.scan-recovery-actions { display: flex; gap: 0.75rem; flex-wrap: wrap; align-items: center; }
.btn-outline {
  display: inline-flex;
  align-items: center;
  gap: 0.5rem;
  background: var(--white);
  color: var(--dark);
  border: 1px solid var(--border);
  border-radius: 50px;
  padding: 0.9rem 2rem;
  font-family: var(--sans);
  font-size: 0.95rem;
  font-weight: 700;
  text-decoration: none;
  cursor: pointer;
  transition: background 0.15s, border-color 0.15s;
}
.btn-outline:hover { background: var(--gray-bg); border-color: var(--text-muted); }

/* ── Running tally (live verdict counts above the progress card, COD-263) ── */
.scan-tally {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: 0.5rem;
  max-width: 460px;
  margin: 1rem 0 1.25rem;
}
.scan-stat {
  background: var(--blue-pale);
  border-radius: 10px;
  padding: 0.55rem 0.5rem 0.6rem;
  text-align: center;
}
.scan-stat-num {
  font-family: var(--mono);
  font-size: 1.5rem;
  font-weight: 800;
  line-height: 1;
  color: var(--blue);
}
.scan-stat-num.green { color: var(--green); }
.scan-stat-num.amber { color: var(--amber); }
.scan-stat-lbl { font-size: 0.62rem; color: var(--text-muted); margin-top: 0.25rem; }

/* ── Conversion offer (present throughout the wait, COD-263) ── */
.scan-offer {
  display: flex;
  align-items: center;
  gap: 1rem;
  border: 1.5px solid var(--blue);
  border-radius: 16px;
  background: var(--blue-pale);
  padding: 1.1rem 1.25rem;
  max-width: 460px;
  margin: 1.25rem 0 0;
}
/* A flex display would otherwise win over the [hidden] UA rule, so the failure
   state (which sets hidden) could still show the offer — keep it withheld. */
.scan-offer[hidden] { display: none; }
.scan-offer-body { flex: 1; }
.scan-offer-h { font-size: 1rem; font-weight: 800; color: var(--dark); line-height: 1.3; margin-bottom: 0.3rem; }
.scan-offer-p { font-size: 0.8rem; color: var(--text-muted); line-height: 1.5; }
.scan-offer-p strong { color: var(--dark); }

/* Card variant — the live-progress closing CTA (batch-5 mock, s6): a centred
   card with the platform mark, rather than the inline strip the access-block
   panel still uses. Quieter chrome than the strip's blue fill, so the mark and
   the CTA carry it. */
.scan-offer--card {
  flex-direction: column;
  align-items: center;
  text-align: center;
  gap: 0;
  background: var(--white);
  border-color: var(--border);
  border-radius: 16px;
  padding: 1.5rem 1.25rem 1.4rem;
  transition: box-shadow 0.2s;
}
.scan-offer--card:hover { box-shadow: 0 6px 24px rgba(11, 69, 255, 0.1); }
.scan-offer--card .scan-offer-illo { margin-bottom: 1rem; line-height: 0; }
.scan-offer--card .scan-offer-h { font-size: 1rem; font-weight: 700; line-height: 1.35; margin-bottom: 0.6rem; }
.scan-offer--card .scan-offer-p { font-size: 0.75rem; line-height: 1.55; margin-bottom: 1.1rem; }
/* The CTA is an inline <a>, so it needs a block box before width applies. */
.scan-offer--card .scan-offer-cta { display: block; width: 100%; border-radius: 50px; text-align: center; }
.scan-offer-cta {
  flex-shrink: 0;
  background: var(--blue);
  color: var(--white);
  border-radius: 8px;
  padding: 0.7rem 1.1rem;
  font-size: 0.88rem;
  font-weight: 700;
  text-decoration: none;
  white-space: nowrap;
  transition: background 0.15s;
}
.scan-offer-cta:hover { background: var(--blue-dark); }

/* The strip stacks on narrow screens; the card variant is already a centred
   column, so it opts out rather than being stretched flat. */
@media (max-width: 560px) {
  .scan-offer:not(.scan-offer--card) { flex-direction: column; align-items: stretch; }
  .scan-offer:not(.scan-offer--card) .scan-offer-cta { text-align: center; }
}

/* ── Right-panel scan scene (interstitial / analyzing) ──
   Ported from the dsa-onboarding-revised.html mockup's RP2 "abstract scan"
   illustration: a platform frame swept by a scan beam, with two findings
   filling in and ambient tree/dot motion. Replaces the default doc-frame on
   the analyzing page so the right panel reads as a live scan. */
.scan-svg { width: 100%; max-width: 420px; height: auto; display: block; overflow: visible; position: relative; z-index: 2; }
@keyframes scan-beam-sweep {
  0%   { transform: translateY(0); opacity: 0; }
  15%  { opacity: 0.85; }
  85%  { opacity: 0.85; }
  100% { transform: translateY(180px); opacity: 0; }
}
@keyframes scan-block-fill {
  0%, 100% { stroke-dashoffset: 240; }
  50%      { stroke-dashoffset: 0; }
}
@keyframes scan-tree-sway {
  0%, 100% { transform: rotate(-3deg) translateY(0); }
  50%      { transform: rotate(3deg) translateY(-6px); }
}
@keyframes scan-dot-pulse {
  0%, 100% { transform: scale(1); opacity: 1; }
  50%      { transform: scale(1.18); opacity: 0.8; }
}
.scan-beam    { animation: scan-beam-sweep 4.5s ease-in-out infinite; }
.scan-block   { stroke-dasharray: 240; animation: scan-block-fill 6s ease-in-out infinite; }
.scan-block-2 { animation-delay: 1.2s; }
.scan-tree    { transform-origin: bottom center; transform-box: fill-box; animation: scan-tree-sway 4.5s ease-in-out infinite; }
.scan-dot     { transform-origin: center; transform-box: fill-box; animation: scan-dot-pulse 1.8s ease-in-out infinite; }

/* ── Per-step right-panel scenes (ported from the mockup, RP1/RP3/RP5/RP6) ──
   Each wizard step renders one scene in `{% block right %}`; .funnel-right
   centers it. Class names keep the mockup's rp1-/rp3-/rp5-/rp6- prefixes
   (self-contained, no collision with the funnel- / doc- / scan- families). */
.funnel-scene { position: relative; z-index: 2; }
/* TODO(COD-154): Einat's mockup had an RP4 "rings" scene whose centre showed a
   live "N ARTICLES APPLY" count that updated as the user picked platform
   categories — answers→obligation-count feedback. We removed it (it was orphaned,
   and we don't yet compute the applicable-article count). When COD-154 lands,
   revive the *count* version (git history / the mockup's #rp4), not the inert
   port, as the classification-step scene. */

/* RP1 — journey illustration */
.rp1-card { width: 100%; max-width: 420px; }
.rp1-svg { width: 100%; height: auto; display: block; overflow: visible; }
@keyframes rp1-float-big    { 0%,100% { transform: translateY(0) translateX(0); } 50% { transform: translateY(-16px) translateX(4px); } }
@keyframes rp1-float-y-soft { 0%,100% { transform: translateY(0); } 50% { transform: translateY(-10px); } }
@keyframes rp1-rock         { 0%,100% { transform: translateY(0) rotate(-6deg); } 50% { transform: translateY(-6px) rotate(6deg); } }
@keyframes rp1-pulse        { 0%,100% { transform: scale(1); opacity: 1; } 50% { transform: scale(1.18); opacity: 0.8; } }
@keyframes rp1-birds        { 0% { transform: translate(0,0); } 50% { transform: translate(-22px,-10px); } 100% { transform: translate(0,0); } }
@keyframes rp1-sway         { 0%,100% { transform: rotate(-3deg) translateY(0); } 50% { transform: rotate(3deg) translateY(-6px); } }
@keyframes rp1-dash-flow    { to { stroke-dashoffset: -40; } }
@keyframes rp1-x-pulse      { 0%,100% { transform: scale(1) rotate(0deg); } 50% { transform: scale(1.25) rotate(8deg); } }
.rp1-mountains { transform-origin: center; transform-box: fill-box; animation: rp1-float-big 5s ease-in-out infinite; }
.rp1-sun       { transform-origin: center; transform-box: fill-box; animation: rp1-pulse 3s ease-in-out infinite; }
.rp1-birds     { transform-origin: center; transform-box: fill-box; animation: rp1-birds 6s ease-in-out infinite; }
.rp1-boat      { transform-origin: center bottom; transform-box: fill-box; animation: rp1-rock 2.6s ease-in-out infinite; }
.rp1-tree      { transform-origin: bottom center; transform-box: fill-box; animation: rp1-sway 4.5s ease-in-out infinite; }
.rp1-dot       { transform-origin: center; transform-box: fill-box; animation: rp1-pulse 1.8s ease-in-out infinite; }
.rp1-x         { transform-origin: center; transform-box: fill-box; animation: rp1-x-pulse 2.4s ease-in-out infinite; }
.rp1-baretrees { transform-origin: center; transform-box: fill-box; animation: rp1-float-y-soft 4s ease-in-out infinite; }
.rp1-dashed    { stroke-dasharray: 5 4; animation: rp1-dash-flow 2.5s linear infinite; }

/* RP3 — platform / checkpoint scene */
.rp3-svg { width: 100%; max-width: 420px; height: auto; display: block; overflow: visible; }
@keyframes rp3-bob       { 0%,100% { transform: translateY(0); } 50% { transform: translateY(-3px); } }
@keyframes rp3-twinkle   { 0%,100% { opacity: 0.5; } 50% { opacity: 0.95; } }
@keyframes rp3-sway-soft { 0%,100% { transform: rotate(-0.8deg); } 50% { transform: rotate(0.8deg); } }
@keyframes rp3-slide-knob { 0%,100% { transform: translateX(0); } 50% { transform: translateX(10px); } }
@keyframes rp3-leaf-sway  { 0%,100% { transform: rotate(-1.2deg); } 50% { transform: rotate(1.2deg); } }
@keyframes rp3-square-bob { 0%,100% { transform: translateY(0) rotate(0deg); } 50% { transform: translateY(-2px) rotate(3deg); } }
.rp3-twinkle  { transform-origin: center; transform-box: fill-box; animation: rp3-twinkle 5s ease-in-out infinite; }
.rp3-twinkle-2 { animation-delay: 1s; }
.rp3-twinkle-3 { animation-delay: 2s; }
.rp3-twinkle-4 { animation-delay: 3s; }
.rp3-twinkle-5 { animation-delay: 0.7s; }
.rp3-bob      { transform-origin: center; transform-box: fill-box; animation: rp3-bob 8s ease-in-out infinite; }
.rp3-tree     { transform-origin: bottom center; transform-box: fill-box; animation: rp3-sway-soft 10s ease-in-out infinite; }
.rp3-knob-1    { animation: rp3-slide-knob 9s ease-in-out infinite; }
.rp3-knob-2    { animation: rp3-slide-knob 10s ease-in-out infinite reverse; animation-delay: 0.8s; }
.rp3-knob-3    { animation: rp3-slide-knob 8.5s ease-in-out infinite; animation-delay: 1.6s; }
.rp3-leaves    { transform-origin: bottom center; transform-box: fill-box; animation: rp3-leaf-sway 8s ease-in-out infinite; }
.rp3-leaves-2  { animation-delay: 2s; }
.rp3-square    { transform-origin: center; transform-box: fill-box; animation: rp3-square-bob 6s ease-in-out infinite; }
.rp3-square-2  { animation-delay: 1.5s; }

/* RP5 — drifting article pills over an ambient backdrop */
/* Hold the backdrop's own 480:520 aspect. Without it the wrap collapses to the
   pills' height, and since the bg SVG is `meet`-fitted it scales down and centres
   its artwork — landing the tree on top of the pills instead of down in the
   corner. Pills stay vertically centred in the resulting box. */
.rp5-wrap {
  width: 100%;
  max-width: 420px;
  aspect-ratio: 480 / 520;
  display: flex;
  flex-direction: column;
  justify-content: center;
}
.rp5-ambient {
  display: flex;
  flex-direction: column;
  gap: 0.65rem;
  align-items: flex-start;
  position: relative;
  z-index: 2;
}
.rp5-bg-svg {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  pointer-events: none;
  z-index: 1;
  overflow: visible;
}
.rp5-amb-pill {
  background: rgba(255,255,255,0.1);
  border: 1px solid rgba(255,255,255,0.15);
  border-radius: 50px;
  padding: 0.45rem 0.9rem;
  font-family: var(--mono);
  font-size: 0.62rem;
  font-weight: 500;
  color: rgba(255,255,255,0.55);
  white-space: nowrap;
  animation: rp5-pill-float 6s ease-in-out infinite;
}
.rp5-amb-pill:nth-child(2) { animation-delay: 0.8s; margin-left: 1rem; }
.rp5-amb-pill:nth-child(3) { animation-delay: 1.6s; margin-left: 0.4rem; }
.rp5-amb-pill:nth-child(4) { animation-delay: 2.4s; margin-left: 1.5rem; }
.rp5-amb-pill:nth-child(5) { animation-delay: 3.2s; margin-left: 0.2rem; }
.rp5-amb-pill:nth-child(6) { animation-delay: 4s;   margin-left: 1.2rem; }
.rp5-amb-dim  { opacity: 0.45; }
@keyframes rp5-pill-float { 0%,100% { transform: translateY(0); } 50% { transform: translateY(-4px); } }

/* RP6 — stopwatch / notify scene */
.rp6-svg { width: 100%; max-width: 360px; height: auto; display: block; overflow: visible; }
.rp6-hl   { font-family: var(--sans); font-size: 13px; font-weight: 700; fill: white; }
.rp6-mono { font-family: var(--mono); font-size: 8px; font-weight: 700; fill: rgba(255,255,255,0.5); letter-spacing: 0.16em; }
@keyframes rp6-ring-spin { from { transform: rotate(0deg); } to { transform: rotate(360deg); } }
@keyframes rp6-arc-sweep { from { transform: rotate(0deg); } to { transform: rotate(360deg); } }
@keyframes rp6-tick      { 0%,49%,100% { opacity: 0.6; } 50%,99% { opacity: 1; } }
.rp6-ring     { transform-origin: 180px 150px; animation: rp6-ring-spin 14s linear infinite; }
.rp6-arc      { transform-origin: 180px 150px; animation: rp6-arc-sweep 5s linear infinite; }
.rp6-tick-dot { animation: rp6-tick 1s steps(2, end) infinite; }

/* ── Mobile fallback (desktop-first; polish out of scope) ── */
@media (max-width: 900px) {
  .funnel-shell { flex-direction: column; }
  .funnel-left { width: 100%; padding: 1.5rem 1.25rem; }
  .funnel-right { display: none; }
}

/* ── Step 4 scan approval, inside the email card above its button (COD-477) ── */
.scan-approve {
  display: flex;
  align-items: flex-start;
  gap: 0.5rem;
  margin: 0.2rem 0.35rem 0.85rem;
  font-size: 0.78rem;
  line-height: 1.5;
  color: var(--text-sub);
  text-align: left;
  cursor: pointer;
}
.scan-approve-input { margin-top: 0.2rem; accent-color: var(--blue); flex-shrink: 0; }
