/* ================================================================== */
/*  public/assets/css/portal-funnel.css                                */
/*                                                                     */
/*  Public booking funnel — mobile-first step styles.                  */
/*                                                                     */
/*  Consumed by layouts/portal-funnel.html.twig (extends portal.html). */
/*  Loads AFTER portal.css so it inherits the --sps-* token palette.   */
/*                                                                     */
/*  Reference viewport: iPhone SE / 320px wide.                        */
/*  Design constraints (per operator directive B.2 quality bar):       */
/*    - 44×44px minimum tap targets (Apple HIG)                        */
/*    - 16px minimum input font (no iOS zoom-on-focus)                 */
/*    - Safe-area-inset padding for notched devices                    */
/*    - Single-column layout at every viewport                         */
/*    - No hover-only affordances                                      */
/*    - Respects prefers-reduced-motion                                */
/*    - Progressive-enhancement: styles work without JS                */
/*                                                                     */
/*  Token block below is the source of truth for B.2.1+ funnel steps.  */
/*  New step templates consume these tokens — do NOT redefine locally. */
/* ================================================================== */

/* ── Funnel-scoped tokens ───────────────────────────────────────── */
.portal-body {
    /* Spacing rhythm — use these, not raw px values, in step styles. */
    --sps-funnel-gutter:       16px;   /* page-edge breathing room    */
    --sps-funnel-stack:        14px;   /* vertical rhythm between rows */
    --sps-funnel-stack-lg:     22px;   /* between field groups         */
    --sps-funnel-card-pad:     20px;   /* inside step card             */

    /* Control sizing — every interactive element honours --sps-tap. */
    --sps-tap:                 44px;
    --sps-input-height:        48px;   /* tap + 4px visual breathing  */
    --sps-input-font:          16px;   /* MUST stay >= 16px on mobile  */
    --sps-input-radius:        10px;
    --sps-input-border:        1px solid rgba(148,163,184,0.22);
    --sps-input-border-focus:  1px solid var(--sps-accent);
    --sps-input-bg:            #0b1220;

    /* Error-state colours — match dashboard's field error tone */
    --sps-field-error:         var(--sps-danger);
    --sps-field-error-bg:      rgba(248,81,73,0.08);

    /* Success-state dim — mirrors the dashboard token palette.  Used
       for the selected-toggle fill on Home/Business and anywhere else
       a "selected" / "confirmed" micro-surface needs a green wash. */
    --sps-success-dim:         rgba(46,204,113,0.14);

    /* Progress-indicator dimensions */
    --sps-dot-size:            10px;
    --sps-dot-gap:             8px;
}

/* ── Funnel frame ───────────────────────────────────────────────── */
/*    The parent .portal-main centres its children; for the funnel we  */
/*    want a top-aligned flow so a tall Step 3 calendar doesn't push   */
/*    the progress indicator off-screen.  :has() is Baseline 2023 —    */
/*    safe for the customer-facing portal without a polyfill.          */
.portal-main:has(.portal-funnel) {
    align-items: flex-start;
    padding-top: 24px;
    padding-bottom: calc(40px + env(safe-area-inset-bottom));
    padding-left:   calc(var(--sps-funnel-gutter) + env(safe-area-inset-left));
    padding-right:  calc(var(--sps-funnel-gutter) + env(safe-area-inset-right));
}

.portal-funnel {
    width: 100%;
    max-width: 540px;
    margin: 0 auto;
}

.portal-funnel__card {
    background: var(--sps-surface-1);
    border: 1px solid var(--sps-border);
    border-radius: 18px;
    padding: var(--sps-funnel-card-pad);
    box-shadow: 0 24px 64px rgba(0, 0, 0, 0.35);
}

/* ── Step header (icon + title + subtext) ─────────────────────── */
/*  Rendered OUTSIDE the form card, centered, so the page reads as    */
/*  a heading over a form — not a form section.  Icon + title share   */
/*  one inline row; subtext sits centered under the whole thing.      */
.portal-step-header {
    margin: 0 0 var(--sps-funnel-stack-lg);
    text-align: center;
}

.portal-step-header__row {
    display: inline-flex;
    align-items: center;
    gap: 12px;
    flex-wrap: nowrap;
}

.portal-step-header__icon {
    flex: 0 0 auto;
    width: 38px;
    height: 38px;
    border-radius: 11px;
    background: var(--sps-accent-dim);
    color: var(--sps-accent);
    display: inline-flex;
    align-items: center;
    justify-content: center;
}

.portal-step-icon {
    width: 22px;
    height: 22px;
}

.portal-step-header__title {
    margin: 0;
    font-size: 1.5rem;
    font-weight: 700;
    line-height: 1.2;
    letter-spacing: -0.01em;
    color: var(--sps-text-primary);
}

.portal-step-header__subtext {
    margin: 10px auto 0;
    max-width: 420px;
    font-size: 0.95rem;
    line-height: 1.45;
    color: var(--sps-text-secondary);
}

/* ── Progress indicator ────────────────────────────────────────── */
.portal-progress {
    margin: 0 0 var(--sps-funnel-stack-lg);
    text-align: left;
}

.portal-progress__text {
    margin: 0 0 10px;
    font-size: 0.82rem;
    font-weight: 600;
    letter-spacing: 0.04em;
    text-transform: uppercase;
    color: var(--sps-text-secondary);
}

.portal-progress__text-label {
    color: var(--sps-text-primary);
    text-transform: none;
    letter-spacing: 0;
    font-weight: 700;
    margin-left: 6px;
}

.portal-progress__dots {
    display: flex;
    gap: var(--sps-dot-gap);
    padding: 0;
    margin: 0;
    list-style: none;
}

.portal-progress__dot {
    width: var(--sps-dot-size);
    height: var(--sps-dot-size);
    border-radius: 50%;
    background: var(--sps-surface-2);
    border: 1px solid var(--sps-border);
    transition: background 0.2s ease, border-color 0.2s ease;
}

.portal-progress__dot--done,
.portal-progress__dot--current {
    background: var(--sps-accent);
    border-color: var(--sps-accent);
}

/* ── Field group ───────────────────────────────────────────────── */
.portal-field {
    margin: 0 0 var(--sps-funnel-stack-lg);
}

.portal-field__label {
    display: block;
    margin: 0 0 6px;
    font-size: 0.9rem;
    font-weight: 600;
    color: var(--sps-text-primary);
}

.portal-field__label-optional {
    margin-left: 6px;
    font-weight: 400;
    color: var(--sps-text-muted);
    font-size: 0.82rem;
}

.portal-field__hint {
    margin: 6px 0 0;
    font-size: 0.82rem;
    color: var(--sps-text-muted);
}

/* ── Inputs ────────────────────────────────────────────────────── */
.portal-input,
.portal-select {
    width: 100%;
    min-height: var(--sps-input-height);
    padding: 10px 14px;
    border: var(--sps-input-border);
    border-radius: var(--sps-input-radius);
    background: var(--sps-input-bg);
    color: var(--sps-text-primary);
    font-size: var(--sps-input-font);
    line-height: 1.3;
    font-family: inherit;
    transition: border-color 0.15s ease, box-shadow 0.15s ease;
    /* iOS zoom-on-focus protection: font-size >= 16px is the fix */
    -webkit-appearance: none;
    appearance: none;
}

.portal-select {
    padding-right: 40px;
    background-image:
        linear-gradient(45deg, transparent 50%, var(--sps-text-secondary) 50%),
        linear-gradient(135deg, var(--sps-text-secondary) 50%, transparent 50%);
    background-position:
        calc(100% - 18px) calc(50% - 2px),
        calc(100% - 13px) calc(50% - 2px);
    background-size: 5px 5px, 5px 5px;
    background-repeat: no-repeat;
}

.portal-input:focus,
.portal-select:focus {
    outline: none;
    border: var(--sps-input-border-focus);
    box-shadow: 0 0 0 3px rgba(56, 139, 253, 0.22);
}

.portal-input::placeholder {
    color: var(--sps-text-muted);
}

/* Error state */
.portal-input--error,
.portal-select--error {
    border-color: var(--sps-field-error);
    background-color: var(--sps-field-error-bg);
}

.portal-field__error {
    display: none;
    margin: 6px 0 0;
    font-size: 0.85rem;
    color: var(--sps-field-error);
}

.portal-field__error--visible {
    display: block;
}

/* ── Row grouping for city / state / zip ───────────────────────── */
.portal-field-row {
    display: grid;
    gap: var(--sps-funnel-stack);
    grid-template-columns: 1fr;
}

@media (min-width: 480px) {
    /* Only collapse to two columns when we have comfortable width.   */
    /* 320px stays single-column per the reference-viewport brief.    */
    .portal-field-row--state-zip {
        grid-template-columns: 1fr 1.1fr;
    }
}

/* ── Home / Business toggle buttons ────────────────────────────── */
/*  Native radios are visually hidden (accessibility intact — the      */
/*  label wraps each radio so click/tap/keyboard still work).  The     */
/*  visible "button" is the label itself.  Selected state:             */
/*    - Dotted accent-green border                                     */
/*    - Success-dim fill                                               */
/*    - Green check mark SVG revealed in the leading corner            */
/*  Unselected state: quiet neutral border + muted text.               */
.portal-toggle {
    display: grid;
    grid-template-columns: 1fr 1fr;
    gap: 10px;
    margin: 0;
}

.portal-toggle__option {
    position: relative;
    display: flex;
    align-items: center;
    justify-content: center;
    min-height: var(--sps-tap);
    padding: 12px 16px;
    border: 2px solid rgba(148, 163, 184, 0.18);
    border-radius: var(--sps-input-radius);
    background: var(--sps-input-bg);
    color: var(--sps-text-secondary);
    font-size: 1rem;
    font-weight: 600;
    cursor: pointer;
    transition: border-color 0.15s ease, color 0.15s ease, background 0.15s ease;
}

.portal-toggle__option:hover {
    border-color: rgba(148, 163, 184, 0.35);
    color: var(--sps-text-primary);
}

/* Visually hidden but focusable and form-submittable. */
.portal-toggle__native {
    position: absolute;
    width: 1px;
    height: 1px;
    padding: 0;
    margin: -1px;
    overflow: hidden;
    clip: rect(0, 0, 0, 0);
    white-space: nowrap;
    border: 0;
    pointer-events: none;
}

/* Green check lives inside every label but only shows when selected. */
.portal-toggle__check {
    display: none;
    align-items: center;
    justify-content: center;
    width: 22px;
    height: 22px;
    margin-right: 10px;
    border-radius: 50%;
    background: var(--sps-success);
    color: #ffffff;
    flex: 0 0 auto;
}

.portal-toggle__check-icon {
    width: 14px;
    height: 14px;
}

/* Selected state — dotted border + dim fill + check visible. */
.portal-toggle__option:has(.portal-toggle__native:checked) {
    border: 2px dashed var(--sps-success);
    background: var(--sps-success-dim);
    color: var(--sps-text-primary);
}

.portal-toggle__option:has(.portal-toggle__native:checked) .portal-toggle__check {
    display: inline-flex;
}

/* Focus ring on the label when the hidden radio is focused. */
.portal-toggle__option:has(.portal-toggle__native:focus-visible) {
    box-shadow: 0 0 0 3px rgba(46, 204, 113, 0.28);
    outline: none;
}

/* ── Places Autocomplete search field ──────────────────────────── */
/*    Rendered above the manual fields as a speed-up.  Styled like    */
/*    any other .portal-input so the widget fits the UI seamlessly.   */
.portal-search {
    position: relative;
    margin-bottom: var(--sps-funnel-stack-lg);
}

.portal-search__label-row {
    display: flex;
    align-items: baseline;
    justify-content: space-between;
    gap: 8px;
    margin-bottom: 6px;
}

.portal-search__manual-toggle {
    background: transparent;
    border: none;
    padding: 0;
    font-size: 0.82rem;
    color: var(--sps-accent);
    cursor: pointer;
    min-height: var(--sps-tap);
}

.portal-search__manual-toggle:hover {
    color: var(--sps-accent-hover);
    text-decoration: underline;
}

/* Escape-hatch link under the Google search; small breathing room from
 * the hint above it. */
.portal-search__manual-link {
    margin-top: 8px;
    text-decoration: underline;
}

.portal-search__manual-link[hidden] {
    display: none;
}

.portal-search__hint {
    margin: 8px 0 0;
    font-size: 0.82rem;
    color: var(--sps-text-muted);
}

/* The Google widget renders its suggestion dropdown outside the card. */
/* We only need to ensure the underlying <input> inherits our styles.  */
/* The .pac-container (legacy Autocomplete) dropdown is styled below.  */

/* ── Manual-fields collapsible wrapper ─────────────────────────── */
/*    Hidden by default until the user either picks from Places or    */
/*    explicitly toggles "Enter manually" — keeps Step 1 visually     */
/*    compact on mobile.  When JS is disabled, .no-js rule unhides.   */
.portal-manual {
    display: block;
}

.portal-manual[hidden] {
    display: none !important;
}

/* ── Submit / back buttons ─────────────────────────────────────── */
.portal-funnel__actions {
    display: flex;
    flex-direction: column-reverse;
    gap: 12px;
    margin-top: var(--sps-funnel-stack-lg);
}

@media (min-width: 480px) {
    .portal-funnel__actions {
        flex-direction: row;
        justify-content: space-between;
    }
}

/* Equal-width Go Back / Keep Going pair — used on /situation, the
 * dataplate phase (inner pair), and /situation/notes.  The two buttons
 * are 50/50 on every viewport so the visual weight matches their
 * symmetric semantic role (one steps forward, one steps back).
 */
.portal-funnel__actions--equal {
    flex-direction: row;
    gap: 12px;
}
.portal-funnel__actions--equal > .portal-btn {
    flex: 1 1 0;
    min-width: 0;
}
.portal-funnel__actions--inline {
    margin-top: 0;
}

/* Phase 2 layout puts a tertiary "Skip" text-link above the Back/Keep
 * Going pair so the symmetric pair stays balanced.
 */
.portal-funnel__actions--with-skip {
    flex-direction: column;
    align-items: stretch;
    gap: 14px;
}
@media (min-width: 480px) {
    .portal-funnel__actions--with-skip {
        flex-direction: column;
        align-items: stretch;
    }
}

.portal-btn {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    gap: 8px;
    min-height: var(--sps-tap);
    padding: 12px 22px;
    border-radius: var(--sps-input-radius);
    font-size: 1rem;
    font-weight: 600;
    font-family: inherit;
    cursor: pointer;
    border: 1px solid transparent;
    text-decoration: none;
    transition: background 0.15s ease, border-color 0.15s ease, transform 0.05s ease;
}

.portal-btn--primary {
    background: var(--sps-accent);
    color: #ffffff;
    border-color: var(--sps-accent);
    flex: 1;
}

.portal-btn--primary:hover {
    background: var(--sps-accent-hover);
    color: #ffffff;
    text-decoration: none;
}

.portal-btn--primary:focus-visible {
    outline: none;
    box-shadow: 0 0 0 3px rgba(56, 139, 253, 0.35);
}

.portal-btn--primary:active {
    transform: translateY(1px);
}

.portal-btn--primary[disabled],
.portal-btn--primary[aria-busy="true"] {
    background: var(--sps-accent-dim);
    color: var(--sps-text-secondary);
    border-color: var(--sps-border);
    cursor: wait;
}

.portal-btn--ghost {
    background: transparent;
    color: var(--sps-text-secondary);
    border-color: var(--sps-border);
}

.portal-btn--ghost:hover {
    color: var(--sps-text-primary);
    background: var(--sps-surface-2);
    text-decoration: none;
}

/* Danger variant — destructive primary action (cancel-and-rebook,
 * customer cancel confirm). Same shape/weight as --primary; red surface
 * communicates irreversibility without resorting to a separate modal.
 */
.portal-btn--danger {
    background: var(--sps-danger);
    color: #ffffff;
    border-color: var(--sps-danger);
    flex: 1;
}

.portal-btn--danger:hover {
    background: var(--sps-danger-hover, #d83a31);
    color: #ffffff;
    text-decoration: none;
}

.portal-btn--danger:focus-visible {
    outline: none;
    box-shadow: 0 0 0 3px rgba(248, 81, 73, 0.35);
}

.portal-btn--danger:active {
    transform: translateY(1px);
}

.portal-btn--danger[disabled],
.portal-btn--danger[aria-busy="true"] {
    background: rgba(248, 81, 73, 0.45);
    color: #ffffff;
    border-color: rgba(248, 81, 73, 0.45);
    cursor: wait;
}

/* Tertiary text-link button — used for Phase 2's "Skip" so it reads as
 * an opt-out link rather than a peer of the symmetric Back / Keep Going
 * pair.  Same touch target so mobile taps stay reliable.
 */
.portal-btn--text {
    background: transparent;
    color: var(--sps-text-muted);
    border: none;
    text-decoration: underline;
    align-self: center;
    padding: 8px 12px;
    min-height: var(--sps-tap);
}
.portal-btn--text:hover {
    color: var(--sps-text-primary);
    text-decoration: underline;
}

/* Step 1 button is the only primary on its row — fill the available
 * width so the customer's tap target matches the funnel's hero feel.
 */
.portal-btn--full {
    flex: 1 1 100%;
    width: 100%;
}

.portal-btn__spinner {
    width: 16px;
    height: 16px;
    border-radius: 50%;
    border: 2px solid rgba(255, 255, 255, 0.35);
    border-top-color: #ffffff;
    animation: portal-spin 0.7s linear infinite;
}

.portal-btn__spinner[hidden] {
    display: none;
}

@keyframes portal-spin {
    to { transform: rotate(360deg); }
}

@media (prefers-reduced-motion: reduce) {
    .portal-btn__spinner {
        animation-duration: 2s;
    }
    .portal-progress__dot,
    .portal-input,
    .portal-select,
    .portal-btn,
    .portal-toggle__option {
        transition: none;
    }
}

/* ── B.2.0 Step 2 stub ─────────────────────────────────────────── */
/*  Temporary styling for portal/funnel/appliance-stub.html.twig.     */
/*  Remove when Step 2 (My Situation) ships in B.2.1.                 */
.portal-stub-note {
    margin: 0 0 var(--sps-funnel-stack-lg);
    text-align: center;
    color: var(--sps-text-secondary);
}

.portal-stub-receipt {
    padding: 14px 16px;
    margin: 0 0 var(--sps-funnel-stack-lg);
    border-radius: var(--sps-input-radius);
    background: var(--sps-input-bg);
    border: 1px solid var(--sps-border);
}

.portal-stub-receipt__label {
    margin: 0 0 6px;
    font-size: 0.82rem;
    text-transform: uppercase;
    letter-spacing: 0.04em;
    color: var(--sps-text-muted);
    font-weight: 600;
}

.portal-stub-receipt__address {
    margin: 0;
    font-style: normal;
    color: var(--sps-text-primary);
    line-height: 1.5;
}

/* ── Form-level warning banner (geocode failure, etc.) ─────────── */
.portal-notice {
    margin: 0 0 var(--sps-funnel-stack-lg);
    padding: 12px 14px;
    border-radius: var(--sps-input-radius);
    border: 1px solid rgba(245, 165, 36, 0.35);
    background: rgba(245, 165, 36, 0.08);
    color: var(--sps-text-primary);
    font-size: 0.9rem;
    line-height: 1.4;
}

.portal-notice--error {
    border-color: rgba(248, 81, 73, 0.45);
    background: rgba(248, 81, 73, 0.10);
}

/* ── Embed-mode tweaks ─────────────────────────────────────────── */
/*    Inside the iframe the hero chrome is already stripped by        */
/*    portal.css (.portal-body--embed).  Tighten the funnel padding.  */
.portal-body--embed .portal-main:has(.portal-funnel) {
    padding-top: 16px;
    padding-bottom: 20px;
}

.portal-body--embed .portal-funnel__card {
    box-shadow: none;
    border-radius: 14px;
}

/* ── Honeypot — visually hidden, keyboard + AT skipped ────────── */
/*  Positioned off-screen rather than display:none so naïve bot      */
/*  auto-fillers still see + populate it (display:none inputs are    */
/*  skipped by some frameworks).  Real users never see or tab into   */
/*  it — aria-hidden + tabindex=-1 keep screen readers + keyboard    */
/*  navigation out.                                                  */
/* display:none, not off-screen: browser autofill skips display:none but
 * fills off-screen fields, which would trip a real customer as a bot. */
.portal-honeypot {
    display: none;
}

/* ── Abuse-gate notice (lockout / captcha / honeypot) ───────────── */
/*    Calm, centralized banner that portal-funnel-app.js reveals when  */
/*    gateFunnelAjax rejects a request. Deliberately non-alarming: a    */
/*    false-positive real customer should read it as "try again soon,   */
/*    or call us" — never as an error wall. Bots are the usual audience */
/*    for the honeypot variant and get nothing diagnostic.              */
.portal-gate-notice {
    margin: 1rem auto;
    max-width: 32rem;
    padding: 1rem 1.25rem;
    background: var(--sps-surface-1);
    border: 1px solid var(--sps-border);
    border-left: 3px solid var(--sps-accent);
    border-radius: var(--sps-input-radius);
    color: var(--sps-text-primary);
    text-align: center;
}
.portal-gate-notice__msg {
    margin: 0;
    font-weight: 600;
}
.portal-gate-notice__cta {
    margin: 0.5rem 0 0;
    color: var(--sps-text-secondary);
}
.portal-gate-notice__tel {
    color: var(--sps-accent);
    font-weight: 600;
    text-decoration: none;
}
.portal-gate-notice__tel:hover {
    text-decoration: underline;
}

/* ── Horizontal-scroll guard (mobile 320px) ───────────────────── */
/*    Belt and suspenders: nothing inside the portal should cause a  */
/*    horizontal scrollbar at 320px wide.                             */
.portal-body {
    overflow-x: hidden;
}

/* ── Google PlaceAutocompleteElement — portal theming ───────────── */
/*                                                                     */
/*  The new web-component widget ships with shadow DOM, so internal   */
/*  markup is not stylable from outside — instead it exposes CSS      */
/*  custom properties (--gmpx-*) that we set here to match the        */
/*  portal's dark palette.  If Google ships additional properties in  */
/*  a future Maps JS release, they slot in here.                       */
/*                                                                     */
/*  The element sizes itself to the container.  We give the custom    */
/*  element block display + full width so it fills the search row     */
/*  the same way our <input> stub did.                                 */
.portal-places-element {
    display: block;
    width: 100%;
    min-height: var(--sps-input-height);

    /* Core colour tokens — docs: developers.google.com/maps/
       documentation/javascript/reference/place-autocomplete-element */
    --gmpx-color-surface:          var(--sps-input-bg);
    --gmpx-color-on-surface:       var(--sps-text-primary);
    --gmpx-color-on-surface-variant: var(--sps-text-secondary);
    --gmpx-color-primary:          var(--sps-accent);
    --gmpx-color-outline:          rgba(148, 163, 184, 0.22);
    --gmpx-color-outline-variant:  rgba(148, 163, 184, 0.14);
    --gmpx-font-family-base:       inherit;
    --gmpx-font-family-headings:   inherit;
    --gmpx-font-size-base:         16px;
    --gmpx-radius-base:            var(--sps-input-radius);
}

/* Fallback styling for the LEGACY .pac-container in case a browser   */
/* extension or cached API version still emits it.  Low risk to keep  */
/* — harmless if the new element replaces it in practice.              */
.pac-container {
    background: var(--sps-surface-1) !important;
    border: 1px solid var(--sps-border) !important;
    border-radius: var(--sps-input-radius) !important;
    box-shadow: 0 18px 48px rgba(0, 0, 0, 0.45) !important;
    font-family: inherit !important;
    margin-top: 4px !important;
    z-index: 2147483647;
}
.pac-item,
.pac-item-query {
    color: var(--sps-text-primary) !important;
    font-size: 14px !important;
    padding: 8px 12px !important;
    border-top: 1px solid rgba(148, 163, 184, 0.08) !important;
}
.pac-item:hover,
.pac-item-selected {
    background: var(--sps-accent-dim) !important;
}
.pac-matched {
    color: var(--sps-accent) !important;
    font-weight: 600 !important;
}
.pac-icon {
    filter: invert(0.85) hue-rotate(180deg);
}

/* ─────────────────────────────────────────────────────────────────── */
/*  Step 2 — Phase 1: Smart Match + Manual cascade                    */
/*  ────────────────────────────────────────────────────────────────  */
/*  Three modes share visual language:                                 */
/*    selected state = dashed accent-green border + check-mark icon   */
/*  Reused on the confirmed-card and the manual cascade's selected   */
/*  step pill, so customers see the same affirmation everywhere.     */

/* Mode container shared layout. */
.portal-mode {
    margin: 0 0 var(--sps-funnel-stack-lg);
}

.portal-mode[hidden] {
    display: none;
}

.portal-mode__switch {
    margin: 14px 0 0;
    text-align: center;
}

/* ── Mode A — Smart Match ─────────────────────────────────────── */
.portal-smart__input {
    margin-top: 8px;
    min-height: 88px;
}

.portal-smart__hint {
    margin: 8px 0 14px;
    font-size: 0.85rem;
    color: var(--sps-text-muted);
    line-height: 1.4;
}

.portal-smart__error {
    margin: 0 0 12px;
    padding: 10px 12px;
    border-radius: var(--sps-input-radius);
    border: 1px solid rgba(248, 81, 73, 0.45);
    background: rgba(248, 81, 73, 0.10);
    color: var(--sps-text-primary);
    font-size: 0.9rem;
    line-height: 1.4;
}

.portal-smart__error[hidden] {
    display: none;
}

.portal-smart__actions {
    display: flex;
    flex-direction: column;
    gap: 10px;
}

/* "Or Pick From A List" sits below the primary "Find My Match" button.
 * Give it clear separation so the two never read as a single touching
 * pair, and dim it while a smart match is in flight (JS disables it). */
.portal-smart__pick-list {
    margin-top: var(--sps-funnel-stack-lg);
}

.portal-smart__pick-list[disabled] {
    opacity: 0.5;
    cursor: not-allowed;
}

/* ── Mode B — Manual cascade per-step container ───────────────── */
.portal-step {
    margin: 0 0 14px;
}

.portal-step[hidden] {
    display: none;
}

/* Hide the live-input view when the step is confirmed; hide the
 * confirmed view when the step is open / pending.  JS toggles the
 * `hidden` attribute via setStepState — these rules are belt-and-
 * suspenders for the visual edge cases (e.g. pre-render).
 */
.portal-step[data-step-state="confirmed"] .portal-step__input-view { display: none; }
.portal-step[data-step-state="open"]      .portal-step__input-view { display: block; }
.portal-step[data-step-state="pending"]   .portal-step__input-view { display: block; }

/* ── Type-ahead results list ──────────────────────────────────── */
.portal-typeahead__results {
    display: flex;
    flex-direction: column;
    gap: 6px;
    margin: 8px 0 0;
}

.portal-typeahead__results:empty {
    margin: 0;
}

.portal-typeahead__item {
    appearance: none;
    -webkit-appearance: none;
    display: flex;
    align-items: center;
    justify-content: flex-start;
    width: 100%;
    min-height: var(--sps-tap);
    padding: 10px 14px;
    border: 1px solid rgba(148, 163, 184, 0.18);
    border-radius: var(--sps-input-radius);
    background: var(--sps-input-bg);
    color: var(--sps-text-primary);
    font-size: 0.95rem;
    font-weight: 500;
    text-align: left;
    cursor: pointer;
    transition: border-color 0.15s ease, background 0.15s ease;
}

.portal-typeahead__item:hover,
.portal-typeahead__item:focus-visible {
    border-color: var(--sps-accent);
    background: var(--sps-accent-dim);
    outline: none;
}

.portal-typeahead__empty {
    margin: 8px 0 0;
    color: var(--sps-text-muted);
    font-size: 0.88rem;
    line-height: 1.4;
}

.portal-typeahead__empty[hidden] {
    display: none;
}

/* ── Confirmed-card visual (Mode B confirmed step + Mode C cards) ── */
/* Same dashed-border + check-mark language as the address form's
 * Home / Business toggle, applied to a horizontal card layout.
 */
.portal-confirm-card {
    display: flex;
    align-items: center;
    gap: 12px;
    margin: 0 0 10px;
    padding: 12px 14px;
    border: 2px dashed var(--sps-success);
    border-radius: var(--sps-input-radius);
    background: var(--sps-success-dim);
    color: var(--sps-text-primary);
}

.portal-confirm-card[hidden] {
    display: none;
}

.portal-confirm-card__check {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 22px;
    height: 22px;
    border-radius: 50%;
    background: var(--sps-success);
    color: #ffffff;
    flex: 0 0 auto;
}

.portal-confirm-card__check svg {
    width: 14px;
    height: 14px;
}

.portal-confirm-card__body {
    flex: 1 1 auto;
    min-width: 0;
}

.portal-confirm-card__intro {
    margin: 0 0 12px;
    font-size: 0.95rem;
    font-weight: 600;
    color: var(--sps-text-primary);
}

.portal-confirm-card__label {
    margin: 0 0 2px;
    font-size: 0.78rem;
    text-transform: uppercase;
    letter-spacing: 0.04em;
    color: var(--sps-text-muted);
    font-weight: 600;
}

.portal-confirm-card__value {
    margin: 0;
    font-size: 1rem;
    font-weight: 600;
    color: var(--sps-text-primary);
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

.portal-confirm-card__change {
    flex: 0 0 auto;
    align-self: center;
    padding: 6px 10px;
    min-height: 36px;
}

/* The Mode C list visually separates final confirmation from the
 * inline confirmed cards in Mode B — same border but slightly more
 * presence, since this is the "review before continuing" moment.
 */
.portal-confirm-card--final {
    padding: 14px 16px;
}

/* ─────────────────────────────────────────────────────────────────── */
/*  Step 2 — Phases 2 + 3: Photo upload widget                        */
/*  ────────────────────────────────────────────────────────────────  */
/*  Browser-direct-to-S3 upload via presigned PUT.  The widget owns   */
/*  the dashed dropzone, the file input, the progress bar, the AI     */
/*  extract review card, and the retry / replace controls.            */
.portal-upload {
    margin: 0 0 var(--sps-funnel-stack-lg);
}

.portal-upload__legend {
    margin: 0 0 10px;
    font-size: 0.92rem;
    font-weight: 600;
    color: var(--sps-text-primary);
}

.portal-upload__hint {
    margin: 0 0 12px;
    color: var(--sps-text-muted);
    font-size: 0.88rem;
    line-height: 1.4;
}

.portal-upload__dropzone {
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    gap: 10px;
    min-height: 140px;
    padding: 20px;
    border: 2px dashed rgba(148, 163, 184, 0.32);
    border-radius: var(--sps-input-radius);
    background: var(--sps-input-bg);
    color: var(--sps-text-secondary);
    text-align: center;
    cursor: pointer;
    transition: border-color 0.15s ease, background 0.15s ease;
}

.portal-upload__dropzone:hover,
.portal-upload__dropzone:focus-within {
    border-color: var(--sps-accent);
    background: var(--sps-accent-dim);
}

.portal-upload__dropzone[hidden] {
    display: none;
}

.portal-upload__primary-text {
    margin: 0;
    font-size: 1rem;
    font-weight: 600;
    color: var(--sps-text-primary);
}

.portal-upload__secondary-text {
    margin: 0;
    font-size: 0.85rem;
    color: var(--sps-text-muted);
}

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

.portal-upload__progress {
    margin: 12px 0 0;
    height: 8px;
    border-radius: 4px;
    background: rgba(148, 163, 184, 0.18);
    overflow: hidden;
}

.portal-upload__progress[hidden] {
    display: none;
}

.portal-upload__progress-bar {
    height: 100%;
    width: 0%;
    background: var(--sps-accent);
    transition: width 0.2s ease;
}

.portal-upload__status {
    margin: 8px 0 0;
    font-size: 0.88rem;
    color: var(--sps-text-secondary);
    min-height: 1.2em;
}

.portal-upload__status--error {
    color: var(--sps-error, var(--sps-text-primary));
}

.portal-upload__preview {
    display: grid;
    grid-template-columns: auto 1fr;
    gap: 14px;
    align-items: center;
    margin: 12px 0 0;
    padding: 12px;
    border: 1px solid rgba(148, 163, 184, 0.18);
    border-radius: var(--sps-input-radius);
    background: var(--sps-input-bg);
}

.portal-upload__preview[hidden] {
    display: none;
}

.portal-upload__thumb {
    width: 84px;
    height: 84px;
    border-radius: 8px;
    object-fit: cover;
    background: rgba(148, 163, 184, 0.10);
}

.portal-upload__preview-meta {
    display: flex;
    flex-direction: column;
    gap: 6px;
    min-width: 0;
}

.portal-upload__filename {
    font-weight: 600;
    color: var(--sps-text-primary);
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

.portal-upload__actions {
    display: flex;
    gap: 8px;
    flex-wrap: wrap;
}

.portal-upload__btn {
    appearance: none;
    -webkit-appearance: none;
    border: 1px solid rgba(148, 163, 184, 0.35);
    background: transparent;
    color: var(--sps-text-secondary);
    border-radius: var(--sps-input-radius);
    padding: 8px 12px;
    font-size: 0.85rem;
    font-weight: 600;
    cursor: pointer;
}

.portal-upload__btn:hover {
    color: var(--sps-text-primary);
    border-color: rgba(148, 163, 184, 0.55);
}

.portal-upload__btn--danger {
    color: var(--sps-error, var(--sps-text-secondary));
    border-color: rgba(248, 81, 73, 0.35);
}

.portal-upload__btn--danger:hover {
    border-color: rgba(248, 81, 73, 0.55);
}

/* ─────────────────────────────────────────────────────────────────── */
/*  Step 2 — Phase 2: dataplate processing banner + hint + warning     */
/*  ────────────────────────────────────────────────────────────────  */
/*  No wrapper card. Model and serial are plain portal-field blocks    */
/*  stacked vertically. The processing banner appears between upload   */
/*  and AI completion, the hint appears on low-confidence extraction,  */
/*  and the warning appears on a brand mismatch with the Phase 1 pick. */
.portal-dataplate__processing {
    display: flex;
    align-items: center;
    gap: 10px;
    margin: 0 0 12px;
    padding: 10px 14px;
    border-radius: var(--sps-input-radius);
    background: var(--sps-accent-dim);
    color: var(--sps-text-primary);
    font-size: 0.95rem;
    line-height: 1.4;
}

.portal-dataplate__processing[hidden] {
    display: none;
}

.portal-dataplate__processing-spinner {
    width: 16px;
    height: 16px;
    border-radius: 50%;
    border: 2px solid rgba(56, 139, 253, 0.35);
    border-top-color: var(--sps-accent);
    animation: portal-spin 0.7s linear infinite;
    flex: 0 0 auto;
}

.portal-dataplate__processing-text {
    flex: 1 1 auto;
}

.portal-dataplate__hint {
    margin: 4px 0 12px;
    font-size: 0.85rem;
    color: var(--sps-text-muted);
    line-height: 1.4;
}

.portal-dataplate__hint[hidden] {
    display: none;
}

.portal-dataplate__warning {
    margin: 10px 0 0;
    padding: 10px 12px;
    border-radius: var(--sps-input-radius);
    border: 1px solid rgba(245, 165, 36, 0.35);
    background: rgba(245, 165, 36, 0.08);
    color: var(--sps-text-primary);
    font-size: 0.88rem;
    line-height: 1.4;
}

.portal-dataplate__warning[hidden] {
    display: none;
}

/* ─────────────────────────────────────────────────────────────────── */
/*  Step 2 — Phase 3: notes textarea                                   */
/*  ────────────────────────────────────────────────────────────────  */
.portal-textarea {
    width: 100%;
    min-height: 120px;
    padding: 12px 14px;
    border: 2px solid rgba(148, 163, 184, 0.18);
    border-radius: var(--sps-input-radius);
    background: var(--sps-input-bg);
    color: var(--sps-text-primary);
    font: inherit;
    font-size: 1rem;
    line-height: 1.5;
    resize: vertical;
}

.portal-textarea:focus {
    outline: none;
    border-color: var(--sps-accent);
}

.portal-textarea--error {
    border-color: rgba(248, 81, 73, 0.55);
}

.portal-textarea__count {
    display: block;
    margin: 6px 0 0;
    font-size: 0.8rem;
    color: var(--sps-text-muted);
    text-align: right;
}

/* ─────────────────────────────────────────────────────────────────── */
/*  SPS-51 WS-B.3 — Single-page funnel (Contact + section state machine) */
/*  ────────────────────────────────────────────────────────────────  */
/*  All five sections live on one page; visibility is JS-driven via    */
/*  the [hidden] attribute. No animation; this is a real app, not a    */
/*  scrollytelling demo.                                                */
.portal-funnel-section {
    display: block;
}

.portal-funnel-section[hidden] {
    display: none;
}

.portal-funnel-section__placeholder {
    margin: 0 0 var(--sps-funnel-stack-lg);
    padding: 16px;
    color: var(--sps-text-muted);
    font-style: italic;
    border-radius: var(--sps-input-radius);
    background: var(--sps-input-bg);
    text-align: center;
}

/* Three-chip toggle variant for the New / Returning customer chips. */
.portal-toggle--three {
    grid-template-columns: 1fr 1fr;
    margin-bottom: var(--sps-funnel-stack-md);
}

/* Contact section flows — only one visible at a time, JS toggles. */
.portal-contact__flow[hidden] {
    display: none;
}

.portal-contact__flow {
    display: block;
}

/* Inline action row (Find My Account button under the lookup field). */
.portal-funnel__actions--inline {
    margin-top: 12px;
}

/* Returning-customer lookup error inline. */
.portal-contact__lookup-error {
    margin: 12px 0 0;
    padding: 10px 12px;
    border-radius: var(--sps-input-radius);
    border: 1px solid rgba(248, 81, 73, 0.45);
    background: rgba(248, 81, 73, 0.10);
    color: var(--sps-text-primary);
    font-size: 0.9rem;
    line-height: 1.4;
}

.portal-contact__lookup-error[hidden] {
    display: none;
}

/* Returning-customer summary card after a successful lookup. */
.portal-contact__lookup-result {
    margin: 16px 0 0;
}

.portal-contact__lookup-result[hidden] {
    display: none;
}

.portal-contact__welcome {
    margin: 0 0 12px;
    font-size: 1.05rem;
    font-weight: 600;
    color: var(--sps-text-primary);
}

.portal-contact__summary {
    margin: 0 0 16px;
    padding: 12px 14px;
    border-radius: var(--sps-input-radius);
    background: var(--sps-input-bg);
    border: 1px solid rgba(148, 163, 184, 0.18);
}

.portal-contact__summary p {
    margin: 0 0 6px;
    display: flex;
    justify-content: space-between;
    gap: 12px;
}

.portal-contact__summary p:last-child {
    margin-bottom: 0;
}

.portal-contact__summary-label {
    color: var(--sps-text-muted);
    font-size: 0.82rem;
    text-transform: uppercase;
    letter-spacing: 0.04em;
    font-weight: 600;
}

/* Section-level error banner. */
.portal-notice[data-section-error][hidden] {
    display: none;
}

/* Acknowledgement notice — passive, never interactive, sits below the
 * primary action row to look advisory rather than gating.
 */
.portal-funnel__notice {
    margin: 14px 0 0;
    text-align: center;
    color: var(--sps-text-muted);
    font-size: 0.82rem;
    line-height: 1.4;
}


/* ── Section 2 (Address) ───────────────────────────────────────────── */

/* Returning-customer chip stack — one chip per stored address plus a
 * "Enter a new address" sentinel chip. Reuses portal-toggle__option
 * styles so checked-state visuals come for free; .portal-address__chip
 * adjusts the layout for a multi-line address-line + city/state/zip
 * sub-line presentation.
 */
.portal-address__chips {
    display: flex;
    flex-direction: column;
    gap: 8px;
    margin-bottom: var(--sps-funnel-stack-md);
}

.portal-address__chip {
    /* Multi-line content needs left-align text and auto height. */
    text-align: left;
    align-items: flex-start;
    padding: 12px 14px;
    min-height: 54px;
}

.portal-address__chip-text {
    display: flex;
    flex-direction: column;
    gap: 2px;
    line-height: 1.3;
}

.portal-address__chip-line {
    font-size: 0.96rem;
    font-weight: 600;
    color: var(--sps-text-primary);
}

.portal-address__chip-sub {
    font-size: 0.84rem;
    color: var(--sps-text-muted);
}

.portal-address__chip--new .portal-address__chip-line {
    font-weight: 500;
    color: var(--sps-text-muted);
    font-style: italic;
}

.portal-address__chip--new input.portal-toggle__native:checked
    ~ .portal-toggle__check
    ~ .portal-address__chip-text .portal-address__chip-line {
    color: var(--sps-text-primary);
    font-style: normal;
    font-weight: 600;
}

/* Conditional landlord/tenant question — sits below the manual fields
 * with a small top margin so it visually separates from the address
 * block.
 */
.portal-address__lt-question {
    margin-top: var(--sps-funnel-stack-md);
}

/* Terminal-failure block — used for genuinely terminal codes
 * (out_of_area, duplicate_active_*). No card wrapper; the message
 * and call action sit naturally on the page so the decision feels
 * like part of the conversation, not boxed away.
 */
.portal-address__terminal {
    display: flex;
    flex-direction: column;
    align-items: center;
    text-align: center;
    padding: 12px 0 0;
}

.portal-address__terminal[hidden] {
    display: none;
}

.portal-address__terminal-message {
    margin: 0 0 18px;
    color: var(--sps-text-primary);
    font-size: 1rem;
    line-height: 1.55;
    max-width: 32em;
}

/* Section 1 (contact) terminal block. Mirrors the address-terminal
 * structure for the duplicate_active_customer hard-stop. Reuses
 * .portal-address__call-link for the phone CTA — that class is
 * intentionally shared across early-funnel terminals.
 */
.portal-contact__terminal {
    display: flex;
    flex-direction: column;
    align-items: center;
    text-align: center;
    padding: 12px 0 0;
}

.portal-contact__terminal[hidden] {
    display: none;
}

.portal-contact__terminal-message {
    margin: 0 0 18px;
    color: var(--sps-text-primary);
    font-size: 1rem;
    line-height: 1.55;
    max-width: 32em;
}

/* Recoverable-gate notice — shown above the action row when the
 * commercial or landlord/tenant gate fires. Calmer than a hard
 * error; visible enough that the customer notices BEFORE they try
 * to click Keep Going again, but doesn't unmount the form.
 */
.portal-address__recoverable {
    margin: 0 0 var(--sps-funnel-stack-md);
    padding: 0;
    border: 0;
    background: transparent;
}

.portal-address__recoverable[hidden] {
    display: none;
}

.portal-address__recoverable-message {
    margin: 0 0 12px;
    color: var(--sps-text-primary);
    font-size: 0.96rem;
    line-height: 1.5;
}

.portal-address__recoverable-actions {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 12px;
}

/* Shared call-link style for both the inline recoverable warning
 * and the terminal block. Phone number is the action; reads as a
 * link with phone glyph, behaves as a 44px tap-target on mobile.
 */
.portal-address__call-link {
    display: inline-flex;
    align-items: center;
    gap: 6px;
    color: var(--sps-text-primary);
    text-decoration: none;
    font-weight: 600;
    font-size: 1rem;
    min-height: 44px;
    padding: 8px 0;
}

.portal-address__call-link:hover,
.portal-address__call-link:focus-visible {
    text-decoration: underline;
    outline: none;
}

/* In the terminal block the call link is the primary action — bump
 * its size and weight a touch to anchor the layout.
 */
.portal-address__call-link--primary {
    font-size: 1.15rem;
}


/* ================================================================== */
/*  Section 3, Appliance                                                */
/*                                                                     */
/*  Accordion-style card list. Only one card is expanded at a time;    */
/*  others collapse into a one-line summary chip. Inside each card     */
/*  the existing portal-mode / portal-step / portal-confirm-card /     */
/*  portal-typeahead / portal-smart / portal-upload / portal-dataplate */
/*  classes already styled in this file are reused verbatim, every     */
/*  visual idiom (dashed-green border, green check, etc) flows through */
/*  by class chaining.  Only the appliance-section-specific surfaces   */
/*  are defined below.                                                 */
/* ================================================================== */

.portal-appliance {
    display: flex;
    flex-direction: column;
    gap: var(--sps-funnel-stack);
}

.portal-appliance__list {
    display: flex;
    flex-direction: column;
    gap: var(--sps-funnel-stack);
}

/* ── Add-another row, hidden when at cap or multi disallowed ───── */
.portal-appliance__add-row {
    display: flex;
    flex-direction: column;
    gap: 6px;
    margin-top: 4px;
}

.portal-appliance__add-row[hidden] {
    display: none;
}

.portal-appliance__add-btn {
    border-style: dashed;
    border-width: 1.5px;
    width: 100%;
    justify-content: center;
}

.portal-appliance__add-hint {
    margin: 0;
    font-size: 0.85rem;
    color: var(--sps-text-muted);
    text-align: center;
}

/* ── Appliance card frame ─────────────────────────────────────── */
.portal-appliance-card {
    background: var(--sps-surface-2, rgba(255, 255, 255, 0.02));
    border: 1px solid var(--sps-border);
    border-radius: 14px;
    overflow: hidden;
}

.portal-appliance-card[data-card-state="collapsed"] .portal-appliance-card__body {
    display: none;
}

.portal-appliance-card[data-card-state="expanded"] .portal-appliance-card__summary {
    display: none;
}

/* When a card is expanded the border picks up a calm dim outline so
 * the customer's eye rests on the live one without the surface
 * shouting at them. Muted on purpose, never a hard ring. */
.portal-appliance-card[data-card-state="expanded"] {
    border-color: rgba(46, 204, 113, 0.32);
    box-shadow: none;
}

/* ── Collapsed summary chip ───────────────────────────────────── */
.portal-appliance-card__summary {
    display: flex;
    align-items: center;
    gap: 12px;
    padding: 14px 16px;
    min-height: var(--sps-tap);
}

.portal-appliance-card__summary-check {
    flex-shrink: 0;
    width: 22px;
    height: 22px;
    border-radius: 50%;
    background: var(--sps-success);
    color: #ffffff;
    display: inline-flex;
    align-items: center;
    justify-content: center;
}

.portal-appliance-card__summary-check svg {
    width: 14px;
    height: 14px;
}

.portal-appliance-card__summary-body {
    flex: 1 1 auto;
    min-width: 0;
}

.portal-appliance-card__summary-title {
    margin: 0;
    color: var(--sps-text-primary);
    font-weight: 600;
    font-size: 0.96rem;
    line-height: 1.3;
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}

.portal-appliance-card__summary-sub {
    margin: 2px 0 0;
    color: var(--sps-text-secondary);
    font-size: 0.84rem;
    line-height: 1.3;
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}

.portal-appliance-card__summary-change {
    flex-shrink: 0;
    margin-left: auto;
}

/* ── Expanded body ────────────────────────────────────────────── */
.portal-appliance-card__body {
    display: flex;
    flex-direction: column;
    gap: var(--sps-funnel-stack);
    padding: 16px;
}

.portal-appliance-card__header {
    display: flex;
    align-items: baseline;
    justify-content: space-between;
    gap: 12px;
}

.portal-appliance-card__title {
    margin: 0;
    color: var(--sps-text-primary);
    font-weight: 600;
    font-size: 1rem;
}

.portal-appliance-card__remove {
    color: var(--sps-text-muted);
    font-size: 0.86rem;
}

.portal-appliance-card__remove[hidden] {
    display: none;
}

/* ── Per-card recoverable banner ────────────────────────────────── */
/* Default (soft) treatment — used for brand-mismatch warnings where
   the customer can ack and proceed. The banner sits at the top of
   the card body, above the accordions, and reads as a quiet inline
   note. */
.portal-appliance-card__recoverable {
    margin: 0 0 16px;
    padding: 0;
    border: 0;
    background: transparent;
}

.portal-appliance-card__recoverable[hidden] {
    display: none;
}

/* Hard-stop modifier — applied for booking_action_call_in and
   unsupported_situation kinds where the customer cannot proceed
   without changing their selection. Red tinted background + border
   match the severity. The Keep My Selection button is hidden via
   the [hidden] attribute the JS sets per kind. */
.portal-appliance-card__recoverable--hard-stop {
    padding: 14px 16px;
    border: 1px solid var(--sps-danger);
    border-radius: var(--sps-input-radius);
    background: rgba(220, 53, 69, 0.08);
}

.portal-appliance-card__recoverable--hard-stop .portal-appliance-card__recoverable-message {
    font-weight: 600;
    color: var(--sps-text-primary);
}

/* Defense in depth — flex containers can override the user-agent
   [hidden] { display: none } rule. Make sure the keep button stays
   hidden for hard-stop kinds. */
[data-card-recoverable-keep][hidden] {
    display: none !important;
}

.portal-appliance-card__recoverable-message {
    margin: 0 0 12px;
    color: var(--sps-text-primary);
    font-size: 0.96rem;
    line-height: 1.5;
}

.portal-appliance-card__recoverable-actions {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 12px;
}

.portal-appliance-card__call-link {
    display: inline-flex;
    align-items: center;
    gap: 6px;
    color: var(--sps-text-primary);
    text-decoration: none;
    font-weight: 600;
    font-size: 1rem;
    min-height: var(--sps-tap);
    padding: 8px 0;
}

.portal-appliance-card__call-link:hover,
.portal-appliance-card__call-link:focus-visible {
    text-decoration: underline;
    outline: none;
}

/* ── Description box, visible in manual + confirmed modes ─────── */
/* The customer's typed text from smart-match (or fresh notes from a
 * manual flow) lives here, below the chosen Type/Brand/Problem.
 * Operator directive: customer text MUST persist visibly below the
 * selections so they can review and edit before advancing.
 */
.portal-appliance-card__description {
    display: flex;
    flex-direction: column;
    gap: 6px;
    margin-top: 4px;
}

.portal-appliance-card__description[hidden] {
    display: none;
}

/* ── Section terminal block ───────────────────────────────────── */
.portal-appliance__terminal {
    display: flex;
    flex-direction: column;
    align-items: center;
    text-align: center;
    padding: 12px 0 0;
}

.portal-appliance__terminal[hidden] {
    display: none;
}

.portal-appliance__terminal-message {
    margin: 0 0 18px;
    color: var(--sps-text-primary);
    font-size: 1rem;
    line-height: 1.55;
    max-width: 32em;
}

.portal-appliance__call-link {
    display: inline-flex;
    align-items: center;
    gap: 6px;
    color: var(--sps-text-primary);
    text-decoration: none;
    font-weight: 600;
    font-size: 1rem;
    min-height: var(--sps-tap);
    padding: 8px 0;
}

.portal-appliance__call-link:hover,
.portal-appliance__call-link:focus-visible {
    text-decoration: underline;
    outline: none;
}

.portal-appliance__call-link--primary {
    font-size: 1.15rem;
}

/* ── Per-situation accordion (3 mutually exclusive groups) ─────
 * Used for accordion 1 (Describe), 2 (Dataplate, Model & Serial),
 * and 3 (Photo Of The Whole Situation). Only one open at a time per
 * card; opening one closes the others (managed by JS). The closed
 * state shows the toggle row only; the open state reveals the body
 * underneath, indented under the same border so the visual flow
 * reads as a single panel.
 */
.portal-appliance-card__acc {
    display: flex;
    flex-direction: column;
}

.portal-appliance-card__acc-toggle {
    display: flex;
    align-items: center;
    gap: 12px;
    width: 100%;
    min-height: var(--sps-tap);
    padding: 12px 14px;
    background: rgba(255, 255, 255, 0.02);
    border: 1px solid rgba(148, 163, 184, 0.22);
    border-radius: var(--sps-input-radius);
    color: var(--sps-text-primary);
    font-size: 0.95rem;
    font-weight: 600;
    cursor: pointer;
    text-align: left;
    transition: border-color 0.15s ease, background 0.15s ease;
}

.portal-appliance-card__acc-toggle:hover,
.portal-appliance-card__acc-toggle:focus-visible {
    border-color: var(--sps-accent);
    outline: none;
}

.portal-appliance-card__acc-toggle.is-open {
    background: var(--sps-accent-dim);
    border-color: var(--sps-accent);
    color: var(--sps-text-primary);
}

/* Step number badge sits before the label; reads as a numbered
 * progression so the customer feels the sequence (1, 2, 3). */
.portal-appliance-card__acc-step {
    flex-shrink: 0;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 24px;
    height: 24px;
    border-radius: 50%;
    background: var(--sps-input-bg);
    color: var(--sps-text-secondary);
    font-size: 0.8rem;
    font-weight: 700;
    line-height: 1;
}

.portal-appliance-card__acc-toggle.is-open .portal-appliance-card__acc-step {
    background: var(--sps-accent);
    color: #ffffff;
}

.portal-appliance-card__acc-label {
    flex: 1 1 auto;
    min-width: 0;
    overflow-wrap: anywhere;
}

.portal-appliance-card__acc-caret {
    flex-shrink: 0;
    color: var(--sps-text-muted);
    transition: transform 0.18s ease;
}

@media (prefers-reduced-motion: reduce) {
    .portal-appliance-card__acc-caret {
        transition: none;
    }
}

.portal-appliance-card__acc-toggle.is-open .portal-appliance-card__acc-caret {
    transform: rotate(180deg);
    color: var(--sps-accent);
}

.portal-appliance-card__acc-body {
    display: flex;
    flex-direction: column;
    gap: 14px;
    padding: 14px 4px 4px;
}

.portal-appliance-card__acc-body[hidden] {
    display: none;
}

.portal-appliance-card__acc-hint {
    margin: 0;
    color: var(--sps-text-muted);
    font-size: 0.85rem;
    line-height: 1.4;
}

/* ── Long-string wrap defense ───────────────────────────────────
 * Long unbroken strings (raw model numbers, place names without
 * spaces, slammed-together brand strings) get aggressive wrapping
 * so they never push past the viewport edge on a phone. Width is
 * NOT touched here on purpose, the funnel's own max-width: 540px
 * (set above) is the centering rule and the @media block at the
 * bottom overrides it for phones. box-sizing is already universal
 * via portal.css so it stays out of this file. */
.portal-funnel-section {
    overflow-wrap: anywhere;
    word-break: break-word;
}

/* ================================================================== */
/*  Mobile-first edge-to-edge layout (phones + small portrait)         */
/*                                                                     */
/*  At narrow viewports the funnel becomes the page: drop the outer    */
/*  card chrome (border, padding, box-shadow), drop container side     */
/*  padding, give the form the full width minus safe-area insets.     */
/*  The progress dots and step header keep their own breathing room.  */
/*  PWA-ready: no boxed-in form on a phone.                            */
/* ================================================================== */
@media (max-width: 600px) {
    .portal-main:has(.portal-funnel) {
        padding-top:    16px;
        padding-bottom: calc(28px + env(safe-area-inset-bottom));
        padding-left:   env(safe-area-inset-left);
        padding-right:  env(safe-area-inset-right);
    }

    .portal-funnel {
        max-width: 100%;
    }

    .portal-funnel__card {
        background: transparent;
        border: 0;
        border-radius: 0;
        box-shadow: none;
        padding: 0 12px;
    }

    /* Progress + step header keep their own padding so the SVG icon
     * and the title text don't kiss the screen edge. */
    .portal-progress,
    .portal-step-header {
        padding-left:  12px;
        padding-right: 12px;
    }

    /* Appliance card: keep the soft border so each Situation reads
     * as its own block, but let the list breathe edge-to-edge inside
     * the funnel-card padding. */
    .portal-appliance-card {
        border-radius: 12px;
    }

    .portal-appliance-card__body {
        padding: 14px;
    }

    /* Action row sits flush against the bottom edge on mobile so the
     * Keep Going button is always thumb-reachable. */
    .portal-funnel__actions,
    .portal-funnel__actions--equal {
        margin-top: 16px;
    }
}

/* ================================================================== */
/*  Reusable section alert                                              */
/*                                                                     */
/*  One alert region per section, two visual variants:                 */
/*    .portal-alert--warn     yellow, soft heads-up, Keep Going stays  */
/*                            enabled.                                 */
/*    .portal-alert--danger   red,    hard stop,    Keep Going is      */
/*                            disabled until the customer clicks       */
/*                            Change My Answer (which dismisses the    */
/*                            alert and re-enables advance).           */
/*                                                                     */
/*  Lives at the bottom of the section, above the action row, so the   */
/*  customer sees the alert in the same spot regardless of variant.    */
/* ================================================================== */
.portal-alert {
    display: flex;
    flex-direction: column;
    gap: 12px;
    padding: 14px 16px;
    border-radius: var(--sps-input-radius);
    border-width: 1px;
    border-style: solid;
    margin: var(--sps-funnel-stack) 0 0;
}

.portal-alert[hidden] {
    display: none;
}

.portal-alert--warn {
    border-color: rgba(245, 165, 36, 0.55);
    background: rgba(245, 165, 36, 0.10);
}

.portal-alert--danger {
    border-color: rgba(248, 81, 73, 0.55);
    background: rgba(248, 81, 73, 0.10);
}

.portal-alert__message {
    margin: 0;
    color: var(--sps-text-primary);
    font-size: 0.95rem;
    line-height: 1.5;
}

.portal-alert__actions {
    display: flex;
    flex-wrap: wrap;
    gap: 10px;
}

/* Both buttons stretch to share the row equally on a phone, becoming
 * comfortable thumb-sized targets without dominating the screen. */
.portal-alert__call,
.portal-alert__change {
    flex: 1 1 160px;
    justify-content: center;
    text-align: center;
}

/* Call button gets a per-variant accent border so it reads as the
 * positive action (warn = amber outline, danger = red outline) while
 * the body of the alert carries the matching dim background. */
.portal-alert--warn .portal-alert__call {
    border-color: rgba(245, 165, 36, 0.85);
    color: var(--sps-text-primary);
}

.portal-alert--danger .portal-alert__call {
    background: var(--sps-danger);
    border-color: var(--sps-danger);
    color: #ffffff;
}

.portal-alert--danger .portal-alert__call:hover,
.portal-alert--danger .portal-alert__call:focus-visible {
    filter: brightness(1.05);
    text-decoration: none;
}

.portal-alert__change[hidden] {
    display: none;
}

/* ─────────────────────────────────────────────────────────────────── */
/*  Section 4 — Schedule                                               */
/*  ────────────────────────────────────────────────────────────────   */
/*  Two surfaces in one section: the first-available offer card        */
/*  (default) and the calendar-fallback date strip + chip grid.        */
/*  The dashed-border + green-check idiom is shared with               */
/*  .portal-confirm-card from Sections 2/3 so the visual language      */
/*  stays consistent across the funnel.                                */
.portal-schedule {
    display: block;
}

.portal-schedule__loading {
    display: flex;
    align-items: center;
    justify-content: center;
    gap: 12px;
    padding: 32px 16px;
    color: var(--sps-text-muted);
}

.portal-schedule__loading[hidden] {
    display: none;
}

.portal-schedule__spinner {
    width: 18px;
    height: 18px;
    border-radius: 50%;
    border: 2px solid var(--sps-border);
    border-top-color: var(--sps-primary);
    animation: portal-schedule-spin 0.8s linear infinite;
}

@keyframes portal-schedule-spin {
    to { transform: rotate(360deg); }
}

.portal-schedule__loading-text {
    margin: 0;
    font-size: 0.95rem;
}

/* ── Offer card ─────────────────────────────────────────────── */
.portal-schedule__offer {
    margin: 0 0 var(--sps-funnel-stack-lg);
}

.portal-schedule__offer[hidden] {
    display: none;
}

.portal-schedule__offer-eyebrow {
    margin: 0 0 8px;
    font-size: 0.78rem;
    text-transform: uppercase;
    letter-spacing: 0.04em;
    color: var(--sps-text-muted);
    font-weight: 600;
}

.portal-schedule__offer-card {
    display: flex;
    align-items: center;
    gap: 14px;
    margin: 0 0 14px;
    padding: 16px 18px;
    border: 2px dashed var(--sps-success);
    border-radius: var(--sps-input-radius);
    background: var(--sps-success-dim);
    color: var(--sps-text-primary);
}

.portal-schedule__offer-check {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 26px;
    height: 26px;
    border-radius: 50%;
    background: var(--sps-success);
    color: #ffffff;
    flex: 0 0 auto;
}

.portal-schedule__offer-check svg {
    width: 16px;
    height: 16px;
}

.portal-schedule__offer-body {
    flex: 1 1 auto;
    min-width: 0;
}

.portal-schedule__offer-date {
    margin: 0 0 2px;
    font-size: 1.05rem;
    font-weight: 700;
    color: var(--sps-text-primary);
}

.portal-schedule__offer-slot {
    margin: 0;
    font-size: 0.95rem;
    color: var(--sps-text-primary);
}

.portal-schedule__offer-actions {
    display: flex;
    flex-direction: column;
    gap: 8px;
}

/* ── Calendar date strip ────────────────────────────────────── */
.portal-schedule__calendar {
    margin: 0 0 var(--sps-funnel-stack-lg);
}

.portal-schedule__calendar[hidden] {
    display: none;
}

.portal-schedule__calendar-eyebrow {
    margin: 0 0 8px;
    font-size: 0.78rem;
    text-transform: uppercase;
    letter-spacing: 0.04em;
    color: var(--sps-text-muted);
    font-weight: 600;
}

/* 3-up grid, vertically stacked. Show More raises the visible
 * count in 6-pill batches via JS; the grid simply grows. */
.portal-schedule__date-grid {
    display: grid;
    grid-template-columns: repeat(3, 1fr);
    gap: 8px;
    margin: 0 0 12px;
}

.portal-schedule__calendar-empty {
    margin: 8px 0 12px;
    color: var(--sps-text-muted);
    font-size: 0.95rem;
}

.portal-schedule__calendar-empty[hidden] {
    display: none;
}

.portal-schedule__date-pill {
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    gap: 4px;
    padding: 16px 10px;
    border: 2px dashed var(--sps-border);
    border-radius: var(--sps-input-radius);
    background: transparent;
    color: var(--sps-text-primary);
    cursor: pointer;
    font-family: inherit;
    transition: border-color 120ms ease;
}

.portal-schedule__date-pill:hover,
.portal-schedule__date-pill:focus-visible {
    border-color: var(--sps-success);
    outline: none;
}

.portal-schedule__date-pill--today {
    border-color: var(--sps-success);
}

/* Active (currently-showing-its-chips) pill: solid green border,
 * brand-green tinted fill, day-number switches to brand-green so the
 * pill reads unambiguously as "this is the date whose times are
 * below". Mirrors the chip is-active treatment. */
.portal-schedule__date-pill.is-active {
    border-style: solid;
    border-color: var(--sps-success);
    background: rgba(46, 204, 113, 0.18);
}

.portal-schedule__date-pill.is-active .portal-schedule__date-pill-day {
    color: var(--sps-success);
}

/* Day-of-week is the brand-green accent on every pill; the day
 * number stays primary text colour for legibility, and the month
 * stays muted so the eye scans the green-grey-bold rhythm
 * top-to-bottom. */
.portal-schedule__date-pill-dow {
    font-size: 0.72rem;
    text-transform: uppercase;
    letter-spacing: 0.04em;
    color: var(--sps-success);
    font-weight: 700;
}

.portal-schedule__date-pill-day {
    font-size: 1.35rem;
    font-weight: 700;
    line-height: 1;
}

.portal-schedule__date-pill-mon {
    font-size: 0.75rem;
    color: var(--sps-text-muted);
}

/* Show More / Show Less wrapper. Flex with `flex: 1` on each
 * button means a single visible button stretches to full width;
 * two visible buttons split the row 50/50 with the existing gap. */
.portal-schedule__more-actions {
    display: flex;
    gap: 8px;
    margin: 8px 0 0;
}

.portal-schedule__more-actions[hidden] {
    display: none;
}

.portal-schedule__more-actions .portal-btn {
    flex: 1 1 0;
}

.portal-schedule__more-actions .portal-btn[hidden] {
    display: none;
}

/* Generic disabled state for ghost buttons — used by Show More
 * when the customer has revealed every available date. The button
 * stays in place (operator directive: don't jump the layout) but
 * goes muted + non-interactive. */
.portal-btn--ghost[disabled],
.portal-btn--ghost[aria-disabled="true"] {
    opacity: 0.45;
    cursor: not-allowed;
}

.portal-btn--ghost[disabled]:hover,
.portal-btn--ghost[aria-disabled="true"]:hover {
    background: transparent;
    color: var(--sps-text-secondary);
}

/* ── Selected-date header (above the chips) ────────────────── */
/* Shared with the customer-portal appointment reschedule flow
 * (app/Views/twig/portal/appointment.html.twig). The booking
 * funnel itself no longer uses this header — the active date pill
 * provides confirmation in place — but the appointment reschedule
 * surface still does. */
.portal-schedule__date-header {
    display: flex;
    align-items: center;
    gap: 12px;
    margin: 0 0 12px;
    padding: 12px 14px;
    border: 2px dashed var(--sps-success);
    border-radius: var(--sps-input-radius);
    background: var(--sps-success-dim);
    color: var(--sps-text-primary);
}

.portal-schedule__date-header[hidden] {
    display: none;
}

.portal-schedule__date-header-check {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 22px;
    height: 22px;
    border-radius: 50%;
    background: var(--sps-success);
    color: #ffffff;
    flex: 0 0 auto;
}

.portal-schedule__date-header-check svg {
    width: 14px;
    height: 14px;
}

.portal-schedule__date-header-text {
    flex: 1 1 auto;
    margin: 0;
    font-size: 1rem;
    font-weight: 600;
    color: var(--sps-text-primary);
}

/* ── Slot chip grid ────────────────────────────────────────── */
.portal-schedule__slots {
    margin: 0 0 var(--sps-funnel-stack-lg);
}

.portal-schedule__slots[hidden] {
    display: none;
}

/* Two chips per row, full-width split. Operator directive: chips
 * shouldn't crowd more than 2 per row even on wider viewports. */
.portal-schedule__chip-grid {
    display: grid;
    grid-template-columns: repeat(2, 1fr);
    gap: 10px;
}

.portal-schedule__chip {
    display: flex;
    align-items: center;
    gap: 10px;
    padding: 14px 14px;
    border: 2px dashed var(--sps-border);
    border-radius: var(--sps-input-radius);
    background: transparent;
    color: var(--sps-text-primary);
    cursor: pointer;
    font-family: inherit;
    text-align: left;
    transition: border-color 120ms ease, background 120ms ease, color 120ms ease;
}

.portal-schedule__chip:hover,
.portal-schedule__chip:focus-visible {
    border-color: var(--sps-success);
    outline: none;
}

/* Active (accepted) chip: solid green border, brand-green tinted
 * fill, label colour switches to the brand green so the chip reads
 * unambiguously as "this is the time you've picked". */
.portal-schedule__chip.is-active {
    border-style: solid;
    border-color: var(--sps-success);
    background: rgba(46, 204, 113, 0.18);
    color: var(--sps-success);
}

.portal-schedule__chip-check {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 22px;
    height: 22px;
    border-radius: 50%;
    background: var(--sps-border);
    color: #ffffff;
    flex: 0 0 auto;
    transition: background 120ms ease;
}

.portal-schedule__chip-check svg {
    width: 14px;
    height: 14px;
    opacity: 0;
    transition: opacity 120ms ease;
}

.portal-schedule__chip.is-active .portal-schedule__chip-check {
    background: var(--sps-success);
}

.portal-schedule__chip.is-active .portal-schedule__chip-check svg {
    opacity: 1;
}

.portal-schedule__chip-label {
    font-size: 0.95rem;
    font-weight: 600;
}

/* ── Terminal block ────────────────────────────────────────── */
.portal-schedule__terminal[hidden] {
    display: none;
}

.portal-schedule__terminal-message {
    margin: 0 0 12px;
    padding: 12px 14px;
    border: 1px solid var(--sps-danger);
    border-radius: var(--sps-input-radius);
    background: rgba(220, 53, 69, 0.08);
    color: var(--sps-text-primary);
}

.portal-schedule__call-link {
    display: inline-flex;
    align-items: center;
    gap: 6px;
}

/* ────────────────────────────────────────────────────────────────── */
/* Section 5 (Review) — accordion summary + financial-ack switch       */
/* ────────────────────────────────────────────────────────────────── */

.portal-review {
    display: flex;
    flex-direction: column;
    gap: 16px;
}

.portal-review[hidden] {
    display: none;
}

/* ── Accordion wrapper ─────────────────────────────────────────── */
.portal-review__accordion {
    display: flex;
    flex-direction: column;
    gap: 8px;
}

.portal-review__item {
    background: var(--sps-surface-1);
    border: 1px solid var(--sps-border);
    border-radius: var(--sps-input-radius);
    overflow: hidden;
}

.portal-review__item.is-open {
    border-color: var(--sps-accent);
}

/* ── Header (clickable button) ─────────────────────────────────── */
.portal-review__header {
    width: 100%;
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 12px;
    padding: 14px 16px;
    background: transparent;
    border: 0;
    color: var(--sps-text-primary);
    cursor: pointer;
    font: inherit;
    text-align: left;
    transition: background 120ms ease;
}

.portal-review__header:hover,
.portal-review__header:focus-visible {
    background: rgba(56, 139, 253, 0.06);
    outline: none;
}

.portal-review__header-titles {
    display: flex;
    flex-direction: column;
    gap: 3px;
    min-width: 0;
    flex: 1 1 auto;
}

.portal-review__header-title {
    font-size: 13px;
    font-weight: 600;
    letter-spacing: 0.06em;
    text-transform: uppercase;
    color: var(--sps-text-secondary);
}

.portal-review__header-summary {
    font-size: 15px;
    color: var(--sps-text-primary);
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

.portal-review__header-summary:empty::before {
    content: 'Tap to review';
    color: var(--sps-text-muted);
    font-style: italic;
}

.portal-review__header-chevron {
    flex: 0 0 auto;
    width: 20px;
    height: 20px;
    color: var(--sps-text-secondary);
    transition: transform 160ms ease;
}

.portal-review__header-chevron svg {
    width: 100%;
    height: 100%;
    display: block;
}

.portal-review__item.is-open .portal-review__header-chevron {
    transform: rotate(180deg);
}

/* ── Body (hidden when closed) ─────────────────────────────────── */
.portal-review__body {
    padding: 0 16px 16px;
    display: flex;
    flex-direction: column;
    gap: 8px;
    border-top: 1px solid var(--sps-border);
    padding-top: 14px;
    background: var(--sps-surface-2);
}

.portal-review__body[hidden] {
    display: none;
}

.portal-review__line {
    margin: 0;
    font-size: 15px;
    color: var(--sps-text-primary);
    line-height: 1.4;
}

.portal-review__line--strong {
    font-weight: 600;
}

/* ── Appliance summary inside the Situation accordion ──────────── */
.portal-review__appliances {
    display: flex;
    flex-direction: column;
    gap: 10px;
}

.portal-review__appliance {
    background: var(--sps-surface-1);
    border: 1px solid var(--sps-border);
    border-radius: 8px;
    padding: 10px 12px;
    display: flex;
    flex-direction: column;
    gap: 4px;
}

.portal-review__appliance-headline {
    margin: 0;
    font-size: 14px;
    font-weight: 600;
    color: var(--sps-text-primary);
}

.portal-review__appliance-warranty {
    margin: 0;
    font-size: 11px;
    font-weight: 600;
    letter-spacing: 0.04em;
    text-transform: uppercase;
    color: var(--sps-warning);
}

.portal-review__appliance-warranty[hidden],
.portal-review__appliance-desc[hidden],
.portal-review__appliance-photos[hidden] {
    display: none;
}

.portal-review__appliance-desc {
    margin: 0;
    font-size: 13px;
    color: var(--sps-text-secondary);
    line-height: 1.4;
}

.portal-review__appliance-photos {
    margin: 0;
    font-size: 12px;
    color: var(--sps-text-muted);
}

/* ── Edit button (real button, not a text link) ────────────────── */
.portal-review__edit-btn {
    margin-top: 8px;
    align-self: flex-start;
}

/* ── Financial-responsibility acknowledgement ──────────────────── */
.portal-review__ack {
    padding: 14px 16px;
    border: 1px solid var(--sps-border);
    border-radius: var(--sps-input-radius);
    background: var(--sps-surface-1);
    display: flex;
    flex-direction: column;
    gap: 12px;
}

.portal-review__ack.is-on {
    border-color: var(--sps-accent);
    background: var(--sps-accent-dim);
}

.portal-review__ack-prompt {
    margin: 0;
    font-size: 14px;
    color: var(--sps-text-primary);
    line-height: 1.5;
}

/* ── iOS-style toggle switch ───────────────────────────────────── */
.portal-review__switch {
    display: flex;
    align-items: center;
    gap: 12px;
    cursor: pointer;
    user-select: none;
}

.portal-review__switch-native {
    /* Visually hidden — the track + thumb are the visual surface,
       the native checkbox owns the state for forms / a11y. */
    position: absolute;
    width: 1px;
    height: 1px;
    padding: 0;
    margin: -1px;
    overflow: hidden;
    clip: rect(0, 0, 0, 0);
    white-space: nowrap;
    border: 0;
}

.portal-review__switch-track {
    position: relative;
    flex: 0 0 auto;
    width: 44px;
    height: 26px;
    border-radius: 13px;
    background: var(--sps-surface-2);
    border: 1px solid var(--sps-border);
    transition: background 160ms ease, border-color 160ms ease;
}

.portal-review__switch-thumb {
    position: absolute;
    top: 2px;
    left: 2px;
    width: 20px;
    height: 20px;
    border-radius: 50%;
    background: var(--sps-text-secondary);
    transition: transform 160ms ease, background 160ms ease;
}

.portal-review__switch-native:checked ~ .portal-review__switch-track {
    background: var(--sps-accent);
    border-color: var(--sps-accent);
}

.portal-review__switch-native:checked ~ .portal-review__switch-track .portal-review__switch-thumb {
    transform: translateX(18px);
    background: #ffffff;
}

.portal-review__switch-native:focus-visible ~ .portal-review__switch-track {
    box-shadow: 0 0 0 3px rgba(56, 139, 253, 0.35);
}

.portal-review__switch-label {
    font-size: 14px;
    font-weight: 500;
    color: var(--sps-text-primary);
}

/* ── Terminal failure block ────────────────────────────────────── */
.portal-review__terminal[hidden] {
    display: none;
}

.portal-review__terminal-message {
    margin: 0 0 12px;
    padding: 12px 14px;
    border: 1px solid var(--sps-danger);
    border-radius: var(--sps-input-radius);
    background: rgba(220, 53, 69, 0.08);
    color: var(--sps-text-primary);
}

.portal-review__call-link {
    display: inline-flex;
    align-items: center;
    gap: 6px;
}

/* ── Race-recovery banner ──────────────────────────────────────── */
/* Apologetic header set by Section 5 commit handlers when the
   customer is rerouted back to Section 4 after a slot_taken /
   commit_race_lost / grouped_miles / slot_not_bookable race. The
   schedule onEnter consumes the pending message, paints once, and
   clears the pending state so a subsequent Section 4 visit (e.g. via
   browser back) does not re-render a stale banner. */
.portal-schedule__race-banner {
    margin-bottom: 12px;
    padding: 12px 14px;
    border: 1px solid var(--sps-warning);
    border-radius: var(--sps-input-radius);
    background: rgba(245, 165, 36, 0.08);
    color: var(--sps-text-primary);
}

.portal-schedule__race-banner[hidden] {
    display: none;
}

.portal-schedule__race-banner-message {
    margin: 0;
    font-size: 14px;
    line-height: 1.5;
}

/* ── Embed-mode commit success ─────────────────────────────────────
 *
 * Section 6 in the funnel SPA, shown only when commit succeeds inside
 * an iframe embed (the non-embed flow hard-navigates to
 * /appointment/{token} instead). The customer reads the confirmation
 * line, optionally opens the appointment portal in a new tab via the
 * View My Appointment ghost link, then either waits the countdown or
 * taps Start a New Booking. After reset the SPA reloads /book/
 * {identifier}?embed=1: fresh server session, fresh DOM.
 * ───────────────────────────────────────────────────────────────── */
.portal-success {
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: 16px;
    padding: 8px 4px 4px;
    text-align: center;
}

.portal-success__icon {
    color: var(--sps-success);
    width: 64px;
    height: 64px;
}

.portal-success__icon svg {
    width: 100%;
    height: 100%;
    display: block;
}

.portal-success__lead {
    margin: 0;
    font-size: 16px;
    line-height: 1.5;
    color: var(--sps-text-primary);
    max-width: 36ch;
}

.portal-success__view-link[hidden] {
    display: none;
}

.portal-success__countdown {
    margin: 0;
    font-size: 14px;
    line-height: 1.4;
    color: var(--sps-text-muted, var(--sps-text-primary));
}

.portal-success__countdown [data-success-countdown] {
    font-weight: 600;
    color: var(--sps-text-primary);
}
