  :root {
    /* Brand */
    --mercury-red: #ad1b1e;
    --mercury-red-hover: #911517;
    --mercury-red-soft: #fbe9ea;
    --mercury-red-tint: #f5d5d6;
    --mercury-black: #000000;

    /* Surfaces */
    --surface-white: #ffffff;
    --surface-50: #f7f7f8;
    --surface-100: #eceef1;
    --border-grey: #d6d9de;
    /* v23.6 (D-01): form-control borders need >=3:1 against their background
       (WCAG 2.2 AA 1.4.11 Non-text Contrast). The decorative --border-grey
       (#d6d9de, 1.42:1 on white) is fine for dividers but too faint as the
       only boundary of a text input, so inputs use this darker token (3.30:1
       on white). Keep dividers on --border-grey; controls on --border-input. */
    --border-input: #888e96;
    --text-secondary: #5a5f66;
    --surface-inverse: #0f1216;
    --on-inverse: #ffffff;

    /* v18.9: the stock-status palette (in-stock green, on-order amber,
       sold-out gray) went out with stock. The two muted grays below
       survive only because the empty-results count chip reuses them
       (.of-card-count[data-empty="true"]) to read as "quiet, nothing
       here" — they're no longer an availability signal. */
    --status-soldout: #595f68;
    --status-soldout-soft: #ecedf0;

    /* Money-positive accent: green for savings, gain pills,
       AggregateRating chips. (Until v18.9 this was kept distinct from
       the in-stock green so the two reads didn't collide; stock is gone
       now, but the tuned value stays.) Darkened from #1a7f4a to #157a3f
       for clear ≥5:1 contrast on white and ≥4.5:1 on the soft fill. */
    --savings-green: #157a3f;
    --savings-green-soft: #e7f4ed;
    --savings-green-border: #b5dcc4;

    /* PNW water accent. Used very sparingly: separators, footer
       watermarks, the no-match background tint. Cool steel-blue
       reads as "Pacific Northwest marine" without competing with
       Mercury red. */
    --water-blue: #2f6477;
    --water-blue-soft: #e9f1f4;

    /* Warning + error */
    --warn-bg: #fff8e6;
    --warn-border: #f0d28a;
    --warn-text: #5c4a13;
    --warn-strong: #3d3009;
    --error-bg: #fff5f5;
    --error-border: #f5c1c2;
    --error-text: #6f1316;

    /* Geometry */
    --radius-sm: 6px;
    --radius: 10px;
    --radius-lg: 14px;
    --shadow-1: 0 1px 2px rgba(15, 18, 22, 0.06);
    --shadow-2: 0 6px 16px rgba(15, 18, 22, 0.08), 0 2px 4px rgba(15, 18, 22, 0.04);
    --shadow-3: 0 14px 32px rgba(15, 18, 22, 0.14), 0 4px 8px rgba(15, 18, 22, 0.06);
    --ease: cubic-bezier(0.2, 0.0, 0.0, 1.0);
    --ease-out-back: cubic-bezier(0.34, 1.56, 0.64, 1);
    --dur-fast: 160ms;
    --dur-med: 250ms;
    interpolate-size: allow-keywords; /* lets block-size animate to auto (#9) */

    /* Display font for the H1 + step progress numerals + the
       engine HP cowl badge. Falls back to Roboto-Slab-equivalents
       if the Slab font fails to load. */
    --font-display: 'Roboto Slab', 'Roboto Slab Fallback', 'Source Serif Pro', 'Georgia', serif;
  }

  /* When a sticky bottom bar is present (mobile top-match bar, or
     the compare bar), browser-driven scroll-to-focus needs to
     leave room for it. Otherwise the focused element can land
     entirely beneath the bar (WCAG 2.4.11 Focus Not Obscured Min).
     `env(safe-area-inset-bottom)` accounts for iOS home indicator. */
  html {
    scroll-padding-bottom: calc(96px + env(safe-area-inset-bottom, 0px));
  }

  *, *::before, *::after { box-sizing: border-box; }
  html { -webkit-text-size-adjust: 100%; }
  body {
    margin: 0;
    font-family: 'Roboto', system-ui, -apple-system, 'Segoe UI', Arial, sans-serif;
    font-size: 16px;
    line-height: 24px;
    color: var(--mercury-black);
    background:
      radial-gradient(1200px 600px at 50% -200px, var(--mercury-red-soft), transparent 70%),
      var(--surface-50);
    min-height: 100vh;
    -webkit-font-smoothing: antialiased;
    text-rendering: optimizeLegibility;
  }

  /* v23.5 (I-01): suppress the default iOS Safari gray tap-highlight box on
     the Finder's own tappable elements. Our :active / aria-pressed / [open]
     states already provide tap feedback; the gray flash reads as unfinished
     on the brand surface. Scoped to the Finder-owned containers (#of-app, the
     modal, and the report-problem footer all sit under the Finder, with the
     modal a <body> sibling) so we don't restyle CMS chrome outside the app. */
  #of-app, #of-modal, .of-site-foot { -webkit-tap-highlight-color: transparent; }

  h1, h2, h3, h4 { font-weight: 700; margin: 0; color: var(--mercury-black); }
  h1 { font-size: 32px; line-height: 40px; }
  h2 { font-size: 24px; line-height: 32px; }
  h3 { font-size: 18px; line-height: 24px; }
  p  { margin: 0; }

  a { color: var(--mercury-red); text-decoration: none; }
  a:hover { text-decoration: underline; }
  .of-btn:hover, .of-btn:focus { text-decoration: none; }

  button { font-family: inherit; }

  /* v16.8: pointer is the default for everything clickable, so no button,
     link, disclosure, or ARIA button can fall back to the arrow cursor.
     Disabled controls stay not-allowed — the per-class disabled rules below
     have higher specificity, and this catch-all covers any others. */
  a[href], button, summary, select, [role="button"], label[for] { cursor: pointer; }
  button:disabled, [role="button"][aria-disabled="true"], [disabled] { cursor: not-allowed; }

  :focus-visible {
    outline: 3px solid var(--mercury-red);
    outline-offset: 2px;
    border-radius: var(--radius-sm);
  }

  /* ============ Outer shell (one rounded box) ============ */
  .of-shell {
    max-width: 1200px;
    margin: 24px auto;
    padding: 32px 20px 40px;
    background: var(--surface-white);
    border: 1px solid var(--border-grey);
    border-radius: 24px;
    box-shadow: var(--shadow-2);
    position: relative;
    overflow: hidden;
  }
  .of-shell::before {
    content: "";
    position: absolute;
    inset: 0 0 auto 0;
    height: 10px;
    background: linear-gradient(90deg, var(--mercury-red) 0%, #d23438 60%, var(--mercury-red) 100%);
  }
  @media (min-width: 768px) {
    .of-shell { margin: 40px auto; padding: 48px 40px 56px; border-radius: 28px; }
  }

  .of-header {
    text-align: center;
    margin-bottom: 16px;
    position: relative;
    padding-bottom: 14px;
  }
  @media (min-width: 600px) {
    .of-header { margin-bottom: 28px; padding-bottom: 22px; }
  }
  /* Thin solid divider under the header, scoped to the landing view.
     (The compact-header rule hides it on steps 2-7.) Replaces the old
     dotted "water-line" accent, which read as a broken/loading element. */
  .of-header::after {
    content: "";
    position: absolute;
    left: 50%;
    transform: translateX(-50%);
    bottom: 0;
    width: min(420px, 70%);
    height: 1px;
    background: var(--border-grey);
    pointer-events: none;
  }
  .of-eyebrow {
    display: inline-block;
    font-size: 12px;
    font-weight: 700;
    letter-spacing: 0.14em;
    color: var(--mercury-red);
    text-transform: uppercase;
    margin-bottom: 8px;
    padding: 4px 12px;
    background: var(--mercury-red-soft);
    border-radius: 999px;
  }
  /* Display H1: larger, heavier, with a slab serif for editorial
     weight. Slab also reads as "marine technical" — pairs well
     with the dealer-tool positioning. */
  #of-title {
    font-family: var(--font-display);
    font-weight: 800;
    font-size: 26px;
    line-height: 30px;
    letter-spacing: -0.01em;
    color: var(--mercury-black);
    margin: 0;
  }
  @media (min-width: 600px) {
    #of-title { font-size: 40px; line-height: 46px; letter-spacing: -0.015em; }
  }
  @media (min-width: 900px) {
    #of-title { font-size: 48px; line-height: 54px; letter-spacing: -0.02em; }
  }
  /* Compact header (set by updateHeaderCompact once the user leaves the
     landing view). The eyebrow and water-line accent drop out, and the
     H1 shrinks to a one-line brand label so it no longer competes
     with the per-step question heading below it. Reclaims vertical space
     so the phone buyer reaches the choice sooner. */
  .of-header--compact {
    margin-bottom: 16px;
    padding-bottom: 0;
  }
  .of-header--compact .of-eyebrow {
    display: none;
  }
  .of-header--compact::after {
    display: none;
  }
  .of-header--compact #of-title {
    font-size: 20px;
    line-height: 26px;
    letter-spacing: -0.01em;
  }
  @media (min-width: 600px) {
    .of-header--compact #of-title { font-size: 22px; line-height: 28px; }
  }
  @media (min-width: 900px) {
    .of-header--compact #of-title { font-size: 24px; line-height: 30px; }
  }

  /* ============ Mode toggle (tablist) ============ */
  .of-modes-wrap {
    display: flex;
    justify-content: center;
    align-items: center;
    gap: 16px;
    margin: 14px 0 12px;
  }
  @media (min-width: 600px) {
    .of-modes-wrap { margin: 24px 0 16px; }
  }
  .of-modes {
    display: inline-flex;
    padding: 4px;
    background: var(--surface-100);
    border-radius: 999px;
    gap: 2px;
  }
  .of-mode-tab {
    appearance: none;
    border: 0;
    background: transparent;
    font-weight: 500;
    font-size: 14px;
    line-height: 20px;
    padding: 12px 22px; /* v19.9: 12+12+20 = 44px min touch target */
    border-radius: 999px;
    cursor: pointer;
    color: var(--text-secondary);
    transition: color var(--dur-fast) var(--ease), background var(--dur-fast) var(--ease), box-shadow var(--dur-fast) var(--ease);
  }
  .of-mode-tab:hover { color: var(--mercury-red); }
  /* Visual hierarchy: Guided is the recommended path for most users,
     so the inactive Guided tab carries a hint of brand color while
     Quick stays fully neutral. The selected (aria-selected) tab gets
     the elevated white surface treatment regardless. */
  .of-mode-tab#of-tab-guided:not([aria-selected="true"]) {
    color: var(--mercury-red);
  }
  /* v24.3: widen the Guided-primary hierarchy. Guided already carries brand
     color when inactive; drop the inactive Quick tab to a lighter weight so
     Guided reads as the default for the not-sure-on-specs buyer and Quick
     reads as the shortcut. Weight only, so text contrast is unchanged. */
  .of-mode-tab#of-tab-quick:not([aria-selected="true"]) {
    font-weight: 400;
  }
  .of-mode-tab[aria-selected="true"] {
    background: var(--surface-white);
    color: var(--mercury-black);
    box-shadow: var(--shadow-1);
    border: 1px solid var(--mercury-red);
  }
  /* Mode-tab labels: full "Guided search" / "Quick filters" on wider
     screens, compact "Guided" / "Quick" on phones and small tablets in
     portrait. Swapped purely in CSS. Breakpoint is 540px because the
     full labels wrap on large phones whose CSS viewport runs wider than
     you'd expect — the S26 Ultra reports up to ~480px — so we clear that
     with headroom while keeping full labels on real tablets/desktop. */
  .of-mode-tab .of-mode-tab-full { display: inline; }
  .of-mode-tab .of-mode-tab-short { display: none; }
  @media (max-width: 540px) {
    .of-mode-tab .of-mode-tab-full { display: none; }
    .of-mode-tab .of-mode-tab-short { display: inline; }
    .of-mode-tab { padding: 10px 16px; }
  }

  /* ============ Panels (cards container) ============ */
  .of-panel {
    background: transparent;
    padding: 0;
  }
  @media (min-width: 768px) { .of-panel { padding: 0; } }

  /* ============ Back link ============ */
  /* Light top-left "go back" affordance on steps 2+. Kept visually quiet
     (a text link, not a filled button) so it doesn't compete with the
     option cards, but with a 44px hit area for touch (WCAG 2.5.5). */
  .of-back-link {
    display: inline-flex;
    align-items: center;
    gap: 6px;
    margin: 0 0 8px;
    padding: 6px 6px 6px 2px;
    min-height: 44px;
    background: none;
    border: 0;
    color: var(--mercury-red);
    font-family: inherit;
    font-size: 14px;
    font-weight: 600;
    line-height: 1.2;
    cursor: pointer;
    border-radius: 6px;
  }
  .of-back-link:hover { text-decoration: underline; }
  .of-back-link:focus-visible { outline: 3px solid var(--mercury-red); outline-offset: 2px; }
  .of-back-link-arrow { font-size: 16px; line-height: 1; }

  /* ============ Breadcrumb ============ */
  .of-breadcrumb { margin-bottom: 20px; }
  .of-breadcrumb-list {
    list-style: none;
    padding: 0;
    margin: 0;
    display: flex;
    flex-wrap: wrap;
    gap: 6px;
  }
  /* Breadcrumb chips: subtler than result-page selection chips
     because they're navigational, not editable selections. Lighter
     border, neutral background. */
  .of-crumb-btn {
    appearance: none;
    border: 1px solid var(--border-grey);
    background: var(--surface-50);
    color: var(--text-secondary);
    font-size: 12px;
    font-weight: 500;
    line-height: 18px;
    padding: 5px 11px;
    border-radius: 999px;
    cursor: pointer;
    transition: background var(--dur-fast) var(--ease), border-color var(--dur-fast) var(--ease), color var(--dur-fast) var(--ease);
  }
  .of-crumb-btn:hover {
    background: var(--mercury-red-soft);
    border-color: var(--mercury-red);
    color: var(--mercury-red);
  }
  .of-crumb-sep {
    color: var(--mercury-red);
    font-size: 14px;
    font-weight: 700;
    align-self: center;
  }
  /* Tiny "auto" pill nested inside a crumb or selection chip. Signals
     that this value was inferred for the user (today: HP bucket from
     the Application choice in Step 1) rather than picked explicitly,
     while keeping the chip itself clickable for edit. Visual treatment
     is subordinate to the chip — uppercase micro-caps in a softer tint
     so it reads as a label, not a separate control. */
  .of-crumb-btn-auto { padding-right: 6px; }
  .of-crumb-auto,
  .of-selection-chip-auto {
    display: inline-block;
    margin-left: 6px;
    padding: 0 5px;
    font-size: 9px;
    font-weight: 700;
    line-height: 14px;
    letter-spacing: 0.06em;
    text-transform: uppercase;
    color: #6a4f00;
    background: #fff4d1;
    border: 1px solid #e8c870;
    border-radius: 3px;
    vertical-align: 1px;
  }
  .of-crumb-btn:hover .of-crumb-auto {
    color: #5b3d00;
    border-color: #d49a3a;
  }
  .of-selection-chip--auto .of-selection-chip-auto {
    /* The selection chip itself is taller than a crumb, so the badge
       can carry slightly more weight without dominating. */
    font-size: 10px;
    line-height: 16px;
  }

  /* ============ Step heading ============ */
  /* .of-step-label is defined in the Step Progress block below */
  /* ============ Step progress indicator ============
     Horizontal strip of connected pills above the step heading.
     Completed steps are filled Mercury red, the current step is
     outlined with a stronger ring, and future steps stay subtly
     gray. Replaces the small "STEP X OF Y" text label as the
     primary funnel-orientation cue (the small label is still
     rendered below for screen readers and as a compact reference).
     ============================================================ */
  .of-progress {
    display: flex;
    align-items: center;
    gap: 4px;
    margin: 0 0 16px;
    padding: 0;
    list-style: none;
  }
  .of-progress-item {
    flex: 1;
    height: 6px;
    border-radius: 999px;
    background: var(--surface-100);
    position: relative;
    transition: background var(--dur-med) var(--ease);
  }
  .of-progress-item[data-state="done"] {
    background: var(--mercury-red);
  }
  .of-progress-item[data-state="current"] {
    background: var(--mercury-red);
  }
  /* Option A marker: a raised dot centred on the current segment so the
     buyer sees "you are here" without reading the step label. ::before is
     the red dot (paints first), ::after the white centre (paints on top).
     Done segments stay solid red, upcoming neutral. */
  .of-progress-item[data-state="current"]::before {
    content: "";
    position: absolute;
    left: 50%;
    top: 50%;
    width: 16px;
    height: 16px;
    transform: translate(-50%, -50%);
    border-radius: 50%;
    background: var(--mercury-red);
    box-shadow: 0 0 0 3px var(--surface-white);
  }
  .of-progress-item[data-state="current"]::after {
    content: "";
    position: absolute;
    left: 50%;
    top: 50%;
    width: 6px;
    height: 6px;
    transform: translate(-50%, -50%);
    border-radius: 50%;
    background: var(--surface-white);
  }
  /* Optional small numerals visible at larger breakpoints */
  .of-progress-num {
    display: none;
    font-family: var(--font-display);
    font-size: 11px;
    font-weight: 700;
    color: var(--text-secondary);
    text-align: center;
    margin-top: 4px;
  }
  @media (min-width: 600px) {
    .of-progress { gap: 6px; }
    .of-progress-item { height: 8px; }
  }
  .of-step-label {
    font-size: 12px;
    font-weight: 700;
    letter-spacing: 0.12em;
    text-transform: uppercase;
    color: var(--mercury-red);
    margin: 0 0 6px;
    padding: 3px 10px;
    background: var(--mercury-red-soft);
    border-radius: 999px;
    display: inline-block;
  }

  /* ============ Skip-to-content link ============
     Keyboard users can skip past the utility bar and mode toggle
     to land directly on the active panel. Visually hidden until
     focused (WCAG 2.4.1 Bypass Blocks). */
  .of-skip {
    position: absolute;
    left: -10000px;
    top: auto;
    width: 1px;
    height: 1px;
    overflow: hidden;
  }
  .of-skip:focus,
  .of-skip:focus-visible {
    position: fixed;
    left: 16px;
    top: 16px;
    width: auto;
    height: auto;
    z-index: 1000;
    padding: 10px 16px;
    background: var(--mercury-red);
    color: var(--surface-white);
    border-radius: 8px;
    font-weight: 700;
    font-size: 14px;
    text-decoration: none;
    box-shadow: var(--shadow-2);
    outline: 3px solid var(--surface-white);
    outline-offset: 2px;
  }

  .of-step-title {
    margin-top: 8px;
    margin-bottom: 12px;
    position: relative;
    padding-bottom: 8px;
  }
  /* The step heading uses tabindex="-1" and receives focus on each
     step transition so screen readers announce the new context. That
     focus is programmatic, not keyboard-reachable (the heading is never
     in the tab order), so it does not need a visible focus indicator of
     its own; a box drawn on every step change just reads as a stray
     border. !important because the global :focus-visible rule above and
     the Turnkey host styles both try to paint a ring here, and this is a
     non-interactive scripted-focus target. The interactive cards below
     carry their own focus styles. WCAG 2.4.7 governs components focused
     via keyboard operation, which this heading cannot be, so suppressing
     the outline stays conformant. */
  .of-step-title:focus,
  .of-step-title:focus-visible {
    outline: none !important;
  }
  .of-step-title::after {
    content: "";
    position: absolute;
    left: 0;
    bottom: 0;
    width: 40px;
    height: 3px;
    background: var(--mercury-red);
    border-radius: 999px;
  }
  .of-step-help {
    color: var(--text-secondary);
    font-size: 15px;
    line-height: 22px;
    margin-bottom: 20px;
  }

  /* ============ Selection card grid ============ */
  .of-options {
    display: grid;
    gap: 12px;
    grid-template-columns: 1fr;
  }
  @media (min-width: 560px) { .of-options { grid-template-columns: repeat(2, 1fr); } }
  /* Mobile (<560px): the 4- and 5-card steps go two-up so the phone buyer
     sees more choices per screen and reaches the answer sooner, instead of
     scrolling a long single-column stack. Scoped via :has() to grids with
     EXACTLY 4 or 5 cards, mirroring the v14.8 pattern below — the 2-card
     steps (steering, gas/electric, starting) stay single-column because two
     narrow cards with a wide side-gap reads worse than one clean column.
     The 5th card on the boat-type step (odd one out) spans both columns so
     a lone half-width card doesn't read as a bug. Falls back to the base
     single-column if :has is unsupported. */
  @media (max-width: 559.98px) {
    .of-options:has(> :nth-child(4):last-child),
    .of-options:has(> :nth-child(5):last-child) {
      grid-template-columns: repeat(2, 1fr);
    }
    .of-options:has(> :nth-child(5):last-child) > :nth-child(5) {
      grid-column: 1 / -1;
    }
  }
  /* At >=900px we render the option cards in a single row of four.
     Steps 1, 2, and 4 each have exactly four options, so this fits
     cleanly. Step 6 has five options (the fifth is "Recommend for
     me") which renders as 4+1, intentionally setting the recommend
     card apart from the four configuration choices. */
  @media (min-width: 900px) { .of-options { grid-template-columns: repeat(4, 1fr); } }
  /* v14.8: the 5-option step (boat type) goes 1 -> 2 -> 3 -> 5 instead of
     wrapping 4+1. Scoped via :has() to a grid with EXACTLY 5 cards, so the
     2/3/4-option steps are untouched; falls back to 4-across if :has is absent. */
  @media (min-width: 760px) { .of-options:has(> :nth-child(5):last-child) { grid-template-columns: repeat(3, 1fr); } }
  @media (min-width: 900px) { .of-options:has(> :nth-child(5):last-child) { grid-template-columns: repeat(5, 1fr); } }
  .of-options.of-options-tight { grid-template-columns: repeat(2, 1fr); }
  @media (min-width: 560px) {
    .of-options.of-options-tight { grid-template-columns: repeat(4, 1fr); }
  }

  .of-card {
    position: relative;
    appearance: none;
    text-align: center;
    background: var(--surface-white);
    border: 1px solid var(--border-grey);
    border-radius: var(--radius);
    padding: 22px 16px 18px;
    cursor: pointer;
    transition: border-color var(--dur-fast) var(--ease), box-shadow var(--dur-fast) var(--ease), background var(--dur-fast) var(--ease), transform var(--dur-fast) var(--ease);
    overflow: hidden;
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: 4px;
  }
  .of-card::after {
    content: "";
    position: absolute;
    inset: 0;
    background: var(--mercury-red);
    opacity: 0;
    pointer-events: none;
    transition: opacity var(--dur-fast) var(--ease);
    border-radius: inherit;
  }
  .of-card:hover {
    box-shadow: var(--shadow-2);
    border-color: var(--mercury-red);
  }
  .of-card:hover::after { opacity: 0.04; }
  .of-card[aria-pressed="true"] {
    border: 2px solid var(--mercury-red);
    padding: 21px 15px 17px;
    background: var(--mercury-red-soft);
  }
  .of-card[aria-pressed="true"]::after { opacity: 0.06; background: var(--mercury-red); }
  /* v19.9: a disabled bucket's sub-text IS the recovery message ("No electric
     engines in this HP range"), so don't fade it into illegibility. Signal
     disabled via a muted fill + dimmed icon/title, but keep the explanation at
     full contrast (it was ~2.5:1 at the old 0.45 card opacity — marginal in sun).
     Disabled controls are WCAG-exempt, but the buyer still needs to read why. */
  .of-card[disabled] {
    cursor: not-allowed;
    background: var(--surface-50);
    border-color: var(--border-grey);
    opacity: 0.6;
  }
  .of-card[disabled] .of-card-icon,
  .of-card[disabled] .of-card-title { opacity: 0.5; }
  .of-card[disabled]:hover { box-shadow: none; border-color: var(--border-grey); }
  .of-card[disabled]:hover::after { opacity: 0; }

  .of-card-icon {
    width: 48px;
    height: 48px;
    margin-bottom: 8px;
    position: relative;
    display: flex;
    align-items: center;
    justify-content: center;
  }
  .of-card-icon svg { width: 100%; height: 100%; display: block; }
  /* Subtle hover lift on the option-card icons and the engine
     illustration SVGs. Tells the user the card is interactive
     without being distracting. Animations are disabled for users
     with prefers-reduced-motion. */
  .of-card-icon svg {
    transition: transform 0.22s ease-out;
  }
  .of-card:hover:not([disabled]) .of-card-icon svg {
    transform: scale(1.08);
  }
  .of-engine-illustration svg {
    transition: transform 0.28s ease-out;
  }
  .of-engine:hover .of-engine-illustration svg {
    transform: scale(1.04) translateY(-2px);
  }
  @media (prefers-reduced-motion: reduce) {
    .of-card-icon svg,
    .of-engine-illustration svg {
      transition: none;
    }
    .of-card:hover:not([disabled]) .of-card-icon svg,
    .of-engine:hover .of-engine-illustration svg {
      transform: none;
    }
  }
  .of-card-title {
    font-weight: 700;
    font-size: 16px;
    line-height: 22px;
    margin-bottom: 2px;
    position: relative;
  }
  .of-card-sub {
    font-size: 14px;
    line-height: 20px;
    color: var(--text-secondary);
    position: relative;
  }
  /* Per-option result count chip. Positioned top-right of each card
     so users see how many engines remain if they pick this option.
     Hidden via JS when the count equals the full pool. */
  .of-card {
    position: relative;
  }
  .of-card-count {
    position: absolute;
    top: 10px;
    right: 10px;
    min-width: 24px;
    height: 22px;
    padding: 0 7px;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    background: var(--surface-100);
    color: var(--text-secondary);
    border-radius: 999px;
    font-size: 12px;
    font-weight: 700;
    line-height: 1;
    font-variant-numeric: tabular-nums;
    pointer-events: none;
    transition: background var(--dur-fast) var(--ease), color var(--dur-fast) var(--ease);
  }
  .of-card:hover .of-card-count,
  .of-card[aria-pressed="true"] .of-card-count {
    background: var(--mercury-red-soft);
    color: var(--mercury-red);
  }
  /* Zero-match: gray out the entire card and label the count chip
     as the exclusion reason. Card stays tabbable so the user can
     still inspect it, but visually it's clearly not a path forward. */
  .of-card-count[data-empty="true"] {
    background: var(--status-soldout-soft);
    color: var(--status-soldout);
  }
  .of-card:has(.of-card-count[data-empty="true"]) {
    opacity: 0.55;
  }
  .of-card:has(.of-card-count[data-empty="true"]):hover {
    opacity: 0.7;
  }
  /* Mobile compactness: at very narrow widths each option card
     gets denser (less padding, hide sub-text) so the user can see
     more options without scrolling. Sub-text is still announced to
     screen readers via the existing markup. */
  @media (max-width: 480px) {
    .of-card { padding: 14px 14px 12px; }
    .of-card[aria-pressed="true"] { padding: 13px 13px 11px; }
    .of-card .of-card-sub {
      display: none;
    }
    .of-card-icon { width: 36px; height: 36px; margin-bottom: 6px; }
    .of-card-title { font-size: 15px; line-height: 20px; }
  }

  /* Inline helper image for "I'm not sure" shaft helper */
  .of-helper-diagram {
    margin-top: 20px;
    padding: 16px;
    background: var(--surface-50);
    border: 1px dashed var(--border-grey);
    border-radius: var(--radius);
    font-size: 14px;
    line-height: 20px;
    color: var(--text-secondary);
  }
  .of-helper-diagram strong { color: var(--mercury-black); }
  .of-helper-diagram svg { display: block; margin: 12px auto 0; max-width: 100%; height: auto; }

  /* Interactive transom visualizer — lives inside the shaft helper.
     The user measures their transom and we map it to a shaft length
     (15/20/25), then write that straight into state.selections.shaft
     via the same pickAndAdvance path the option cards use. No new
     filter logic: the recommendation IS the existing selection. */
  .of-tv { margin-top: 16px; }
  .of-tv-controls {
    display: flex;
    align-items: center;
    gap: 12px;
    flex-wrap: wrap;
    margin-top: 4px;
  }
  .of-tv-num {
    width: 88px;
    padding: 8px 10px;
    font-size: 16px; /* >=16px so iOS doesn't zoom on focus */
    font-weight: 700;
    color: var(--mercury-black);
    border: 1px solid var(--border-grey);
    border-radius: var(--radius-sm);
    text-align: center;
  }
  .of-tv-unit { font-size: 14px; color: var(--text-secondary); }
  .of-tv-slider { flex: 1 1 180px; min-width: 160px; accent-color: var(--mercury-red); }
  .of-tv-ticks {
    display: flex; justify-content: space-between;
    font-size: 12px; color: var(--text-secondary); /* v23.6 (D-05): 11->12px, sunlight legibility */
    margin-top: 2px; padding: 0 2px;
  }
  /* aria-live result card. State carried by text + icon, never colour alone. */
  .of-tv-result {
    margin-top: 14px; padding: 12px 14px;
    border: 1px solid var(--border-grey);
    border-radius: var(--radius);
    background: var(--surface-50);
    font-size: 14px; line-height: 20px; color: var(--mercury-black);
  }
  .of-tv-result.is-good { background: var(--savings-green-soft); border-color: var(--savings-green-border); }
  .of-tv-result.is-good .of-tv-headline { color: var(--savings-green); }
  .of-tv-result.is-ambiguous { background: var(--warn-bg); border-color: var(--warn-border); }
  .of-tv-result.is-ambiguous .of-tv-headline { color: var(--warn-strong); }
  .of-tv-result.is-out { background: var(--mercury-red-soft); border-color: var(--mercury-red-tint); }
  .of-tv-result.is-out .of-tv-headline { color: var(--mercury-red); }
  .of-tv-headline { font-weight: 700; font-size: 15px; margin-bottom: 4px; }
  .of-tv-actions { display: flex; gap: 8px; flex-wrap: wrap; margin-top: 12px; }
  .of-tv-actions .of-btn { padding: 8px 14px; font-size: 14px; }
  .of-tv-note { margin-top: 10px; font-size: 13px; color: var(--text-secondary); }

  /* Disclosure that surfaces the visualizer on the transom step.
     The summary carries .of-btn .of-btn-secondary so it reads as a
     button; here we just strip the native disclosure marker and
     size it to match the in-tool action buttons. */
  .of-tv-disclosure { margin-top: 16px; }
  .of-tv-summary {
    list-style: none;
    font-size: 14px;
    padding: 8px 16px;
    transition: background var(--dur-fast) var(--ease), box-shadow var(--dur-fast) var(--ease), transform var(--dur-fast) var(--ease);
  }
  /* stronger hover: darken, lift, deepen the shadow. Double class so it beats
     .of-btn-primary:hover (equal specificity otherwise, and that one wins on order). */
  .of-btn-primary.of-tv-summary:hover, .of-btn-primary.of-tv-summary:focus-visible {
    background: var(--mercury-red-hover, #911517);
    box-shadow: var(--shadow-3);
    transform: translateY(-1px);
  }
  .of-btn-primary.of-tv-summary:active { transform: translateY(0); box-shadow: var(--shadow-2); }
  @media (prefers-reduced-motion: reduce) { .of-btn-primary.of-tv-summary:hover, .of-btn-primary.of-tv-summary:focus-visible { transform: none; } }
  .of-tv-summary::-webkit-details-marker { display: none; }
  .of-tv-summary::marker { content: ''; }
  /* Inside the disclosure the helper box already carries its own
     border, so drop its extra top margin to avoid a double gap. */
  .of-tv-disclosure .of-helper-diagram { margin-top: 8px; }

  /* ============ v10.5 grouped variant selector ============ */
  .of-results-reason { font-size: 14px; color: var(--text-secondary); margin-top: 6px; }
  .of-results-pills { display: flex; flex-wrap: wrap; align-items: stretch; gap: 8px; margin-top: 10px; }
  .of-results-pills:empty { display: none; }
  /* #21 (v17.0): shareable deep-link row in the save & share modal. */
  .of-modal-sharelink { margin: 4px 0 16px; }
  .of-modal-sharelink-help { font-size: 13px; color: var(--text-secondary); margin-bottom: 6px; }
  .of-modal-sharelink-row { display: flex; gap: 8px; align-items: stretch; }
  /* font-size:16px (I-03) prevents iOS Safari auto-zoom when the copy handler
     focuses+selects this field; min-height:44px (I-05) matches the adjacent
     copy button as a tap target, since this input is the manual-copy fallback. */
  .of-modal-sharelink-input { flex: 1 1 auto; min-width: 0; font-size: 16px; font-family: inherit; padding: 8px 10px; min-height: 44px; box-sizing: border-box; border: 1px solid var(--border-input, #888e96); border-radius: var(--radius-sm, 6px); background: var(--surface-100, #f4f5f7); color: var(--text-secondary); }
  .of-modal-sharelink-copy { flex: none; white-space: nowrap; padding: 8px 14px; min-height: 44px; border: 1px solid var(--mercury-red, #ad1b1e); border-radius: var(--radius-sm, 6px); background: var(--surface-white, #fff); color: var(--mercury-red, #ad1b1e); font-size: 13px; font-weight: 600; font-family: inherit; cursor: pointer; }
  .of-modal-sharelink-copy[data-copied="true"] { background: var(--mercury-red, #ad1b1e); color: #fff; }
  .of-results-sort { display: flex; align-items: center; gap: 8px; margin: 16px 0 14px; }
  .of-results-sort-label { font-size: 13px; font-weight: 600; color: var(--text-secondary); }
  .of-results-sort-select { min-height: 44px; padding: 8px 34px 8px 12px; max-width: 220px; }
  /* v24.2: results controls strip - Sort on the left, Why these / Compare /
     Save & share on the right, sitting directly above the grid. The finish
     scroll anchors this row so these controls land on screen with the cards
     instead of stranded in the header above the fold. */
  .of-results-controls {
    display: flex;
    justify-content: space-between;
    align-items: center;
    flex-wrap: wrap;
    gap: 10px;
    margin: 16px 0 14px;
  }
  .of-results-controls-left { display: flex; align-items: center; gap: 8px; }
  .of-results-controls-right { display: flex; align-items: center; flex-wrap: wrap; gap: 8px; }

  .of-variant-hint { font-size: 13px; font-weight: 600; color: var(--text-secondary); margin: 4px 0 8px; }
  .of-variant-shaft { display: flex; gap: 6px; margin: 0 0 10px; }
  .of-variant-shaft-btn {
    flex: 1 1 auto; min-height: 44px; padding: 8px 6px; cursor: pointer;
    font: inherit; font-weight: 700; font-size: 14px;
    background: var(--surface-white); color: var(--mercury-black);
    border: 1px solid var(--border-grey); border-radius: var(--radius-sm);
    transition: background var(--dur-fast) var(--ease), border-color var(--dur-fast) var(--ease);
  }
  .of-variant-shaft-btn[data-active="true"] { background: var(--mercury-red); color: var(--surface-white); border-color: var(--mercury-red); }
  .of-variant-shaft-btn:disabled { opacity: 0.4; cursor: not-allowed; text-decoration: line-through; }
  .of-variant-configs { display: flex; flex-direction: column; gap: 6px; margin: 0 0 12px; }
  .of-variant-config {
    display: flex; align-items: center; justify-content: space-between; gap: 10px;
    width: 100%; min-height: 44px; padding: 8px 12px; cursor: pointer; text-align: left;
    font: inherit; font-size: 13px;
    background: var(--surface-white); color: var(--mercury-black);
    border: 1px solid var(--border-grey); border-radius: var(--radius-sm);
    transition: border-color var(--dur-fast) var(--ease), background var(--dur-fast) var(--ease);
  }
  .of-variant-config[data-active="true"] { border-color: var(--mercury-red); background: var(--mercury-red-soft); }
  .of-variant-config-label { font-weight: 500; }
  .of-variant-config-price { font-weight: 700; white-space: nowrap; }

  /* #1 (v25.6): "See all setups" collapsed disclosure wrapping the config
     matrix. Summary reads as a quiet red text toggle (not a button); the
     matrix inside keeps its own .of-variant-configs styling. */
  .of-setups-disc { margin: 0 0 12px; }
  .of-setups-summary {
    display: inline-flex; align-items: center; gap: 6px;
    min-height: 44px; padding: 6px 2px;
    color: var(--mercury-red); font-size: 13px; font-weight: 600;
    cursor: pointer; list-style: none; -webkit-tap-highlight-color: transparent;
  }
  .of-setups-summary::-webkit-details-marker { display: none; }
  .of-setups-summary::after { content: '\25BE'; font-size: 11px; line-height: 1; }
  .of-setups-disc[open] .of-setups-summary::after { content: '\25B4'; }
  .of-setups-summary:focus-visible { outline: 3px solid var(--mercury-red); outline-offset: 2px; border-radius: 6px; }
  .of-setups-disc[open] .of-variant-configs { margin-top: 8px; }

  .of-engine-chips { display: flex; flex-wrap: wrap; gap: 6px; margin: 8px 0 12px; }
  .of-engine-chip {
    font-size: 12px; line-height: 18px; padding: 2px 9px; border-radius: 999px;
    background: var(--surface-100); color: var(--text-secondary);
  }
  .of-show-more { display: block; margin: 18px auto 0; }

  /* ============ Quick filters ============ */
  .of-quick {
    display: grid;
    gap: 16px;
    grid-template-columns: 1fr;
  }
  @media (min-width: 560px) { .of-quick { grid-template-columns: repeat(2, 1fr); } }
  @media (min-width: 900px) { .of-quick { grid-template-columns: repeat(5, 1fr); } }
  .of-field { display: flex; flex-direction: column; gap: 6px; }
  .of-field label {
    font-size: 13px;
    font-weight: 500;
    color: var(--mercury-black);
  }
  .of-select {
    appearance: none;
    -webkit-appearance: none;
    font-family: inherit;
    font-size: 16px; /* v24.3 audit I-01: 15->16px clears the iOS focus auto-zoom floor on the Quick-filter and Sort dropdowns; also a legibility win (D-) */
    line-height: 22px;
    padding: 12px 36px 12px 14px;
    border: 1px solid var(--border-input);
    border-radius: var(--radius);
    background-color: var(--surface-white);
    background-image: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' width='12' height='8' viewBox='0 0 12 8'><path d='M1 1l5 5 5-5' stroke='%23000' stroke-width='1.5' fill='none' stroke-linecap='round' stroke-linejoin='round'/></svg>");
    background-repeat: no-repeat;
    background-position: right 14px center;
    color: var(--mercury-black);
    cursor: pointer;
    transition: border-color var(--dur-fast) var(--ease), box-shadow var(--dur-fast) var(--ease);
  }
  .of-select:hover { border-color: var(--mercury-red); }
  .of-select:focus { border-color: var(--mercury-red); }
  .of-quick-actions {
    margin-top: 24px;
    display: flex;
    flex-wrap: wrap;
    gap: 12px;
    align-items: center;
  }
  .of-result-count {
    color: var(--text-secondary);
    font-size: 14px;
  }

  /* ============ Buttons ============ */
  .of-btn {
    appearance: none;
    border: 0;
    cursor: pointer;
    font-weight: 500;
    font-size: 15px;
    line-height: 22px;
    padding: 12px 22px;
    border-radius: var(--radius);
    transition: background var(--dur-fast) var(--ease), color var(--dur-fast) var(--ease), box-shadow var(--dur-fast) var(--ease);
    text-decoration: none;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    gap: 8px;
    position: relative;
    overflow: hidden;
  }
  .of-btn::after {
    content: "";
    position: absolute;
    inset: 0;
    background: var(--surface-white);
    opacity: 0;
    transition: opacity var(--dur-fast) var(--ease);
    pointer-events: none;
  }
  .of-btn:hover::after { opacity: 0.08; }
  .of-btn:active::after { opacity: 0.16; }
  .of-btn-primary { background: var(--mercury-red); color: var(--surface-white); box-shadow: var(--shadow-1); }
  /* The empty-state CTA is an <a>, so the host Turnkey site's global
     a:hover / link-color rules can recolor its label red-on-red on hover
     even though our background darkens correctly. Pin the label white
     across link states with !important to beat the host rule we can't
     edit. Scoped to primary buttons only. */
  a.of-btn-primary,
  a.of-btn-primary:link,
  a.of-btn-primary:visited,
  a.of-btn-primary:hover,
  a.of-btn-primary:focus,
  a.of-btn-primary:active {
    color: var(--surface-white) !important;
    text-decoration: none;
  }
  /* Plain primary hover: darken the background (this used to re-set the same
     --mercury-red, a no-op, which left the white ::after overlay as the only
     hover feedback — and that 8% white film over white text ghosts the label,
     making the button look disabled on hover). Darkening to --mercury-red-hover
     is the real signal and matches the -engine-cta / -tv-summary buttons. */
  .of-btn-primary:hover { background: var(--mercury-red-hover); box-shadow: var(--shadow-2); }
  /* With the background now darkening, the white overlay is redundant on every
     primary button and only serves to wash out the white label. Suppress it
     across all primary buttons; the darker red is the hover state. (Secondary,
     ghost, and link variants keep the overlay — they don't swap background.) */
  .of-btn-primary:hover::after, .of-btn-primary:active::after { opacity: 0; }
  .of-btn-primary:disabled { background: var(--surface-100); color: var(--text-secondary); cursor: not-allowed; box-shadow: none; }
  .of-btn-primary:disabled::after { opacity: 0; }
  .of-btn-secondary {
    background: var(--surface-white);
    color: var(--mercury-red);
    border: 1px solid var(--mercury-red);
  }
  .of-btn-secondary::after { background: var(--mercury-red); }
  .of-btn-secondary:hover { background: var(--mercury-red-soft); box-shadow: var(--shadow-1); }
  .of-btn-ghost {
    background: transparent;
    color: var(--mercury-black);
    padding: 8px 12px;
  }
  .of-btn-ghost::after { background: var(--mercury-black); }
  .of-btn-link {
    background: transparent;
    color: var(--mercury-red);
    padding: 8px 4px;
    font-weight: 500;
  }
  .of-btn-link::after { display: none; }
  .of-btn-link:hover { text-decoration: underline; }

  /* ============ Results area ============ */
  /* When the Finder scrolls a section into view (deep-link landing,
     finish, step advance), scrollIntoView({block:'start'}) puts the
     element's top at the viewport top — which on the live embed tucks
     it under the platform's sticky header. scroll-margin-top reserves
     clearance. --of-sticky-offset defaults to 90px (typical Bootstrap
     navbar height); tune it in :root if the deployed header differs. */
  .of-results-section,
  #of-mode-panel,
  #of-step-wrap,
  #of-app {
    scroll-margin-top: var(--of-sticky-offset, 90px);
  }
  .of-results-section {
    margin-top: 40px;
    padding-top: 32px;
    border-top: 2px solid var(--mercury-red-tint);
    position: relative;
  }
  .of-results-section::before {
    content: "";
    position: absolute;
    top: -2px;
    left: 0;
    width: 80px;
    height: 2px;
    background: var(--mercury-red);
  }
  .of-results-section:empty {
    margin-top: 0;
    padding-top: 0;
    border-top: 0;
  }
  .of-results-section:empty::before { display: none; }
  /* When the completed progress bar leads the results (guided mode), it
     is the section's top marker — so drop the thin accent rule and its
     red tab to avoid two stacked red lines. */
  .of-results-section.of-results-section--has-progress {
    border-top: 0;
    padding-top: 0;
  }
  .of-results-section.of-results-section--has-progress::before { display: none; }
  .of-results-header {
    display: flex;
    justify-content: space-between;
    align-items: flex-end;
    flex-wrap: wrap;
    gap: 12px;
    margin-bottom: 20px;
  }
  .of-results-count {
    font-size: 14px;
    color: var(--mercury-red);
    font-weight: 500;
  }
  .of-results {
    display: grid;
    gap: 16px;
    grid-template-columns: 1fr;
    /* v15.9: top-align cards. Grid rows stretch every card to the tallest one,
       so an expanded "Things to consider" on one card left its row-mates with a
       tall empty box below their collapsed panel. CTAs flow top-down (not
       bottom-pinned), so natural heights don't misalign prices or buttons. */
    align-items: start;
  }
  /* v15.6: base spacing for the trust band. The only prior rule lived inside a
     mobile media query, so on desktop the band had no margin and the card grid
     hugged it. Defined here so it holds at every width. */
  .of-trust-band { margin: var(--of-space-4, 18px) 0 var(--of-space-3, 14px) 0; padding: var(--of-space-3, 14px) 0; }
  /* v15.6: base bottom margin for the remote panel. The 20px added in 15.2 lived
     in a desktop media block, so on mobile the panel hugged the selections strip. */
  .of-remote-panel { margin-bottom: 20px; }
  /* v15.7: lift results title/subtitle and the sail advisory to base spacing.
     Their only margin rules lived in media queries, so default-width desktop had
     none and relied on neighbours. Values match the media versions, so the look
     at each breakpoint is unchanged; this just guarantees spacing at every width. */
  .of-results-title { margin: 0 0 var(--of-space-2, 8px) 0; }
  .of-results-subtitle { margin: 4px 0 0 0; }
  .of-sail-advisory { margin-bottom: var(--of-space-5, 28px); }
  /* v19.6: electric advisory — the parallel of the sail advisory, for the
     "you want electric but the Avator is filtered out" case. Defined at top
     level (not inside a media query) so it's styled for every user; green
     accent + lightning glyph so it reads as electric, not sail. */
  .of-electric-advisory {
    background: #eef7f1;
    border-left: 4px solid #2e7d52;
    border-radius: 4px;
    padding: var(--of-space-5, 28px);
    margin-bottom: var(--of-space-5, 28px);
    position: relative;
  }
  .of-electric-advisory:focus-visible { outline: 2px solid #2e7d52; outline-offset: 2px; }
  .of-electric-advisory-title {
    display: flex; align-items: center; gap: 0.5rem;
    margin: 0 0 var(--of-space-3, 12px) 0; font-size: 1.1rem; color: #1f4733;
  }
  .of-electric-advisory-title::before {
    content: '\26A1'; /* lightning — electric context */
    font-size: 1.2rem; color: #2e7d52; flex: 0 0 auto;
  }
  .of-electric-advisory-body { margin: 0; color: #2a4d3a; line-height: 1.55; max-width: 70ch; }
  .of-electric-advisory-actions {
    display: flex; flex-wrap: wrap; align-items: center;
    gap: var(--of-space-3, 12px); margin-top: var(--of-space-4, 18px);
  }
  @media (max-width: 480px) {
    .of-electric-advisory-actions { flex-direction: column; align-items: stretch; }
  }
  /* v19.7: neutral in-flow note for on-order (findable-but-not-stocked) SKUs.
     Quiet on purpose — it sets ordering expectations without reading as a
     stock badge. */
  /* v16.0: base layout for the results action bar. The flex+gap rule lived only
     in a mobile media query, so on desktop Compare engines and Save & share build
     sat as inline-blocks with no gap and touched. */
  .of-header-actions { display: flex; gap: 8px; flex-wrap: wrap; align-items: center; }
  @media (min-width: 600px) { .of-results { grid-template-columns: repeat(2, 1fr); } }
  @media (min-width: 960px) { .of-results { grid-template-columns: repeat(3, 1fr); } }

  .of-engine {
    position: relative;
    background: var(--surface-white);
    border: 1px solid var(--border-grey);
    border-radius: var(--radius-lg);
    /* v23.7: was overflow:hidden, which clipped the absolutely-positioned
       Compare pill at the rounded top-left corner (the "C..." cutoff). The
       image is now rounded by .of-engine-media itself, so the card doesn't
       need to clip; dropping the clip lets the pill render in full. */
    overflow: visible;
    display: flex;
    flex-direction: column;
    transition: box-shadow var(--dur-fast) var(--ease), border-color var(--dur-fast) var(--ease);
    /* SC 2.4.11 Focus Not Obscured: when a card is tabbed to, keep it
       clear of the sticky compare bar (~80px tall with safe-area). */
    scroll-margin-bottom: 96px;
    scroll-margin-top: 16px;
  }
  .of-engine:hover { box-shadow: var(--shadow-2); border-color: var(--mercury-red); }
  /* Compare-mode selected card outline + brief pulse when added */
  .of-engine[data-compare-selected="true"] {
    border-color: var(--mercury-red);
    box-shadow: 0 0 0 2px var(--mercury-red-soft), var(--shadow-1);
  }
  .of-engine[data-compare-just-added="true"] {
    animation: ofCompareAdd 320ms var(--ease-out-back) both;
  }
  @keyframes ofCompareAdd {
    0%   { transform: scale(1); }
    50%  { transform: scale(1.015); }
    100% { transform: scale(1); }
  }
  @media (prefers-reduced-motion: reduce) {
    .of-engine[data-compare-just-added="true"] { animation: none; }
  }

  /* SC 2.4.13 Focus Appearance: on pressed/selected option cards the
     red focus ring sits on a red-tinted background, which can dip
     below 3:1 contrast. We add an inner white ring so the focus
     indicator stays distinguishable regardless of background. */
  .of-card[aria-pressed="true"]:focus-visible {
    outline: 3px solid var(--mercury-red);
    outline-offset: 2px;
    box-shadow: 0 0 0 2px var(--surface-white) inset;
  }
  .of-engine-media {
    position: relative;
    aspect-ratio: 4 / 3;
    background: var(--surface-white); /* #12: white blends the white-bg product photos (was surface-100 grey) */
    display: flex;
    align-items: center;
    justify-content: center;
    overflow: hidden;
    /* v23.7: round our own top corners (matching the card radius) so the
       card no longer needs overflow:hidden to clip the image — that clip was
       cutting off the absolutely-positioned Compare pill at the rounded
       corner. Bottom corners stay square; the body sits below. */
    border-top-left-radius: var(--radius-lg);
    border-top-right-radius: var(--radius-lg);
  }
  .of-engine-media img {
    width: 100%; height: 100%;
    object-fit: contain;
    padding: 16px;
  }
  /* v10.2: real product photo overlaid on the SVG floor. Absolute +
     opacity:0 (NOT display:none, which would stop loading="lazy" from
     ever firing). Fades in only once it actually loads; on error the JS
     removes it and the SVG underneath stays. */
  .of-engine-photo {
    position: absolute; inset: 0;
    width: 100%; height: 100%;
    object-fit: contain;
    padding: 16px;
    z-index: 2;
    opacity: 0;
    transition: opacity .2s ease;
  }
  .of-engine-photo.is-loaded { opacity: 1; }
  /* Once the real photo is in, hide the SVG floor entirely. The photo is
     object-fit:contain with padding, so without this the illustration's
     tiller arm pokes out past the photo's letterboxed edges. On error the
     photo is removed, :has() stops matching, and the illustration returns. */
  .of-engine-media:has(.of-engine-photo.is-loaded) .of-engine-illustration { visibility: hidden; }
  @media (prefers-reduced-motion: reduce) { .of-engine-photo { transition: none; } }
  /* Inline SVG illustration of the engine. Fills the 4:3 media area
     with a stylized side-profile of the motor. Replaces hotlinked
     product photos (which were blocked by every CDN we tried).
     The radial gradient + subtle inner shadow + faint linear noise
     give the surface a premium "product photo backdrop" feel
     instead of reading as a flat placeholder. */
  .of-engine-illustration {
    width: 100%; height: 100%;
    display: flex;
    align-items: center;
    justify-content: center;
    padding: 14px 18px;
    box-sizing: border-box;
    position: relative;
    background:
      radial-gradient(circle at 30% 30%, rgba(173,27,30,0.06), transparent 65%),
      radial-gradient(circle at 70% 100%, rgba(15,18,22,0.08), transparent 60%),
      linear-gradient(180deg, var(--surface-100) 0%, var(--surface-50) 60%, var(--surface-100) 100%);
  }
  /* Inner shadow at edges for "set into a frame" feel */
  .of-engine-illustration::before {
    content: "";
    position: absolute;
    inset: 0;
    pointer-events: none;
    box-shadow: inset 0 0 24px rgba(15, 18, 22, 0.06);
  }
  /* Faint reflection line under the lower unit suggests a glossy
     studio surface without committing to a full reflection. */
  .of-engine-illustration::after {
    content: "";
    position: absolute;
    left: 14%;
    right: 14%;
    bottom: 14px;
    height: 1px;
    background: linear-gradient(90deg, transparent, rgba(15,18,22,0.18), transparent);
    pointer-events: none;
  }
  .of-engine-illustration svg {
    width: 100%;
    height: 100%;
    max-width: 300px;
    display: block;
    position: relative;
    z-index: 1;
  }
  /* Secondary "Ask about this engine" button on each result card.
     Sits directly below the View Details CTA. Styled as a Mercury-
     red bordered button so it reads as a real action affordance
     (not a stealth text link), while staying visually quieter
     than the filled-red primary CTA above it. */
  .of-engine-ask {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    gap: 6px;
    margin-top: 12px;
    padding: 8px 14px;
    min-height: 44px; /* F2 (v12.3): >=44px touch target */
    cursor: pointer;
    background: var(--mercury-red);
    border: 1px solid var(--mercury-red);
    border-radius: var(--radius);
    color: var(--surface-white);
    box-shadow: var(--shadow-1);
    font-size: 13px;
    font-weight: 600;
    text-decoration: none;
    align-self: stretch;
    transition: background var(--dur-fast) var(--ease), border-color var(--dur-fast) var(--ease), color var(--dur-fast) var(--ease), box-shadow var(--dur-fast) var(--ease);
    font-family: inherit;
  }
  .of-engine-ask:hover {
    background: var(--mercury-red-hover);
    border-color: var(--mercury-red-hover);
    color: var(--surface-white);
    box-shadow: var(--shadow-2);
    text-decoration: none;
  }
  .of-engine-ask:focus-visible {
    outline: 3px solid var(--mercury-red);
    outline-offset: 2px;
    border-radius: 8px;
  }
  .of-engine-ask-icon { font-size: 14px; }
  /* v19.7: a misplaced brace had opened a prefers-reduced-motion block here,
     trapping ~460 lines of always-on component styling (phone CTA, remote
     badge variants, results subtitle, the sail advisory) so they applied only
     to reduced-motion users. The promoted rules below run unconditionally as
     intended. (v23.1: the avator-callout block this comment used to point to
     was removed with the electric upsell.) Indentation of the promoted rules
     is left as-was to keep the original fix diff small; it's cosmetic only. */
    .of-phone-cta {
      display: inline-flex;
      align-items: center;
      gap: 0.45rem;
      margin-top: 0.7rem;
      padding: 0.55rem 0.9rem;
      background: #fff;
      border: 1px solid rgba(0,0,0,0.12);
      border-radius: var(--radius);
      color: #111;
      text-decoration: none;
      font-size: 0.95rem;
      transition: border-color 0.15s, background 0.15s;
    }
    .of-phone-cta:hover,
    .of-phone-cta:focus-visible {
      border-color: #ad1b1e;
      background: #fdf3f3;
      outline: none;
    }
    /* Hardened against host CMS CSS leakage: !important on every
       dimension, inline styles on the SVG itself, max-* caps as a
       belt-and-braces safeguard. Without this the Turnkey CMS
       global `svg { width: 100% }` rule blows the icon to ~400px. */
    .of-phone-cta .of-phone-cta-icon,
    .of-remote-panel-phone .of-phone-cta-icon {
      display: inline-flex !important;
      width: 18px !important;
      height: 18px !important;
      min-width: 18px !important;
      max-width: 18px !important;
      max-height: 18px !important;
      flex: 0 0 18px !important;
      color: #ad1b1e;
    }
    .of-phone-cta .of-phone-cta-icon svg,
    .of-remote-panel-phone .of-phone-cta-icon svg {
      width: 18px !important;
      height: 18px !important;
      max-width: 18px !important;
      max-height: 18px !important;
      display: block !important;
    }
    .of-phone-cta-number {
      font-variant-numeric: tabular-nums;
      letter-spacing: 0.01em;
    }
    /* v18.9: the "Only 1 left" (lastone) badge + its pulse keyframe were
       removed here with the rest of stock. */

    /* ============================================================
       Design-critique fixes (suggestions 5-20).
       Spacing scale variables establish vertical rhythm.
       ============================================================ */
    :root {
      --of-space-1: 0.25rem;  /*  4px */
      --of-space-2: 0.5rem;   /*  8px */
      --of-space-3: 0.75rem;  /* 12px */
      --of-space-4: 1rem;     /* 16px */
      --of-space-5: 1.5rem;   /* 24px */
      --of-space-6: 2rem;     /* 32px */
      --of-space-7: 3rem;     /* 48px */
    }

    /* Sug 5, 19: Softer "Available as Remote" badge. Cream pill
       with red border — informational treatment, not button-like.
       Drop the prior solid-red treatment.
       v9.5: bumped weight and added a leading icon so the badge
       reads as a real status marker rather than ad-hoc body copy.
       The card cluster is the user's primary visual reference, so
       the per-card badge needs to stand on its own without depending
       on the panel above. */
    .of-engine-badge[data-variant="remote"] {
      background: #fff7e6 !important;
      color: #7a3d0d !important;
      border: 1px solid #d49a3a !important;
      font-weight: 700;
      cursor: default;
      letter-spacing: 0.02em;
    }
    .of-engine-badge[data-variant="remote"]::before {
      content: '\24D8'; /* circled i */
      margin-right: 4px;
      font-size: 1.05em;
      vertical-align: -1px;
      color: #d49a3a;
    }
    /* v9.5: One-line clarifier under the results count when the user
       is in remote-steering mode. Reads like a caption — the heavy
       copy is in the notice panel above. */
    .of-results-subtitle {
      margin: 4px 0 0 0;
      font-size: 13px;
      line-height: 1.45;
      color: var(--text-secondary);
      max-width: 60ch;
    }

    /* v9.5: Sail-intent advisory. Same family as the remote panel —
       cream / amber palette signals "informational, action available"
       — but visually distinct via a sail-blue accent so a user looking
       at both at once (rare but possible: sailaux app + remote pick)
       can tell them apart. */
    .of-sail-advisory {
      background: #f3f8fc;
      border-left: 4px solid #4a7ba6;
      border-radius: 4px;
      padding: var(--of-space-5);
      margin-bottom: var(--of-space-5);
      position: relative;
    }
    .of-sail-advisory:focus-visible {
      outline: 2px solid #4a7ba6;
      outline-offset: 2px;
    }
    .of-sail-advisory-title {
      display: flex;
      align-items: center;
      gap: 0.5rem;
      margin: 0 0 var(--of-space-3) 0;
      font-size: 1.1rem;
      color: #1f3a52;
    }
    .of-sail-advisory-title::before {
      content: '\2693'; /* anchor — sail/marine context */
      font-size: 1.2rem;
      color: #4a7ba6;
      flex: 0 0 auto;
    }
    .of-sail-advisory-body {
      margin: 0;
      color: #2a4259;
      line-height: 1.55;
      max-width: 70ch;
    }
    .of-sail-advisory-actions {
      display: flex;
      flex-wrap: wrap;
      align-items: center;
      gap: var(--of-space-3);
      margin-top: var(--of-space-4);
    }
    @media (max-width: 480px) {
      .of-sail-advisory-actions {
        flex-direction: column;
        align-items: stretch;
      }
    }

    /* Sug 9: Remote panel as a proper info banner. Background tint
       fill + stronger left border + info icon space. */
    .of-remote-panel {
      background: #fffaf0 !important;
      border-left: 4px solid #d49a3a !important;
      border-radius: 4px;
      padding: var(--of-space-5) var(--of-space-5);
      margin-bottom: var(--of-space-5);
      position: relative;
    }
    .of-remote-panel-title {
      display: flex;
      align-items: center;
      gap: 0.5rem;
      margin: 0 0 var(--of-space-3) 0;
      font-size: 1.1rem;
      color: #5b3d00;
    }
    .of-remote-panel-title::before {
      content: '\24D8'; /* circled i */
      font-size: 1.3rem;
      color: #d49a3a;
      flex: 0 0 auto;
    }
    /* v10.0: Utility classes that replace the few remaining inline
       style= attributes inside the JS. Naming intentionally matches
       what the inline style did. Keeps the JS file CSP-friendly: if
       the host CMS ever adopts a strict style-src policy, the script
       output doesn't have to change. */
    .of-header-actions {
      display: flex;
      gap: 8px;
      flex-wrap: wrap;
    }
    .of-modal-actions--right {
      justify-content: flex-end;
      margin-top: 14px;
    }
    .of-modal-actions--center {
      justify-content: center;
      margin-top: 8px;
    }
    .of-diag-spacer { margin-top: 6px; }
    .of-required-star { color: var(--mercury-red); }

    .of-remote-panel-actions {
      display: flex;
      flex-direction: column;
      align-items: flex-start;
      flex-wrap: wrap;
      gap: var(--of-space-3);
      margin-top: var(--of-space-4);
    }
    /* "or call <number>" grouped on one line, below the CTA. */
    .of-remote-panel-callrow {
      display: inline-flex;
      align-items: center;
      gap: 0.4rem;
    }
    .of-remote-panel-or {
      color: #777;
      font-size: 0.95rem;
    }
    .of-remote-panel-phone {
      display: inline-flex;
      align-items: center;
      gap: 0.45rem;
      color: #ad1b1e;
      text-decoration: none;
      font-size: 0.95rem;
      font-weight: 700;
      min-height: 44px;
      padding: 0.5rem 1.1rem;
      border: 1.5px solid #ad1b1e;
      border-radius: 8px;
      background: #fff;
      transition: background 0.15s, color 0.15s, box-shadow 0.15s;
    }
    .of-remote-panel-phone:hover,
    .of-remote-panel-phone:focus-visible {
      background: #ad1b1e;
      color: #fff;
      text-decoration: none;
      outline: none;
    }
    .of-remote-panel-phone:focus-visible { box-shadow: 0 0 0 3px rgba(173, 27, 30, 0.35); }
    .of-remote-panel-phone-icon {
      font-size: 1rem;
    }
    .of-btn-ext-icon {
      display: inline-block;
      font-size: 0.85em;
      margin-left: 0.15em;
      opacity: 0.85;
    }

    /* Sug 4: Trust band — its own visual section immediately above
       the engine grid. Softer chrome than the header. */
    .of-trust-band {
      margin: var(--of-space-4) 0 var(--of-space-3) 0;
      padding: var(--of-space-3) 0;
    }

    /* Sug 15: Results title ("Tiller equivalents we can rig as
       Remote" / "Matches for your build") needs more presence —
       it's the section headline, not body text. */
    .of-results-title {
      font-size: 1.5rem;
      font-weight: 700;
      letter-spacing: -0.01em;
      margin: 0 0 var(--of-space-2) 0;
      color: #111;
    }

    /* Sug 7: Card "reason" line is the why-this-engine signal —
       bump weight, drop italics, keep the left-border accent. */
    .of-engine-reason {
      font-weight: 500 !important;
      font-style: normal !important;
      color: #2a2a2a !important;
    }

    /* Sug 8: Result grid — auto-fill so the catalog reflows
       cleanly across viewports instead of orphaning the last row. */
    .of-results {
      grid-template-columns: repeat(auto-fill, minmax(280px, 1fr)) !important;
    }

    /* v25.6: on remote the quote button is now the PRIMARY action and leads,
       so the old gray "reduce CTA density" treatment is gone — it stays filled
       red on mobile to match desktop. The outline View Product Page below it
       is the secondary. */
    .of-engine-ask {
      color: var(--surface-white) !important;
      border-color: var(--mercury-red) !important;
      background: var(--mercury-red) !important;
    }
    .of-engine-ask:hover,
    .of-engine-ask:focus-visible {
      color: var(--surface-white) !important;
      border-color: var(--mercury-red-hover) !important;
      background: var(--mercury-red-hover) !important;
    }

    /* Sug 17: Vertical rhythm — establish consistent spacing
       between major sections of the results page. */
    .of-results-header {
      margin-bottom: var(--of-space-3) !important;
    }
    .of-selections-strip {
      margin-bottom: var(--of-space-3) !important;
    }

    /* Sug 20: Mobile defensive — !important on touch-target sizing
       and the SVG dimension caps so host responsive-image rules
       can't break the layout below 480px width. */
    @media (max-width: 480px) {
      .of-engine svg,
      .of-results svg {
        max-width: 100% !important;
        height: auto;
      }
      .of-phone-cta .of-phone-cta-icon svg,
      .of-remote-panel-phone .of-phone-cta-icon svg {
        max-width: 18px !important;
        max-height: 18px !important;
      }
      .of-remote-panel-actions {
        flex-direction: column;
        align-items: stretch;
      }
      .of-remote-panel-actions .of-remote-panel-or {
        text-align: center;
      }
    }
    /* WCAG 2.5.5 — 44x44 minimum tap target on interactive controls. */
    .of-card,
    .of-btn,
    .of-mode-tab,
    .of-crumb-btn,
    .of-chip,
    .of-util-bar button,
    .of-compare-bar-btn-clear,
    .of-engine-cta,
    .of-phone-cta {
      min-height: 44px;
    }
    /* Selects inherit native control min-heights from the browser;
       the field wrapper just needs enough padding so the hit area
       around the chevron is comfortable on touch. */
    .of-field .of-select { min-height: 44px; }
    .of-remote-panel {
      display: block;
      margin: 0 0 1.25rem 0;
      padding: 1rem 1.25rem;
      background: #fff7e0;
      border: 1px solid #e5b800;
      border-left: 4px solid #b07c00;
      border-radius: 8px;
    }
    .of-remote-panel-title {
      margin: 0 0 0.4rem 0;
      font-size: 1.05rem;
      color: #4a3500;
    }
    .of-remote-panel-body {
      margin: 0 0 0.85rem 0;
      color: #4a3500;
      line-height: 1.45;
    }
    .of-engine-badge[data-variant="remote"] {
      top: auto;
      bottom: 8px;
      right: 8px;
      left: auto;
      background: #fff7e0;
      color: #4a3500;
      border: 1px solid #e5b800;
    }
  .of-engine-body {
    padding: 16px 18px 18px;
    display: flex;
    flex-direction: column;
    flex: 1;
    gap: 8px;
  }
  .of-engine-name { font-size: 18px; line-height: 24px; }
  .of-engine-spec {
    color: var(--text-secondary);
    font-size: 14px;
    line-height: 20px;
  }
  .of-engine-price {
    font-weight: 700;
    font-size: 22px;
    line-height: 28px;
    color: var(--mercury-black);
    margin-top: 6px;
    font-variant-numeric: tabular-nums;
    letter-spacing: -0.01em;
  }
  /* Savings pill: green money-positive accent. Sits below the
     price as a separate visual element instead of being buried
     in a footnote line with the MSRP strikethrough. */
  .of-engine-savings {
    display: inline-flex;
    align-items: center;
    gap: 4px;
    margin-top: 6px;
    padding: 3px 10px;
    background: var(--savings-green-soft);
    border: 1px solid var(--savings-green-border);
    border-radius: 999px;
    color: var(--savings-green);
    font-size: 13px;
    font-weight: 700;
    line-height: 18px;
    align-self: flex-start;
    font-variant-numeric: tabular-nums;
  }
  .of-engine-savings::before {
    content: "\2193";
    font-size: 11px;
    font-weight: 900;
  }
  .of-engine-msrp {
    font-size: 13px;
    line-height: 17px;
    color: var(--text-secondary);
    margin-top: 2px;
    font-variant-numeric: tabular-nums;
  }
  .of-engine-msrp-strike {
    text-decoration: line-through;
  }
  .of-engine-price-fallback {
    font-weight: 500;
    font-size: 14px;
    color: var(--text-secondary);
  }
  /* #6 (v25.6): remote price is a quiet "from $X (tiller)" reference, not the
     big headline — it isn't the buyer's price (the rig is quoted), so it's
     deliberately smaller and muted, with no Save/MSRP pill. */
  .of-engine-price-remote {
    margin-top: 6px;
    font-size: 16px;
    font-weight: 600;
    color: var(--text-secondary);
    font-variant-numeric: tabular-nums;
  }
  .of-engine-price-tag {
    font-size: 12px;
    font-weight: 400;
    color: var(--text-secondary);
  }
  /* Value reassurance at the point of decision. Subtle, not a sales pill. */
  .of-engine-assure {
    display: flex; flex-wrap: wrap; gap: 4px 14px;
    margin: 8px 0 0; padding: 0;
    font-size: 12px; line-height: 17px;
    color: var(--text-secondary, #5a5f66);
  }
  .of-engine-assure-item { display: inline-flex; align-items: center; gap: 5px; white-space: nowrap; }
  .of-engine-assure-ic { color: var(--mercury-red, #ad1b1e); font-weight: 700; }
  .of-engine-cta {
    margin-top: 12px;
    transition: background var(--dur-fast) var(--ease), box-shadow var(--dur-fast) var(--ease), transform var(--dur-fast) var(--ease);
  }
  /* Stronger hover on the primary "View engine details" CTA: darken, lift, deepen
     the shadow. Double class beats .of-btn-primary:hover (equal specificity, and
     that one wins on order otherwise). */
  .of-btn-primary.of-engine-cta:hover, .of-btn-primary.of-engine-cta:focus-visible {
    background: var(--mercury-red-hover);
    box-shadow: var(--shadow-3);
    transform: translateY(-1px);
  }
  .of-btn-primary.of-engine-cta:active { transform: translateY(0); box-shadow: var(--shadow-2); }
  @media (prefers-reduced-motion: reduce) {
    .of-btn-primary.of-engine-cta:hover, .of-btn-primary.of-engine-cta:focus-visible { transform: none; }
  }
  /* Marketing highlight badge (Mercury red), shown in the card's top
     corner for labels like "Best Seller" / "New". v18.9: the stock
     variants (in-stock / on-order / sold-out) were removed with stock,
     so this badge is marketing only now. The remote-rig badge below is
     a separate, non-stock data-variant and is unaffected. */
  .of-engine-badge {
    position: absolute;
    top: 12px;
    right: 12px;
    white-space: nowrap; /* corner pill stays one line; never clips on the card's overflow:hidden */
    max-width: calc(100% - 24px);
    background: var(--mercury-red);
    color: var(--surface-white);
    font-size: 11px;
    line-height: 16px;
    font-weight: 700;
    letter-spacing: 0.08em;
    text-transform: uppercase;
    padding: 4px 10px;
    border-radius: 999px;
    z-index: 4; /* above the product photo (z-index 2) so the badge is never covered */
    display: inline-flex;
    align-items: center;
    gap: 4px;
  }
  .of-engine-badge::before {
    /* Tiny dot only shown on status badges, not marketing labels */
    content: "";
    display: none;
  }
  .of-engine-badge[data-variant]::before {
    display: inline-block;
    width: 6px;
    height: 6px;
    border-radius: 50%;
    background: currentColor;
    opacity: 0.85;
  }
  /* v18.9: removed with stock — the status-dot pulse keyframe
     (ofStatusPulse), its reduced-motion rule, and the sold-out
     illustration graying ([data-sold]). The generic dot above is kept
     because the remote-rig badge (a non-stock variant) renders it. */

  /* ============ New: Mode toggle helper copy (Finding 5) ============ */
  .of-mode-help-wrap {
    margin-top: 0.5rem;
    width: 100%;
    clear: both;
  }
  .of-mode-help {
    margin-top: 0;
    font-size: 14px;
    line-height: 20px;
    color: var(--text-secondary);
    text-align: center;
    max-width: 480px;
    margin-left: auto;
    margin-right: auto;
  }

  /* ============ New: Persistent contact path (Finding 6) ============ */
  .of-panel-help {
    margin-top: 24px;
    padding-top: 16px;
    border-top: 1px solid var(--border-grey);
    font-size: 13px;
    line-height: 20px;
    color: var(--text-secondary);
    text-align: center;
  }
  .of-panel-help a {
    color: var(--mercury-red);
    text-decoration: underline;
    font-weight: 500;
  }
  .of-panel-help a:hover { color: var(--mercury-black); }

  /* ============ New: Card recommendation reason (Finding 1) ============ */
  .of-engine-reason {
    font-size: 13px;
    line-height: 19px;
    color: var(--text-secondary);
    font-style: italic;
    padding: 8px 10px;
    background: var(--surface-100);
    border-left: 3px solid var(--mercury-red);
    border-radius: 4px;
    margin-top: 4px;
  }

  /* ============ New: Trust microcopy under result count ============ */
  /* Trust pills with leading icon. Each item is a small chip with
     a colored icon (green check, truck, shield, location). Reads
     as a row of badges instead of a fine-print fact list. */
  .of-results-trust {
    display: flex;
    flex-wrap: wrap;
    gap: 6px 8px;
    margin-top: 10px;
  }
  .of-results-trust-item {
    display: inline-flex;
    align-items: center;
    gap: 6px;
    font-size: 12px;
    line-height: 18px;
    font-weight: 500;
    color: var(--mercury-black);
    padding: 4px 10px 4px 8px;
    background: var(--surface-50);
    border: 1px solid var(--border-grey);
    border-radius: 999px;
  }
  .of-results-trust-item::before {
    content: "\2713";
    color: var(--savings-green);
    font-weight: 900;
    font-size: 12px;
  }
  /* Priority pill: Mercury-authorized dealer carries the most
     conversion signal so it gets stronger visual weight: red-soft
     fill, red border, red check, and a Mercury badge prefix. The
     other trust items stay neutral so the priority pill anchors
     attention without the whole row competing. */
  .of-results-trust-item[data-priority="true"] {
    background: var(--mercury-red-soft);
    border-color: var(--mercury-red-tint);
    color: var(--mercury-red-hover);
    font-weight: 600;
  }
  .of-results-trust-item[data-priority="true"]::before {
    content: "\2605";
    color: var(--mercury-red);
  }
  /* Rating chip gets its own treatment when shown */
  .of-results-trust-rating {
    background: var(--surface-white);
    border-color: var(--savings-green-border);
  }
  .of-results-trust-rating::before { content: ""; display: none; }

  /* ============ Step 1 confirmation prompt ============
     Surfaces when the user picks an HP bucket that falls outside
     their selected boat type's recommended range. Conversational
     framing (a lightbulb icon + "Quick check") with a clear
     primary/secondary button hierarchy so the user can confirm or
     adjust without thinking the wizard is yelling at them. */
  .of-step-confirm {
    margin-top: 16px;
    padding: 16px 18px;
    background: var(--warn-bg);
    border: 1px solid var(--warn-border);
    border-left: 4px solid var(--warn-strong);
    border-radius: var(--radius);
    font-size: 14px;
    line-height: 22px;
    color: var(--warn-text);
    display: grid;
    grid-template-columns: 32px 1fr;
    gap: 12px;
    align-items: start;
    animation: ofConfirmEnter 280ms var(--ease) both;
  }
  @keyframes ofConfirmEnter {
    0% { opacity: 0; transform: translateY(-4px); }
    100% { opacity: 1; transform: translateY(0); }
  }
  @media (prefers-reduced-motion: reduce) {
    .of-step-confirm { animation: none; }
  }
  .of-step-confirm-icon {
    width: 28px;
    height: 28px;
    border-radius: 50%;
    background: var(--warn-strong);
    color: var(--warn-bg);
    display: flex;
    align-items: center;
    justify-content: center;
    font-size: 15px;
    font-weight: 900;
    flex: 0 0 auto;
  }
  .of-step-confirm-body p { margin: 0 0 4px; }
  .of-step-confirm strong { color: var(--warn-strong); }
  .of-step-confirm-actions {
    display: flex;
    gap: 10px;
    margin-top: 12px;
    flex-wrap: wrap;
  }
  /* Primary button: "Show me anyway" is the action the user explicitly
     asked for (their pick stands). Visually dominant. */
  .of-step-confirm-btn[data-variant="primary"] {
    appearance: none;
    background: var(--warn-strong);
    color: var(--surface-white);
    border: 1px solid var(--warn-strong);
    font-size: 13px;
    font-weight: 600;
    padding: 8px 16px;
    border-radius: 8px;
    cursor: pointer;
    font-family: inherit;
    transition: background var(--dur-fast) var(--ease), transform var(--dur-fast) var(--ease);
  }
  .of-step-confirm-btn[data-variant="primary"]:hover {
    background: #2a2207;
    transform: translateY(-1px);
  }
  /* Secondary button: gentler exit path - subtle outline */
  .of-step-confirm-btn[data-variant="secondary"] {
    appearance: none;
    background: transparent;
    color: var(--warn-text);
    border: 1px solid var(--warn-border);
    font-size: 13px;
    font-weight: 500;
    padding: 8px 14px;
    border-radius: 8px;
    cursor: pointer;
    font-family: inherit;
    transition: background var(--dur-fast) var(--ease), border-color var(--dur-fast) var(--ease);
  }
  .of-step-confirm-btn[data-variant="secondary"]:hover {
    background: var(--surface-white);
    border-color: var(--warn-strong);
  }
  @media (prefers-reduced-motion: reduce) {
    .of-step-confirm-btn[data-variant="primary"]:hover { transform: none; }
  }

  /* ============ New: Compare mode (Finding 8) ============ */
  .of-compare-mode-toggle {
    display: inline-flex;
    align-items: center;
    gap: 8px;
    padding: 6px 14px;
    min-height: 44px; /* v24.3 audit: 44px tap-target floor (Apple HIG), matches the Why these? pill */
    border-radius: 999px;
    /* v24.2: was a grey low-contrast pill that read as disabled chrome and
       went near-unused (GA4: ~5 users/week). Red outline matches the Why
       these? pill so it reads as an actionable control. */
    border: 1px solid var(--mercury-red, #ad1b1e);
    background: var(--surface-white);
    color: var(--mercury-red, #ad1b1e);
    font-size: 13px;
    font-weight: 600;
    cursor: pointer;
  }
  .of-compare-mode-toggle:hover { background: var(--mercury-red-soft, #fbe9ea); }
  .of-compare-mode-toggle[aria-pressed="true"] {
    background: var(--mercury-red, #ad1b1e);
    color: #fff;
    border-color: var(--mercury-red, #ad1b1e);
  }
  .of-engine-compare {
    position: absolute;
    top: 12px;
    left: 12px;
    /* v23.7: z-index 5 puts the pill above the media block and the product
       photo (z-index 2) and the status badge (z-index 4). At z-index 2 it was
       equal to the photo and the media subtree painted over its right half —
       that was the "C..." clip, not a corner-radius clip. */
    z-index: 5;
    display: flex;
    align-items: center;
    gap: 8px;
    padding: 10px 14px;
    min-height: 44px; /* F1 (v12.3): >=44px touch target */
    background: rgba(255, 255, 255, 0.95);
    border: 1px solid var(--border-grey);
    border-radius: 999px;
    font-size: 13px;
    font-weight: 500;
    color: var(--mercury-black);
    cursor: pointer;
    /* v23.5 (iOS opinion): user-select:none here is deliberate and stays
       scoped to this toggle control — we intentionally do NOT suppress text
       selection app-wide, so a buyer can long-press to copy a spec or price
       into a message. Don't blanket this onto cards or spec rows. */
    user-select: none;
  }
  .of-engine-compare input { margin: 0; cursor: pointer; accent-color: var(--mercury-red); width: 16px; height: 16px; }
  .of-engine-compare:hover { border-color: var(--mercury-red); }
  .of-engine[data-compare-selected="true"] {
    box-shadow: 0 0 0 2px var(--mercury-red);
  }
  .of-compare-bar {
    /* v23.7: was position:sticky; bottom:16px (a bottom tray). Now it sits
       in normal flow directly under the Sort row, above the engine grid, so
       the count + Compare action are where the user is looking as they tick
       cards. Sticky-to-top keeps it visible while scrolling the results. */
    position: sticky;
    top: 12px;
    z-index: 10;
    margin: 16px 0 24px; /* v23.7a: balanced gap — was 4px/16px, read as flush against the cards */
    padding: 12px 18px;
    background: var(--surface-inverse);
    color: var(--on-inverse);
    border-radius: var(--radius-lg);
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 12px;
    box-shadow: var(--shadow-2);
  }
  .of-compare-bar-text {
    font-size: 14px;
    font-weight: 500;
  }
  .of-compare-bar-actions { display: flex; gap: 8px; }
  .of-compare-bar .of-btn { padding: 8px 16px; font-size: 13px; }
  .of-compare-bar-btn-clear {
    appearance: none;
    background: transparent;
    color: var(--on-inverse);
    border: 1px solid var(--on-inverse);
    border-radius: 6px;
    padding: 8px 16px;
    font-size: 13px;
    font-weight: 500;
    cursor: pointer;
    transition: background var(--dur-fast) var(--ease);
  }
  .of-compare-bar-btn-clear:hover { background: rgba(255, 255, 255, 0.12); }
  .of-compare-table-wrap {
    overflow-x: auto;
    margin-top: 8px;
  }
  .of-compare-table {
    width: 100%;
    border-collapse: collapse;
    font-size: 13px;
  }
  .of-compare-table th, .of-compare-table td {
    padding: 10px 12px;
    border-bottom: 1px solid var(--border-grey);
    text-align: left;
    vertical-align: top;
  }
  .of-compare-table th {
    background: var(--surface-100);
    font-weight: 700;
    color: var(--mercury-black);
    white-space: nowrap;
  }
  .of-compare-table thead th {
    font-size: 14px;
    background: var(--surface-white);
    border-bottom: 2px solid var(--mercury-red);
  }
  .of-compare-table .of-compare-name {
    font-weight: 700;
    color: var(--mercury-black);
  }
  .of-compare-table tfoot td {
    background: var(--surface-100);
    border-bottom: 0;
    padding-top: 12px;
  }
  .of-compare-cta {
    display: inline-block;
    padding: 8px 14px;
    border-radius: var(--radius);
    font-size: 13px;
    font-weight: 500;
    text-decoration: none;
    text-align: center;
  }
  .of-compare-cta-primary {
    background: var(--mercury-red);
    color: var(--surface-white);
  }
  .of-compare-cta-secondary {
    background: var(--surface-white);
    color: var(--mercury-black);
    border: 1px solid var(--mercury-black);
  }
  .of-compare-cta:hover { opacity: 0.9; }

  /* Pending bucket state when Step 1 confirmation prompt is showing (Finding 10) */
  .of-card[data-pending="true"] {
    outline: 2px solid var(--warn-border);
    outline-offset: 2px;
  }

  /* ============ Skeleton loader ============ */
  .of-skeleton {
    display: grid;
    gap: 16px;
    grid-template-columns: 1fr;
  }
  @media (min-width: 600px) { .of-skeleton { grid-template-columns: repeat(2, 1fr); } }
  @media (min-width: 960px) { .of-skeleton { grid-template-columns: repeat(3, 1fr); } }
  .of-skel-card {
    background: var(--surface-white);
    border: 1px solid var(--border-grey);
    border-radius: var(--radius-lg);
    overflow: hidden;
  }
  .of-skel-media {
    aspect-ratio: 4 / 3;
    background: var(--surface-100);
  }
  .of-skel-body { padding: 18px; display: flex; flex-direction: column; gap: 10px; }
  .of-skel-bar {
    height: 14px;
    background: var(--surface-100);
    border-radius: 6px;
  }
  .of-skel-bar.w-70 { width: 70%; }
  .of-skel-bar.w-40 { width: 40%; }
  .of-skel-bar.w-50 { width: 50%; }
  .of-skel-bar.tall { height: 40px; width: 60%; margin-top: 4px; }

  @keyframes ofPulse {
    0%, 100% { opacity: 1; }
    50% { opacity: 0.55; }
  }
  .of-skeleton .of-skel-media,
  .of-skeleton .of-skel-bar {
    animation: ofPulse 1.4s ease-in-out infinite;
  }

  /* ============ No match / error ============ */
  .of-empty {
    background: var(--surface-white);
    border: 1px solid var(--border-grey);
    border-radius: var(--radius-lg);
    padding: 32px 24px;
    text-align: center;
  }
  .of-empty h3 { margin-bottom: 8px; font-size: 22px; line-height: 30px; }
  .of-empty p { color: var(--text-secondary); margin-bottom: 20px; max-width: 520px; margin-left: auto; margin-right: auto; }
  .of-empty-actions {
    display: flex;
    gap: 12px;
    justify-content: center;
    flex-wrap: wrap;
  }
  .of-error-banner {
    background: var(--error-bg);
    border: 1px solid var(--error-border);
    border-radius: var(--radius);
    padding: 16px 20px;
    color: var(--error-text);
    margin-bottom: 24px;
  }
  .of-error-banner a { color: var(--mercury-red); text-decoration: underline; }

  /* ============ Step transition animation ============ */
  .of-step-enter {
    animation: ofStepEnter 320ms var(--ease) both;
  }
  @keyframes ofStepEnter {
    0% { opacity: 0; transform: translateY(8px); }
    100% { opacity: 1; transform: translateY(0); }
  }
  /* Exit-side cross-fade for the previous step. The .of-step-exit
     class is briefly applied to the outgoing step wrap by
     pickAndAdvance just before the new step's render replaces it.
     Total cross-fade is ~250ms (exit) + 320ms (enter, overlapping)
     so the user perceives a smooth swap rather than a jump-cut. */
  .of-step-exit {
    animation: ofStepExit 220ms var(--ease) both;
  }
  @keyframes ofStepExit {
    0% { opacity: 1; transform: translateY(0); }
    100% { opacity: 0; transform: translateY(-6px); }
  }

  /* Card selection pulse (no scale) plus a brief check-mark icon
     overlay so the user gets explicit visual confirmation their
     tap registered before the step transitions away. */
  .of-card-just-picked {
    animation: ofBorderFlash 250ms var(--ease) both;
    position: relative;
  }
  .of-card-just-picked::before {
    content: "\2713";
    position: absolute;
    top: 8px;
    right: 8px;
    width: 24px;
    height: 24px;
    border-radius: 50%;
    background: var(--mercury-red);
    color: var(--surface-white);
    font-size: 14px;
    font-weight: 900;
    line-height: 24px;
    text-align: center;
    box-shadow: 0 2px 6px rgba(173, 27, 30, 0.4);
    z-index: 3;
    animation: ofCheckPop 350ms var(--ease-out-back) both;
  }
  @keyframes ofBorderFlash {
    0% { box-shadow: 0 0 0 0 rgba(173,27,30,0.45); }
    100% { box-shadow: 0 0 0 6px rgba(173,27,30,0); }
  }
  @keyframes ofCheckPop {
    0%   { opacity: 0; transform: scale(0.4) rotate(-20deg); }
    60%  { opacity: 1; transform: scale(1.15) rotate(0deg); }
    100% { opacity: 1; transform: scale(1) rotate(0deg); }
  }

  /* Staggered entrance for engine result cards. Each card delays
     its enter animation by ~40ms * index so the grid populates
     left-to-right instead of all appearing at once. Capped at 8
     cards to keep total wait under ~320ms. */
  .of-engine {
    animation: ofCardEnter 320ms var(--ease) both;
  }
  .of-engine:nth-child(1)  { animation-delay: 0ms; }
  .of-engine:nth-child(2)  { animation-delay: 40ms; }
  .of-engine:nth-child(3)  { animation-delay: 80ms; }
  .of-engine:nth-child(4)  { animation-delay: 120ms; }
  .of-engine:nth-child(5)  { animation-delay: 160ms; }
  .of-engine:nth-child(6)  { animation-delay: 200ms; }
  .of-engine:nth-child(7)  { animation-delay: 240ms; }
  .of-engine:nth-child(8)  { animation-delay: 280ms; }
  .of-engine:nth-child(n+9) { animation-delay: 320ms; }
  @keyframes ofCardEnter {
    0%   { opacity: 0; transform: translateY(12px); }
    100% { opacity: 1; transform: translateY(0); }
  }

  @media (prefers-reduced-motion: reduce) {
    .of-step-enter, .of-step-exit,
    .of-card-just-picked, .of-card-just-picked::before,
    .of-engine { animation: none !important; }
    /* v23.6 (D-02): the loading skeleton's infinite opacity pulse was the one
       unguarded animation. Users who set reduce do so precisely to stop
       looping motion; freeze it to a static placeholder. */
    .of-skeleton .of-skel-media, .of-skeleton .of-skel-bar { animation: none; }
  }

  /* ============ Modal ============ */
  .of-modal {
    position: fixed;
    inset: 0;
    background: rgba(15, 18, 22, 0.45);
    display: flex;
    align-items: flex-end;
    justify-content: center;
    padding: 16px;
    z-index: 100;
    opacity: 0;
    pointer-events: none;
    transition: opacity 200ms var(--ease);
  }
  @media (min-width: 600px) { .of-modal { align-items: center; } }
  .of-modal[data-open="true"] { opacity: 1; pointer-events: auto; }
  .of-modal-dialog {
    background: var(--surface-white);
    border-radius: var(--radius-lg);
    width: 100%;
    max-width: 480px;
    /* Constrain to viewport so the dialog never exceeds the screen.
       The internal #of-modal-content scrolls instead of the whole
       page. dvh handles mobile browser chrome (URL bar collapse). */
    max-height: min(90vh, 90dvh);
    display: flex;
    flex-direction: column;
    padding: 0;
    box-shadow: var(--shadow-3);
    transform: translateY(20px);
    transition: transform 220ms var(--ease);
    overflow: hidden;
  }
  .of-modal[data-open="true"] .of-modal-dialog { transform: translateY(0); }
  /* Header sits flush at the top of the dialog and stays visible
     while the content area scrolls. The horizontal padding matches
     the body so the title aligns with form content. */
  .of-modal-header {
    display: flex;
    align-items: flex-start;
    justify-content: space-between;
    gap: 12px;
    padding: 22px 24px 8px;
    margin-bottom: 0;
    flex: 0 0 auto;
    background: var(--surface-white);
    border-radius: var(--radius-lg) var(--radius-lg) 0 0;
  }
  /* Scrollable content region. Padding moved here from the dialog
     so the scrollbar appears at the right edge of the dialog rather
     than the right edge of the content text. */
  #of-modal-content {
    padding: 0 24px 24px;
    overflow-y: auto;
    overflow-x: hidden;
    flex: 1 1 auto;
    /* Smooth overscroll behavior on iOS without rubber-banding the
       page underneath. */
    overscroll-behavior: contain;
    /* Subtle scrollbar styling for browsers that allow it. */
    scrollbar-width: thin;
    scrollbar-color: var(--border-grey) transparent;
  }
  #of-modal-content::-webkit-scrollbar { width: 8px; }
  #of-modal-content::-webkit-scrollbar-thumb {
    background: var(--border-grey);
    border-radius: 4px;
  }
  /* v17.8: fixed footer for the action buttons, OUTSIDE the scroll area, so it
     stays pinned at the bottom of the dialog and never overlaps form fields. */
  .of-modal-footer {
    flex: 0 0 auto;
    padding: 14px 24px;
    background: var(--surface-white);
    border-top: 1px solid var(--border-grey);
    box-shadow: 0 -6px 12px -8px rgba(15, 18, 22, 0.12);
    border-radius: 0 0 var(--radius-lg) var(--radius-lg);
  }
  .of-modal-footer[hidden] { display: none; }
  /* The footer is now the bar, so the relocated row drops its own bar chrome
     and is just a flex row (its justify/gap, incl. --right/--center, remain). */
  .of-modal-footer .of-modal-actions {
    margin: 0;
    padding: 0;
    border-top: 0;
    background: none;
    box-shadow: none;
  }
  .of-modal-close {
    appearance: none;
    border: 0;
    background: transparent;
    cursor: pointer;
    padding: 6px;
    /* v19.9: 44x44 touch target; glyph stays centered via inline-flex. */
    min-width: 44px;
    min-height: 44px;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    border-radius: var(--radius-sm);
    color: var(--text-secondary);
    font-size: 22px;
    line-height: 1;
  }
  .of-modal-close:hover { color: var(--mercury-black); background: var(--surface-100); }
  .of-modal-desc {
    color: var(--text-secondary);
    margin-bottom: 16px;
    font-size: 15px;
    line-height: 22px;
  }
  .of-form-field { display: flex; flex-direction: column; gap: 6px; margin-bottom: 14px; }
  .of-form-field .of-field-count { align-self: flex-end; margin-top: 2px; font-size: 12px; color: var(--text-secondary); }
  .of-form-field label {
    font-size: 13px;
    font-weight: 500;
  }
  .of-input, .of-textarea {
    font: inherit;
    padding: 12px 14px;
    border: 1px solid var(--border-input);
    border-radius: var(--radius);
    background: var(--surface-white);
    color: var(--mercury-black);
    transition: border-color var(--dur-fast) var(--ease);
  }
  .of-input:focus, .of-textarea:focus { border-color: var(--mercury-red); }
  .of-textarea { resize: vertical; min-height: 80px; }
  /* Sticky action row anchored to the bottom of the scrollable
     content area. Cancel/Continue stay visible regardless of how
     much the user has scrolled inside the modal. The negative
     margins compensate for the content padding so the row spans
     the full dialog width. */
  .of-modal-actions {
    display: flex;
    justify-content: flex-end;
    gap: 10px;
    padding: 14px 24px;
    margin: 16px -24px 0;
    background: var(--surface-white);
    border-top: 1px solid var(--border-grey);
    z-index: 2;
    /* Soft shadow above the row hints at content scrolled behind it. */
    box-shadow: 0 -6px 12px -8px rgba(15, 18, 22, 0.12);
  }
  .of-modal-success {
    text-align: center;
    padding: 24px 8px 8px;
  }
  .of-modal-success-icon {
    width: 48px; height: 48px;
    border-radius: 50%;
    background: var(--mercury-red);
    color: var(--surface-white);
    display: flex;
    align-items: center;
    justify-content: center;
    font-size: 24px;
    margin: 0 auto 12px;
    font-weight: 700;
  }
  /* Refreshed Save & Share modal layout */
  .of-modal-intro {
    font-size: 14px;
    line-height: 22px;
    color: var(--text-secondary);
    margin: 0 0 16px;
  }
  /* v20.0: quieter than the intro — used for the "we attach a few technical
     details" note under the bug-report form. */
  .of-modal-fineprint {
    font-size: 12.5px;
    line-height: 19px;
    color: var(--text-secondary);
    margin: 4px 0 14px;
  }
  .of-modal-card {
    background: var(--surface-50);
    border: 1px solid var(--border-grey);
    border-radius: var(--radius);
    padding: 14px 16px;
    margin-bottom: 14px;
  }
  .of-modal-card-label {
    display: block;
    font-size: 11px;
    font-weight: 700;
    letter-spacing: 0.1em;
    text-transform: uppercase;
    color: var(--mercury-red);
    margin-bottom: 8px;
  }
  .of-modal-card-list {
    margin: 0;
    padding: 0;
    list-style: none;
  }
  .of-modal-card-list li {
    font-size: 13px;
    line-height: 22px;
    color: var(--mercury-black);
    padding-left: 18px;
    position: relative;
  }
  .of-modal-card-list li::before {
    content: "\2022";
    color: var(--mercury-red);
    position: absolute;
    left: 4px;
    top: 0;
    font-weight: 900;
  }
  .of-modal-build-dl {
    margin: 0;
    display: grid;
    grid-template-columns: max-content 1fr;
    column-gap: 14px;
    row-gap: 4px;
    font-size: 13px;
    line-height: 20px;
  }
  .of-modal-build-dl dt {
    color: var(--text-secondary);
    font-weight: 500;
  }
  .of-modal-build-dl dd {
    margin: 0;
    color: var(--mercury-black);
    font-weight: 600;
  }

  /* Copy summary section. Sits after the optional note field.
     The button writes the formatted build summary to the
     clipboard; we don't render the summary text inline because
     the build cards above already show that information. */
  .of-modal-copy-wrap {
    margin-top: 14px;
    padding: 14px 16px;
    background: var(--surface-50);
    border: 1px solid var(--border-grey);
    border-radius: var(--radius);
    display: flex;
    flex-direction: column;
    align-items: flex-start;
    gap: 10px;
  }
  .of-modal-copy-help {
    margin: 0;
    font-size: 12.5px;
    line-height: 18px;
    color: var(--text-secondary);
  }
  .of-modal-summary-copy {
    appearance: none;
    display: inline-flex;
    align-items: center;
    gap: 8px;
    padding: 9px 18px;
    min-height: 38px;
    background: var(--mercury-red);
    color: var(--surface-white);
    border: 0;
    border-radius: 8px;
    font-weight: 700;
    font-size: 13px;
    cursor: pointer;
    font-family: inherit;
    transition: background var(--dur-fast) var(--ease), transform var(--dur-fast) var(--ease);
  }
  .of-modal-summary-copy:hover {
    background: var(--mercury-red-hover);
    transform: translateY(-1px);
  }
  .of-modal-summary-copy:focus-visible {
    outline: 3px solid var(--mercury-red);
    outline-offset: 2px;
  }
  .of-modal-summary-copy[data-copied="true"] {
    background: var(--savings-green);
  }
  .of-modal-summary-copy[data-copied="true"]:hover {
    background: var(--savings-green);
    transform: none;
  }
  .of-modal-summary-copy-icon {
    font-size: 15px;
    line-height: 1;
  }
  @media (prefers-reduced-motion: reduce) {
    .of-modal-summary-copy { transition: none; }
    .of-modal-summary-copy:hover { transform: none; }
  }

  /* Visually hidden 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;
  }

  /* ============ Utility bar (Start Over + Help) ============
     Sits between the mode toggle and the panel; always visible.
     Provides one consistent location for the help affordance
     (WCAG SC 3.2.6 Consistent Help) and a persistent escape
     hatch (User Control & Freedom). */
  .of-util-bar {
    display: flex;
    justify-content: flex-start;
    align-items: center;
    gap: 12px;
    margin: 16px 0 0;
    padding: 6px 0;
  }
  /* Start over carries the hidden attribute on the landing view (set in
     updateHeaderCompact). The .of-util-bar button rule below sets
     display:inline-flex, which as an author rule outranks the UA
     [hidden] { display:none } by origin, so without this the button would
     stay visible. This restores hidden's effect inside the bar. */
  .of-util-bar [hidden] { display: none; }
  .of-util-bar a, .of-util-bar button {
    background: none;
    border: 0;
    padding: 8px 4px;
    min-height: 32px;
    color: var(--text-secondary);
    font-size: 13px;
    font-weight: 500;
    cursor: pointer;
    display: inline-flex;
    align-items: center;
    gap: 6px;
    text-decoration: none;
    font-family: inherit;
  }
  .of-util-bar a:hover, .of-util-bar button:hover { color: var(--mercury-red); }
  .of-util-bar a:hover { text-decoration: underline; }
  .of-util-bar svg { width: 14px; height: 14px; }
  /* Start Over and Help both render as bordered buttons in
     Mercury red. Start Over is destructive (resets the search) and
     Help is a passive disclosure, but visually they're paired so
     the utility bar reads as a row of two equally-weighted actions
     rather than a button + a quiet link. */
  .of-util-bar #of-start-over,
  .of-util-bar #of-help-toggle {
    padding: 7px 14px;
    border: 1px solid var(--mercury-red);
    border-radius: 8px;
    background: var(--surface-white);
    color: var(--mercury-red);
    font-weight: 600;
    transition: border-color 0.15s ease-out, background-color 0.15s ease-out, color 0.15s ease-out;
  }
  .of-util-bar #of-start-over:hover,
  .of-util-bar #of-help-toggle:hover {
    background: var(--mercury-red-soft);
    border-color: var(--mercury-red-hover);
    color: var(--mercury-red-hover);
    text-decoration: none;
  }
  .of-util-bar #of-help-toggle[aria-expanded="true"] {
    background: var(--mercury-red-soft);
  }

  /* ============ Inline help panel (opens from utility bar Help button) ============
     Anchored under the panel; same DOM position every time so screen
     readers and keyboard users find help in a consistent spot
     (WCAG SC 3.2.6). Hidden by default; toggled via aria-expanded. */
  .of-help-panel {
    margin: 0 0 20px;
    padding: 14px 16px;
    background: var(--mercury-red-soft);
    border: 1px solid var(--mercury-red-tint);
    border-left: 4px solid var(--mercury-red);
    border-radius: var(--radius);
    font-size: 14px;
    line-height: 20px;
    color: var(--mercury-black);
  }
  .of-help-panel[hidden] { display: none; }
  .of-help-panel-title {
    font-weight: 700;
    margin-bottom: 4px;
    color: var(--mercury-red);
    font-size: 13px;
    letter-spacing: 0.06em;
    text-transform: uppercase;
  }
  .of-help-panel a {
    color: var(--mercury-red);
    font-weight: 500;
    text-decoration: underline;
  }

  /* ============ Selections summary chip strip ============
     Renders above the results grid. Each chip shows one selection;
     clicking returns the user to that step for edit. Solves
     "Recognition rather than recall" without forcing a back-walk. */
  .of-selections-strip {
    margin-bottom: 20px;
    padding: 12px 14px;
    background: var(--surface-50);
    border: 1px solid var(--border-grey);
    border-radius: var(--radius);
  }
  .of-selections-strip-label {
    font-size: 12px;
    font-weight: 700;
    letter-spacing: 0.08em;
    text-transform: uppercase;
    color: var(--text-secondary);
    margin-right: 4px;
  }
  .of-selections-list {
    display: inline-flex;
    flex-wrap: wrap;
    gap: 6px;
    margin-top: 6px;
  }
  /* "Clear all" affordance, only rendered in Quick Filters mode
     when 2+ chips are present. Subtle text-link styling so it
     doesn't compete with the chip row, but tappable. */
  .of-selections-clear {
    appearance: none;
    background: none;
    border: 0;
    padding: 6px 4px;
    margin-left: 8px;
    color: var(--text-secondary);
    font-size: 12px;
    font-weight: 600;
    text-decoration: underline;
    text-underline-offset: 3px;
    cursor: pointer;
    font-family: inherit;
    min-height: 32px;
    transition: color var(--dur-fast) var(--ease);
  }
  .of-selections-clear:hover { color: var(--mercury-red); }
  .of-selections-clear:focus-visible {
    outline: 3px solid var(--mercury-red);
    outline-offset: 2px;
    border-radius: 4px;
  }
  .of-selection-chip {
    appearance: none;
    display: inline-flex;
    align-items: center;
    gap: 6px;
    padding: 6px 10px 6px 12px;
    min-height: 32px;
    border: 1px solid var(--mercury-red-tint);
    background: var(--surface-white);
    color: var(--mercury-black);
    border-radius: 999px;
    font-size: 13px;
    font-weight: 500;
    cursor: pointer;
    font-family: inherit;
    transition: background var(--dur-fast) var(--ease), border-color var(--dur-fast) var(--ease), color var(--dur-fast) var(--ease);
  }
  .of-selection-chip:hover {
    background: var(--mercury-red-soft);
    border-color: var(--mercury-red);
  }
  .of-selection-chip-pencil {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 16px;
    height: 16px;
    color: var(--mercury-red);
    transition: transform var(--dur-fast) var(--ease);
  }
  .of-selection-chip-pencil svg { width: 12px; height: 12px; display: block; }
  .of-selection-chip:hover .of-selection-chip-pencil { transform: translateY(-1px) rotate(-6deg); }
  @media (prefers-reduced-motion: reduce) {
    .of-selection-chip-pencil { transition: none; }
    .of-selection-chip:hover .of-selection-chip-pencil { transform: none; }
  }

  /* ============ Mobile sticky "Top match" bar ============
     Mobile-only bottom-fixed bar that surfaces the highest-ranked
     result with one-tap access from anywhere on the results page.
     Hides on desktop (where the result grid is already visible at
     a glance), hides when no matches exist, and yields the screen
     to the Compare sticky bar when Compare mode is active. */
  .of-topmatch-bar {
    position: fixed;
    left: 12px;
    right: 12px;
    bottom: 12px;
    z-index: 90;
    background: var(--surface-inverse);
    color: var(--on-inverse);
    border-radius: var(--radius-lg);
    box-shadow: var(--shadow-3);
    padding: 10px 14px;
    display: none;
    align-items: center;
    gap: 12px;
    transform: translateY(120%);
    opacity: 0;
    transition: transform var(--dur-med) var(--ease-out-back), opacity var(--dur-fast) var(--ease);
    /* Respect iOS safe area on devices with home-bar indicator */
    padding-bottom: calc(10px + env(safe-area-inset-bottom, 0px));
  }
  .of-topmatch-bar[data-visible="true"] {
    transform: translateY(0);
    opacity: 1;
  }
  .of-topmatch-cowl {
    flex: 0 0 auto;
    width: 56px;
    height: 42px;
    border-radius: 8px;
    background: rgba(255, 255, 255, 0.08);
    overflow: hidden;
    display: flex;
    align-items: center;
    justify-content: center;
  }
  .of-topmatch-cowl svg {
    width: 90%;
    height: 90%;
    display: block;
  }
  .of-topmatch-info {
    flex: 1 1 auto;
    min-width: 0;
    line-height: 1.2;
  }
  .of-topmatch-label {
    display: block;
    font-size: 10px;
    font-weight: 700;
    text-transform: uppercase;
    letter-spacing: 0.1em;
    color: var(--mercury-red-soft);
    opacity: 0.85;
  }
  .of-topmatch-name {
    display: block;
    font-size: 13px;
    font-weight: 600;
    margin-top: 2px;
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
  }
  .of-topmatch-price {
    display: block;
    font-size: 12px;
    margin-top: 2px;
    opacity: 0.8;
    font-variant-numeric: tabular-nums;
  }
  .of-topmatch-cta {
    flex: 0 0 auto;
    background: var(--mercury-red);
    color: var(--surface-white);
    border: 0;
    border-radius: 999px;
    padding: 8px 14px;
    font-size: 13px;
    font-weight: 700;
    text-decoration: none;
    cursor: pointer;
    font-family: inherit;
    transition: background var(--dur-fast) var(--ease);
  }
  .of-topmatch-cta:hover { background: var(--mercury-red-hover); }
  .of-topmatch-close {
    flex: 0 0 auto;
    width: 28px;
    height: 28px;
    border-radius: 50%;
    border: 0;
    background: rgba(255, 255, 255, 0.10);
    color: var(--on-inverse);
    font-size: 16px;
    line-height: 1;
    cursor: pointer;
    display: flex;
    align-items: center;
    justify-content: center;
    font-family: inherit;
  }
  .of-topmatch-close:hover { background: rgba(255, 255, 255, 0.18); }
  /* Show on mobile only. Above ~640px the results grid is dense
     enough that the bar would be redundant. */
  @media (max-width: 640px) {
    .of-topmatch-bar { display: flex; }
  }
  @media (prefers-reduced-motion: reduce) {
    .of-topmatch-bar { transition: none; }
  }

  /* ============ Wave separator ============
     Reusable horizontal-rule replacement that prints a subtle
     water-line motif tied to the PNW market. Used between major
     sections (results header → grid, empty state, footer).
     Atmospheric, not gimmicky. */
  .of-wave-sep {
    height: 8px;
    margin: 24px 0;
    background:
      radial-gradient(6px 3px at 6px 50%, var(--water-blue) 0 3px, transparent 4px),
      radial-gradient(6px 3px at 22px 50%, var(--water-blue) 0 3px, transparent 4px),
      radial-gradient(6px 3px at 38px 50%, var(--water-blue) 0 3px, transparent 4px);
    background-repeat: repeat-x;
    background-size: 48px 8px;
    opacity: 0.20;
  }
  .of-wave-sep[data-soft="true"] { opacity: 0.12; }

  /* ============ Empty-state diagnostic ============
     When no matches, identify the most likely culprit selection
     and suggest changing it (Error recovery + diagnostic, Nielsen #9). */
  .of-empty-diagnostic {
    background: var(--warn-bg);
    border: 1px solid var(--warn-border);
    border-radius: var(--radius);
    padding: 14px 16px;
    margin-bottom: 20px;
    text-align: left;
    color: var(--warn-text);
    font-size: 14px;
    line-height: 20px;
  }
  .of-empty-diagnostic strong { color: var(--warn-strong); }
  .of-empty-diagnostic button {
    margin-top: 10px;
    appearance: none;
    background: var(--surface-white);
    border: 1px solid var(--warn-border);
    color: var(--warn-text);
    padding: 8px 14px;
    font-size: 13px;
    font-weight: 500;
    border-radius: 6px;
    cursor: pointer;
    font-family: inherit;
  }
  .of-empty-diagnostic button:hover { background: var(--warn-bg); }

  /* Step 6 intro copy (Match between system and real world). */
  .of-type-intro {
    margin-bottom: 16px;
    padding: 12px 14px;
    background: var(--surface-50);
    border-radius: var(--radius);
    font-size: 14px;
    line-height: 20px;
    color: var(--text-secondary);
  }
  .of-type-intro strong { color: var(--mercury-black); }

  /* "Or" divider between the real configuration tiles and the
     algorithmic "Recommend for me" tile. Signals to users that
     the tile below is a shortcut, not a product choice. */
  .of-type-divider {
    display: flex;
    align-items: center;
    margin: 20px 0 12px;
    color: var(--text-secondary);
    font-size: 12px;
    font-weight: 700;
    letter-spacing: 0.16em;
    text-transform: uppercase;
  }
  .of-type-divider::before,
  .of-type-divider::after {
    content: "";
    flex: 1;
    height: 1px;
    background: var(--border-grey);
  }
  .of-type-divider::before { margin-right: 12px; }
  .of-type-divider::after  { margin-left: 12px; }

  .of-options-recommend {
    grid-template-columns: 1fr !important;
    max-width: 480px;
    margin: 0 auto;
  }
  .of-card-recommend {
    border-style: dashed;
    background:
      linear-gradient(135deg, rgba(173,27,30,0.03) 0%, rgba(173,27,30,0.06) 100%),
      var(--surface-white);
  }
  .of-card-recommend:hover {
    border-style: solid;
  }
  /* "Auto" badge sits in the top-left of the Recommend tile so it
     reads at a glance as an algorithmic shortcut. */
  .of-card-auto-badge {
    position: absolute;
    top: 10px;
    left: 10px;
    padding: 2px 8px;
    background: var(--mercury-red);
    color: var(--surface-white);
    border-radius: 999px;
    font-size: 10px;
    font-weight: 800;
    letter-spacing: 0.1em;
    text-transform: uppercase;
    line-height: 16px;
  }

  /* Disabled bucket explanation in Step 4. */
  .of-card[disabled] .of-card-sub {
    font-style: italic;
    color: var(--text-secondary);
  }
  @media (prefers-reduced-motion: reduce) {
    *, *::before, *::after {
      animation-duration: 0.001ms !important;
      animation-iteration-count: 1 !important;
      animation-delay: 0ms !important;
      transition-duration: 0.001ms !important;
      transition-delay: 0ms !important;
      scroll-behavior: auto !important;
    }
  }  /* ============ Print stylesheet ============
     A buyer comparing engines may want to print or save-to-PDF the
     match list for in-person reference (at the boat, at the dealer,
     reviewing later). Hide the wizard, sticky bars, util bar, mode
     toggle, and CTAs. Show only the breadcrumb of selections + the
     result grid with prices and key specs. */
  @media print {
    @page { margin: 1cm; }
    body { background: #fff; color: #000; }
    .of-shell {
      box-shadow: none !important;
      border: none !important;
      background: #fff !important;
      padding: 0 !important;
      max-width: 100% !important;
    }
    /* Hide everything that isn't results content */
    .of-skip,
    .of-util-bar,
    .of-modes-wrap,
    .of-progress,
    .of-step-label,
    #of-step-wrap,
    .of-topmatch-bar,
    .of-compare-bar,
    .of-results-trust,
    .of-engine-ask,
    .of-engine-cta,
    .of-engine-badge,
    .of-engine-compare,
    .of-results-header-actions,
    .of-modal,
    .of-help-panel,
    .of-wave-sep,
    .of-header::after,
    .of-eyebrow {
      display: none !important;
    }
    .of-header {
      text-align: left;
      margin-bottom: 16pt;
      padding-bottom: 8pt;
      border-bottom: 1pt solid #000;
    }
    #of-title {
      font-size: 18pt;
      color: #000;
    }
    .of-breadcrumb { margin: 8pt 0 !important; }
    .of-crumb-btn {
      background: none !important;
      border: 1pt solid #888 !important;
      color: #333 !important;
      font-size: 9pt !important;
    }
    .of-selections-strip {
      background: none !important;
      border: 1pt solid #888 !important;
    }
    .of-selection-chip-pencil { display: none !important; }
    .of-results { display: block !important; }
    .of-engine {
      page-break-inside: avoid;
      break-inside: avoid;
      box-shadow: none !important;
      border: 1pt solid #888 !important;
      margin-bottom: 12pt;
      padding: 8pt;
      background: #fff !important;
      display: grid !important;
      grid-template-columns: 1.5in 1fr !important;
      gap: 12pt !important;
    }
    .of-engine-illustration {
      background: none !important;
      padding: 4pt !important;
      max-height: 1.5in;
    }
    .of-engine-illustration::before,
    .of-engine-illustration::after { display: none !important; }
    .of-engine-name { font-size: 13pt !important; color: #000 !important; }
    .of-engine-specs { font-size: 10pt !important; }
    .of-engine-price {
      font-size: 14pt !important;
      color: #000 !important;
    }
    .of-engine-savings {
      background: none !important;
      border: 1pt solid #157a3f !important;
      color: #157a3f !important;
    }
    .of-engine-msrp { font-size: 9pt !important; }
    /* Add the URL after each linked engine name so the printout
       is actionable (reader can type the URL or scan from QR if
       added later). */
    .of-engine .of-engine-name::after {
      content: " (SKU: " attr(data-sku) ")";
      font-weight: normal;
      color: #555;
      font-size: 10pt;
    }
  }

  /* v11.3: "Things to consider" accordion on each engine card (ported from 11.x). */
  .of-consider {
    margin: 8px 0 4px;
    border: 1.5px solid var(--mercury-red, #ad1b1e);
    border-radius: 8px;
    background: var(--surface-50, #fafbfc);
  }
  .of-consider summary.of-consider-q {
    cursor: pointer;
    list-style: none;
    padding: 8px 12px;
    font-size: 13px;
    font-weight: 600;
    color: var(--mercury-black, #1a1a1a);
    display: flex;
    align-items: center;
    justify-content: space-between;
  }
  .of-consider summary.of-consider-q::-webkit-details-marker { display: none; }
  .of-consider summary.of-consider-q::after {
    content: '';
    width: 18px;
    height: 18px;
    display: inline-flex;
    margin-left: 8px;
    flex: none;
    background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23ad1b1e' stroke-width='3' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpolyline points='6 9 12 15 18 9'/%3E%3C/svg%3E");
    background-repeat: no-repeat;
    background-position: center;
    background-size: 18px 18px;
    transition: transform 0.18s ease;
  }
  .of-consider[open] summary.of-consider-q::after { transform: rotate(180deg); }
  /* #9 (v17.1): smooth open/close for every disclosure (Things to consider,
     the transom measuring guides, and the FAQ) where the browser supports
     ::details-content + interpolate-size; elsewhere it snaps (graceful).
     Off under reduced-motion. The FAQ keeps its complementary fade reveal. */
  @media (prefers-reduced-motion: no-preference) {
    details::details-content {
      block-size: 0; opacity: 0; overflow: clip;
      transition: block-size var(--dur-med) var(--ease), opacity var(--dur-med) var(--ease), content-visibility var(--dur-med) allow-discrete;
      transition-behavior: allow-discrete;
    }
    details[open]::details-content { block-size: auto; opacity: 1; }
  }
  .of-consider:hover { border-color: var(--mercury-red, #ad1b1e); }
  .of-consider .of-consider-list { margin: 0; padding: 4px 12px 10px; font-size: 13px; line-height: 19px; }
  .of-consider .of-consider-list dt { font-weight: 600; color: var(--mercury-black, #1a1a1a); margin-top: 6px; }
  .of-consider .of-consider-list dd { margin: 0 0 2px; color: var(--text-secondary, #5a5f66); }
  /* v15.8: Things-to-consider prominence — teaser line, caveat flags, icons. */
  .of-consider-head { display: flex; flex-direction: column; gap: 1px; min-width: 0; flex: 1 1 auto; }
  .of-consider-title { font-size: 13px; font-weight: 600; color: var(--mercury-red, #ad1b1e); }
  .of-consider-teaser { font-size: 12px; font-weight: 400; color: var(--text-secondary, #5a5f66); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; max-width: 100%; }
  .of-consider-teaser.is-caveat { color: #8a5300; }
  .of-consider[open] .of-consider-teaser { display: none; }
  .of-consider-ic { width: 14px; height: 14px; flex: 0 0 14px; color: var(--text-secondary, #5a5f66); }
  .of-consider .of-consider-list dt { display: flex; align-items: center; gap: 6px; }
  .of-consider .of-consider-list dt.is-caveat { color: #8a5300; }
  .of-consider .of-consider-list dt.is-caveat .of-consider-ic { color: #c98a2b; }
  .of-consider .of-consider-list dd.is-caveat { color: #6e4a14; border-left: 2px solid #d9a441; padding-left: 8px; }
  /* v12.0 (A1): "Recommended for your boat" badge on the inferred HP bucket. */
  .of-card-rec {
    display: inline-block; margin-top: 8px; padding: 2px 8px; border-radius: 999px;
    font-size: 10px; font-weight: 700; text-transform: uppercase; letter-spacing: 0.04em;
    color: #fff; background: var(--mercury-red, #ad1b1e);
  }
  /* v12.0 (B1\u2192C2): inline transom diagram disclosure inside Things to consider. */
  /* F4 (v12.3): subtle ring on the recommended HP bucket so it invites a tap. */
  .of-card--recommended { outline: 2px dashed var(--mercury-red, #ad1b1e); outline-offset: 2px; }
  /* S1 (v12.3): visible step tracker label. */
  .of-step-label { margin: 0 0 6px; font-size: 12px; font-weight: 700; text-transform: uppercase; letter-spacing: .05em; color: var(--text-secondary, #5a5f66); }
  /* ===== v12.4 motion ===== Functional, restrained, transform/opacity only,
     reusing the existing easing tokens. All guarded for reduced-motion. */
  .of-options .of-card { animation: ofOptEnter 300ms var(--ease) both; }
  .of-options .of-card:nth-child(1) { animation-delay: 0ms; }
  .of-options .of-card:nth-child(2) { animation-delay: 55ms; }
  .of-options .of-card:nth-child(3) { animation-delay: 110ms; }
  .of-options .of-card:nth-child(4) { animation-delay: 165ms; }
  .of-options .of-card:nth-child(5) { animation-delay: 220ms; }
  .of-options .of-card:nth-child(6) { animation-delay: 275ms; }
  .of-options .of-card:nth-child(n+7) { animation-delay: 300ms; }
  @keyframes ofOptEnter { 0% { opacity: 0; transform: translateY(10px); } 100% { opacity: 1; transform: translateY(0); } }
  .of-card:hover:not([disabled]) { transform: translateY(-2px); }
  .of-card:active:not([disabled]) { transform: scale(0.985); transition-duration: 80ms; }
  .of-card-just-picked .of-card-icon svg { animation: ofIconPop 360ms var(--ease-out-back) both; }
  @keyframes ofIconPop { 0% { transform: scale(1); } 35% { transform: scale(1.18); } 100% { transform: scale(1); } }
  .of-card--recommended { animation: ofRecPulse 1.6s var(--ease) 2; }
  @keyframes ofRecPulse { 0%, 100% { outline-color: rgba(173,27,30,0.95); } 50% { outline-color: rgba(173,27,30,0.3); } }
  @media (prefers-reduced-motion: reduce) {
    .of-options .of-card,
    .of-card-just-picked .of-card-icon svg,
    .of-card--recommended { animation: none !important; }
    .of-card:hover:not([disabled]),
    .of-card:active:not([disabled]) { transform: none; }
  }
  /* ===== v12.5 remote-page fixes (R1\u2013R6) ===== */
  @media (min-width: 481px) {
    /* R2: give the notice panel a calm desktop card (it was bare on desktop). */
    .of-remote-panel { background: var(--surface-50, #fafbfc); border: 1px solid var(--surface-300, #e0e3e8); border-top: 3px solid var(--mercury-red, #ad1b1e); border-radius: 12px; padding: 18px 20px; margin-bottom: 20px; }
    /* R1: stack the CTA and the phone line (no flex rule applied here before, so they ran inline). */
    .of-remote-panel-actions { display: flex; flex-direction: column; align-items: flex-start; gap: 0.6rem; }
  }
  /* R2: the panel is focused programmatically for the SR announcement, not a tab stop \u2014 kill the red focus box. */
  #of-remote-panel:focus { outline: none; }
  /* R4 */
  .of-engine-tiller-note { font-size: 12px; color: var(--text-secondary, #5a5f66); margin: 2px 0 0; }
  .of-engine-ask--primary { background: var(--mercury-red, #ad1b1e); color: #fff; border-color: var(--mercury-red, #ad1b1e); }
  .of-engine-ask--primary:hover { background: #8f1619; border-color: #8f1619; }
  .of-engine-ask--primary .of-engine-ask-icon { color: #fff; }
  /* R5 */
  .of-remote-below { display: flex; flex-wrap: wrap; gap: 12px; align-items: center; margin-top: 20px; }
  /* R6: few remote matches center instead of stranding one card on the left. */
  .of-results--remote { justify-content: center; }
  @media (min-width: 600px) { .of-results--remote { grid-template-columns: repeat(auto-fit, minmax(280px, 380px)); } }
  /* v13.0 remote conversion page */
  .of-remote-panel-note { font-size: 13px; color: var(--text-secondary, #5a5f66); margin: 0 0 12px; }
  .of-remote-empty-note { font-size: 14px; color: var(--text-secondary, #5a5f66); margin: 4px 0 0; }
  .of-remote-checklist-disc { margin: 6px 0 2px; }
  .of-remote-checklist-summary {
    font-weight: 700;
    cursor: pointer;
    padding: 4px 0;
    color: var(--mercury-red);
  }
  .of-remote-checklist-summary:focus-visible {
    outline: 2px solid var(--mercury-red);
    outline-offset: 2px;
    border-radius: 4px;
  }
  .of-remote-checklist { margin: 4px 0 4px; padding-left: 1.15rem; }
  .of-remote-checklist li { margin: 3px 0; }
  /* v13.5: in-app remote quote form layout */
  .of-quote-form .of-quote-subhead { font-size: 12px; font-weight: 700; text-transform: uppercase; letter-spacing: .04em; color: var(--text-secondary, #5a5f66); margin: 20px 0 10px; }
  .of-quote-form .of-quote-subhead:first-child { margin-top: 4px; }
  .of-quote-grid { display: grid; grid-template-columns: 1fr 1fr; gap: 14px 14px; }
  @media (max-width: 520px) { .of-quote-grid { grid-template-columns: 1fr; } }
  .of-quote-form .of-form-field { margin: 0 0 14px; }
  .of-quote-grid .of-form-field { margin: 0; }
  .of-quote-form select.of-input { width: 100%; min-height: 44px; }
  /* v14.9: validation error as a prominent banner (icon + tint + red accent),
     shown only when populated so the empty state reserves no space. */
  .of-quote-error { margin: 0; }
  .of-quote-error:not(:empty) {
    display: flex; align-items: flex-start; gap: 8px;
    margin: 4px 0 12px; padding: 10px 12px;
    font-size: 14px; font-weight: 600; line-height: 1.4;
    color: #8a1014; background: #fbeaea;
    border: 1px solid #e3b5b6; border-left: 4px solid var(--mercury-red, #ad1b1e);
    border-radius: 8px;
  }
  .of-quote-error:not(:empty)::before { content: "\26a0"; font-size: 15px; line-height: 1.4; flex: none; }
  /* invalid required fields: red border + ring (paired with the banner + icon, never colour alone). */
  .of-quote-form .of-input.is-invalid { border-color: var(--mercury-red, #ad1b1e); box-shadow: 0 0 0 1px var(--mercury-red, #ad1b1e); }
  /* a static cue at the button itself, where the tap happened (no motion). */
  .of-quote-form .of-btn-primary.is-error { box-shadow: 0 0 0 3px rgba(173, 27, 30, 0.40); }
  /* v13.5 spacing audit: the 'Things to consider' terms ran together. */
  .of-consider .of-consider-list dt { margin-top: 12px; }
  .of-consider .of-consider-list dt:first-of-type { margin-top: 2px; }
  .of-consider .of-consider-list dd { margin: 2px 0 0; }
  /* v14.5: quote-form selects were missing the dropdown caret because .of-input
     clobbered the .of-select background; re-assert it with higher specificity. */
  select.of-input.of-select {
    appearance: none; -webkit-appearance: none;
    padding-right: 36px;
    background-image: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' width='12' height='8' viewBox='0 0 12 8'><path d='M1 1l5 5 5-5' stroke='%23ad1b1e' stroke-width='1.5' fill='none' stroke-linecap='round' stroke-linejoin='round'/></svg>");
    background-repeat: no-repeat;
    background-position: right 14px center;
  }
  /* v14.5: phone CTA as a red-border button at ALL widths (the rule above
     lives in the mobile media block; this one wins everywhere). */
  .of-remote-panel .of-remote-panel-phone {
    display: inline-flex; align-items: center; gap: 0.45rem;
    color: #ad1b1e; text-decoration: none; font-size: 0.95rem; font-weight: 700;
    min-height: 44px; padding: 0.5rem 1.1rem;
    border: 1.5px solid #ad1b1e; border-radius: 8px; background: #fff;
    transition: background 0.15s, color 0.15s, box-shadow 0.15s;
  }
  .of-remote-panel .of-remote-panel-phone:hover,
  .of-remote-panel .of-remote-panel-phone:focus-visible {
    background: #ad1b1e; color: #fff; text-decoration: none; outline: none;
  }
  .of-remote-panel .of-remote-panel-phone:focus-visible { box-shadow: 0 0 0 3px rgba(173, 27, 30, 0.35); }
  /* #4 (v14.5): metrics-matched fallback to cut font-swap CLS on the serif
     headline. NOTE: these overrides are conservative STARTING estimates —
     verify p75 CLS in CrUX and tune (e.g. with the Fontaine generator). */
  @font-face {
    font-family: 'Roboto Slab Fallback';
    src: local('Georgia'), local('Times New Roman'), local('serif');
    size-adjust: 106%;
    ascent-override: 88%;
    descent-override: 24%;
    line-gap-override: 0%;
  }
  .of-quote-form .of-quote-eng { margin: 0 0 10px; font-weight: 700; color: var(--mercury-black, #1a1a1a); }
  /* v15.3: the transom diagram lights up the tier that matches the slider. */
  .of-tr-tier { transition: fill .12s var(--ease, ease); }
  .of-tr-tier.is-active { fill: var(--mercury-red, #ad1b1e); font-weight: 700; }  /* ============ Site footer (Report a problem) ============
     Deliberately quiet: it shouldn't compete with the search or the
     results. Muted by default, picks up Mercury red on hover/focus the
     same way the utility bar does, so it reads as "here if you need it"
     rather than a primary action. The water-blue hairline is the accent
     we reserve for separators. Focus ring is inherited from the global
     :focus-visible rule — don't restyle it here. */
  .of-site-foot {
    max-width: 1200px;
    margin: 0 auto 24px;
    padding: 16px 20px 0;
    border-top: 1px solid var(--water-blue-soft);
    text-align: center;
  }
  .of-foot-report {
    display: inline-flex;
    align-items: center;
    gap: 7px;
    padding: 8px 14px;
    background: none;
    border: 1px solid var(--border-grey);
    border-radius: var(--radius-sm);
    color: var(--text-secondary);
    font-family: inherit;
    font-size: 0.92rem;
    font-weight: 600;
    cursor: pointer;
    transition: color 0.15s ease-out, border-color 0.15s ease-out;
  }
  .of-foot-report svg { width: 15px; height: 15px; }
  .of-foot-report:hover { color: var(--mercury-red); border-color: var(--mercury-red); }

  /* v23.4 (I-06): on touch devices iOS Safari latches :hover after a tap and
     holds it until the next tap elsewhere. The card/tile hover treatments
     recolor the border red and raise the ::after tint — which on a phone reads
     as a persistent "selected" state and competes with the real selection
     signal (aria-pressed / [open] / the recommend ring). On devices with no
     hover capability we neutralize just those mis-readable treatments; buttons
     and links keep their (harmless) hover styling. Desktop is untouched —
     this only applies where the pointer can't hover. */
  @media (hover: none) {
    .of-card:hover:not([disabled]),
    .of-card[disabled]:hover {
      box-shadow: none;
      border-color: var(--border-grey);
      transform: none;
    }
    .of-card:hover::after,
    .of-card[disabled]:hover::after { opacity: 0; }
    .of-engine:hover {
      box-shadow: var(--shadow-1);
      border-color: var(--border-grey);
    }
    .of-consider:hover { border-color: var(--border-grey); }
  }

  /* v25.8 (audit-05): forced-colors (Windows High Contrast) support.
     Our selection states lean on background fills, box-shadow, and a low-opacity
     ::after tint. Forced-colors mode overrides all three with the system palette,
     so the active tab and a picked option can become indistinguishable from the
     rest. Outline survives the forced palette, so we re-assert each selected
     state with a Highlight outline. Offset inward so it reads as a selected
     border rather than a focus ring. */
  @media (forced-colors: active) {
    .of-mode-tab[aria-selected="true"],
    .of-card[aria-pressed="true"],
    .of-compare-mode-toggle[aria-pressed="true"] {
      outline: 2px solid Highlight;
      outline-offset: -2px;
    }
    .of-card--recommended { outline-color: Highlight; }
  }
