/* ══════════════════════════════════════════
   SignBridge — Main Stylesheet
   ══════════════════════════════════════════ */

/* ── CSS Custom Properties (Light Theme) ── */
:root {
    --bg-primary: #fdf0e6;
    --bg-card: #fff;
    --bg-header: rgba(245,225,210,0.95);
    --bg-toggle: rgba(180,140,100,0.12);
    --bg-toggle-hover: rgba(180,140,100,0.15);
    --bg-chip: rgba(180,140,100,0.08);
    --text-primary: #2a2420;
    --text-secondary: #7a6e60;
    --text-muted: rgba(80,60,40,0.5);
    --text-dimmed: rgba(80,60,40,0.4);
    --accent: #c4872a;
    --accent-hover: #d4972e;
    --accent-gradient: linear-gradient(135deg, #c4872a, #a8701e);
    --border-light: rgba(180,140,100,0.15);
    --border-subtle: rgba(180,140,100,0.1);
    --border-medium: rgba(180,140,100,0.2);
    --shadow-card: 0 4px 24px rgba(160,120,60,0.08);
    --shadow-hover: 0 12px 32px rgba(160,120,60,0.15);
    --disabled-bg: #d8cfc0;
    --disabled-text: #a09888;
    --success: #4caf80;
    --danger: #ff4444;
    --purple: #b07cf0;
    --blue: #7cb3f0;
    --logo-dark: #3a2e20;
    /* Wordmark identity color -- deliberately NOT the amber accent, so the title
       pops instead of blending into the UI. Cool teal->violet jewel gradient. */
    --logo-gradient: linear-gradient(115deg, #0ea5a4 0%, #7c3aed 100%);
    --logo-solid: #6d28d9;   /* fallback where background-clip:text is unsupported */
    --camera-bg: #000;
    --msg-doctor-bg: rgba(180,140,100,0.08);
    --msg-patient-bg: #c4872a;
    --scrollbar-thumb: #c8b8a0;
}

/* ── Dark Theme ── */
[data-theme="dark"] {
    --bg-primary: #1a1612;
    --bg-card: #2a2420;
    --bg-header: rgba(36,30,24,0.95);
    --bg-toggle: rgba(100,80,60,0.2);
    --bg-toggle-hover: rgba(100,80,60,0.3);
    --bg-chip: rgba(100,80,60,0.15);
    --text-primary: #f0e6d8;
    --text-secondary: #b0a090;
    --text-muted: rgba(200,180,160,0.5);
    --text-dimmed: rgba(200,180,160,0.4);
    --accent: #d4972e;
    --accent-hover: #e0a840;
    --accent-gradient: linear-gradient(135deg, #d4972e, #c4872a);
    --border-light: rgba(100,80,60,0.25);
    --border-subtle: rgba(100,80,60,0.15);
    --border-medium: rgba(100,80,60,0.3);
    --shadow-card: 0 4px 24px rgba(0,0,0,0.3);
    --shadow-hover: 0 12px 32px rgba(0,0,0,0.4);
    --disabled-bg: #3a3430;
    --disabled-text: #6a6258;
    --logo-dark: #f0e6d8;
    /* Brighter stops so the wordmark stays vivid on the dark-brown ground. */
    --logo-gradient: linear-gradient(115deg, #2dd4bf 0%, #b48bff 100%);
    --logo-solid: #b48bff;
    --camera-bg: #0a0808;
    --msg-doctor-bg: rgba(100,80,60,0.15);
    --msg-patient-bg: #c4872a;
    --scrollbar-thumb: #5a4e40;
}

/* ══════════════════════════════════════════
   BASE
   ══════════════════════════════════════════ */
* { margin: 0; padding: 0; box-sizing: border-box; }

body {
    font-family: -apple-system, BlinkMacSystemFont, 'SF Pro Display', 'Segoe UI', Roboto, sans-serif;
    background: var(--bg-primary);
    color: var(--text-primary);
    min-height: 100vh;
    overflow-x: hidden;
    -webkit-font-smoothing: antialiased;
    transition: background 0.3s, color 0.3s;
}

/* ── Header ── */
.app-header {
    display: flex;
    flex-direction: column;
    gap: 8px;
    padding: 10px 20px;
    background: var(--bg-header);
    border-bottom: 1px solid var(--border-medium);
    position: sticky;
    top: 0;
    z-index: 100;
    transition: background 0.3s;
}
/* The blur lives on a pseudo-element, NOT on .app-header itself: backdrop-filter
   makes its element a containing block for position:fixed descendants, which
   would trap the phone bottom tab bar (now a child of the header) inside the
   header instead of pinning it to the viewport bottom. */
.app-header::before {
    content: "";
    position: absolute;
    inset: 0;
    z-index: -1;
    pointer-events: none;
    backdrop-filter: saturate(180%) blur(20px);
    -webkit-backdrop-filter: saturate(180%) blur(20px);
}

.header-row-top {
    display: flex;
    align-items: center;
    justify-content: center;
    position: relative;
    min-height: 32px;
}

.header-row-top #headerHomeBtn {
    position: absolute;
    left: 0;
}

.header-actions {
    position: absolute;
    right: 0;
    display: flex;
    gap: 6px;
    align-items: center;
}
.header-welcome {
    font-size: 0.72rem;
    color: var(--text-muted);
    font-weight: 500;
    white-space: nowrap;
}

.header-row-toggles {
    display: flex;
    align-items: center;
    justify-content: center;
    gap: 8px;
}

.app-logo {
    /* Gesturely wordmark: warm display serif (Fraunces, loaded in <head>) with a
       gentle amber gradient, distinct from the sans-serif UI so it reads as a
       logo rather than a label. Falls back to a serif stack + solid accent. */
    font-family: 'Fraunces', Georgia, 'Times New Roman', serif;
    font-optical-sizing: auto;
    font-size: 1.7rem;
    font-weight: 700;
    letter-spacing: 0.2px;
    color: var(--logo-solid);        /* fallback if background-clip:text unsupported */
    cursor: pointer;
    transition: opacity 0.25s;
}
/* Gradient fill guarded by @supports so browsers without background-clip:text
   keep the solid color above instead of rendering invisible transparent text. */
@supports ((-webkit-background-clip: text) or (background-clip: text)) {
    .app-logo {
        background: var(--logo-gradient);
        -webkit-background-clip: text;
        background-clip: text;
        -webkit-text-fill-color: transparent;
    }
}
.app-logo:hover { opacity: 0.82; }

/* ── Merged title + nav row (tablet / desktop, >=769px) ──────────────
   The app nav lives inside .header-row-top now: SignBridge title left,
   menu centered, settings/theme right -- all on one line, so the old
   standalone 52px nav bar is reclaimed for the camera pages. On phone
   (<=768px) the .sidebar block turns this same <nav> back into a fixed
   bottom tab bar, so these rules are scoped to >=769px only. */
@media (min-width: 769px) {
    .header-row-top {
        justify-content: flex-start;   /* logo starts at the left edge */
        gap: 16px;
    }
    .header-row-top .header-actions {
        position: static;              /* flow item, not an absolute overlay */
        margin-left: auto;             /* stay right if the nav doesn't fill */
    }
    /* Nav: an inline, centered band that grows to fill between logo and actions. */
    .header-row-top > .sidebar {
        flex: 1 1 auto;
        position: static;
        top: auto;
        height: auto;
        width: auto;
        background: transparent;       /* sit on the glass header, not a dark bar */
        padding: 0;
        overflow: visible;
        box-shadow: none;
        z-index: auto;
        justify-content: center;
    }
    /* Dark mode: the [data-theme="dark"] .sidebar rule (near-black #121010, meant
       for the mobile bottom bar) appears later in the file with equal specificity,
       so it was overriding the transparent inline nav above and painting a black
       box on the dark-brown header. Re-assert transparent for the inline nav only;
       the mobile bottom bar (matched only <=768px) keeps its dark background. */
    [data-theme="dark"] .header-row-top > .sidebar { background: transparent; }
    /* Recolor for the light/glass header (the dark-bar white text was invisible),
       and keep items within the ~32px title row so the header stays ~84px tall
       (the 84px offset the camera panes subtract). */
    .header-row-top .sidebar-item {
        color: var(--text-muted);
        padding: 5px 12px;
        font-size: 0.82rem;
    }
    .header-row-top .sidebar-item:hover {
        background: rgba(127,127,127,0.12);
        color: var(--text-primary);
    }
    .header-row-top .sidebar-item.active {
        background: var(--accent);
        color: #fff;
    }
}

/* ── Header toggles ── */
.header-toggle {
    display: inline-flex;
    background: var(--bg-toggle);
    border: none;
    border-radius: 20px;
    overflow: hidden;
    gap: 2px;
    padding: 3px;
}
.htog-btn {
    background: transparent;
    border: none;
    color: var(--text-muted);
    padding: 6px 18px;
    font-size: 0.72rem;
    font-weight: 600;
    letter-spacing: 0.5px;
    text-transform: uppercase;
    cursor: pointer;
    transition: all 0.3s ease;
    border-radius: 18px;
}
.htog-btn.active {
    background: var(--accent);
    color: #fff;
}
.htog-btn:hover:not(.active) {
    color: var(--text-secondary);
}
.htog-btn.locked {
    cursor: default;
    opacity: 0.4;
}
.htog-btn.locked:hover:not(.active) {
    color: var(--text-muted);
}

/* ── Theme toggle button ── */
.theme-toggle,
.header-icon-btn {
    background: var(--bg-toggle);
    border: none;
    color: var(--text-muted);
    width: 32px;
    height: 32px;
    border-radius: 50%;
    cursor: pointer;
    display: flex;
    align-items: center;
    justify-content: center;
    font-size: 1rem;
    transition: all 0.2s;
}
.theme-toggle:hover,
.header-icon-btn:hover {
    background: var(--bg-toggle-hover);
    color: var(--text-primary);
}
/* Settings is a destination as well as a control, so unlike the theme toggle
   it needs to show when you are already there. */
/* Settings > Device mode toggle. Reuses .htog-btn (the I-Sign/I-Speak pill
   style) so it reads as a segmented control rather than two loose buttons. */
.settings-mode-toggle {
    display: flex;
    gap: 6px;
    flex-shrink: 0;
}
.settings-mode-toggle .htog-btn {
    padding: 7px 14px;
    font-size: 0.8rem;
    white-space: nowrap;
}

/* Conf-Call: present so the roadmap is visible, but not selectable. */
.htog-btn-soon {
    opacity: 0.45;
}
.htog-btn:disabled {
    cursor: not-allowed;
}
/* A disabled-but-inactive mode button dims; the active one keeps full contrast
   so the current mode is readable outside Edit mode. */
.htog-btn:disabled:not(.active) {
    opacity: 0.6;
}
.htog-btn-soon:hover {
    background: inherit;
}
.soon-tag {
    font-size: 0.6rem;
    text-transform: uppercase;
    letter-spacing: 0.04em;
    padding: 1px 5px;
    margin-left: 4px;
    border-radius: 6px;
    background: var(--bg-toggle-hover, rgba(255,255,255,0.12));
    vertical-align: middle;
}

.settings-readonly-value {
    font-size: 0.85rem;
    color: var(--text-secondary, #b9b2a8);
    flex-shrink: 0;
}
.settings-required-note {
    font-size: 0.72rem;
    color: #ff6666;
    white-space: nowrap;
}

.header-icon-btn.active {
    background: var(--accent, #c4872a);
    color: #fff;
}

/* ══════════════════════════════════════════
   TOAST NOTIFICATIONS (1.6)
   ══════════════════════════════════════════ */
.toast-container {
    position: fixed;
    top: 80px;
    right: 20px;
    z-index: 1000;
    display: flex;
    flex-direction: column;
    gap: 8px;
    pointer-events: none;
}
.toast {
    background: var(--bg-card);
    border: 1px solid var(--border-light);
    border-radius: 12px;
    padding: 12px 16px;
    display: flex;
    align-items: center;
    gap: 10px;
    box-shadow: 0 8px 32px rgba(0,0,0,0.15);
    animation: toastIn 0.3s ease;
    pointer-events: auto;
    max-width: 360px;
    font-size: 0.85rem;
    color: var(--text-primary);
    transition: opacity 0.3s, transform 0.3s;
}
.toast.toast-exit {
    opacity: 0;
    transform: translateX(20px);
}
.toast-icon { font-size: 1.1rem; flex-shrink: 0; }
.toast-msg { flex: 1; line-height: 1.4; }
.toast-close {
    background: none; border: none; color: var(--text-muted);
    cursor: pointer; font-size: 1rem; padding: 2px;
}
.toast.toast-error { border-left: 3px solid var(--danger); }
.toast.toast-success { border-left: 3px solid var(--success); }
.toast.toast-info { border-left: 3px solid var(--blue); }
.toast.toast-warn { border-left: 3px solid #f0a050; }

@keyframes toastIn { from { opacity:0; transform:translateX(20px) } to { opacity:1; transform:translateX(0) } }

/* ══════════════════════════════════════════
   CAMERA PERMISSION MODAL (1.5)
   ══════════════════════════════════════════ */
.camera-modal-overlay {
    display: none;
    position: fixed;
    inset: 0;
    background: rgba(0,0,0,0.5);
    z-index: 500;
    align-items: center;
    justify-content: center;
}
.camera-modal-overlay.visible { display: flex; }
.camera-modal {
    background: var(--bg-card);
    border-radius: 20px;
    padding: 32px;
    max-width: 400px;
    width: 90%;
    text-align: center;
    box-shadow: 0 16px 48px rgba(0,0,0,0.2);
}
.camera-modal-icon { font-size: 2.5rem; margin-bottom: 16px; }
.camera-modal h3 {
    font-size: 1.1rem;
    font-weight: 700;
    color: var(--text-primary);
    margin-bottom: 8px;
}
.camera-modal p {
    font-size: 0.85rem;
    color: var(--text-secondary);
    line-height: 1.6;
    margin-bottom: 20px;
}
.camera-modal-actions { display: flex; gap: 10px; justify-content: center; }

/* ══════════════════════════════════════════
   LOADING / SKELETON (1.2)
   ══════════════════════════════════════════ */
.skeleton {
    background: linear-gradient(90deg, var(--bg-chip) 25%, var(--bg-toggle) 50%, var(--bg-chip) 75%);
    background-size: 200% 100%;
    animation: shimmer 1.5s infinite;
    border-radius: 8px;
}
@keyframes shimmer { 0% { background-position: 200% 0 } 100% { background-position: -200% 0 } }

.spinner {
    display: inline-block;
    width: 16px;
    height: 16px;
    border: 2px solid var(--border-light);
    border-top-color: var(--accent);
    border-radius: 50%;
    animation: spin 0.6s linear infinite;
}
@keyframes spin { to { transform: rotate(360deg) } }

/* ══════════════════════════════════════════
   LANDING PAGE / HERO (1.3)
   ══════════════════════════════════════════ */
/* ── Unified tab intro (consistent across all tabs) ── */
.tab-intro {
    padding: 24px 20px 16px;
    text-align: left;
}
.tab-body {
    padding: 0 20px 20px;
}
.tab-intro h2 {
    font-size: 1.3rem;
    font-weight: 700;
    color: var(--text-primary);
    margin-bottom: 8px;
}
.tab-intro p {
    font-size: 0.88rem;
    color: var(--text-secondary);
    line-height: 1.5;
    margin-bottom: 0;
}
.tab-intro-welcome {
    font-size: 0.95rem;
    color: var(--accent);
    font-weight: 600;
    margin-bottom: 6px;
}
.hero-divider {
    width: 60px;
    height: 2px;
    background: var(--accent);
    margin: 0 auto 32px;
    border-radius: 1px;
}
.hero-start-hint {
    font-size: 0.78rem;
    color: var(--text-dimmed);
    margin-bottom: 16px;
    display: flex;
    align-items: center;
    justify-content: center;
    gap: 6px;
}

/* ══════════════════════════════════════════
   PHASE 1: CONVERSATION
   ══════════════════════════════════════════ */
.conversation-layout {
    position: relative;      /* anchors .transcript-side, which overlays */
    display: flex;
    gap: 10px;
    padding: 8px 10px;
    height: calc(100dvh - 84px - var(--topnav-h));
    max-width: 1400px;
    margin: 0 auto;
    width: 100%;
    background: var(--bg-primary);
}

/* Camera side */
.camera-side {
    flex: 2;
    display: flex;
    flex-direction: column;
    gap: 6px;
    min-height: 0;
}

.camera-box {
    flex: 1;
    background: var(--bg-card);
    border-radius: 20px;
    /* overflow:hidden moved to .camera-view so the video still gets clipped
     * to rounded corners, but the status-bar (last flex child) can never be
     * clipped if flex math gets tight. */
    position: relative;
    display: flex;
    flex-direction: column;
    border: 1px solid var(--border-light);
    box-shadow: var(--shadow-card);
    transition: background 0.3s, border-color 0.3s;
    /* min-height:0 lets the camera-view inside shrink past its preferred
     * size when the viewport is tight, instead of overflowing the box. */
    min-height: 0;
}

/* One row: Back on the left, the scenario name in the middle, the camera
   controls pushed to the right. */
.convo-topline {
    display: flex;
    align-items: center;
    gap: 10px;
    flex-wrap: wrap;
}

.camera-topbar {
    display: flex;
    align-items: center;
    gap: 8px;
    /* Right-hand end of .convo-topline. It is no longer a bar across the top of
       the card, so it drops the card background and the divider it used to
       need. */
    margin-left: auto;
    padding: 0;
    background: transparent;
    border-bottom: none;
}

.camera-title {
    font-size: 0.75rem;
    font-weight: 600;
    text-transform: uppercase;
    letter-spacing: 1.2px;
    color: var(--accent);
}

/* The camera banner doubles as a link to where the source is configured. */
.camera-title-link {
    cursor: pointer;
    text-decoration: underline;
    text-underline-offset: 2px;
    text-decoration-thickness: 1px;
    text-decoration-color: color-mix(in srgb, var(--accent) 45%, transparent);
}
.camera-title-link:hover,
.camera-title-link:focus-visible {
    text-decoration-color: var(--accent);
}

/* Demo tab: setup-walkthrough carousel */
.demo-video-card {
    background: var(--bg-card);
    border: 1px solid var(--border-light);
    border-radius: 12px;
    padding: 14px 16px 12px;
    margin-bottom: 18px;
}
.demo-video-head {
    display: flex;
    align-items: flex-start;
    justify-content: space-between;
    gap: 16px;
    margin-bottom: 10px;
}
.demo-video-title { font-size: 0.95rem; font-weight: 600; color: var(--text-primary); }
.demo-video-desc  { font-size: 0.78rem; color: var(--text-muted); margin-top: 2px; }
.demo-video-nav   { display: flex; align-items: center; gap: 8px; flex-shrink: 0; }
.demo-video-count { font-size: 0.75rem; color: var(--text-muted); font-variant-numeric: tabular-nums; }
.demo-video-arrow {
    background: var(--bg-toggle);
    border: none;
    color: var(--text-secondary);
    width: 28px; height: 28px;
    border-radius: 50%;
    cursor: pointer;
    font-size: 1rem;
    line-height: 1;
}
.demo-video-arrow:hover { background: var(--bg-toggle-hover); color: var(--text-primary); }

/* 16:9 stage that holds either the poster or the iframe. */
.demo-video-stage {
    position: relative;
    width: 100%;
    aspect-ratio: 16 / 9;
    border-radius: 8px;
    overflow: hidden;
    background: #000;
}
.demo-video-stage iframe,
.demo-video-poster,
.demo-video-placeholder {
    position: absolute;
    inset: 0;
    width: 100%;
    height: 100%;
    border: 0;
}
.demo-video-poster { cursor: pointer; padding: 0; background: #000; }
.demo-video-poster img { width: 100%; height: 100%; object-fit: cover; opacity: 0.8; }
.demo-video-poster:hover img { opacity: 1; }
.demo-video-play {
    position: absolute;
    top: 50%; left: 50%;
    transform: translate(-50%, -50%);
    width: 56px; height: 56px;
    border-radius: 50%;
    background: rgba(0,0,0,0.6);
    color: #fff;
    display: flex; align-items: center; justify-content: center;
    font-size: 1.3rem;
    padding-left: 4px;
}
.demo-video-placeholder {
    display: flex; align-items: center; justify-content: center;
    color: var(--text-muted);
    font-size: 0.82rem;
    background: var(--bg-toggle);
}
.demo-video-dots { display: flex; gap: 6px; justify-content: center; margin-top: 10px; }
.demo-video-dot {
    width: 7px; height: 7px;
    border-radius: 50%;
    border: none;
    padding: 0;
    background: var(--border-light);
    cursor: pointer;
}
.demo-video-dot.active { background: var(--accent); }

/* ── Demo tab: "How taking turns works" help (copy from
   static/content/turn-taking-help.json) ─────────────────────────── */
.turn-help {
    margin: 0 0 18px;                 /* full width like the other demo panels */
    background: var(--bg-card); border: 1px solid var(--border-light);
    border-radius: 12px; padding: 20px 22px;
}
.turn-help:empty { display: none; }
.turn-help-title { margin: 0 0 6px; font-size: 1.05rem; font-weight: 700; color: var(--text-primary); }
.turn-help-intro { margin: 0 0 16px; font-size: 0.9rem; line-height: 1.5; color: var(--text-secondary); }
.turn-help-steps { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: 12px; }
.turn-help-step { display: flex; align-items: flex-start; gap: 12px; }
.turn-help-ico { flex: 0 0 auto; font-size: 1.3rem; line-height: 1.45; width: 28px; text-align: center; }
.turn-help-text { font-size: 0.9rem; line-height: 1.5; color: var(--text-secondary); }
.turn-help-text strong { display: block; color: var(--text-primary); font-weight: 600; margin-bottom: 1px; }
.turn-help-footer { margin: 16px 0 0; font-size: 0.9rem; font-weight: 600; color: var(--text-primary); }

/* Demo tab: wrap the scripted/counter demo groups in the SAME white panel as the
   video carousel and turn-help card so all three sections read consistently.
   Scoped to #demoScenarios so the Live tab's scenario cards are untouched. */
#demoScenarios {
    background: var(--bg-card); border: 1px solid var(--border-light);
    border-radius: 12px; padding: 16px 18px 4px; margin-bottom: 18px;
}
#demoScenarios:empty { display: none; }
#demoScenarios .scenario-grid { margin-bottom: 14px; }
/* Inner demo cards become flat outlined tiles (no white-on-white nested card
   look, no lift on hover) since they now sit inside the panel. */
#demoScenarios .scenario-card {
    background: transparent; box-shadow: none; border: 1px solid var(--border-light);
    border-radius: 12px;
}
#demoScenarios .scenario-card:hover {
    transform: none; box-shadow: none;
    border-color: var(--border-medium); background: rgba(127,127,127,0.06);
}

.rec-badge {
    display: none;
    padding: 2px 10px;
    border-radius: 10px;
    background: var(--danger);
    color: #fff;
    font-size: 0.7rem;
    font-weight: 700;
    animation: pulse 1s infinite;
}
.rec-badge.on { display: inline-block; }

@keyframes pulse { 0%,100%{opacity:1} 50%{opacity:0.4} }

.camera-stage-row {
    /* Positioning context for the gloss panel, which overlays rather than
       shares the row -- see .debug-panel. */
    position: relative;
    flex: 1;
    display: flex;
    flex-direction: row;
    align-items: center;
    justify-content: center;
    gap: 10px;
    min-height: 0;
    min-width: 0;
    padding: 0 8px;
}

.camera-view {
    flex: 1;
    display: flex;
    align-items: center;
    justify-content: center;
    position: relative;
    background: var(--camera-bg);
    min-height: 200px;
    /* Clips the absolutely-positioned video (and the rounded-corner shape
     * inherited from camera-box). Was on .camera-box but that clipped the
     * status-bar; moving it here is safe because the video and overlays
     * (pip, live-caption, speaker-turn-overlay) all live inside camera-view. */
    overflow: hidden;
}

.camera-view > video {
    position: absolute;
    inset: 0;
    width: 100%;
    height: 100%;
    object-fit: contain;
    transform: scaleX(-1);
    /* Paint the letterbox area dark so it reads as a camera "bezel"
     * instead of glaring white. object-fit:contain leaves uncovered
     * strips inside the video element; without an explicit background
     * those strips show the camera-box's white --bg-card through. */
    background: var(--camera-bg);
}

.camera-placeholder {
    color: #999;
    font-size: 0.9rem;
    text-align: center;
    padding: 20px;
}

.rec-border {
    display: none;
    position: absolute;
    inset: 0;
    border: 3px solid var(--danger);
    pointer-events: none;
    z-index: 5;
}
.rec-border.on { display: block; }

/* ── Signing-zone line (streaming mode) ──
 * Thin orange line drawn over the camera at the configured zone_top /
 * zone_bottom positions. Motion outside the band is ignored by the server-
 * side motion gate. Bottom line gets a small "signing area" label so the
 * user knows what it represents. */
.live-zone-line {
    position: absolute;
    left: 0;
    right: 0;
    height: 2px;
    background: rgba(245, 158, 11, 0.7);
    pointer-events: none;
    z-index: 5;
    transition: top 0.15s ease;
}
.live-zone-line-bottom::after {
    content: 'signing area ↑';
    position: absolute;
    right: 6px;
    top: -18px;
    font-size: 10px;
    font-family: ui-monospace, Consolas, monospace;
    color: rgba(255, 255, 255, 0.85);
    background: rgba(245, 158, 11, 0.85);
    padding: 1px 6px;
    border-radius: 2px;
    white-space: nowrap;
}
.live-zone-line-top::after {
    content: 'signing area ↓';
    position: absolute;
    right: 6px;
    bottom: -18px;
    font-size: 10px;
    font-family: ui-monospace, Consolas, monospace;
    color: rgba(255, 255, 255, 0.85);
    background: rgba(245, 158, 11, 0.85);
    padding: 1px 6px;
    border-radius: 2px;
    white-space: nowrap;
}

/* Feedback panel below the camera is retired -- the corner prediction badge
 * on the camera is now the single result surface. Keep the element in the DOM
 * for any latent references but never show it. */
#pracFeedback { display: none !important; }
#customPracticePicker { display: none !important; }

/* ── Sign Bank practice mode badges (streaming-pipeline UI) ── */
/* Prediction badge on the top-right of the practice camera. Green if the
 * model's top-1 matches the target sign, red otherwise. Stays visible until
 * the next attempt. */
.prac-prediction-badge {
    position: absolute;
    top: 10px;
    right: 10px;
    z-index: 8;
    padding: 6px 12px;
    border-radius: 6px;
    font-weight: 700;
    font-size: 0.95rem;
    font-family: ui-monospace, Consolas, monospace;
    letter-spacing: 0.04em;
    text-shadow: 0 1px 3px rgba(0,0,0,0.5);
    box-shadow: 0 4px 14px rgba(0,0,0,0.4);
    pointer-events: none;
}
.prac-prediction-badge.match { background: rgba(52, 211, 153, 0.94); color: #06280f; }
.prac-prediction-badge.miss  { background: rgba(248, 113, 113, 0.94); color: #350a0a; }

/* Word + domain selector that replaces the static prac-sign-name. Lets the
 * user switch which sign they're practicing without leaving the screen. */
.prac-word-selector {
    display: flex;
    align-items: center;
    gap: 10px;
    justify-content: center;
    flex-wrap: wrap;
    margin: 6px 0 14px;
}
.prac-word-selector .prac-sel-label {
    font-size: 0.85rem;
    color: var(--text-muted);
    font-weight: 500;
}
.prac-word-selector select {
    padding: 6px 28px 6px 12px;
    font-size: 1.15rem;
    font-weight: 700;
    background: var(--bg-card);
    color: var(--accent);
    border: 1px solid var(--border-light);
    border-radius: 6px;
    cursor: pointer;
    text-transform: uppercase;
    letter-spacing: 0.04em;
}
.prac-word-selector select:focus { outline: 2px solid var(--accent); }

/* Static practice header replaces the selectors. Inline run of text
 * with the gloss and domain visually emphasized but not interactive. */
.prac-word-selector.prac-word-static {
    font-size: 1.05rem;
    color: var(--text-secondary);
    font-weight: 500;
    line-height: 1.4;
}
.prac-word-selector.prac-word-static .prac-header-gloss {
    color: var(--accent);
    font-weight: 700;
    font-size: 1.15rem;
    margin: 0 4px;
}
.prac-word-selector.prac-word-static .prac-header-domain {
    color: var(--text-primary);
    font-weight: 700;
    letter-spacing: 0.06em;
    margin-left: 4px;
}

/* ── Live closed-caption overlay (streaming mode) ──
 * Anchored to the bottom of the camera view, sits on top of the video feed.
 * Updates in place each time the LLM rewrites the rolling sentence — like
 * TV closed-captioning. */
.live-sign-hint {
    position: absolute;
    top: 10px;
    left: 50%;
    transform: translateX(-50%);
    z-index: 9;              /* over the zone lines, under the framing gate (12) */
    max-width: calc(100% - 20px);
    padding: 6px 14px;
    border-radius: 999px;
    background: rgba(0, 0, 0, 0.66);
    color: #fff;
    font-size: 0.82rem;
    font-weight: 600;
    letter-spacing: 0.2px;
    white-space: nowrap;
    pointer-events: none;
    box-shadow: 0 2px 10px rgba(0, 0, 0, 0.35);
}
@media (max-width: 640px) {
    .live-sign-hint { font-size: 0.7rem; padding: 4px 10px; }
}

.live-caption {
    position: absolute;
    left: 8px;
    right: 8px;
    bottom: 12px;
    /* z-index above .speaker-turn-overlay (z-index:10) so the caption
     * (and its Regenerate / Edit chips) stays visible AND clickable
     * even when a wake-word session opens the centered speaker badge
     * over the same area. Without this the chips were obscured/blocked. */
    z-index: 15;
    padding: 10px 14px;
    background: rgba(0, 0, 0, 0.72);
    color: #fff;
    font-size: 1.15rem;
    font-weight: 500;
    line-height: 1.35;
    text-align: center;
    border-radius: 6px;
    box-shadow: 0 4px 16px rgba(0,0,0,0.45);
    pointer-events: none;
    transition: opacity 180ms ease, background 180ms ease;
    /* counteract the .camera-view > video mirror so caption text reads normally */
    transform: scaleX(1);
}
.live-caption.updating {
    background: rgba(0, 0, 0, 0.55);
    opacity: 0.8;
}
/* Speaker-turn overlay (wake-word capture in Kiosk / Lanyard).
 * Big centered badge over the camera so the signer can SEE the
 * system is listening / processing speech (the status-bar text is
 * easy to miss).
 */
.speaker-turn-overlay {
    position: absolute;
    top: 50%;
    left: 50%;
    transform: translate(-50%, -50%);
    background: rgba(91, 155, 213, 0.92);   /* accent blue */
    color: #fff;
    padding: 16px 24px;
    border-radius: 14px;
    font-size: 1.2rem;
    font-weight: 700;
    z-index: 10;
    box-shadow: 0 8px 24px rgba(0, 0, 0, 0.55);
    display: inline-flex;
    align-items: center;
    gap: 10px;
    pointer-events: none;
    animation: speakerOverlayPulse 1.6s ease-in-out infinite;
}
.speaker-turn-overlay.processing {
    background: rgba(176, 124, 240, 0.92);  /* purple */
}
.speaker-turn-icon { font-size: 1.4rem; }
@keyframes speakerOverlayPulse {
    0%, 100% { box-shadow: 0 8px 24px rgba(0,0,0,0.55), 0 0 0 0 rgba(91,155,213,0.55); }
    50%      { box-shadow: 0 8px 24px rgba(0,0,0,0.55), 0 0 0 10px rgba(91,155,213,0.0); }
}

/* Low-CTQI alert: pulsing red border so the signer cannot miss that
 * the translation is questionable and they should look at the
 * Regenerate / Edit chips. Threshold lives in app.js
 * (CTQI_LOW_THRESHOLD = 80; user-tunable from unified Settings later). */
.live-caption.low-ctqi {
    border: 2px solid #e74c3c;
    animation: lowCtqiPulse 1.4s ease-in-out infinite;
}
@keyframes lowCtqiPulse {
    0%, 100% { box-shadow: 0 4px 16px rgba(0,0,0,0.45), 0 0 0 0 rgba(231,76,60,0.55); }
    50%      { box-shadow: 0 4px 16px rgba(0,0,0,0.45), 0 0 0 6px rgba(231,76,60,0.0); }
}
.live-caption .ctqi-good {
    color: #34d399;
    font-weight: 700;
    margin-left: 0.6em;
}
.live-caption .ctqi-bad {
    color: #f87171;
    font-weight: 700;
    margin-left: 0.6em;
}
.live-caption .ctqi-missing {
    color: rgba(255, 255, 255, 0.5);
    font-weight: 600;
    margin-left: 0.6em;
    font-style: italic;
}
.live-caption .ctqi-edited {
    color: rgba(180, 200, 255, 0.85);
    font-weight: 600;
    margin-left: 0.6em;
    font-style: italic;
}

/* Inline action chips next to a low/missing CTQI score (regenerate, edit).
 * Parent .live-caption has pointer-events: none for the camera image; these
 * children opt back in so they're clickable. */
.live-caption .caption-action {
    display: inline-block;
    margin-left: 0.45em;
    cursor: pointer;
    user-select: none;
    /* !important defends against any sibling/parent style that turns
     * pointer-events off (parent .live-caption is pointer-events:none
     * for the camera-image passthrough; the chips must opt back in). */
    pointer-events: auto !important;
    position: relative;
    z-index: 1;
    opacity: 0.85;
    font-size: 1em;
    transition: opacity 0.15s, transform 0.15s;
    padding: 0 4px;
}
.live-caption .caption-action:hover {
    opacity: 1;
    transform: scale(1.18);
}
.live-caption .caption-action.caption-action-disabled {
    opacity: 0.3;
    cursor: not-allowed;
    pointer-events: auto;   /* keep tooltip visible */
}
.live-caption .caption-action.caption-action-disabled:hover {
    opacity: 0.3;
    transform: none;
}

/* Inline edit form (replaces sentence text temporarily when Edit clicked). */
.live-caption .caption-edit-input {
    background: rgba(255, 255, 255, 0.12);
    color: #fff;
    border: 1px solid rgba(255, 255, 255, 0.3);
    border-radius: 4px;
    padding: 4px 8px;
    font-size: 1em;
    font-weight: 500;
    min-width: 60%;
    pointer-events: auto;
}
@media (max-width: 768px) {
    .live-caption { font-size: 0.95rem; padding: 8px 10px; bottom: 8px; }
}
@media (max-width: 480px) {
    .live-caption { font-size: 0.85rem; padding: 6px 8px; }
}

/* ── Recording countdown overlay (1.10) ── */
.rec-countdown {
    display: none;
    position: absolute;
    top: 12px;
    left: 12px;
    background: rgba(0,0,0,0.7);
    color: #fff;
    padding: 6px 14px;
    border-radius: 8px;
    font-size: 0.8rem;
    font-weight: 600;
    z-index: 6;
    font-variant-numeric: tabular-nums;
}
.rec-countdown.on { display: flex; align-items: center; gap: 6px; }
.rec-countdown .rec-dot {
    width: 8px;
    height: 8px;
    border-radius: 50%;
    background: var(--danger);
    animation: pulse 1s infinite;
}

/* Self-view PIP overlay */
.pip-self {
    display: none;
    position: absolute;
    bottom: 12px;
    right: 12px;
    width: 160px;
    height: 120px;
    border-radius: 10px;
    overflow: hidden;
    border: 2px solid rgba(108, 99, 255, 0.8);
    z-index: 10;
    background: #0a0a14;
    box-shadow: 0 4px 12px rgba(0,0,0,0.5);
}
.pip-self video {
    width: 100%;
    height: 100%;
    object-fit: cover;
    transform: scaleX(-1);
}
.pip-self .pip-label {
    position: absolute;
    bottom: 0;
    left: 0;
    right: 0;
    font-size: 0.65rem;
    color: #fff;
    background: rgba(0,0,0,0.6);
    padding: 3px 8px;
    text-align: center;
    font-weight: 600;
    letter-spacing: 0.5px;
}

/* Status bar at the bottom of .camera-box.
 * Used to be clipped invisible because .camera-box had overflow:hidden and
 * camera-view's min-height pushed the status-bar past the clip boundary.
 * Fixed by moving overflow:hidden to .camera-view + min-height:0 on
 * camera-box -- now status-bar always renders as the bottom strip of the
 * camera card. Accent tint gives it visible identity vs the camera area. */
.status-bar {
    padding: 8px 14px;
    min-height: 38px;
    background: color-mix(in srgb, var(--accent) 12%, var(--bg-card));
    display: flex;
    align-items: center;
    justify-content: center;
    text-align: center;
    font-size: 0.9rem;
    border-top: 1px solid color-mix(in srgb, var(--accent) 30%, var(--border-light));
    transition: background 0.3s, border-top-color 0.3s;
    flex-shrink: 0;
}

.status-bar .gloss { color: var(--text-primary); font-family: 'SF Mono', monospace; font-weight: 600; letter-spacing: 1px; }
.status-bar .sentence { color: #2d7a2d; font-weight: 500; }
.status-bar .prompt { color: var(--text-secondary); font-style: italic; font-weight: 500; }
.status-bar .processing {
    color: var(--accent);
    font-weight: 600;
    /* No flicker by default -- the old pulse 1s drop-to-0.4 made the text
     * unreadable. Active AI states use the .ai variant below for a subtler
     * highlight + sparkle, the .spinner element next to the text is the
     * actual motion cue. */
}
.status-bar .processing.ai {
    color: var(--accent);
    font-weight: 700;
    animation: pulseSubtle 2s ease-in-out infinite;
}
.status-bar .processing.ai .ai-sparkle {
    display: inline-block;
    margin-right: 4px;
    /* Slow rotation gives a gentle "AI is thinking" cue without strobing. */
    animation: sparkleSpin 3s linear infinite;
}
@keyframes pulseSubtle  { 0%,100%{opacity:1} 50%{opacity:0.85} }
@keyframes sparkleSpin  { 0%{transform:rotate(0deg)} 100%{transform:rotate(360deg)} }
.status-bar .doctor-script {
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: 6px;
}
.status-bar .script-label {
    color: var(--blue);
    font-size: 0.7rem;
    text-transform: uppercase;
    letter-spacing: 1px;
}
.status-bar .script-line {
    color: var(--text-primary);
    font-size: 1.1rem;
    font-weight: 600;
}
.status-bar .script-hint {
    color: var(--text-secondary);
    font-size: 0.75rem;
}
.status-bar .listening-indicator {
    display: inline-block;
    width: 8px;
    height: 8px;
    border-radius: 50%;
    background: var(--danger);
    margin-right: 6px;
    animation: pulse 1s infinite;
}

/* Controls under camera */
.controls-row {
    display: flex;
    align-items: center;
    justify-content: center;
    flex-wrap: wrap;
    gap: 10px;
    padding: 6px 12px;
}

.btn {
    padding: 10px 28px;
    font-size: 0.85rem;
    font-weight: 600;
    border: none;
    border-radius: 980px;
    cursor: pointer;
    transition: all 0.3s ease;
    letter-spacing: 0.2px;
}

.btn-primary { background: var(--accent); color: #fff; }
.btn-primary:hover { background: var(--accent-hover); }
.btn-primary:disabled { background: var(--disabled-bg); color: var(--disabled-text); cursor: not-allowed; }

.btn-secondary { background: transparent; color: var(--text-primary); border: 1.5px solid var(--border-light); }
.btn-secondary:hover { background: var(--accent); color: #fff; }


/* Transcript side */
.transcript-side {
    /* Overlay, not a column. As a flex sibling it took ~360px off .camera-side
       every time it opened, which shrank the camera; as an overlay the camera
       keeps the size it has whether the transcript is up or not. */
    position: absolute;
    top: 8px;
    right: 8px;
    bottom: 8px;
    width: min(360px, 42%);
    z-index: 30;
    background: var(--bg-card);
    border-radius: 20px;
    display: flex;
    flex-direction: column;
    border: 1px solid var(--border-light);
    box-shadow: var(--shadow-card);
    min-height: 0;
    transition: background 0.3s, border-color 0.3s;
}

.transcript-side.collapsed {
    display: none !important;
}

.transcript-toggle {
    position: absolute;
    right: 16px;
    top: 50%;
    transform: translateY(-50%);
    background: var(--bg-card);
    border: 1px solid var(--border-light);
    color: var(--accent);
    width: 28px;
    height: 56px;
    border-radius: 14px;
    cursor: pointer;
    display: flex;
    align-items: center;
    justify-content: center;
    font-size: 0.8rem;
    transition: all 0.2s;
    z-index: 10;
}
.transcript-toggle:hover {
    border-color: #5b4fe6;
    color: var(--text-primary);
}
.transcript-toggle.show-btn {
    position: fixed;
    right: 12px;
    top: 50%;
    border-radius: 8px 0 0 8px;
}

.transcript-title {
    padding: 14px 18px;
    font-weight: 600;
    font-size: 0.9rem;
    color: var(--text-primary);
    border-bottom: 1px solid var(--border-subtle);
    text-transform: uppercase;
    letter-spacing: 0.5px;
    display: flex;
    justify-content: space-between;
    align-items: center;
}
.transcript-collapse-btn {
    background: var(--bg-chip);
    border: none;
    color: var(--accent);
    width: 24px;
    height: 24px;
    border-radius: 6px;
    cursor: pointer;
    font-size: 0.75rem;
    display: flex;
    align-items: center;
    justify-content: center;
    transition: all 0.2s;
}
.transcript-collapse-btn:hover {
    background: var(--bg-toggle-hover);
}

.transcript-msgs {
    flex: 1;
    overflow-y: auto;
    padding: 14px;
    display: flex;
    flex-direction: column;
    gap: 10px;
}

.msg {
    padding: 10px 14px;
    border-radius: 10px;
    font-size: 0.88rem;
    line-height: 1.5;
    max-width: 90%;
    animation: fadeUp 0.3s ease;
}

@keyframes fadeUp { from { opacity:0; transform:translateY(8px) } to { opacity:1; transform:translateY(0) } }

.msg.doctor { background: var(--msg-doctor-bg); color: var(--text-primary); align-self: flex-start; border-bottom-left-radius: 3px; }
.msg.patient { background: var(--msg-patient-bg); color: #fff; align-self: flex-end; border-bottom-right-radius: 3px; }

.msg .speaker {
    font-weight: 700;
    font-size: 0.65rem;
    text-transform: uppercase;
    letter-spacing: 0.8px;
    margin-bottom: 4px;
    display: flex;
    justify-content: space-between;
    align-items: center;
}
.msg.doctor .speaker { color: #d4a843; }
.msg.patient .speaker { color: rgba(255,255,255,0.6); }

/* 1.7 — Timestamp in messages */
.msg-timestamp {
    font-weight: 400;
    font-size: 0.6rem;
    opacity: 0.6;
    letter-spacing: 0;
    text-transform: none;
}

/* Backlog #8 — original English shown beneath a translated caption.
   The translated text gets the normal .msg-text styling; this is the
   small muted line under it. .msg-text-warn variant is used when the
   translate call failed and we fell back to showing English. */
.msg-text-original {
    margin-top: 6px;
    padding-top: 4px;
    border-top: 1px dashed rgba(255,255,255,0.18);
    font-size: 0.78rem;
    font-style: italic;
    opacity: 0.65;
    line-height: 1.3;
}
.msg.doctor .msg-text-original {
    border-top-color: rgba(0,0,0,0.18);
}
.msg-text-warn {
    color: #ffb86c;
    font-style: normal;
    opacity: 0.85;
}

/* ── Vocabulary Panel (1.8 improved) ── */
.vocab-panel {
    /* Anchor for the floating chip list. */
    position: relative;
}
.vocab-toggle-btn {
    background: var(--accent);
    border: none;
    color: #fff;
    padding: 8px 16px;
    border-radius: 10px;
    font-size: 0.75rem;
    font-weight: 600;
    cursor: pointer;
    display: inline-flex;
    align-items: center;
    gap: 6px;
    transition: all 0.2s;
    letter-spacing: 0.3px;
}
.vocab-toggle-btn:hover { background: var(--accent-hover); }
.vocab-toggle-btn .vocab-arrow { transition: transform 0.2s; display: inline-block; font-size: 0.6rem; }
.vocab-toggle-btn.open .vocab-arrow { transform: rotate(180deg); }
.vocab-chip-container {
    display: none;
    flex-wrap: wrap;
    gap: 5px;
    /* Floats above the button row rather than expanding it -- opening the list
     * must not shrink the camera. */
    position: absolute;
    bottom: calc(100% + 8px);
    left: 0;
    width: max(320px, 40vw);
    max-height: 46vh;
    overflow-y: auto;
    z-index: 40;
    padding: 10px;
    background: var(--bg-card);
    border: 1px solid var(--border-light);
    border-radius: 12px;
    box-shadow: 0 10px 30px rgba(0, 0, 0, 0.28);
}
.vocab-chip-container.open { display: flex; }
.vocab-chip {
    background: var(--bg-chip);
    border: none;
    color: var(--text-primary);
    padding: 3px 9px;
    border-radius: 12px;
    font-size: 0.68rem;
    font-weight: 500;
    white-space: nowrap;
    letter-spacing: 0.3px;
}
.vocab-chip.cat-medical { color: #8bc78b; background: rgba(139,199,139,0.1); }
.vocab-chip.cat-action { color: #8ba8e8; background: rgba(139,168,232,0.1); }
.vocab-chip.cat-question { color: #b07518; background: rgba(196,135,42,0.1); }
.vocab-chip.cat-descriptor { color: #c08ae8; background: rgba(192,138,232,0.1); }
.vocab-chip.cat-time { color: #6cc0d4; background: rgba(108,192,212,0.1); }
.vocab-chip.cat-general { color: var(--text-muted); background: var(--bg-chip); }
.vocab-chip-container::-webkit-scrollbar { width: 4px; }
.vocab-chip-container::-webkit-scrollbar-thumb { background: var(--scrollbar-thumb); border-radius: 2px; }
.vocab-chip-container::-webkit-scrollbar-track { background: transparent; }

/* ══════════════════════════════════════════
   HOME / SCENARIO SELECTION
   ══════════════════════════════════════════ */
.home-screen {
    max-width: 900px;
    margin: 0 auto;
    padding: 0 20px 32px;
}
.home-title {
    text-align: center;
    margin-bottom: 8px;
}
.home-title h1 {
    font-size: 2.2rem;
    font-weight: 700;
    color: var(--text-primary);
    letter-spacing: -0.5px;
}
.home-title h1 em { color: var(--text-primary); font-style: normal; }
.home-title h1 em span { color: var(--accent); }
.home-subtitle {
    text-align: center;
    color: var(--text-muted);
    font-size: 0.95rem;
    margin-bottom: 36px;
}
.home-section-label {
    color: var(--accent);
    font-size: 0.7rem;
    text-transform: uppercase;
    letter-spacing: 1.5px;
    font-weight: 600;
    margin-bottom: 12px;
    padding-left: 4px;
}
.scenario-grid {
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(250px, 1fr));
    gap: 12px;
    margin-bottom: 28px;
}
.scenario-card {
    background: var(--bg-card);
    border: 1px solid var(--border-light);
    border-radius: 18px;
    padding: 20px 22px;
    cursor: pointer;
    transition: all 0.3s ease;
    display: flex;
    align-items: flex-start;
    gap: 14px;
    box-shadow: var(--shadow-card);
}
.scenario-card:hover {
    transform: translateY(-4px);
    border-color: rgba(196,135,42,0.3);
    box-shadow: var(--shadow-hover);
}
.scenario-card.featured {
    background: var(--accent-gradient);
    color: #fff;
    border: none;
}
.scenario-card.featured:hover {
    box-shadow: 0 12px 32px rgba(196,135,42,0.3);
}
.scenario-icon {
    font-size: 1.6rem;
    flex-shrink: 0;
    width: 42px;
    height: 42px;
    text-align: center;
    line-height: 42px;
    background: rgba(196,135,42,0.1);
    border-radius: 12px;
}
.scenario-card.featured .scenario-icon {
    background: rgba(255,255,255,0.2);
}
.scenario-icon .ico {
    display: block;
    font-size: 1.3rem;
    color: var(--accent);
    line-height: 42px;
}
.scenario-card.featured .scenario-icon .ico {
    color: #fff;
}
.scenario-info h3 {
    font-size: 1.05rem;
    font-weight: 700;
    color: var(--text-primary);
    margin-bottom: 5px;
    letter-spacing: 0.2px;
}
.scenario-info p {
    font-size: 0.78rem;
    color: var(--text-secondary);
    line-height: 1.5;
}
.scenario-card.featured .scenario-info h3 { color: #fff; }
.scenario-card.featured .scenario-info p { color: rgba(255,255,255,0.7); }
.scenario-badge {
    display: inline-block;
    font-size: 0.6rem;
    padding: 3px 10px;
    border-radius: 980px;
    font-weight: 600;
    text-transform: uppercase;
    letter-spacing: 0.5px;
    margin-top: 6px;
}
.badge-healthcare { background: rgba(196,135,42,0.12); color: var(--accent); }
.badge-generic { background: var(--bg-chip); color: var(--text-muted); }
.scenario-card.featured .badge-healthcare { background: rgba(255,255,255,0.2); color: #fff; }

/* ── Coming-soon scenario card (1.4) ── */
.scenario-card.coming-soon {
    opacity: 0.45;
    cursor: not-allowed;
    pointer-events: none;
    position: relative;
}
.badge-coming { background: rgba(100,80,60,0.06); color: rgba(100,80,60,0.35); }
.badge-training { background: rgba(240,160,80,0.1); color: #d4972e; }
/* "Start here" badge for recommended first scenario */
.badge-start-here {
    background: rgba(76,175,128,0.15);
    color: var(--success);
    animation: pulse 2s infinite;
}
.scenario-card.featured .badge-start-here {
    background: rgba(255,255,255,0.25);
    color: #fff;
}

/* ── Live scenario cards: compact list style ── */
.live-card-style .scenario-card {
    border-radius: 12px;
    padding: 14px 16px;
    background: transparent;
    border: 1px solid var(--border-light);
    box-shadow: none;
}
.live-card-style .scenario-card:hover {
    background: var(--bg-card);
    transform: none;
    box-shadow: var(--shadow-card);
}
.live-card-style .scenario-card.featured {
    background: transparent;
    border: 1px solid var(--accent);
    color: var(--text-primary) !important;
}
.live-card-style .scenario-card .scenario-info h3,
.live-card-style .scenario-card.featured .scenario-info h3 { color: var(--text-primary) !important; }
.live-card-style .scenario-card .scenario-info p,
.live-card-style .scenario-card.featured .scenario-info p { color: var(--text-secondary) !important; }
.live-card-style .scenario-card.featured .scenario-icon { background: rgba(196,135,42,0.1); }
.live-card-style .scenario-card.featured .scenario-icon .ico { color: var(--accent); }
.live-card-style .scenario-card.featured:hover {
    background: var(--bg-card);
    box-shadow: var(--shadow-card);
}

/* ── Mode Toggle ── */
.mode-toggle-row {
    text-align: center;
    margin-bottom: 28px;
}
.mode-toggle {
    display: inline-flex;
    background: var(--bg-chip);
    border: none;
    border-radius: 12px;
    overflow: hidden;
    margin-bottom: 8px;
    padding: 3px;
    gap: 2px;
}
.mode-btn {
    background: transparent;
    border: none;
    color: var(--text-muted);
    padding: 10px 24px;
    font-size: 0.85rem;
    font-weight: 600;
    cursor: pointer;
    transition: all 0.3s ease;
    display: flex;
    align-items: center;
    gap: 8px;
    border-radius: 10px;
}
.mode-btn.active {
    background: var(--accent);
    color: #fff;
}
.mode-btn:hover:not(.active) {
    color: var(--text-primary);
}
.mode-icon { font-size: 1rem; }
.mode-btn-disabled,
.mode-btn[disabled] {
    opacity: 0.4;
    cursor: not-allowed;
    pointer-events: none;
}
.mode-description {
    color: var(--text-dimmed);
    font-size: 0.75rem;
}

/* ══════════════════════════════════════════
   SIDEBAR NAV (desktop) / BOTTOM TAB BAR (mobile)
   ══════════════════════════════════════════ */
/* Extra vertical space the nav costs a viewport-sized pane BELOW the 84px
 * sticky header. Now 0 everywhere: on tablet/desktop the nav rides inside the
 * header's title row (no separate bar), and on phones it's a FIXED bottom tab
 * bar -- neither adds a row above the content. Kept as a token so the many
 * calc(100vh - 84px - var(--topnav-h)) panes reclaim the old 52px automatically. */
:root { --topnav-h: 0px; }

.home-layout {
    display: flex;
    /* Column on every size now: the nav is a bar above the content on
     * tablet/desktop and a fixed bottom tab bar on phones -- never a side rail. */
    flex-direction: column;
    min-height: calc(100vh - 84px);
}

.sidebar {
    /* Horizontal menu bar, sticky directly beneath the app header. The
     * max-width:768px block further down turns this back into a fixed bottom
     * tab bar for phones; keep the two in sync. */
    width: 100%;
    box-sizing: border-box;
    /* Explicit height so --topnav-h is authoritative rather than a guess at
     * what the padding and font size happen to add up to. */
    height: var(--topnav-h);
    flex-shrink: 0;
    background: #1a1612;
    padding: 0 14px;
    display: flex;
    flex-direction: row;
    align-items: center;
    justify-content: center;
    gap: 4px;
    position: sticky;
    top: 84px;
    /* Below the header (z-index 100) so it scrolls under nothing, above content. */
    z-index: 80;
    overflow-x: auto;
    overflow-y: hidden;
}
[data-theme="dark"] .sidebar {
    background: #121010;
}

.sidebar-item {
    display: flex;
    align-items: center;
    gap: 10px;
    padding: 10px 12px;
    border: none;
    background: transparent;
    border-radius: 10px;
    cursor: pointer;
    font-size: 0.85rem;
    font-weight: 500;
    color: rgba(255,255,255,0.5);
    transition: all 0.2s;
    text-align: left;
    /* Row layout: size to content instead of filling the bar. The mobile block
     * overrides this with flex:1 so the bottom tabs share the width evenly. */
    width: auto;
    flex: 0 0 auto;
    white-space: nowrap;
}
.sidebar-item:hover {
    background: rgba(255,255,255,0.08);
    color: rgba(255,255,255,0.85);
}
.sidebar-item.active {
    background: #5b9bd5;
    color: #fff;
    font-weight: 600;
}

.sidebar-icon {
    font-size: 1rem;
    width: 20px;
    text-align: center;
    flex-shrink: 0;
}
.sidebar-label {
    white-space: nowrap;
}
.sidebar-spacer {
    flex: 1;
}

/* ── Settings page (backlog #7) ────────────────────────────────── */
.settings-page {
    max-width: 760px;
    margin: 0 auto;
    padding: 8px 20px 40px;
    display: flex;
    flex-direction: column;
    gap: 22px;
}
.settings-section {
    background: var(--bg-card);
    border: 1px solid var(--border-light);
    border-radius: 14px;
    padding: 16px 20px;
}
.settings-section h3 {
    margin: 0 0 14px;
    color: var(--text-primary);
    font-size: 1.05rem;
    letter-spacing: 0.3px;
    text-transform: uppercase;
    font-weight: 700;
}
.settings-row {
    display: flex;
    align-items: flex-start;
    justify-content: space-between;
    gap: 16px;
    padding: 12px 0;
    border-top: 1px solid var(--border-subtle);
}
.settings-row:first-of-type { border-top: none; padding-top: 0; }
.settings-row-text {
    flex: 1;
    min-width: 0;
}
.settings-row-label {
    color: var(--text-primary);
    font-weight: 600;
    font-size: 0.92rem;
    margin-bottom: 4px;
}
.settings-row-desc {
    color: var(--text-muted);
    font-size: 0.78rem;
    line-height: 1.4;
}
.settings-row-desc code {
    background: rgba(255,255,255,0.07);
    padding: 1px 5px;
    border-radius: 4px;
    font-size: 0.85em;
}
.settings-toggle {
    display: flex;
    align-items: center;
    gap: 6px;
    font-size: 0.85rem;
    color: var(--text-secondary);
    cursor: pointer;
    flex-shrink: 0;
    margin-top: 2px;
}
.settings-text-input,
.settings-number-input {
    flex-shrink: 0;
    width: 140px;
    padding: 7px 9px;
    font-size: 0.88rem;
    /* Theme-aware: bg + text follow whichever theme is active. The old
     * hardcoded #1f1b18 fallback collided with --text-primary in light
     * theme (dark text on dark bg = invisible). */
    background: var(--bg-primary);
    border: 1px solid var(--border-light);
    border-radius: 6px;
    color: var(--text-primary);
    text-align: center;
    outline: none;
    transition: border-color 0.15s, opacity 0.15s;
}
.settings-text-input:focus,
.settings-number-input:focus {
    border-color: var(--accent);
}
.settings-text-input:disabled,
.settings-number-input:disabled,
.settings-toggle input:disabled {
    opacity: 0.55;
    cursor: not-allowed;
}
.settings-readonly {
    opacity: 0.55 !important;
    cursor: not-allowed;
}
.settings-edit-bar {
    display: flex;
    align-items: center;
    gap: 8px;
    flex-shrink: 0;
}
.settings-edit-bar .btn {
    padding: 8px 18px;
    font-size: 0.85rem;
}
.settings-page.editing .settings-section {
    /* Subtle ring so the user knows edit mode is active. */
    box-shadow: 0 0 0 1px var(--accent);
}
.settings-stats-row {
    display: flex;
    gap: 12px;
    margin-top: 4px;
}
.settings-stat-card {
    flex: 1;
    background: var(--bg-primary);
    border: 1px solid var(--border-light);
    border-radius: 10px;
    padding: 14px 12px;
    text-align: center;
}
.settings-stat-value {
    font-size: 1.6rem;
    font-weight: 700;
    color: var(--accent);
    line-height: 1.1;
}
.settings-stat-label {
    margin-top: 4px;
    font-size: 0.72rem;
    color: var(--text-muted);
    text-transform: uppercase;
    letter-spacing: 0.6px;
}
.settings-footer-note {
    color: var(--text-muted);
    font-size: 0.78rem;
    font-style: italic;
    padding: 0 8px;
}

/* ── Live nav sub-menu (mode selector, hover-revealed) ─────────── */
/* The sub-menu is appended to document.body and positioned via JS
   (showNavLiveSubmenu) so it can't be clipped by .sidebar's overflow.
   Hover detection is JS-driven on .sidebar-item-wrap + the submenu. */
.sidebar-item-wrap {
    position: relative;
}
.sidebar-mode-chip {
    margin-left: auto;
    font-size: 0.65rem;
    font-weight: 600;
    padding: 2px 7px;
    border-radius: 8px;
    background: rgba(255,255,255,0.12);
    color: rgba(255,255,255,0.85);
    letter-spacing: 0.3px;
}
.sidebar-item.active .sidebar-mode-chip {
    background: rgba(0,0,0,0.18);
    color: #fff;
}
.sidebar-submenu {
    position: fixed;
    min-width: 180px;
    background: #1a1612;
    border: 1px solid rgba(255,255,255,0.1);
    border-radius: 10px;
    padding: 6px;
    box-shadow: 0 8px 24px rgba(0,0,0,0.45);
    display: none;
    z-index: 1500;
}
[data-theme="dark"] .sidebar-submenu {
    background: #121010;
}
.sidebar-submenu.is-open {
    display: block;
}
.sidebar-submenu-item {
    display: flex;
    align-items: center;
    gap: 10px;
    width: 100%;
    padding: 8px 10px;
    background: transparent;
    border: none;
    border-radius: 7px;
    color: rgba(255,255,255,0.75);
    font-size: 0.82rem;
    font-weight: 500;
    cursor: pointer;
    text-align: left;
}
.sidebar-submenu-item:hover:not(.sidebar-submenu-item-disabled) {
    background: rgba(255,255,255,0.08);
    color: #fff;
}
.sidebar-submenu-item.active {
    background: rgba(91,155,213,0.22);
    color: #fff;
}
.sidebar-submenu-item-disabled,
.sidebar-submenu-item[disabled] {
    opacity: 0.45;
    cursor: not-allowed;
}
.sidebar-submenu-tag {
    margin-left: auto;
    font-size: 0.62rem;
    padding: 1px 6px;
    border-radius: 6px;
    background: rgba(255,255,255,0.1);
    color: rgba(255,255,255,0.6);
    letter-spacing: 0.4px;
    text-transform: uppercase;
}

.home-content {
    flex: 1;
    min-width: 0;
    overflow-y: auto;
}

/* ── Back Button ── */
.back-btn {
    background: var(--bg-chip);
    border: none;
    color: var(--text-secondary);
    padding: 6px 14px;
    border-radius: 8px;
    font-size: 0.75rem;
    cursor: pointer;
    transition: all 0.2s;
    display: flex;
    align-items: center;
    gap: 6px;
}
.back-btn:hover {
    background: var(--bg-toggle-hover);
    color: var(--text-primary);
}

/* ── Training Sub-tabs ── */
.training-subtabs {
    display: flex;
    background: var(--bg-chip);
    border: none;
    border-radius: 12px;
    overflow: hidden;
    margin: 0 auto 28px;
    width: fit-content;
    padding: 3px;
    gap: 2px;
}
.subtab-btn {
    background: transparent;
    border: none;
    color: var(--text-muted);
    padding: 9px 22px;
    font-size: 0.82rem;
    font-weight: 600;
    cursor: pointer;
    transition: all 0.3s ease;
    letter-spacing: 0.3px;
    border-radius: 10px;
}
.subtab-btn.active {
    background: var(--accent);
    color: #fff;
}
.subtab-btn:hover:not(.active) { color: var(--text-primary); }

/* ── Caption Video / Live Captions screen ── */
.live-captions-layout {
    display: none;
    flex-direction: column;
    height: calc(100vh - 84px - var(--topnav-h));
    max-width: 820px;
    margin: 0 auto;
    padding: 16px;
    gap: 14px;
}
.lc-topbar { display: flex; align-items: center; justify-content: space-between; }
.lc-camera-box {
    background: var(--bg-card);
    border: 1px solid var(--border-light);
    border-radius: 20px;
    overflow: hidden;
    flex-shrink: 0;
    box-shadow: var(--shadow-card);
}
.lc-camera-view {
    width: 100%; height: 200px;
    position: relative; background: var(--camera-bg);
}
.lc-camera-view video { width: 100%; height: 100%; object-fit: cover; display: block; }
.lc-camera-placeholder {
    position: absolute; inset: 0;
    display: flex; align-items: center; justify-content: center;
    color: var(--text-dimmed); font-size: 0.85rem;
}
.lc-status-bar {
    padding: 8px 14px; font-size: 0.75rem;
    color: var(--text-dimmed); border-top: 1px solid var(--border-subtle); min-height: 34px;
}
.lc-caption-area {
    flex: 1;
    background: var(--bg-card);
    border: 1px solid var(--border-light);
    border-radius: 20px;
    padding: 20px;
    overflow-y: auto;
    display: flex;
    flex-direction: column;
    gap: 14px;
    min-height: 0;
}
.lc-section-label {
    font-size: 0.65rem; color: var(--accent);
    text-transform: uppercase; letter-spacing: 1.5px; font-weight: 600;
}
.lc-paragraph {
    font-size: 1.05rem; line-height: 1.85; color: var(--text-primary); min-height: 48px;
}
.lc-chunk { transition: color 0.5s; }
.lc-chunk.fresh { color: var(--accent); }
.lc-gloss-buffer {
    display: flex; gap: 8px; flex-wrap: wrap; align-items: center;
    padding-top: 10px; border-top: 1px solid var(--border-subtle);
}
.lc-buffer-label {
    font-size: 0.65rem; color: var(--accent);
    text-transform: uppercase; letter-spacing: 1px; font-weight: 600;
}
.lc-gloss-chip {
    background: rgba(196,135,42,0.12); color: var(--accent); border: none;
    padding: 4px 12px; border-radius: 980px; font-size: 0.75rem; font-weight: 600;
}
.lc-thinking {
    display: none; font-size: 0.75rem; color: var(--accent); font-style: italic;
}
.lc-controls { display: flex; gap: 10px; align-items: center; flex-shrink: 0; }

/* ══════════════════════════════════════════
   LIVE DEMO -- Camera + Caption
   ══════════════════════════════════════════ */
#phaseLiveDemo {
    display: none;
    flex-direction: column;
    align-items: center;
    padding: 24px;
    height: calc(100vh - 84px - var(--topnav-h));
    background: var(--bg-primary);
}
.live-demo-branding {
    text-align: center;
    margin-bottom: 20px;
}
.live-demo-branding .brand-name {
    font-size: 4.5rem;
    font-weight: 800;
    letter-spacing: 2px;
    color: var(--logo-dark);   /* wordmark is one token now (no two-tone spans) */
}
.live-demo-branding .brand-name .brand-sign { color: var(--logo-dark); }
.live-demo-branding .brand-name .brand-bridge { color: var(--accent); }
.live-demo-branding .brand-tagline {
    font-size: 0.9rem;
    color: var(--text-dimmed);
    margin-top: 6px;
    letter-spacing: 2px;
    text-transform: uppercase;
    font-weight: 500;
}
.live-demo-container {
    max-width: 700px;
    width: 100%;
    display: flex;
    flex-direction: column;
    gap: 20px;
    flex: 1;
}
.live-demo-topbar {
    display: flex;
    align-items: center;
    justify-content: space-between;
}
.live-demo-topbar .back-btn {
    border-radius: 980px;
}
.live-demo-camera {
    position: relative;
    width: 100%;
    aspect-ratio: 4/3;
    background: #000;
    border-radius: 24px;
    overflow: hidden;
    box-shadow: 0 8px 40px rgba(0,0,0,0.5);
}
.live-demo-camera video {
    width: 100%;
    height: 100%;
    object-fit: cover;
    display: block;
}
.live-demo-camera .placeholder {
    position: absolute;
    inset: 0;
    display: flex;
    align-items: center;
    justify-content: center;
    color: rgba(255,255,255,0.25);
    font-size: 0.95rem;
}
.live-demo-caption-box {
    background: var(--bg-card);
    border-radius: 20px;
    padding: 32px 36px;
    text-align: center;
}
.live-demo-caption-label {
    font-size: 0.65rem;
    color: var(--accent);
    text-transform: uppercase;
    letter-spacing: 3px;
    font-weight: 600;
    margin-bottom: 14px;
}
.live-demo-caption-text {
    font-size: 2rem;
    font-weight: 600;
    color: var(--text-primary);
    line-height: 1.5;
    letter-spacing: -0.3px;
}

/* ══════════════════════════════════════════
   BREAKDOWN PHASE
   ══════════════════════════════════════════ */
#phaseBreakdown {
    display: none;
    flex-direction: column;
    padding: 24px;
    max-width: 960px;
    margin: 0 auto;
    width: 100%;
}
.breakdown-header { text-align: center; margin-bottom: 24px; }
.breakdown-header h2 { font-size: 1.4rem; color: var(--text-primary); margin-bottom: 6px; font-weight: 700; letter-spacing: -0.3px; }
.breakdown-header p { color: var(--text-muted); font-size: 0.9rem; }

.sentence-tabs { display: flex; gap: 8px; margin-bottom: 20px; flex-wrap: wrap; }
.sentence-tab {
    background: var(--bg-chip); border: none; color: var(--text-muted);
    border-radius: 980px; padding: 8px 18px; cursor: pointer; font-size: 0.85rem;
    transition: all 0.3s ease;
}
.sentence-tab.active { background: var(--accent); color: #fff; }

.pipeline-section { display: none; }
.pipeline-section.active { display: block; }

.pipeline-step {
    background: var(--bg-card); border: 1px solid var(--border-light); border-radius: 12px;
    padding: 20px; margin-bottom: 16px; box-shadow: 0 1px 4px rgba(0,0,0,0.04);
}
.step-header { display: flex; align-items: center; gap: 12px; margin-bottom: 14px; }
.step-number {
    width: 28px; height: 28px; border-radius: 50%; background: #6c63ff;
    color: #fff; font-weight: 700; font-size: 0.85rem;
    display: flex; align-items: center; justify-content: center; flex-shrink: 0;
}
.step-title { font-weight: 600; color: #5b4fe6; font-size: 0.95rem; }

.segment-grid { display: flex; flex-wrap: wrap; gap: 12px; }
.segment-card, .prediction-card {
    background: var(--bg-chip); border: 1px solid var(--border-light); border-radius: 8px;
    overflow: hidden; width: 150px; flex-shrink: 0;
}
.segment-card video, .prediction-card video {
    width: 100%; height: 100px; object-fit: cover; display: block;
}
.seg-label { padding: 4px 8px; font-size: 0.75rem; color: var(--text-secondary); text-align: center; }

.pred-info { padding: 8px; }
.pred-top1 { font-size: 0.8rem; font-weight: 700; margin-bottom: 4px; }
.confidence-bar { height: 4px; background: var(--bg-toggle); border-radius: 2px; margin-bottom: 6px; }
.conf-fill { height: 100%; border-radius: 2px; }
.top-k-list { font-size: 0.7rem; color: var(--text-secondary); }
.top-k-item { display: flex; justify-content: space-between; padding: 1px 0; }

.construction-display {
    display: flex; align-items: center; gap: 16px; flex-wrap: wrap;
}
.construct-box {
    flex: 1; min-width: 180px; background: var(--bg-chip); border-radius: 8px; padding: 14px;
}
.construct-box.raw { border: 1px solid #e0d0b0; }
.construct-box.llm { border: 1px solid #b0e0c0; }
.box-label { font-size: 0.7rem; color: var(--text-secondary); text-transform: uppercase; letter-spacing: 1px; margin-bottom: 6px; }
.box-content { font-size: 1rem; font-weight: 600; color: var(--text-primary); }
.construct-box.llm .box-content { color: #1a9e5c; }
.construct-arrow { font-size: 1.5rem; color: #5b4fe6; flex-shrink: 0; }

.breakdown-nav {
    display: flex; gap: 12px; justify-content: center; margin-top: 24px; flex-wrap: wrap;
}
.breakdown-nav button {
    padding: 10px 24px; border-radius: 8px; border: 1px solid var(--border-light);
    background: var(--bg-chip); color: var(--text-secondary); cursor: pointer; font-size: 0.9rem; transition: all 0.2s;
}
.breakdown-nav button:hover { border-color: #5b4fe6; color: var(--text-primary); background: #e8e6ff; }
.breakdown-nav button.primary { background: #5b4fe6; border-color: #5b4fe6; color: #fff; }

#btnHowItWorks {
    display: none; padding: 12px 28px; border-radius: 10px;
    background: linear-gradient(135deg, #5b4fe6, #8b6cf0);
    color: #fff; font-size: 0.95rem; font-weight: 600;
    border: none; cursor: pointer; margin-top: 8px; transition: opacity 0.2s;
}
#btnHowItWorks:hover { opacity: 0.85; }

/* ══════════════════════════════════════════
   SIGN BANK (2.1)
   ══════════════════════════════════════════ */
#signBankScreen {
    display: none;
}

.sb-hero {
    text-align: center;
    margin-bottom: 32px;
}
.sb-hero-icon {
    font-size: 2.8rem;
    margin-bottom: 12px;
}
.sb-hero h2 {
    font-size: 1.6rem;
    font-weight: 700;
    color: var(--text-primary);
    margin-bottom: 8px;
    letter-spacing: -0.3px;
}
.sb-hero p {
    font-size: 0.9rem;
    color: var(--text-secondary);
    line-height: 1.6;
    max-width: 560px;
    margin: 0 auto;
}
.sb-hero .sb-stat {
    display: inline-flex;
    align-items: center;
    gap: 6px;
    margin-top: 14px;
    background: var(--bg-chip);
    padding: 6px 14px;
    border-radius: 980px;
    font-size: 0.78rem;
    color: var(--accent);
    font-weight: 600;
}

.sb-search-row {
    display: flex;
    align-items: center;
    gap: 12px;
    margin-bottom: 24px;
}
.sb-search {
    flex: 1;
    padding: 10px 16px;
    border: 1px solid var(--border-light);
    border-radius: 12px;
    font-size: 0.9rem;
    background: var(--bg-card);
    color: var(--text-primary);
    outline: none;
    transition: border-color 0.2s;
}
.sb-search:focus {
    border-color: var(--accent);
}
.sb-search::placeholder {
    color: var(--text-dimmed);
}
.sb-count {
    font-size: 0.78rem;
    color: var(--text-muted);
    white-space: nowrap;
}

.sb-grid {
    display: flex;
    flex-direction: column;
    gap: 28px;
}

/* Each SECTOR ("Libraries & Community", "Health & Pharmacy") is one bounded
   panel that groups all its counters/scenarios together, instead of a flat list.
   A faint tinted ground (not solid white) lets the white sign cards inside pop. */
.sb-sector-panel {
    background: var(--bg-chip);            /* warm, theme-aware -- reads as a panel in both modes */
    border: 1px solid var(--border-medium);
    border-radius: 16px;
    padding: 8px 18px 18px;
}
/* Scenarios stack inside their sector panel -- space them; no per-scenario box. */
.sb-sector-panel .sb-group + .sb-group { margin-top: 22px; }
/* The sector heading now lives inside the panel. */
.sb-sector-panel .sb-section-label { margin: 4px 0 12px !important; }

.sb-group-header {
    display: flex;
    align-items: center;
    gap: 10px;
    margin-bottom: 12px;
    padding-bottom: 8px;
    border-bottom: 1px solid var(--border-subtle);
}
.sb-group-icon {
    width: 32px;
    height: 32px;
    border-radius: 8px;
    display: flex;
    align-items: center;
    justify-content: center;
    font-size: 1rem;
    flex-shrink: 0;
}
.sb-group-label {
    font-size: 0.95rem;
    font-weight: 700;
    color: var(--text-primary);
    letter-spacing: 0.2px;
}
.sb-group-count {
    font-size: 0.72rem;
    color: var(--text-muted);
    margin-left: auto;
}

.sb-group-grid {
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(170px, 1fr));
    gap: 14px;
}

/* "Practice another sign" picker shown below the 4-preview cap.
 * Native <select> so it scales to thousands of options without
 * any rendering cost. Type-ahead works in all browsers. */
.sb-group-picker-row {
    margin-top: 12px;
    display: flex;
    justify-content: flex-start;
}
.sb-group-picker {
    width: 100%;
    max-width: 360px;
    padding: 9px 12px;
    font-size: 0.88rem;
    background: var(--bg-primary);
    border: 1px solid var(--border-light);
    border-radius: 8px;
    color: var(--text-primary);
    cursor: pointer;
}
.sb-group-picker:focus {
    outline: none;
    border-color: var(--accent);
}

.sb-card {
    background: var(--bg-card);
    border: 1px solid var(--border-light);
    border-radius: 14px;
    overflow: hidden;
    transition: all 0.3s ease;
    cursor: pointer;
    box-shadow: var(--shadow-card);
}
.sb-card:hover {
    transform: translateY(-3px);
    box-shadow: var(--shadow-hover);
    border-color: rgba(196,135,42,0.3);
}
.sb-card-video {
    width: 100%;
    aspect-ratio: 4/3;
    background: var(--camera-bg);
    display: flex;
    align-items: center;
    justify-content: center;
    position: relative;
    overflow: hidden;
}
.sb-card-video video {
    width: 100%;
    height: 100%;
    object-fit: cover;
    display: block;
}
.sb-card-video .sb-play-hint {
    position: absolute;
    inset: 0;
    display: flex;
    align-items: center;
    justify-content: center;
    background: rgba(0,0,0,0.3);
    color: #fff;
    font-size: 1.8rem;
    opacity: 0;
    transition: opacity 0.2s;
    pointer-events: none;
}
.sb-card:hover .sb-play-hint {
    opacity: 1;
}
.sb-card-label {
    padding: 10px 14px;
    display: flex;
    align-items: center;
    justify-content: space-between;
}
.sb-card-gloss {
    font-size: 0.9rem;
    font-weight: 700;
    color: var(--text-primary);
    text-transform: uppercase;
    letter-spacing: 0.5px;
}
.sb-card-cat {
    font-size: 0.65rem;
    padding: 2px 8px;
    border-radius: 980px;
    font-weight: 500;
}

.sb-card-actions {
    padding: 0 14px 10px;
}
.sb-practice-btn {
    width: 100%;
    padding: 6px 12px;
    border: 1px solid var(--border-light);
    border-radius: 8px;
    background: var(--bg-chip);
    color: var(--accent);
    font-size: 0.72rem;
    font-weight: 600;
    cursor: pointer;
    transition: all 0.2s;
    letter-spacing: 0.3px;
}
.sb-practice-btn:hover {
    background: var(--accent);
    color: #fff;
    border-color: var(--accent);
}

.sb-empty {
    text-align: center;
    padding: 48px 20px;
    color: var(--text-muted);
    font-size: 0.9rem;
}

/* ══════════════════════════════════════════
   PRACTICE MODE (2.6)
   ══════════════════════════════════════════ */
/* Practice session layout (inline within Sign Bank) */
.prac-topbar {
    display: flex;
    align-items: center;
    justify-content: space-between;
    margin-bottom: 16px;
}
.prac-score {
    font-size: 0.85rem;
    font-weight: 600;
    color: var(--accent);
    background: var(--bg-chip);
    padding: 6px 14px;
    border-radius: 980px;
}

.prac-sign-name {
    text-align: center;
    font-size: 2rem;
    font-weight: 800;
    color: var(--text-primary);
    text-transform: uppercase;
    letter-spacing: 2px;
    margin-bottom: 20px;
}

.prac-split {
    display: grid;
    grid-template-columns: 1fr 1fr;
    gap: 16px;
    margin-bottom: 16px;
}

.prac-panel {
    display: flex;
    flex-direction: column;
    gap: 8px;
}
.prac-panel-label {
    font-size: 0.7rem;
    font-weight: 600;
    text-transform: uppercase;
    letter-spacing: 1.2px;
    color: var(--accent);
    text-align: center;
}
.prac-video-box {
    background: var(--camera-bg);
    border-radius: 16px;
    overflow: hidden;
    aspect-ratio: 4/3;
    position: relative;
}
/* Camera box gets larger, portrait-friendly on mobile */
.prac-camera-box {
    aspect-ratio: auto;
    min-height: 50vh;
}
/* Playback of a just-recorded capture -- BOTH the video and the pose render,
   which share #pracCapturedVideo.
   min-height matches .prac-camera-box exactly. Without it the live box is taller
   at the same width (50vh floor) and object-fit:cover crops harder to fill it,
   so the recording looked zoomed IN relative to the playback. Same height rule +
   same cover = same crop = same zoom. */
.prac-captured-box { min-height: 50vh; }

.prac-video-box video {
    width: 100%;
    height: 100%;
    object-fit: cover;
    display: block;
}
.prac-camera-placeholder {
    position: absolute;
    inset: 0;
    display: flex;
    align-items: center;
    justify-content: center;
    color: rgba(255,255,255,0.3);
    font-size: 0.85rem;
    text-align: center;
    padding: 16px;
}
.prac-hint {
    font-size: 0.78rem;
    color: var(--text-muted);
    text-align: center;
}
.prac-rec-countdown {
    top: 10px;
    left: 10px;
}

.prac-controls {
    display: flex;
    justify-content: center;
    gap: 12px;
    margin-bottom: 16px;
}
.prac-record-btn {
    font-size: 1rem;
    padding: 14px 36px;
}

/* Feedback card */
.prac-feedback {
    background: var(--bg-card);
    border: 1px solid var(--border-light);
    border-radius: 16px;
    padding: 24px;
    text-align: center;
    box-shadow: var(--shadow-card);
    animation: fadeUp 0.3s ease;
}
.prac-feedback-icon {
    font-size: 2.4rem;
    margin-bottom: 8px;
}
.prac-feedback-text {
    font-size: 1.2rem;
    font-weight: 700;
    margin-bottom: 6px;
}
.prac-feedback-detail {
    font-size: 0.85rem;
    color: var(--text-secondary);
    line-height: 1.5;
}
.prac-feedback.correct {
    border-color: var(--success);
}
.prac-feedback.correct .prac-feedback-text { color: var(--success); }
.prac-feedback.incorrect {
    border-color: #f07070;
}
.prac-feedback.incorrect .prac-feedback-text { color: #f07070; }
.prac-feedback.close-match {
    border-color: #f0a050;
}
.prac-feedback.close-match .prac-feedback-text { color: #f0a050; }

/* Practice diagnostic panel — shows what was captured + model predictions */
.prac-diagnostic {
    text-align: left;
    background: var(--bg-page);
    border: 1px solid var(--border-light);
    border-radius: 10px;
    padding: 14px;
}
.prac-diag-row {
    /* Mirrors .prac-split exactly: the clip lands under the camera at the same
       width, the text under the reference. */
    display: grid;
    grid-template-columns: 1fr 1fr;
    gap: 16px;
    align-items: start;
}
.prac-diag-col { flex: 1; min-width: 0; }
.prac-diag-label {
    font-size: 0.72rem;
    text-transform: uppercase;
    letter-spacing: 1px;
    color: var(--text-muted);
    font-weight: 600;
    margin-bottom: 6px;
}
.prac-topk { display: flex; flex-direction: column; gap: 6px; }
.prac-topk-row {
    display: grid;
    grid-template-columns: 20px 1fr 100px 40px;
    gap: 8px;
    align-items: center;
    font-size: 0.78rem;
    padding: 4px 0;
}
.prac-topk-row.target { color: var(--accent); font-weight: 600; }
.prac-topk-rank { color: var(--text-muted); font-weight: 500; }
.prac-topk-gloss { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; color: var(--text-primary); }
.prac-topk-row.target .prac-topk-gloss { color: var(--accent); }
.prac-topk-bar {
    height: 6px;
    background: var(--bg-toggle);
    border-radius: 3px;
    overflow: hidden;
}
.prac-topk-fill {
    display: block;
    height: 100%;
    background: linear-gradient(90deg, #6c63ff, #4caf80);
    border-radius: 3px;
}
.prac-topk-row.target .prac-topk-fill {
    background: var(--accent);
}
.prac-topk-pct { text-align: right; color: var(--text-secondary); font-size: 0.72rem; }

@media (max-width: 640px) {
    .prac-diag-row { grid-template-columns: 1fr; }   /* stack on narrow screens */
}

/* ══════════════════════════════════════════
   USER PROFILE (2.12)
   ══════════════════════════════════════════ */
.profile-btn {
    background: var(--accent);
    border: none;
    color: #fff;
    width: 32px;
    height: 32px;
    border-radius: 50%;
    cursor: pointer;
    display: flex;
    align-items: center;
    justify-content: center;
    font-size: 0.85rem;
    font-weight: 700;
    transition: all 0.2s;
    text-transform: uppercase;
}
.profile-btn:hover { opacity: 0.85; }

.profile-panel-overlay {
    display: none;
    position: fixed;
    inset: 0;
    background: rgba(0,0,0,0.4);
    z-index: 400;
}
.profile-panel-overlay.visible { display: flex; justify-content: flex-end; }

.profile-panel {
    width: 340px;
    max-width: 90vw;
    height: 100%;
    background: var(--bg-card);
    box-shadow: -8px 0 32px rgba(0,0,0,0.15);
    padding: 28px 24px;
    overflow-y: auto;
    animation: slideInRight 0.25s ease;
    display: flex;
    flex-direction: column;
    gap: 20px;
}
@keyframes slideInRight { from { transform: translateX(100%) } to { transform: translateX(0) } }

.profile-header {
    display: flex;
    align-items: center;
    justify-content: space-between;
}
.profile-header h3 {
    font-size: 1.1rem;
    font-weight: 700;
    color: var(--text-primary);
}
.profile-close {
    background: var(--bg-chip);
    border: none;
    color: var(--text-muted);
    width: 28px;
    height: 28px;
    border-radius: 8px;
    cursor: pointer;
    font-size: 1rem;
    display: flex;
    align-items: center;
    justify-content: center;
}
.profile-close:hover { color: var(--text-primary); }

.profile-avatar-row {
    display: flex;
    align-items: center;
    gap: 14px;
}
.profile-avatar {
    width: 52px;
    height: 52px;
    border-radius: 50%;
    background: var(--accent);
    color: #fff;
    display: flex;
    align-items: center;
    justify-content: center;
    font-size: 1.4rem;
    font-weight: 800;
    text-transform: uppercase;
    flex-shrink: 0;
}
.profile-avatar-info {
    flex: 1;
}
.profile-avatar-info .profile-name-display {
    font-size: 1rem;
    font-weight: 600;
    color: var(--text-primary);
}
.profile-avatar-info .profile-joined {
    font-size: 0.72rem;
    color: var(--text-muted);
}

.profile-section-label {
    font-size: 0.68rem;
    font-weight: 600;
    text-transform: uppercase;
    letter-spacing: 1.2px;
    color: var(--accent);
    margin-bottom: 8px;
}

.profile-field {
    display: flex;
    flex-direction: column;
    gap: 6px;
}
.profile-field label {
    font-size: 0.78rem;
    font-weight: 600;
    color: var(--text-secondary);
}
.profile-field input,
.profile-field select {
    padding: 8px 12px;
    border: 1px solid var(--border-light);
    border-radius: 8px;
    font-size: 0.85rem;
    background: var(--bg-primary);
    color: var(--text-primary);
    outline: none;
    transition: border-color 0.2s;
}
.profile-field input:focus,
.profile-field select:focus {
    border-color: var(--accent);
}

/* Practice stats in profile */
.profile-stats {
    display: grid;
    grid-template-columns: 1fr 1fr 1fr;
    gap: 10px;
}
.profile-stat-card {
    background: var(--bg-chip);
    border-radius: 10px;
    padding: 12px;
    text-align: center;
}
.profile-stat-value {
    font-size: 1.3rem;
    font-weight: 800;
    color: var(--text-primary);
}
.profile-stat-label {
    font-size: 0.65rem;
    color: var(--text-muted);
    margin-top: 2px;
}

/* Recent scenarios */
.profile-recent-list {
    display: flex;
    flex-direction: column;
    gap: 6px;
}
.profile-recent-item {
    display: flex;
    align-items: center;
    gap: 10px;
    padding: 8px 10px;
    background: var(--bg-chip);
    border-radius: 8px;
    font-size: 0.82rem;
    color: var(--text-primary);
    cursor: pointer;
    transition: background 0.2s;
}
.profile-recent-item:hover { background: var(--bg-toggle-hover); }
.profile-recent-icon {
    width: 28px;
    height: 28px;
    border-radius: 6px;
    background: rgba(196,135,42,0.1);
    color: var(--accent);
    display: flex;
    align-items: center;
    justify-content: center;
    font-size: 0.8rem;
    flex-shrink: 0;
}
.profile-recent-label { flex: 1; font-weight: 500; }
.profile-recent-time { font-size: 0.65rem; color: var(--text-muted); }

.profile-clear-btn {
    background: none;
    border: 1px solid var(--border-light);
    color: var(--text-muted);
    padding: 8px 16px;
    border-radius: 8px;
    font-size: 0.78rem;
    cursor: pointer;
    transition: all 0.2s;
    align-self: flex-start;
}
.profile-clear-btn:hover {
    border-color: #f07070;
    color: #f07070;
}

/* Welcome back message on hero */
.hero-welcome {
    font-size: 1rem;
    color: var(--accent);
    font-weight: 600;
    margin-bottom: 8px;
}

/* ══════════════════════════════════════════
   CONVERSATION HISTORY (2.3)
   ══════════════════════════════════════════ */
#historyScreen {
    display: none;
    max-width: 960px;
    margin: 0 auto;
    padding: 24px 20px;
}
.hist-hero {
    text-align: center;
    margin-bottom: 28px;
}
.hist-hero-icon { font-size: 2.8rem; margin-bottom: 12px; }
.hist-hero h2 {
    font-size: 1.6rem;
    font-weight: 700;
    color: var(--text-primary);
    margin-bottom: 8px;
}
.hist-hero p {
    font-size: 0.9rem;
    color: var(--text-secondary);
    line-height: 1.6;
    max-width: 520px;
    margin: 0 auto;
}

.hist-list {
    display: flex;
    flex-direction: column;
    gap: 10px;
}
.hist-card {
    background: var(--bg-card);
    border: 1px solid var(--border-light);
    border-radius: 14px;
    padding: 16px 18px;
    cursor: pointer;
    transition: all 0.2s;
    box-shadow: var(--shadow-card);
    display: flex;
    align-items: center;
    gap: 14px;
}
.hist-card:hover {
    border-color: rgba(196,135,42,0.3);
    box-shadow: var(--shadow-hover);
}
.hist-card-icon {
    width: 40px;
    height: 40px;
    border-radius: 10px;
    background: rgba(196,135,42,0.1);
    color: var(--accent);
    display: flex;
    align-items: center;
    justify-content: center;
    font-size: 1.1rem;
    font-weight: 700;
    flex-shrink: 0;
}
.hist-card-info {
    flex: 1;
    min-width: 0;
}
.hist-card-title {
    font-size: 0.92rem;
    font-weight: 600;
    color: var(--text-primary);
    margin-bottom: 3px;
}
.hist-card-meta {
    font-size: 0.72rem;
    color: var(--text-muted);
    display: flex;
    gap: 10px;
}
.hist-card-preview {
    font-size: 0.78rem;
    color: var(--text-secondary);
    margin-top: 4px;
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}
.hist-card-actions {
    display: flex;
    gap: 6px;
    flex-shrink: 0;
}
.hist-delete-btn {
    background: none;
    border: none;
    color: var(--text-muted);
    cursor: pointer;
    font-size: 0.9rem;
    padding: 4px;
    border-radius: 4px;
    transition: color 0.2s;
}
.hist-delete-btn:hover { color: #f07070; }

/* History viewer (replaying a saved conversation) */
.hist-viewer {
    display: none;
    max-width: 700px;
    margin: 0 auto;
}
.hist-viewer-header {
    display: flex;
    align-items: center;
    justify-content: space-between;
    margin-bottom: 16px;
}
.hist-viewer-title {
    font-size: 1.1rem;
    font-weight: 700;
    color: var(--text-primary);
}
.hist-viewer-meta {
    font-size: 0.75rem;
    color: var(--text-muted);
    margin-bottom: 16px;
}
.hist-viewer-msgs {
    display: flex;
    flex-direction: column;
    gap: 10px;
    background: var(--bg-card);
    border: 1px solid var(--border-light);
    border-radius: 16px;
    padding: 20px;
    max-height: 60vh;
    overflow-y: auto;
}

.hist-empty {
    text-align: center;
    padding: 48px 20px;
    color: var(--text-muted);
    font-size: 0.9rem;
}

/* 1.12 — Breakdown link on home screen */
.home-breakdown-link {
    text-align: center;
    margin-top: 8px;
    padding-bottom: 20px;
}
.home-breakdown-link a {
    color: var(--accent);
    font-size: 0.82rem;
    font-weight: 500;
    text-decoration: none;
    cursor: pointer;
    transition: opacity 0.2s;
}
.home-breakdown-link a:hover { opacity: 0.7; }

/* ══════════════════════════════════════════
   RESPONSIVE -- Tablets (769px - 1024px)
   ══════════════════════════════════════════ */
@media (min-width: 769px) and (max-width: 1024px) {
    .app-header { padding: 8px 16px; gap: 6px; }
    .app-logo { font-size: 1.55rem; }
    .tab-intro { padding: 20px 16px 12px; }
    .tab-body { padding: 0 16px 16px; }


    .conversation-layout { padding: 12px; gap: 12px; }
    .transcript-side { min-width: 240px; max-width: 300px; }

    .home-screen { padding: 0 16px 24px; }
    .scenario-grid { grid-template-columns: repeat(auto-fill, minmax(220px, 1fr)); gap: 10px; }

    .live-demo-container { max-width: 600px; }
    .live-demo-caption-box { padding: 20px 24px; }
    .live-demo-caption-text { font-size: 1.4rem; }

    #phaseBreakdown { padding: 20px 16px; }
    .segment-card, .prediction-card { width: 130px; }
    .segment-card video, .prediction-card video { height: 85px; }

    .pip-self { width: 140px; height: 105px; }
}

/* ══════════════════════════════════════════
   RESPONSIVE -- Phones / Small Tablets (max 768px)
   ══════════════════════════════════════════ */
@media (max-width: 768px) {
    .app-header { padding: 8px 12px; gap: 4px; }
    .app-logo { font-size: 1.45rem; }

    /* Sidebar becomes bottom tab bar on mobile */
    .home-layout {
        flex-direction: column;
        min-height: auto;
    }
    .sidebar {
        width: 100%;
        height: auto;
        position: fixed;
        bottom: 0;
        left: 0;
        right: 0;
        top: auto;
        flex-direction: row;
        padding: 4px 6px;
        gap: 4px;
        border-right: none;
        border-top: none;
        z-index: 90;
        overflow-x: auto;
        overflow-y: hidden;
        justify-content: center;
        box-shadow: 0 -2px 16px rgba(0,0,0,0.2);
    }
    .sidebar-spacer { display: none; }
    .sidebar-item {
        flex-direction: column;
        gap: 2px;
        padding: 6px 8px;
        font-size: 0.65rem;
        min-width: 0;
        flex: 1;
        justify-content: center;
        text-align: center;
        border-radius: 8px;
    }
    .sidebar-icon { font-size: 1.1rem; width: auto; }
    .sidebar-label { white-space: nowrap; font-size: 0.62rem; }
    .home-content {
        padding-bottom: 68px; /* space for bottom bar */
    }
    .htog-btn { padding: 6px 12px; font-size: 0.7rem; min-height: 44px; display: inline-flex; align-items: center; }

    .tab-intro { padding: 18px 14px 10px; }
    .tab-body { padding: 0 14px 14px; }
    .tab-intro h2 { font-size: 1.15rem; }
    .tab-intro p { font-size: 0.82rem; }

    .conversation-layout {
        flex-direction: column;
        height: calc(100vh - 84px);
        padding: 8px;
        gap: 8px;
        /* Explicit full-width + border-box so padding doesn't push content
         * past the viewport and a child can't end up narrower than expected. */
        width: 100%;
        box-sizing: border-box;
    }
    /* Force the column-stack children to actually fill the cross-axis. The
     * align-items:stretch default *should* do this, but on some mobile
     * browsers an inherited width/align-self can leave a phantom whitespace
     * column on the right of the camera card. Explicit width:100% +
     * align-self:stretch is the belt-and-suspenders fix. */
    .camera-side, .camera-box, .camera-view {
        width: 100%;
        box-sizing: border-box;
        align-self: stretch;
    }
    .camera-side { flex: 1; min-height: 0; }
    .camera-view { min-height: 180px; }
    /* Mobile-only: paint the camera card itself dark to match the camera
     * area. On mobile the video letterbox / aspect mismatch was leaving
     * the white card background visible inside the frame (object-fit:cover
     * below doesn't always apply reliably to getUserMedia streams on iOS
     * Safari). Dark card + dark video bg = no visible white inside the
     * frame, regardless of which layer fails to cover. */
    .camera-box { background: var(--camera-bg); }
    /* On mobile portrait the camera stream is often portrait-oriented
     * (e.g. 720x1280 from getUserMedia on a phone in portrait). With
     * object-fit:contain the video letterboxes inside a wider container
     * and the gaps show the camera-box white card background through --
     * which reads as "camera shrunk to half with white on the side."
     * Switch to cover so the video fills the box (slight edge crop is
     * acceptable for signing; central handshapes still in frame), and
     * force the video to actually span camera-view in case iOS Safari
     * doesn't honor inset:0 + width/height:100% on a video element. */
    .camera-view > video {
        object-fit: cover;
        top: 0; right: 0; bottom: 0; left: 0;
        min-width: 100%;
        min-height: 100%;
    }
    .transcript-side { flex: none; min-width: unset; max-width: 100%; max-height: 200px; border-radius: 10px; }
    .transcript-side.collapsed { display: none !important; }
    .transcript-title { padding: 10px 14px; font-size: 0.82rem; }
    .transcript-msgs { padding: 10px; gap: 6px; }
    .msg { font-size: 0.82rem; padding: 8px 10px; }

    .controls-row { padding: 6px 8px; gap: 8px; flex-wrap: wrap; justify-content: center; }
    .btn { padding: 10px 18px; font-size: 0.82rem; min-height: 44px; }

    .pip-self { width: 120px; height: 90px; border-radius: 8px; }
    .status-bar { padding: 8px 12px; font-size: 0.85rem; min-height: 44px; }
    .camera-box { border-radius: 10px; }
    .camera-topbar { padding: 6px 12px; }
    .camera-title { font-size: 0.72rem; }

    #signBankScreen { padding: 0; }
    .sb-hero h2 { font-size: 1.3rem; }
    .sb-hero p { font-size: 0.82rem; }
    .sb-group-grid { grid-template-columns: repeat(auto-fill, minmax(140px, 1fr)); gap: 10px; }
    .sb-card-gloss { font-size: 0.78rem; }

    /* Mobile practice: stack with camera first (big), reference second (small) */
    #practiceSession { max-width: 100%; overflow-x: hidden; }
    .prac-split { grid-template-columns: 1fr; max-width: 100%; overflow-x: hidden; }
    .prac-panel { max-width: 100%; min-width: 0; }
    .prac-sign-name { font-size: 1.5rem; margin: 6px 0; }
    /* Put the camera panel first visually on mobile */
    .prac-panel:nth-child(2) { order: -1; }
    .prac-camera-box {
        width: 100%;
        max-width: 100%;
        aspect-ratio: 9 / 16 !important;
        min-height: 0 !important;
        max-height: 70vh;
    }
    .prac-camera-box video {
        object-fit: cover !important;
        width: 100%;
        height: 100%;
    }
    .prac-panel:nth-child(1) .prac-video-box { max-height: 22vh; min-height: auto; }
    .prac-panel:nth-child(1) .prac-panel-label { font-size: 0.65rem; }
    .prac-panel:nth-child(1) .prac-hint { display: none; }

    .home-screen { padding: 0 14px 20px; }
    .home-title h1 { font-size: 1.5rem; }
    .home-subtitle { font-size: 0.85rem; margin-bottom: 20px; }
    .scenario-grid { grid-template-columns: 1fr; gap: 10px; }
    .scenario-card { padding: 14px 16px; gap: 12px; min-height: 44px; }


    .live-card-style .scenario-card { padding: 10px 14px; gap: 10px; }
    .live-card-style .scenario-icon { width: 34px; height: 34px; line-height: 34px; font-size: 1.2rem; border-radius: 8px; }
    .live-card-style .scenario-icon .ico { font-size: 1rem; line-height: 34px; }
    .live-card-style .scenario-info h3 { font-size: 0.85rem; margin-bottom: 2px; }
    .live-card-style .scenario-info p { font-size: 0.7rem; line-height: 1.3; }
    .scenario-icon { font-size: 1.3rem; }
    .scenario-info h3 { font-size: 0.9rem; }

    .mode-btn { padding: 8px 16px; font-size: 0.8rem; min-height: 44px; }
    .mode-toggle-row { margin-bottom: 20px; }

    .training-subtabs { margin-bottom: 20px; }
    .subtab-btn { padding: 8px 14px; font-size: 0.75rem; min-height: 44px; }

    .live-captions-layout { padding: 12px; gap: 10px; max-width: 100%; }
    .lc-camera-view { height: 150px; }
    .lc-caption-area { padding: 14px; }
    .lc-paragraph { font-size: 0.95rem; line-height: 1.7; }

    #phaseLiveDemo { padding: 14px; height: calc(100vh - 84px); }
    .live-demo-container { max-width: 100%; gap: 12px; }
    .live-demo-branding .brand-name { font-size: 2.2rem; }
    .live-demo-branding .brand-tagline { font-size: 0.78rem; }
    .live-demo-camera { border-radius: 10px; }
    .live-demo-caption-box { padding: 18px 16px; border-radius: 10px; }
    .live-demo-caption-text { font-size: 1.1rem; }

    #phaseBreakdown { padding: 16px 12px; }
    .breakdown-header h2 { font-size: 1.2rem; }
    .breakdown-header p { font-size: 0.82rem; }
    .segment-grid { gap: 8px; }
    .segment-card, .prediction-card { width: 120px; }
    .segment-card video, .prediction-card video { height: 80px; }
    .pipeline-step { padding: 14px; }
    .construction-display { gap: 10px; }
    .construct-box { min-width: 140px; padding: 10px; }
    .construct-arrow { font-size: 1.2rem; }
    .sentence-tab { padding: 6px 12px; font-size: 0.8rem; min-height: 44px; }
    .breakdown-nav button { padding: 10px 18px; font-size: 0.82rem; min-height: 44px; }

    .back-btn { min-height: 44px; padding: 8px 14px; font-size: 0.78rem; }
    .vocab-toggle-btn { min-height: 44px; padding: 8px 14px; }
    .vocab-chip { padding: 4px 10px; font-size: 0.7rem; }
}

/* ══════════════════════════════════════════
   RESPONSIVE -- Small phones portrait (max 480px)
   ══════════════════════════════════════════ */
@media (max-width: 480px) {
    .app-header { padding: 6px 8px; gap: 4px; }
    .app-logo { font-size: 1.3rem; }
    .header-row-toggles { flex-wrap: wrap; gap: 4px; }
    .htog-btn { padding: 5px 10px; font-size: 0.65rem; }

    .tab-intro { padding: 16px 12px 8px; }
    .tab-body { padding: 0 12px 12px; }
    .tab-intro h2 { font-size: 1.1rem; }
    .tab-intro p { font-size: 0.8rem; }

    .conversation-layout { padding: 6px; gap: 6px; height: calc(100vh - 80px); }
    .camera-view { min-height: 140px; }
    .transcript-side { max-height: 160px; }
    .transcript-title { padding: 8px 10px; font-size: 0.78rem; }
    .transcript-msgs { padding: 8px; gap: 4px; }
    .msg { font-size: 0.78rem; padding: 6px 8px; max-width: 95%; }

    .controls-row { padding: 4px 6px; gap: 6px; }
    .btn { padding: 8px 14px; font-size: 0.78rem; }

    .pip-self { width: 100px; height: 75px; bottom: 8px; right: 8px; border-radius: 6px; }
    .status-bar { padding: 6px 8px; font-size: 0.78rem; }
    .status-bar .script-line { font-size: 0.95rem; }

    #signBankScreen { padding: 0; }
    .sb-hero { margin-bottom: 20px; }
    .sb-hero h2 { font-size: 1.15rem; }
    .sb-group-grid { grid-template-columns: repeat(auto-fill, minmax(120px, 1fr)); gap: 8px; }
    .sb-grid { gap: 20px; }
    .sb-card-label { padding: 8px 10px; }
    .sb-card-gloss { font-size: 0.72rem; }
    .sb-card-cat { font-size: 0.6rem; }
    .sb-search { font-size: 0.82rem; padding: 8px 12px; }

    .prac-sign-name { font-size: 1.3rem; }
    .prac-record-btn { font-size: 0.9rem; padding: 12px 28px; }

    .home-screen { padding: 0 12px 14px; }
    .home-title h1 { font-size: 1.3rem; }
    .home-subtitle { font-size: 0.8rem; margin-bottom: 16px; }
    .scenario-card { padding: 12px; gap: 10px; }
    .scenario-icon { font-size: 1.2rem; width: 30px; }
    .scenario-info h3 { font-size: 0.85rem; }
    .scenario-info p { font-size: 0.7rem; }

    .mode-toggle-row { margin-bottom: 16px; }
    .mode-btn { padding: 7px 12px; font-size: 0.75rem; gap: 5px; }
    .mode-icon { font-size: 0.85rem; }
    .mode-description { font-size: 0.7rem; }

    .training-subtabs { margin-bottom: 16px; }
    .subtab-btn { padding: 7px 10px; font-size: 0.7rem; }

    .live-captions-layout { padding: 8px; gap: 8px; }
    .lc-camera-view { height: 120px; }
    .lc-paragraph { font-size: 0.88rem; }
    .lc-caption-area { padding: 10px; gap: 10px; }

    #phaseLiveDemo { padding: 10px; }
    .live-demo-container { gap: 10px; }
    .live-demo-branding { margin-bottom: 6px; }
    .live-demo-branding .brand-name { font-size: 1.8rem; }
    .live-demo-branding .brand-tagline { font-size: 0.72rem; }
    .live-demo-camera { border-radius: 8px; aspect-ratio: 3/4; }
    .live-demo-caption-box { padding: 14px 12px; border-radius: 8px; }
    .live-demo-caption-text { font-size: 1rem; }
    .live-demo-caption-label { font-size: 0.65rem; margin-bottom: 8px; }

    #phaseBreakdown { padding: 12px 8px; }
    .breakdown-header { margin-bottom: 16px; }
    .breakdown-header h2 { font-size: 1.05rem; }
    .breakdown-header p { font-size: 0.78rem; }
    .segment-grid { gap: 6px; }
    .segment-card, .prediction-card { width: 110px; }
    .segment-card video, .prediction-card video { height: 70px; }
    .seg-label { font-size: 0.68rem; padding: 3px 6px; }
    .pred-info { padding: 6px; }
    .pred-top1 { font-size: 0.72rem; }
    .top-k-list { font-size: 0.65rem; }
    .pipeline-step { padding: 12px; margin-bottom: 12px; }
    .step-title { font-size: 0.85rem; }
    .construction-display { flex-direction: column; gap: 8px; }
    .construct-arrow { transform: rotate(90deg); }
    .construct-box { min-width: unset; width: 100%; }
    .sentence-tab { padding: 5px 10px; font-size: 0.75rem; }
    .breakdown-nav { gap: 8px; }
    .breakdown-nav button { padding: 8px 16px; font-size: 0.8rem; width: 100%; }

    .vocab-chip-container { max-height: 40vh; }
}

/* ══════════════════════════════════════════
   RESPONSIVE -- Portrait orientation
   ══════════════════════════════════════════ */
@media (orientation: portrait) and (max-width: 768px) {
    .conversation-layout { flex-direction: column; }
    .camera-side { flex: 1; }
    .transcript-side { flex: none; max-height: 25vh; }
    .live-demo-camera { aspect-ratio: 3/4; }
}

/* ══════════════════════════════════════════
   RESPONSIVE -- Landscape phones
   ══════════════════════════════════════════ */
@media (orientation: landscape) and (max-height: 500px) {
    .app-header { padding: 4px 10px; gap: 2px; }
    .app-logo { font-size: 1.2rem; }
    .header-row-toggles { gap: 4px; }
    .htog-btn { padding: 4px 10px; font-size: 0.65rem; min-height: 36px; }

    .tab-intro { padding: 12px; }
    .tab-body { padding: 0 12px 12px; }
    .tab-intro h2 { font-size: 1rem; }
    .tab-intro p { font-size: 0.78rem; }

    .conversation-layout { flex-direction: row; height: calc(100vh - 40px); padding: 4px 8px; gap: 6px; }
    .camera-side { flex: 2; }
    .camera-view { min-height: 100px; }
    .transcript-side { flex: 1; min-width: 180px; max-width: 260px; max-height: unset; }
    .controls-row { padding: 4px 6px; gap: 6px; }
    .btn { padding: 6px 14px; font-size: 0.75rem; min-height: 36px; }
    .status-bar { padding: 4px 8px; font-size: 0.75rem; min-height: 32px; }
    .camera-topbar { padding: 4px 8px; }
    .pip-self { width: 100px; height: 75px; }

    .home-screen { padding: 0 12px 10px; }
    .home-title h1 { font-size: 1.3rem; }
    .scenario-grid { grid-template-columns: repeat(auto-fill, minmax(200px, 1fr)); }

    #phaseLiveDemo { padding: 8px; height: calc(100vh - 40px); }
    .live-demo-branding { margin-bottom: 4px; }
    .live-demo-branding .brand-name { font-size: 1.6rem; }
    .live-demo-branding .brand-tagline { display: none; }
    .live-demo-camera { aspect-ratio: 16/9; }
    .live-demo-caption-box { padding: 10px 14px; }
    .live-demo-caption-text { font-size: 1rem; }

    #phaseBreakdown { padding: 10px; }
    .breakdown-header { margin-bottom: 12px; }
    .breakdown-header h2 { font-size: 1.1rem; }

    .lc-camera-view { height: 100px; }
}

/* ══════════════════════════════════════════
   RESPONSIVE -- Tablet landscape
   ══════════════════════════════════════════ */
@media (min-width: 769px) and (max-width: 1024px) and (orientation: landscape) {
    .conversation-layout { flex-direction: row; height: calc(100vh - 84px - var(--topnav-h)); }
    .camera-side { flex: 2; }
    .transcript-side { min-width: 240px; max-width: 300px; max-height: unset; }
}

/* Ensure no horizontal scroll globally.
   overflow-x:hidden forces overflow-y to `auto`, which turns <body> into a
   scroll container and breaks `position: sticky` on the header in iOS Safari
   (the header scrolls away and never comes back). `overflow-x: clip` blocks
   sideways scroll WITHOUT creating a scroll container, so sticky keeps working.
   The `hidden` line stays first as a fallback for browsers without clip. */
html, body { max-width: 100vw; overflow-x: hidden; overflow-x: clip; }

/* ══════════════════════════════════════════
   Camera Source Selector
   ══════════════════════════════════════════ */
.camera-source-btn, .pose-overlay-btn {
    background: none; border: none; font-size: 1rem; cursor: pointer;
    opacity: 0.4; transition: opacity 0.2s; padding: 2px 6px; line-height: 1;
}
.camera-source-btn:hover, .pose-overlay-btn:hover { opacity: 1; }
.pose-overlay-btn.active { opacity: 0.85; }
/* Panel open. These three buttons are all show/hide toggles, so they need to
   read as pressed while their panel is up. */
.camera-source-btn.active {
    opacity: 1;
    background: var(--bg-chip);
    border-radius: 6px;
}

.camera-source-panel {
    background: var(--bg-card); border: 1px solid var(--border-light); border-radius: 10px;
    padding: 12px 14px; margin: 0 0 8px 0; font-size: 0.82rem;
}
.camera-source-option { margin-bottom: 6px; }
.camera-source-option label { cursor: pointer; display: flex; align-items: center; gap: 6px; }

#wifiCameraInputRow {
    display: flex; gap: 6px; align-items: center; margin-top: 8px;
}
#wifiCameraUrl {
    flex: 1; padding: 6px 10px; border: 1px solid var(--border-light); border-radius: 8px;
    font-size: 0.78rem; background: var(--bg-page); color: var(--text-primary); outline: none;
}
#wifiCameraUrl:focus { border-color: var(--accent); }

/* ══════════════════════════════════════════
   Kiosk Mode
   ══════════════════════════════════════════ */
.kiosk-mode .app-header,
.kiosk-mode .sidebar,
.kiosk-mode .tab-intro,
.kiosk-mode #btnHowItWorks,
/* #settingsBtn only, NOT all .camera-source-btn. That class is shared by three
   buttons -- camera/zone settings, show-glosses, show-transcript -- and hiding
   the lot took away the two a counter actually wants: staff need to see the
   detected glosses and the conversation so far. Only the camera-and-zone
   settings are an operator concern, and those live behind the PIN. */
.kiosk-mode #settingsBtn { display: none !important; }

.kiosk-mode .home-layout { display: block !important; }
.kiosk-mode .home-content { margin: 0; padding: 0; max-width: 100%; }
/* 100dvh, not 100vh. The base rule already uses dvh; this override did not,
   and that is why the controls row sat below the fold on a tablet: 100vh is the
   viewport as if the browser chrome were hidden, so with Safari's bars visible
   the layout is taller than the screen and has to be scrolled. dvh tracks the
   space actually available. The vh line stays first as a fallback for browsers
   without dvh. */
.kiosk-mode .conversation-layout {
    height: 100vh !important;
    height: 100dvh !important;
}
/* The Start button must never be the thing that gets squeezed out. Pinning the
   controls row at its natural height makes the CAMERA give up space instead,
   which is the right trade -- a camera 40px shorter is fine, a Start button
   below the fold is not. */
.kiosk-mode .camera-side .controls-row { flex: 0 0 auto; }
/* ...but the camera still needs a floor, or a very short window collapses it to
   nothing and the signer cannot see themselves at all. */
.kiosk-mode .camera-box { min-height: 220px; }
.kiosk-mode .camera-side { flex: 2; }

.kiosk-tap-overlay {
    position: fixed; inset: 0; background: var(--bg-page); z-index: 9999;
    display: flex; flex-direction: column; align-items: center; justify-content: center;
    cursor: pointer; text-align: center; padding: 40px;
}
.kiosk-tap-overlay h1 { font-size: 2.2rem; font-weight: 700; color: var(--text-primary); margin-bottom: 12px; }
.kiosk-tap-overlay p { font-size: 1.1rem; color: var(--text-secondary); max-width: 400px; }

/* ══════════════════════════════════════════
   Mode Switch Button (floating)
   ══════════════════════════════════════════ */
.mode-switch-float {
    position: absolute; bottom: 12px; left: 50%; transform: translateX(-50%);
    background: var(--accent); color: #fff; border: none; border-radius: 24px;
    padding: 10px 22px; font-size: 0.82rem; font-weight: 600; cursor: pointer;
    box-shadow: 0 2px 12px rgba(0,0,0,0.2); z-index: 10; transition: background 0.2s;
    white-space: nowrap;
}
.mode-switch-float:hover { filter: brightness(1.1); }

/* ══════════════════════════════════════════
   Debug Panel
   ══════════════════════════════════════════ */
.debug-panel {
    /* Overlays the right-hand end of the stage row rather than sharing it.
     * Sharing would take 240px off the width the 4:3 box has to work with, and
     * the camera size is not negotiable; the gutter it mostly covers is empty
     * anyway. */
    position: absolute;
    top: 4px;
    right: 4px;
    bottom: 4px;
    width: 240px;
    z-index: 8;
    background: rgba(20, 25, 32, 0.92);
    border: 1px solid rgba(255, 255, 255, 0.08);
    border-radius: 8px;
    padding: 8px 10px;
    font-size: 0.75rem;
    font-family: monospace;
    color: var(--text-primary);
    display: flex;
    flex-direction: column;
    box-shadow: 0 4px 16px rgba(0, 0, 0, 0.45);
    /* The mirror transform on the video element doesn't propagate here
     * (we're a sibling, not a child of <video>), so text reads normally. */
}
.debug-panel .debug-title {
    flex-shrink: 0;
    color: #ffffff;
    font-weight: 600;
    font-size: 0.78rem;
    margin-bottom: 6px;
}
.debug-panel .debug-title button { color: rgba(255,255,255,0.6) !important; }
.debug-panel .gloss-list  { flex: 1; max-height: none; }

/* Light-on-dark text for the gloss list since the panel sits on a dark
 * semi-transparent backdrop over the camera. Without these overrides the
 * inherited light-theme colors (dark grays) are invisible. */
.debug-panel .gloss-list-empty { color: rgba(255, 255, 255, 0.5); }
.debug-panel .gloss-row        { border-bottom: 1px solid rgba(255, 255, 255, 0.06); }
.debug-panel .gloss-row:first-child { background: rgba(76, 175, 128, 0.15); }
.debug-panel .gloss-row .gloss-main { color: #ffffff; }
.debug-panel .gloss-row .gloss-conf { color: #34d399; }
.debug-panel .gloss-row .gloss-alts { color: rgba(255, 255, 255, 0.45); }

/* Low-confidence (below 35% gate) rows: dim everything and add an inline
 * "below threshold" tag so the user understands why this didn't reach the
 * LLM. The row is still visible — silent drops were confusing. */
.debug-panel .gloss-row.unconfident { opacity: 0.55; }
.debug-panel .gloss-row.unconfident .gloss-main { font-weight: 500; font-style: italic; }
.debug-panel .gloss-row.unconfident .gloss-conf { color: #f59e0b; }
.debug-panel .gloss-row .gloss-low-tag {
    color: #f59e0b;
    font-size: 0.6rem;
    font-style: italic;
    margin-left: 4px;
}
@media (max-width: 768px) {
    .debug-panel { width: 200px; bottom: 50px; font-size: 0.7rem; }
}
@media (max-width: 480px) {
    .debug-panel { width: 160px; bottom: 40px; }
}
.debug-title { font-weight: 700; margin-bottom: 6px; color: var(--text-primary); font-size: 0.8rem; }
.debug-row { display: flex; justify-content: space-between; padding: 2px 0; color: var(--text-secondary); }
.debug-row span:last-child { font-weight: 600; color: var(--text-primary); }
.debug-velocity-bar {
    height: 8px; background: var(--bg-toggle); border-radius: 4px; position: relative; overflow: visible;
}
.debug-velocity-fill {
    height: 100%; background: #4caf80; border-radius: 4px; transition: width 0.1s; width: 0%;
}
.debug-velocity-threshold {
    position: absolute; top: -2px; height: 12px; width: 2px; background: #ff6666; border-radius: 1px;
}

/* Scrollable gloss list inside the diagnostics panel (streaming mode).
 * Replaces the old hands/velocity/state rows. One row per detected gloss,
 * newest first. Top-1 prominent; alternates dimmer/smaller. */
.gloss-list {
    max-height: 320px;
    overflow-y: auto;
    overflow-x: hidden;
    padding: 2px 0;
}
.gloss-list-empty {
    color: var(--text-dimmed);
    font-style: italic;
    font-size: 0.75rem;
    padding: 6px 4px;
}
.gloss-row {
    display: flex;
    align-items: baseline;
    gap: 8px;
    padding: 4px 4px 4px 6px;
    border-bottom: 1px solid rgba(255,255,255,0.04);
    line-height: 1.25;
}
.gloss-row:first-child {
    background: rgba(76, 175, 128, 0.07);
}
.gloss-row .gloss-main {
    font-weight: 600;
    color: var(--text-primary);
    font-size: 0.85rem;
    letter-spacing: 0.5px;
}
.gloss-row .gloss-conf {
    color: var(--accent);
    font-size: 0.7rem;
    font-weight: 500;
}
.gloss-row .gloss-alts {
    color: var(--text-dimmed);
    font-size: 0.65rem;
    font-weight: 400;
    margin-left: auto;
    text-align: right;
}

/* ══════════════════════════════════════════════════════════════
   Upload tab -- paragraph-length signing -> narrative transcript
   (Backlog item 10; markup in #uploadScreen, behavior in upload.js)
   ══════════════════════════════════════════════════════════════ */
.upload-form-row {
    display: flex;
    align-items: center;
    gap: 12px;
    margin-bottom: 14px;
}
.upload-form-label {
    font-size: 0.78rem;
    font-weight: 600;
    color: var(--text-secondary);
    min-width: 70px;
}
.upload-form-select {
    flex: 1;
    padding: 10px 14px;
    border: 1px solid var(--border-light);
    border-radius: 10px;
    font-size: 0.88rem;
    background: var(--bg-card);
    color: var(--text-primary);
    outline: none;
}
.upload-helper-text {
    background: color-mix(in srgb, var(--accent) 8%, var(--bg-card));
    border: 1px solid color-mix(in srgb, var(--accent) 25%, var(--border-light));
    border-radius: 10px;
    padding: 10px 14px;
    font-size: 0.82rem;
    color: var(--text-secondary);
    line-height: 1.5;
    margin-bottom: 14px;
}

/* Drag-and-drop zone */
.upload-drop-zone {
    border: 2px dashed var(--border-light);
    border-radius: 14px;
    padding: 52px 24px;
    text-align: center;
    cursor: pointer;
    background: var(--bg-card);
    transition: border-color 0.2s, background 0.2s;
}
.upload-drop-zone:hover,
.upload-drop-zone-active {
    border-color: var(--accent);
    background: color-mix(in srgb, var(--accent) 4%, var(--bg-card));
}
.upload-drop-icon { font-size: 2.5rem; margin-bottom: 12px; }
.upload-drop-title {
    font-size: 1rem;
    font-weight: 600;
    color: var(--text-primary);
    margin-bottom: 6px;
}
.upload-drop-hint {
    font-size: 0.78rem;
    color: var(--text-muted);
}

/* Inline error (shown above drop zone on bad file / failure) */
.upload-error {
    background: rgba(231, 76, 60, 0.08);
    border: 1px solid rgba(231, 76, 60, 0.35);
    color: #c0392b;
    border-radius: 10px;
    padding: 10px 14px;
    font-size: 0.85rem;
    font-weight: 500;
    margin-bottom: 12px;
}

/* Processing state (MVP spinner -- not a percentage bar) */
.upload-progress {
    display: flex;
    align-items: center;
    gap: 14px;
    background: var(--bg-card);
    border: 1px solid var(--border-light);
    border-radius: 12px;
    padding: 18px 20px;
}
/* `display: flex` above overrides the browser's UA default for [hidden]
 * (which is `display: none`), so the spinner showed on page load even with
 * the `hidden` attribute set. Re-assert hidden via an attribute selector --
 * higher specificity than the class alone, so this wins. Same fix applies
 * to any future block here that gets an explicit `display` rule. */
.upload-progress[hidden],
.upload-result[hidden],
.upload-drop-zone[hidden] {
    display: none;
}
.upload-progress-spinner {
    width: 22px;
    height: 22px;
    border-width: 3px;
    flex-shrink: 0;
}
.upload-progress-body { flex: 1; min-width: 0; }
.upload-progress-stage {
    font-size: 0.92rem;
    font-weight: 600;
    color: var(--text-primary);
    margin-bottom: 2px;
}
.upload-progress-file {
    font-size: 0.78rem;
    color: var(--text-muted);
    font-family: ui-monospace, Consolas, monospace;
}

/* Result -- summary bar + 2-column (video | transcript) layout */
.upload-result {
    background: var(--bg-card);
    border: 1px solid var(--border-light);
    border-radius: 14px;
    padding: 18px;
}
.upload-result-summary {
    font-size: 0.85rem;
    color: var(--text-secondary);
    margin-bottom: 14px;
    padding-bottom: 12px;
    border-bottom: 1px solid var(--border-subtle);
}
.upload-summary-stat b { color: var(--text-primary); }
.upload-summary-warn {
    color: #c0392b;
    font-weight: 600;
}
.upload-result-grid {
    display: grid;
    grid-template-columns: minmax(280px, 1fr) 1.4fr;
    gap: 18px;
}
.upload-result-video-col video.upload-video-preview {
    width: 100%;
    border-radius: 10px;
    background: #000;
    display: block;
    max-height: 420px;
}
.upload-result-transcript-col {
    display: flex;
    flex-direction: column;
    gap: 12px;
}
/* Paragraph block -- single text block + paragraph-level chips
 * (Regenerate, Edit). Replaces the per-sentence cards: at this stage
 * the user wants the whole narrative to read well, so we surface one
 * affordance to redo the LLM call and one to type a manual fix. */
.upload-paragraph-block {
    position: relative;
    background: color-mix(in srgb, var(--accent) 4%, var(--bg-card));
    border: 1px solid var(--border-subtle);
    border-radius: 10px;
    padding: 14px 16px;
}
.upload-paragraph-block.upload-paragraph-edited {
    border-color: color-mix(in srgb, var(--accent) 50%, var(--border-light));
    background: color-mix(in srgb, var(--accent) 8%, var(--bg-card));
}
.upload-paragraph-text {
    font-size: 0.98rem;
    line-height: 1.55;
    color: var(--text-primary);
    word-wrap: break-word;
    /* Leave room on the right for the chips overlay so a long paragraph
     * doesn't run text under them. */
    padding-right: 60px;
}
/* Spinner-y state while a regenerate is in flight. */
.upload-paragraph-text.upload-paragraph-loading {
    opacity: 0.55;
    transition: opacity 0.15s ease;
}
.upload-paragraph-chips {
    position: absolute;
    top: 10px;
    right: 12px;
    display: flex;
    gap: 4px;
    z-index: 1;
}
.upload-paragraph-chips .caption-action {
    cursor: pointer;
    pointer-events: auto;
    padding: 2px 6px;
    background: none;
    border: none;
    font-size: 1.05em;
    line-height: 1;
    opacity: 0.85;
    transition: opacity 0.15s, transform 0.15s;
}
.upload-paragraph-chips .caption-action:hover:not([disabled]) {
    opacity: 1;
    transform: scale(1.12);
}
.upload-paragraph-chips .caption-action[disabled] {
    opacity: 0.3;
    cursor: not-allowed;
}

/* Edit mode (textarea + Save/Cancel) lives inside the paragraph block
 * after upload.js swaps the chip row out. */
.upload-paragraph-edit {
    width: 100%;
    font-family: inherit;
    font-size: 0.98rem;
    line-height: 1.55;
    color: var(--text-primary);
    background: var(--bg-card);
    border: 1px solid var(--accent);
    border-radius: 6px;
    padding: 10px 12px;
    resize: vertical;
    outline: none;
    min-height: 100px;
}
.upload-paragraph-edit-actions {
    display: flex;
    gap: 6px;
    justify-content: flex-end;
    margin-top: 8px;
}
.upload-paragraph-edit-actions .caption-action {
    cursor: pointer;
    pointer-events: auto;
    padding: 4px 10px;
    background: none;
    border: 1px solid var(--border-light);
    border-radius: 6px;
    font-size: 1em;
}
.upload-paragraph-edit-actions .caption-action:hover {
    border-color: var(--accent);
}

/* Action buttons row under the transcript */
.upload-actions {
    display: flex;
    gap: 8px;
    flex-wrap: wrap;
    padding-top: 4px;
}

/* Library button on the top-bar row */
.upload-library-btn {
    margin-left: auto;
    padding: 8px 14px;
    font-size: 0.82rem;
    white-space: nowrap;
}

/* Library panel -- shown in place of the drop zone / progress / result */
.upload-library-panel {
    background: var(--bg-card);
    border: 1px solid var(--border-light);
    border-radius: 14px;
    padding: 18px;
}
.upload-library-header {
    display: flex;
    align-items: center;
    justify-content: space-between;
    margin-bottom: 14px;
    padding-bottom: 12px;
    border-bottom: 1px solid var(--border-subtle);
}
.upload-library-title {
    margin: 0;
    font-size: 1.05rem;
    color: var(--text-primary);
}
.upload-library-empty {
    padding: 32px 16px;
    text-align: center;
    color: var(--text-muted);
    font-size: 0.88rem;
}
.upload-library-loading {
    padding: 24px 16px;
    text-align: center;
    color: var(--text-muted);
    font-size: 0.85rem;
    display: flex;
    align-items: center;
    justify-content: center;
    gap: 10px;
}
.upload-library-error {
    padding: 16px;
    color: #c0392b;
    font-size: 0.85rem;
    background: rgba(231, 76, 60, 0.08);
    border-radius: 8px;
}
.upload-library-list {
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(280px, 1fr));
    gap: 12px;
}

/* Each saved demo is a clickable card */
.upload-library-card {
    text-align: left;
    background: color-mix(in srgb, var(--accent) 4%, var(--bg-card));
    border: 1px solid var(--border-subtle);
    border-radius: 10px;
    padding: 14px 16px;
    cursor: pointer;
    color: var(--text-primary);
    font: inherit;
    display: flex;
    flex-direction: column;
    gap: 6px;
    transition: border-color 0.15s, background 0.15s, transform 0.15s;
}
.upload-library-card:hover {
    border-color: color-mix(in srgb, var(--accent) 50%, var(--border-light));
    background: color-mix(in srgb, var(--accent) 8%, var(--bg-card));
    transform: translateY(-1px);
}
.upload-library-card-title {
    font-weight: 600;
    font-size: 0.92rem;
    color: var(--text-primary);
}
.upload-library-card-meta {
    font-size: 0.72rem;
    color: var(--text-muted);
    font-family: ui-monospace, Consolas, monospace;
}
.upload-library-card-preview {
    font-size: 0.82rem;
    line-height: 1.4;
    color: var(--text-secondary);
    display: -webkit-box;
    -webkit-line-clamp: 3;
    -webkit-box-orient: vertical;
    overflow: hidden;
    text-overflow: ellipsis;
}

@media (max-width: 768px) {
    .upload-result-grid {
        grid-template-columns: 1fr;
    }
    .upload-drop-zone { padding: 32px 16px; }
    .upload-paragraph-text { padding-right: 56px; }
    .upload-library-list { grid-template-columns: 1fr; }
    .upload-library-btn { margin-left: 0; width: 100%; }
    .upload-form-row { flex-wrap: wrap; }
}

/* ── Practice/Capture mode toggle (Capture folded into Practice) ── */
.prac-mode-toggle {
    display: flex;
    justify-content: center;
    gap: 6px;
    margin: 6px auto 14px;
    padding: 4px;
    background: var(--bg-card);
    border: 1px solid var(--border-light);
    border-radius: 999px;
    width: fit-content;
}
.prac-mode-btn {
    border: none;
    background: transparent;
    color: var(--text-secondary);
    font: inherit;
    font-weight: 600;
    padding: 8px 20px;
    border-radius: 999px;
    cursor: pointer;
    transition: background 0.15s, color 0.15s;
}
.prac-mode-btn.active { background: var(--accent); color: #fff; }
.prac-mode-btn:disabled { opacity: 0.4; cursor: not-allowed; }
.prac-mode-controls {
    display: flex;
    align-items: center;
    justify-content: center;
    flex-wrap: wrap;
    gap: 12px;
}
.prac-ctrl-label { color: var(--text-muted); font-size: 0.85rem; display: inline-flex; align-items: center; gap: 6px; }
.prac-ctrl-label select { font: inherit; padding: 6px 10px; border-radius: 8px; border: 1px solid var(--border-light); background: var(--bg-card); color: var(--text-secondary); }
.prac-split-radio { color: var(--text-secondary); font-size: 0.85rem; display: inline-flex; align-items: center; gap: 4px; cursor: pointer; }
.prac-capture-msg { font-size: 0.85rem; color: var(--text-muted); min-width: 120px; }
.prac-capture-msg.ok { color: var(--success); }
.prac-capture-msg.err { color: var(--danger); }

/* Camera/viewpoint normalization diagnostic (Practice mode) */
.prac-framing-fit {
    max-width: 560px;
    margin: 12px auto 0;
    padding: 12px 14px;
    background: var(--bg-card);
    border: 1px solid var(--border-light);
    border-radius: 10px;
    font-size: 0.85rem;
}
.pff-head { display: flex; align-items: baseline; gap: 8px; font-weight: 600; color: var(--text-secondary); margin-bottom: 6px; }
.pff-score { font-weight: 700; }
.pff-score.good { color: var(--success); }
.pff-score.warn { color: var(--accent); }
.pff-score.bad  { color: var(--danger); }
.pff-sub { font-weight: 400; font-size: 0.75rem; color: var(--text-muted); }
.pff-issue { padding: 4px 0 4px 16px; position: relative; color: var(--text-secondary); }
.pff-issue::before { content: "•"; position: absolute; left: 2px; }
.pff-issue.ok    { color: var(--success); }
.pff-issue.warn  { color: var(--accent); }
.pff-issue.error { color: var(--danger); }
.pff-issue.info  { color: var(--text-muted); }

/* Real-time framing feedback: colored border on the camera window + a bottom
   message bar that says what's wrong and how to fix it. */
.prac-camera-box.framing-good { box-shadow: 0 0 0 3px var(--success) inset; }
.prac-camera-box.framing-warn { box-shadow: 0 0 0 3px var(--accent) inset; }
.prac-camera-box.framing-bad  { box-shadow: 0 0 0 3px var(--danger) inset; }

/* Live stage. Cannot use an inset shadow on .camera-view itself: the video is
   absolutely positioned over the whole box and paints on top of it. A
   pseudo-element stacks above the video (and below the gate overlay at z-index
   12), so the border is actually visible. */
.camera-view.framing-good::after,
.camera-view.framing-warn::after,
.camera-view.framing-bad::after {
    content: '';
    position: absolute;
    inset: 0;
    z-index: 11;
    pointer-events: none;
    border-radius: inherit;
}
.camera-view.framing-good::after { box-shadow: 0 0 0 3px var(--success) inset; }
.camera-view.framing-warn::after { box-shadow: 0 0 0 3px var(--accent) inset; }
.camera-view.framing-bad::after  { box-shadow: 0 0 0 3px var(--danger) inset; }

/* Same reason: the message bar has to clear the video. */
.camera-view 
/* ── Pre-record "get ready, hold still" countdown overlay ─────────────── */
/* Live's framing gate. Deliberately NOT .prac-getready: that one is
   pointer-events:none, and this one has to have a working Skip button -- the
   first version of this gate could hold the conversation for 20 seconds with no
   countdown and no way out, which is indistinguishable from a broken camera. */
.live-getready {
    position: absolute;
    inset: 0;
    z-index: 12;
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    gap: 10px;
    padding: 16px;
    text-align: center;
    background: rgba(0, 0, 0, 0.32);
    color: #fff;
}
.live-getready-msg { font-size: 1rem; font-weight: 600; max-width: 32ch; line-height: 1.4; }
.live-getready-sub { font-size: 0.8rem; opacity: 0.8; font-variant-numeric: tabular-nums; }
.live-getready-skip { padding: 6px 18px; font-size: 0.8rem; }

.prac-getready {
    position: absolute;
    inset: 0;
    z-index: 9;
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    gap: 10px;
    background: rgba(0, 0, 0, 0.42);
    backdrop-filter: blur(1px);
    pointer-events: none;
    text-align: center;
}
/* Waiting-for-green: don't dim the camera (the signer needs to see themselves to
   frame up); just pin a small instruction near the top while the live framing
   border + message do the guiding. */
.prac-getready.waiting {
    background: transparent;
    backdrop-filter: none;
    justify-content: flex-start;
    padding-top: 12px;
}
.prac-getready.waiting .prac-getready-sub {
    background: rgba(0, 0, 0, 0.55);
    padding: 6px 14px;
    border-radius: 999px;
}
.prac-getready-num {
    font-size: clamp(3rem, 14vw, 7rem);
    font-weight: 800;
    line-height: 1;
    color: #fff;
    text-shadow: 0 3px 18px rgba(0, 0, 0, 0.6);
    animation: pracGetReadyPop 0.7s ease-out;
}
.prac-getready-sub {
    font-size: clamp(0.85rem, 2.6vw, 1.05rem);
    font-weight: 600;
    color: #fff;
    text-shadow: 0 1px 6px rgba(0, 0, 0, 0.7);
    letter-spacing: 0.02em;
}
.prac-getready-go {
    font-size: clamp(1.6rem, 7vw, 3rem);
    font-weight: 800;
    color: var(--success, #34d399);
    text-shadow: 0 2px 14px rgba(0, 0, 0, 0.6);
    animation: pracGetReadyPop 0.4s ease-out;
}
@keyframes pracGetReadyPop {
    from { transform: scale(0.6); opacity: 0; }
    to   { transform: scale(1);   opacity: 1; }
}

/* ── Capture review overlay (preview + Save / Redo before saving) ─────── */
.prac-capture-review {
    position: absolute;
    inset: 0;
    z-index: 10;
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    gap: 8px;
    padding: 8px;
    box-sizing: border-box;
    background: rgba(0, 0, 0, 0.78);
    pointer-events: auto;
    overflow: hidden;
}
/* WHAT YOU REVIEW MUST BE WHAT YOU RECORDED.
 * This used to be `flex: 1 1 auto` + `object-fit: contain`: the video shared
 * vertical space with the buttons and then letterboxed inside what was left, so
 * playback showed a smaller, wider image than the live feed — which fills the
 * same box with `object-fit: cover`. That mismatch is the long-standing "playback
 * looks zoomed out" report, and it is not cosmetic: you frame your signing
 * against the live crop, so a preview that disagrees with it teaches you to
 * stand at the wrong distance.
 * Now the video is absolutely positioned over the whole box with the SAME cover
 * fit as #pracCameraFeed, so the framing is pixel-identical. The controls float
 * on top (see .pcr-viewrow/.pcr-hint/.pcr-actions z-index) instead of stealing
 * height from it. */
.prac-capture-review .pcr-video {
    position: absolute;
    inset: 0;
    width: 100%;
    height: 100%;
    object-fit: cover;
    /* MIRRORED, to match #pracCameraFeed's inline transform:scaleX(-1).
       Measured on the device: box 629x471 (1.34), live 960x720 (1.33), clip
       960x720 (1.33) -- same box, same source, same fit, so cover crops nothing
       for either and the field of view was already identical. The ONLY
       difference was that the live feed is mirrored and playback was not, which
       flips anything off-centre to the other side and reads as a framing change.
       You frame your signing against a mirror, so the review has to be a mirror
       too or the comparison is not like-for-like.
       Applies to the pose render as well -- same element, swapped src -- so both
       views agree with the camera and with each other.
       Display only: the saved keypoints come from the UNMIRRORED recording, so
       handedness in the training data is unaffected. */
    transform: scaleX(-1);
    border-radius: 10px;
    box-shadow: 0 8px 30px rgba(0, 0, 0, 0.55);
    background: #000;
}
/* Controls sit ON the video now that it covers the whole box. z-index lifts them
   above it; the overlay's own dark background no longer separates them, so each
   carries its own shadow/scrim for legibility against a bright frame. */
.prac-capture-review .pcr-viewrow,
.prac-capture-review .pcr-hint,
.prac-capture-review .pcr-quality,
.prac-capture-review .pcr-error,
.prac-capture-review .pcr-actions { flex: 0 0 auto; position: relative; z-index: 2; }
/* No scrim any more: the overlay holds ONLY the clip, and the controls that
   needed to be readable over it now live in .prac-review-dock under the
   reference video. Darkening the clip would only make the thing being reviewed
   harder to see. */

/* ── Review controls, docked under the Reference video ──
   They used to sit on top of the camera, covering the framing they were there to
   help judge. The reference video is 4:3 and the camera box is 3:4 portrait on a
   tablet, so this column has the height to spare. */
.prac-review-dock {
    display: flex;
    flex-direction: column;
    align-items: stretch;
    gap: 6px;
    margin-top: 8px;
    padding: 10px;
    border-radius: 12px;
    /* --bg-card, not --card: the latter does not exist in this stylesheet, so it
       silently fell back to a hardcoded dark panel that fought the light theme. */
    background: var(--bg-card, #fff);
    border: 1px solid rgba(128, 128, 128, 0.22);
    color: var(--text-primary, #2a2420);
}
/* Inside the dock the controls sit on a THEMED PANEL, not over video.
   Every .pcr-* colour rule below is scoped `.prac-capture-review .pcr-*`, so
   moving the controls out of that overlay silently dropped all of them and the
   text fell back to --text-primary (dark) on a dark panel -- unreadable in light
   mode. These rules restate colour for the dock against the page background
   rather than against video, so it reads in both themes. */
.prac-review-dock .pcr-hint {
    color: var(--text-secondary, #7a6e60);
    font-size: clamp(0.8rem, 2.4vw, 0.95rem);
    font-weight: 600;
    text-align: center;
    text-shadow: none;
    padding: 0;
}
.prac-review-dock .pcr-privacy { color: var(--text-muted, rgba(80,60,40,0.5)); }
.prac-review-dock .pcr-saved {
    color: var(--success, #34d399);
    font-size: clamp(1.1rem, 3.2vw, 1.5rem);
    text-align: center;
    text-shadow: none;
}
.prac-review-dock .pcr-tally {
    color: var(--text-secondary, #7a6e60);
    text-align: center;
    text-shadow: none;
}
.prac-review-dock .pcr-quality {
    max-width: 100%;
    color: var(--text-primary, #2a2420);
    background: var(--bg-chip, rgba(180,140,100,0.08));
    text-align: center;
}
/* Good / thin keep their signal colour but on a tinted chip, not white-on-video. */
.prac-review-dock .pcr-quality.ok {
    color: #14663a;
    background: rgba(52, 211, 153, 0.18);
}
.prac-review-dock .pcr-quality.warn {
    color: #7a4a06;
    background: rgba(196, 135, 42, 0.22);
}
.prac-review-dock .pcr-quality.pending { color: var(--text-secondary, #7a6e60); }
.prac-review-dock .pcr-error { max-width: 100%; }        /* already white on red */
.prac-review-dock .pcr-viewrow { display: flex; justify-content: center; }
.prac-review-dock .pcr-vt {
    background: var(--bg-toggle, rgba(180,140,100,0.12));
    color: var(--text-primary, #2a2420);
    border: 1px solid rgba(128, 128, 128, 0.25);
}
/* The .pcr-btn / .pcr-vt SIZING rules are scoped to .prac-capture-review, so
   moving the controls into the dock dropped padding, font-size and border along
   with the colours -- they were rendering as browser-default buttons. Restated
   here, and deliberately larger: the dock has spare height under the reference
   video, and these are touch targets on a tablet held at arm's length. */
.prac-review-dock .pcr-actions { width: 100%; display: flex; gap: 8px; }
.prac-review-dock .pcr-btn {
    flex: 1 1 0;
    border: none;
    border-radius: 10px;
    padding: 11px 14px;
    /* 46px: above the 44px touch minimum, but small enough that the whole dock
       still FITS. At 60px the stack (pose toggle + quality + framing + hint +
       actions) overflowed and pushed Save/Redo off screen -- a button you cannot
       reach is worse than a small one. */
    min-height: 46px;
    font-size: clamp(0.92rem, 2.6vw, 1.05rem);
    font-weight: 700;
    cursor: pointer;
    box-shadow: 0 2px 6px rgba(0, 0, 0, 0.15);
}
.prac-review-dock .pcr-save { background: var(--success, #34d399); color: #06280f; }
.prac-review-dock .pcr-redo {
    background: var(--bg-toggle, rgba(180,140,100,0.12));
    color: var(--text-primary, #2a2420);
    border: 1px solid rgba(128, 128, 128, 0.3);
}
.prac-review-dock .pcr-btn:active { transform: translateY(1px); }
.prac-review-dock .pcr-vt {
    border-radius: 999px;
    padding: 8px 18px;
    min-height: 38px;                 /* secondary control: give the height to Save/Redo */
    min-width: 150px;
    font-size: 0.85rem;
    font-weight: 600;
    cursor: pointer;
}
.prac-capture-review .pcr-hint {
    color: #fff;
    font-size: clamp(0.8rem, 2.4vw, 0.98rem);
    font-weight: 600;
    text-shadow: 0 1px 6px rgba(0, 0, 0, 0.7);
    text-align: center;
    padding: 0 16px;
}
.prac-capture-review .pcr-viewrow {
    display: flex;
    justify-content: center;
}
.prac-capture-review .pcr-vt {
    border: 1px solid rgba(255, 255, 255, 0.28);
    background: rgba(255, 255, 255, 0.12);
    color: #fff;
    border-radius: 999px;
    padding: 7px 16px;
    font-size: 0.82rem;
    font-weight: 600;
    cursor: pointer;
    min-width: 150px;
}
.prac-capture-review .pcr-vt:disabled { opacity: 0.7; cursor: default; }
.prac-capture-review .pcr-actions {
    display: flex;
    gap: 14px;
}
.prac-capture-review .pcr-btn {
    border: none;
    border-radius: 9px;
    padding: 12px 22px;
    font-size: clamp(0.95rem, 2.8vw, 1.1rem);
    font-weight: 700;
    cursor: pointer;
    box-shadow: 0 4px 14px rgba(0, 0, 0, 0.4);
}
.prac-capture-review .pcr-save { background: var(--success, #34d399); color: #06280f; }
.prac-capture-review .pcr-redo { background: rgba(255, 255, 255, 0.16); color: #fff; }
.prac-capture-review .pcr-save:active,
.prac-capture-review .pcr-redo:active { transform: translateY(1px); }
.prac-capture-review .pcr-btn:disabled { opacity: 0.75; cursor: default; }
.prac-capture-review .pcr-saved {
    color: var(--success, #34d399);
    font-size: clamp(1.4rem, 5vw, 2.2rem);
    font-weight: 800;
    text-shadow: 0 2px 12px rgba(0, 0, 0, 0.6);
    animation: pracGetReadyPop 0.35s ease-out;
}
.prac-capture-review .pcr-tally {
    color: #fff;
    font-size: clamp(0.85rem, 2.6vw, 1rem);
    font-weight: 600;
    text-shadow: 0 1px 6px rgba(0, 0, 0, 0.7);
}
.prac-capture-review .pcr-quality {
    max-width: 82%;
    text-align: center;
    font-size: clamp(0.8rem, 2.4vw, 0.95rem);
    font-weight: 600;
    padding: 6px 14px;
    border-radius: 8px;
}
.prac-capture-review .pcr-quality.ok   { color: #b9f6d0; }
.prac-capture-review .pcr-quality.warn { background: rgba(196, 135, 42, 0.92); color: #fff; }
/* Pre-save check, shown next to Save/Redo while the server measures the clip.
   Reserves the row's height so the actions don't jump when the result lands. */
.prac-capture-review .pcr-quality.pending {
    color: rgba(255, 255, 255, 0.75);
    font-weight: 500;
}
.prac-capture-review .pcr-quality .pcr-qstat {
    font-weight: 500;
    opacity: 0.9;
}
/* A clip the gate will reject can't be saved — make that unmistakable rather
   than letting the click fail server-side. */
.prac-capture-review .pcr-btn:disabled {
    opacity: 0.45;
    cursor: not-allowed;
}
.prac-capture-review .pcr-error {
    max-width: 82%;
    text-align: center;
    background: rgba(220, 38, 38, 0.92);
    color: #fff;
    font-size: clamp(0.85rem, 2.6vw, 1rem);
    font-weight: 700;
    padding: 10px 16px;
    border-radius: 8px;
    box-shadow: 0 4px 14px rgba(0, 0, 0, 0.4);
}

/* ── Full-screen / expanded camera (Workshop) ─────────────────────── */
.prac-expand-btn {
    position: absolute; bottom: 8px; right: 8px; z-index: 7;
    width: 34px; height: 34px; border-radius: 8px; line-height: 1;
    border: none; background: rgba(0, 0, 0, 0.55); color: #fff;
    font-size: 1rem; cursor: pointer;
}
.prac-expand-btn:hover { background: rgba(0, 0, 0, 0.78); }

#practiceSession.prac-expanded {
    position: fixed; inset: 0; z-index: 1000;
    margin: 0; padding: 0; background: #000; overflow: hidden;
}
/* hide non-essential chrome in full screen */
#practiceSession.prac-expanded .prac-topbar,
#practiceSession.prac-expanded #pracHeader,
#practiceSession.prac-expanded #pracFeedback,
#practiceSession.prac-expanded .prac-framing-fit { display: none !important; }
/* camera panel becomes the stage */
#practiceSession.prac-expanded .prac-split { display: block; height: 100vh; margin: 0; gap: 0; }
#practiceSession.prac-expanded #pracCamPanel { height: 100vh; margin: 0; padding: 0; }
#practiceSession.prac-expanded #pracCamPanel .prac-panel-label { display: none; }
#practiceSession.prac-expanded #pracCamPanel .prac-camera-box {
    height: 100vh !important; max-height: 100vh !important; width: 100vw;
    border-radius: 0; aspect-ratio: auto !important;
}
#practiceSession.prac-expanded #pracCameraFeed { width: 100%; height: 100%; object-fit: cover; }
#practiceSession.prac-expanded .prac-expand-btn { z-index: 1003; }
/* reference demo -> picture-in-picture, top-right */
#practiceSession.prac-expanded #pracRefPanel {
    position: fixed; top: 14px; right: 14px; z-index: 1002;
    width: min(28vw, 240px); margin: 0; padding: 6px;
    background: rgba(0, 0, 0, 0.55); border-radius: 10px;
}
#practiceSession.prac-expanded #pracRefPanel .prac-panel-label,
#practiceSession.prac-expanded #pracRefPanel .prac-hint { display: none; }
#practiceSession.prac-expanded #pracRefPanel video { width: 100%; border-radius: 6px; max-height: 40vh; }
/* mode toggle floats top-center; controls float bottom-center */
#practiceSession.prac-expanded .prac-mode-toggle {
    position: fixed; top: 14px; left: 50%; transform: translateX(-50%); z-index: 1002; margin: 0;
}
#practiceSession.prac-expanded .prac-controls {
    position: fixed; left: 0; right: 0; bottom: 18px; z-index: 1002;
    display: flex; justify-content: center; margin: 0;
}
/* lift the one status strip above the floating controls in full screen */
#practiceSession.prac-expanded .cam-status { bottom: 68px; }


/* ══════════════════════════════════════════════════════════════
   Mobile Workshop (iPhone + iPad) — camera fills the viewport, no scroll.
   Keyed off html.sb-mobile (JS-detected, so it applies at any width — iPad is
   768px+ and width media queries alone miss it) and only while a session is
   active (body.practice-active), so every other screen still scrolls normally.
   High specificity (html + body.class + id) intentionally beats the older
   max-width:640 practice rules.
   ══════════════════════════════════════════════════════════════ */
/* Hide the app header during a mobile session so it can't overlap the practice
   top bar / "Back to Workshop" button (the fixed overlay alone didn't cover it —
   the header sits at a higher stacking level). body.practice-active is removed
   on exit, so the header returns everywhere else. */
html.sb-mobile body.practice-active .app-header { display: none !important; }

/* The practice screen is a full-screen fixed overlay. */
html.sb-mobile body.practice-active #practiceSession {
    position: fixed;
    inset: 0;
    z-index: 60;
    /* !important: the app sets an inline display:block when showing the screen;
       without this the flex column collapses and the camera panel shrinks away. */
    display: flex !important;
    flex-direction: column;
    height: 100vh;            /* fallback */
    height: 100dvh;           /* excludes the mobile browser chrome */
    max-width: 100%;
    overflow: hidden;
    overscroll-behavior: contain;
    padding: 0;
    gap: 4px;
    box-sizing: border-box;
    background: var(--bg-page, #0b0f16);
}
/* Compact single-line chrome above the camera */
html.sb-mobile body.practice-active .prac-topbar { flex: 0 0 auto; padding: 6px 10px; margin: 0; }
html.sb-mobile body.practice-active #pracHeader {
    flex: 0 0 auto; margin: 0 10px; padding: 0;
    font-size: 0.9rem; line-height: 1.25; text-align: center;
    white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
html.sb-mobile body.practice-active .prac-mode-toggle { flex: 0 0 auto; margin: 2px auto; }

/* ── The stage: camera + a "cards" column (reference video on top, practice
   outputs below). Orientation-responsive, matching the /converse app:
   PORTRAIT (default here) = camera on top, cards below;
   LANDSCAPE (media block) = camera left, cards on the right.
   The layout flips automatically when the device rotates. ── */
html.sb-mobile body.practice-active .prac-split {
    flex: 1 1 auto; min-height: 0;
    display: flex; flex-direction: column; gap: 8px;
    grid-template-columns: none; max-width: 100%;
    padding: 0 8px 4px; box-sizing: border-box;
}
/* Portrait: camera takes the top ~60% (bounded so it doesn't elongate into a
   tall, narrow strip), cards take the rest below. */
html.sb-mobile body.practice-active #pracCamPanel {
    margin: 0; gap: 4px; min-height: 0; flex: 0 0 auto; order: 0;
}
html.sb-mobile body.practice-active #pracCamPanel .prac-panel-label { display: none; }
/* Portrait camera: 3:4 box MATCHING the 3:4 stream, so cover fills it completely
   (no letterbox gaps) and shows the FULL frame down to the waist (no crop) at the
   same distance as landscape. Height is pinned to what the 4:3 box occupied —
   min(70vw, 52vh) — so the reference + predictions row still fits below with no
   scrolling; width follows the 3:4 aspect. */
html.sb-mobile body.practice-active #pracCamPanel .prac-camera-box {
    aspect-ratio: 3 / 4 !important;
    height: min(70vw, 52vh) !important; width: auto !important;
    max-width: 96%; min-height: 0 !important;
    margin: 0 auto; border-radius: 12px;
}
/* Fill the box (the box now matches the stream aspect, so nothing is cropped). */
html.sb-mobile body.practice-active #pracCameraFeed { object-fit: cover !important; }
/* Portrait: reference video (left) + outputs column (right) share ONE row below
   the camera, so the Top-3 fills the space beside the reference and nothing needs
   scrolling. The outputs are wrapped into #pracOutputsCol by JS. */
html.sb-mobile body.practice-active #pracRefPanel {
    margin: 0 8px; gap: 8px; min-height: 0; flex: 0 0 auto; order: 1;
    display: flex; flex-direction: row; flex-wrap: wrap; align-items: flex-start;
    overflow: visible;
}
html.sb-mobile body.practice-active #pracRefPanel .prac-panel-label,
html.sb-mobile body.practice-active #pracRefPanel .prac-hint,
html.sb-mobile body.practice-active #pracRefPanel .prac-no-ref { display: none; }
html.sb-mobile body.practice-active #pracRefPanel .prac-video-box {
    aspect-ratio: 4/3; flex: 1 1 44%; min-width: 0; max-height: 30vh;
    border-radius: 10px;
}
html.sb-mobile body.practice-active #pracOutputsCol {
    flex: 1 1 44%; min-width: 0; display: flex; flex-direction: column; gap: 6px;
}
/* Practice outputs, relocated into the column by JS (see _pracMobileRelocateOutputs) */
html.sb-mobile body.practice-active #pracOutputsCol .prac-framing-fit,
html.sb-mobile body.practice-active #pracOutputsCol .prac-topk {
    margin: 0; flex: 0 0 auto; position: static; width: auto;
}

/* Record controls: compact bar at the bottom, always on-screen */
html.sb-mobile body.practice-active .prac-controls { flex: 0 0 auto; padding: 6px 10px 10px; margin: 0; }
html.sb-mobile body.practice-active .prac-record-btn { width: 100%; }

/* Landscape: camera on the left, cards column on the right. Same 4:3 camera box
   as portrait (centered in its column), so both orientations match. */
@media (orientation: landscape) {
    html.sb-mobile body.practice-active .prac-split { flex-direction: row; align-items: stretch; }
    html.sb-mobile body.practice-active #pracCamPanel { flex: 1.7 1 0; justify-content: center; }
    html.sb-mobile body.practice-active #pracCamPanel .prac-camera-box {
        aspect-ratio: 4 / 3 !important;
        width: 100%; height: auto !important;
        max-height: 100% !important; margin: 0 auto;
    }
    html.sb-mobile body.practice-active #pracRefPanel {
        flex: 1 1 0; max-width: 40%;
        flex-direction: column; flex-wrap: nowrap; overflow-y: auto;
    }
    html.sb-mobile body.practice-active #pracRefPanel .prac-video-box { flex: 0 0 auto; max-height: 42vh; }
    html.sb-mobile body.practice-active #pracOutputsCol { flex: 1 1 auto; width: 100%; }
}
/* Portrait: no extra digital zoom (the native 4:3 stream fills the 4:3 box), and
   the whole view SCROLLS — camera on top, reference + outputs below, scroll down
   to reach them — with the record/pause controls pinned at the bottom. */
@media (orientation: portrait) {
    html.sb-mobile { --sb-cam-zoom: 1.0; }
    html.sb-mobile body.practice-active #practiceSession { overflow-y: auto; -webkit-overflow-scrolling: touch; }
    html.sb-mobile body.practice-active .prac-split { flex: 0 0 auto; }
    html.sb-mobile body.practice-active #pracRefPanel { overflow: visible; }
    html.sb-mobile body.practice-active .prac-controls {
        position: sticky; bottom: 0; z-index: 5;
        background: var(--bg-page, #0b0f16);
        border-top: 1px solid var(--line, #2a3141);
    }
}

/* Digital zoom of the mobile practice camera DISPLAY only. Recognition and the
   framing monitor read the raw video frames (CSS transforms don't affect
   drawImage), and the model is shoulder-normalized / scale-invariant — so this
   is purely the "how zoomed-in the signer looks" feel, matching apps like
   /converse. TUNE the one number below: 1.0 = raw wide front-cam FOV, higher =
   more zoomed (crops the edges via the box's overflow:hidden). */
/* 1.0 = show exactly what is recorded. Was 1.2, a display-only crop that made
   the preview feel tighter while the recording stayed wide -- so framing that
   looked good on screen put the signer at ~0.20 of frame width against WLASL's
   ~0.28. A crop cannot add detail; moving closer can, and gives real pixels on
   the hands. Raise this again only for feel, knowing it decouples what you frame
   from what you capture. */
html.sb-mobile { --sb-cam-zoom: 1.0; }
html.sb-mobile body.practice-active #pracCameraFeed {
    transform: scaleX(-1) scale(var(--sb-cam-zoom, 1.2)) !important;
    transform-origin: center center;
}
/* The review clip must carry the SAME display zoom as the live feed.
   Without it the live view was scaled 1.2x and clipped by the box's
   overflow:hidden while playback showed the raw frame -- measured on device,
   live element 754x566 against a 629x471 box, playback 629x471. That is the
   long-running "playback looks zoomed out" report: the playback was correct and
   the LIVE view was the cropped one. Same element as the pose render, so both
   match the camera.
   Display only -- extraction reads the decoded video, not the DOM -- but it is
   not cosmetic: you frame yourself against the zoomed preview, so you end up
   further from the camera than you look, which is why captured signers measure
   ~0.20 of frame width against WLASL's ~0.28. */
html.sb-mobile body.practice-active .prac-capture-review .pcr-video {
    transform: scaleX(-1) scale(var(--sb-cam-zoom, 1.2));
    transform-origin: center center;
}

/* ── Top-3 predictions card (Practice outputs) ─────────────────────────── */
/* The card is always dark (its --surface fallback is dark and --surface isn't
   themed), so ALL text is set to explicit light values here — never the themed
   --text-* tokens, which go dark-on-dark in light mode (the unreadable bug). */
.prac-topk {
    background: #1b2230;
    border: 1px solid #2f3a4d;
    border-radius: 10px;
    padding: 10px 12px;
    font-size: 0.82rem;
    color: #eef2f8;
}
.prac-topk .ptk-head { font-weight: 700; font-size: 0.82rem; margin-bottom: 8px; color: #eef2f8; }
.prac-topk .ptk-head.ok    { color: #4ade80; }
.prac-topk .ptk-head.close { color: #fbbf24; }
.prac-topk .ptk-head.miss  { color: #c3ccda; }
.prac-topk .ptk-row {
    display: flex; align-items: center; gap: 8px;
    padding: 6px; border-radius: 6px;
    font-variant-numeric: tabular-nums;
    color: #eef2f8;
}
.prac-topk .ptk-row + .ptk-row { margin-top: 2px; }
.prac-topk .ptk-row.target {
    background: rgba(74, 222, 128, 0.18);
    outline: 1px solid rgba(74, 222, 128, 0.6);
}
.prac-topk .ptk-row.target .ptk-gloss { color: #86efac; }
.prac-topk .ptk-rank {
    width: 18px; height: 18px; border-radius: 50%; flex: none;
    background: #2f3a4d; color: #dbe2ec;
    display: grid; place-items: center; font-size: 0.7rem; font-weight: 700;
}
.prac-topk .ptk-gloss { flex: 1; font-weight: 600; color: #f6f9fc; }
.prac-topk .ptk-conf { color: #b6c0d0; font-family: ui-monospace, Consolas, monospace; }

/* ── Support tab: lightweight domain + sign picker ──────────────────────── */
.support-picker {
    display: flex; flex-wrap: wrap; align-items: center; gap: 8px;
    background: var(--bg-card); border: 1px solid var(--border-light);
    border-radius: 10px; padding: 10px 14px;
}
.support-picker-label { font-size: 0.8rem; font-weight: 600; color: var(--text-secondary); }
/* Compact, explicit height + appearance:none so the native select / search
   field render at a tidy 32px on every device (iOS otherwise inflates them). */
.support-select, .support-search {
    box-sizing: border-box; height: 32px; min-width: 0; line-height: 1.2;
    -webkit-appearance: none; appearance: none;
    border: 1px solid var(--border-light); border-radius: 6px;
    background: var(--bg-card); color: var(--text-primary); font-size: 0.85rem;
}
.support-select {
    flex: 1 1 160px; padding: 0 30px 0 12px;
    background-image: url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" width="12" height="8"><path d="M1 1l5 5 5-5" fill="none" stroke="%23999" stroke-width="1.5"/></svg>');
    background-repeat: no-repeat; background-position: right 10px center; background-size: 11px 7px;
}
.support-search { flex: 1 1 200px; padding: 0 12px; }
.support-search::-webkit-search-decoration,
.support-search::-webkit-search-cancel-button { -webkit-appearance: none; }
/* Device-agnostic: an inline row on tablet/desktop, a clean vertical stack on
   phones (the old multi-control row wrapped raggedly on iPhone -- label on one
   line, a select stranded on the next). */
@media (max-width: 560px) {
    .support-picker { flex-direction: column; align-items: stretch; }
    .support-picker-label { margin-bottom: -4px; }
    /* Reset flex to none: in a COLUMN the flex-basis (160/200px) becomes the
       VERTICAL size and overrode height, ballooning the controls. flex:none
       -> basis auto -> the 32px height wins. */
    .support-select, .support-search { flex: 0 0 auto; width: 100%; height: 32px; }
}
.support-hint { margin-top: 10px; font-size: 0.82rem; color: var(--text-muted); }

/* ── Support: per-gloss training stats list ─────────────────────────── */
/* Overall model accuracy, above the per-gloss list. */
.support-model-health {
    display: flex;
    align-items: center;
    flex-wrap: wrap;
    gap: 8px;
    margin: 10px 0 6px;
    font-size: 0.8rem;
    color: var(--text-secondary);
}
.smh-label { font-weight: 600; color: var(--text-primary); }
.smh-pct { font-size: 1.05rem; font-weight: 700; font-variant-numeric: tabular-nums; }
.smh-pct.is-live { color: var(--success); }
.smh-pct.is-preview { color: var(--accent); }
.smh-badge {
    font-size: 0.6rem;
    font-weight: 700;
    letter-spacing: 0.6px;
    text-transform: uppercase;
    padding: 3px 8px;
    border-radius: 999px;
}
.smh-badge.is-live {
    background: color-mix(in srgb, var(--success) 18%, transparent);
    color: var(--success);
}
.smh-badge.is-preview {
    background: color-mix(in srgb, var(--accent) 20%, transparent);
    color: var(--accent);
}
.smh-n { color: var(--text-muted); font-size: 0.74rem; }
.smh-none { color: var(--text-muted); font-style: italic; }

.support-gloss-stats { margin-top: 16px; max-width: 560px; }
.support-gloss-stats:empty { display: none; }
.sgs-head { font-size: 0.76rem; color: var(--text-muted); margin-bottom: 8px; }
.sgs-list { display: flex; flex-direction: column; gap: 6px; }
.sgs-row {
    display: flex; align-items: center; gap: 12px; width: 100%;
    padding: 8px 12px; border: 1px solid var(--border-light);
    border-radius: 8px; background: var(--bg-card); cursor: pointer;
    text-align: left; transition: background 0.15s, border-color 0.15s;
}
.sgs-row:hover { background: var(--surface-hover, rgba(127,127,127,0.08)); border-color: var(--border-medium); }
.sgs-gloss { font-weight: 600; font-size: 0.86rem; color: var(--text-primary); min-width: 96px; }
.sgs-counts { flex: 1; font-size: 0.78rem; color: var(--text-muted); font-variant-numeric: tabular-nums; }
/* Tap-to-capture affordance: a theme-token chevron (visible in light AND dark --
   the old emoji washed out on the dark ground), no repeated "capture" text.
   Brightens to the accent on hover/focus so the whole row reads as a button. */
.sgs-go {
    flex: 0 0 auto; font-size: 1.15rem; line-height: 1; color: var(--text-muted);
    transition: color 0.15s;
}
.sgs-row:hover .sgs-go, .sgs-row:focus-visible .sgs-go { color: var(--accent); }
.sgs-empty { font-size: 0.82rem; color: var(--text-muted); padding: 10px 4px; }
.gloss-conf-badge {
    flex: 0 0 auto; font-size: 0.68rem; font-weight: 700; letter-spacing: 0.03em;
    text-transform: uppercase; padding: 3px 9px; border-radius: 999px;
    border: 1px solid transparent;
}
/* Semantic bands -- fixed colors (not the accent), legible on light + dark. */
.gloss-conf-badge.low    { color: #d9534f; background: rgba(217,83,79,0.15);  border-color: rgba(217,83,79,0.35); }
.gloss-conf-badge.medium { color: #c98a1e; background: rgba(217,146,42,0.16); border-color: rgba(217,146,42,0.35); }
.gloss-conf-badge.high   { color: #3fa06a; background: rgba(63,160,106,0.16); border-color: rgba(63,160,106,0.35); }
:root:not([data-theme="light"]) .gloss-conf-badge.medium { color: #e0a94a; }
@media (prefers-color-scheme: dark) { :root:not([data-theme="light"]) .gloss-conf-badge.medium { color: #e0a94a; } }
.support-privacy {
    margin-top: 8px; font-size: 0.82rem; line-height: 1.4; color: var(--text-secondary);
    background: rgba(52, 211, 153, 0.08); border: 1px solid rgba(52, 211, 153, 0.30);
    border-radius: 8px; padding: 8px 12px;
}
.support-privacy strong { color: var(--text-primary); }
/* Privacy line inside the capture-review overlay (dark ground) */
.pcr-privacy { display: inline-block; margin-top: 4px; font-size: 0.72rem; color: #86efac; }

/* ══════════════════════════════════════════════════════════════
   HOME PAGE (static intro)
   ══════════════════════════════════════════════════════════════ */
#homeIntroScreen {
    /* Its own measure, centred. Long-form text needs a column width regardless
     * of how wide the window is. */
    max-width: 1100px;
    margin: 0 auto;
    padding: 0 20px 40px;
}

/* ── Hero ── */
.hp-hero {
    display: grid;
    grid-template-columns: 1.15fr 0.85fr;
    gap: 40px;
    align-items: center;
    padding: 44px 0 40px;
}
.hp-eyebrow {
    font-size: 0.72rem;
    text-transform: uppercase;
    letter-spacing: 1.4px;
    font-weight: 600;
    color: var(--accent);
    margin-bottom: 14px;
}
.hp-title {
    font-size: 2.6rem;
    line-height: 1.12;
    font-weight: 700;
    margin: 0 0 16px;
    color: var(--text-primary);
    letter-spacing: -0.02em;
}
.hp-title span { color: var(--accent); }
.hp-lede {
    font-size: 0.98rem;
    line-height: 1.65;
    color: var(--text-secondary);
    max-width: 46ch;
    margin: 0 0 26px;
}
.hp-cta { display: flex; gap: 10px; flex-wrap: wrap; }
.hp-cta-main { padding: 11px 22px; font-size: 0.9rem; }

/* Hero art: the actual transformation the app performs. */
.hp-hero-art {
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: 8px;
}
.hp-bubble {
    width: 100%;
    max-width: 300px;
    border-radius: 14px;
    padding: 14px 16px;
    border: 1px solid var(--border-light);
    background: var(--bg-card);
}
.hp-bubble-who {
    font-size: 0.62rem;
    text-transform: uppercase;
    letter-spacing: 1px;
    color: var(--text-muted);
    margin-bottom: 6px;
}
.hp-bubble-body { font-size: 0.95rem; color: var(--text-primary); }
.hp-bubble-sign .hp-bubble-body {
    font-family: ui-monospace, Consolas, monospace;
    letter-spacing: 0.02em;
    color: var(--accent);
}
.hp-bubble-text { border-color: color-mix(in srgb, var(--accent) 40%, transparent); }
.hp-arrow { color: var(--text-muted); font-size: 1.1rem; }

/* The Live tab reuses the Home catalogue verbatim; this only spaces it. */
.catalogue-block { padding: 6px 0 4px; }
.catalogue-block .hp-h2 { margin-bottom: 4px; }

/* ── Counter catalogue ───────────────────────────────────────────
   Square cards in one auto-fitting grid. auto-fill + minmax means the column
   count follows the container, so laptop / iPad landscape / iPad portrait /
   phone all resolve without device-specific breakpoints. */
.hp-catalogue {
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(180px, 1fr));
    gap: 14px;
}

/* Per-sector hue. Earthy and desaturated to sit with the app's amber accent
   rather than fight it; each card carries its sector as colour + chip, which is
   what keeps level 1 of the taxonomy readable without five sparse sections. */
.hp-counter[data-sector="libraries_community"] { --sec-1: #c98a35; --sec-2: #8a5219; }
.hp-counter[data-sector="health_pharmacy"]     { --sec-1: #3fa392; --sec-2: #1e6659; }
.hp-counter[data-sector="shops_dining"]        { --sec-1: #c96f6a; --sec-2: #8c3c40; }
.hp-counter[data-sector="getting_around"]      { --sec-1: #5f83c4; --sec-2: #33518a; }
.hp-counter[data-sector="public_services"]     { --sec-1: #7f9560; --sec-2: #4d6137; }
.hp-counter { --sec-1: #8a8f98; --sec-2: #5b616b; }   /* fallback */

/* Status drives the card colour (overrides the sector tint above): a fully-ready
   counter reads GREEN, a limited-preview one reads amber/brown. Placed after the
   sector rules so it wins the equal-specificity tie. Planned keeps the sector
   wash. Driving --sec-1/--sec-2 (not just background) keeps the gradient, shadow
   and focus ring all in the status colour. */
.hp-counter.is-live    { --sec-1: #3fa06a; --sec-2: #276b47; }   /* green */
.hp-counter.is-preview { --sec-1: #c98a35; --sec-2: #8a5219; }   /* amber/brown */

.hp-counter {
    position: relative;
    aspect-ratio: 1;
    display: flex;
    flex-direction: column;
    gap: 6px;
    padding: 16px 15px 15px;
    border: 1px solid var(--border-light);
    border-radius: 18px;
    background: var(--bg-card);
    text-align: left;
    font: inherit;
    overflow: hidden;
    transition: transform 0.18s ease, box-shadow 0.18s ease;
}

/* Live and preview are both usable, so both get the sector gradient. Preview is
   distinguished by its badge, not by being dimmed -- it works, it is just not
   finished, and dimming would read as broken. */
.hp-counter.is-live,
.hp-counter.is-preview {
    border-color: transparent;
    background: linear-gradient(150deg, var(--sec-1), var(--sec-2));
    color: #fff;
    cursor: pointer;
    box-shadow: 0 8px 22px color-mix(in srgb, var(--sec-2) 32%, transparent);
}
.hp-counter.is-live:hover,
.hp-counter.is-preview:hover { transform: translateY(-3px); }
.hp-counter.is-live:focus-visible,
.hp-counter.is-preview:focus-visible { outline: 3px solid var(--sec-1); outline-offset: 3px; }

/* Amber badge: a caveat, not a fault. */
.hp-counter.is-preview .hp-counter-badge {
    background: rgba(255, 255, 255, 0.28);
    color: #fff;
    box-shadow: 0 0 0 1px rgba(255, 255, 255, 0.35) inset;
}

/* Planned: same shape and colour family, clearly not installable. A tinted
   wash and a dashed edge rather than reduced opacity, which reads as broken. */
.hp-counter.is-planned {
    border-style: dashed;
    border-color: color-mix(in srgb, var(--sec-1) 38%, transparent);
    background:
        linear-gradient(150deg,
            color-mix(in srgb, var(--sec-1) 12%, transparent),
            color-mix(in srgb, var(--sec-2) 6%, transparent));
}

.hp-counter-sector {
    font-size: 0.58rem;
    font-weight: 700;
    letter-spacing: 0.8px;
    text-transform: uppercase;
    opacity: 0.75;
    color: inherit;
}
.hp-counter.is-planned .hp-counter-sector { color: var(--text-muted); opacity: 1; }

.hp-counter-ico { font-size: 1.9rem; line-height: 1; margin: 2px 0 2px; }

.hp-counter-name {
    font-size: 0.95rem;
    font-weight: 700;
    line-height: 1.25;
}
.hp-counter.is-planned .hp-counter-name { color: var(--text-primary); }

.hp-counter-desc {
    font-size: 0.72rem;
    line-height: 1.45;
    opacity: 0.88;
    /* Descriptions vary in length; clamp so every card keeps its square. */
    display: -webkit-box;
    -webkit-line-clamp: 3;
    -webkit-box-orient: vertical;
    overflow: hidden;
}
.hp-counter.is-planned .hp-counter-desc { color: var(--text-muted); opacity: 1; }

.hp-counter-badge {
    margin-top: auto;
    align-self: flex-start;
    font-size: 0.58rem;
    font-weight: 700;
    letter-spacing: 0.6px;
    text-transform: uppercase;
    padding: 4px 9px;
    border-radius: 999px;
    background: rgba(255, 255, 255, 0.22);
    color: #fff;
}
.hp-counter.is-planned .hp-counter-badge {
    background: var(--bg-chip);
    color: var(--text-muted);
}

/* Phones: two columns of smaller squares beats one giant card per screen. */
@media (max-width: 560px) {
    .hp-catalogue { grid-template-columns: repeat(auto-fill, minmax(140px, 1fr)); gap: 10px; }
    .hp-counter { padding: 13px 12px; border-radius: 15px; }
    .hp-counter-ico { font-size: 1.5rem; }
    .hp-counter-name { font-size: 0.85rem; }
    .hp-counter-desc { -webkit-line-clamp: 2; font-size: 0.68rem; }
}

/* Motion is decoration here. */
@media (prefers-reduced-motion: reduce) {
    .hp-counter { transition: none; }
    .hp-counter.is-live:hover { transform: none; }
}

/* ── Sections ── */
.hp-section { padding: 34px 0 6px; border-top: 1px solid var(--border-light); }
.hp-h2 {
    font-size: 1.35rem;
    font-weight: 700;
    margin: 0 0 6px;
    color: var(--text-primary);
    letter-spacing: -0.01em;
}
.hp-sub {
    font-size: 0.88rem;
    color: var(--text-muted);
    max-width: 62ch;
    margin: 0 0 22px;
    line-height: 1.6;
}

/* ── Stats ── */
.hp-stats {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(190px, 1fr));
    gap: 14px;
}
.hp-stat {
    background: var(--bg-card);
    border: 1px solid var(--border-light);
    border-radius: 12px;
    padding: 20px 18px;
}
.hp-stat-figure {
    font-size: 2rem;
    font-weight: 700;
    color: var(--accent);
    line-height: 1;
    letter-spacing: -0.02em;
}
.hp-stat-label {
    font-size: 0.84rem;
    color: var(--text-secondary);
    margin-top: 10px;
    line-height: 1.45;
}
.hp-stat-note {
    font-size: 0.68rem;
    color: var(--text-muted);
    margin-top: 10px;
}

/* ── Steps ── */
.hp-steps {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(230px, 1fr));
    gap: 18px;
}
.hp-step { position: relative; padding-top: 6px; }
.hp-step-n {
    width: 30px; height: 30px;
    border-radius: 50%;
    background: color-mix(in srgb, var(--accent) 18%, transparent);
    color: var(--accent);
    font-weight: 700;
    font-size: 0.85rem;
    display: flex; align-items: center; justify-content: center;
    margin-bottom: 12px;
}
.hp-step h3 { font-size: 0.98rem; margin: 0 0 6px; color: var(--text-primary); }
.hp-step p  { font-size: 0.84rem; color: var(--text-muted); line-height: 1.6; margin: 0; }

/* ── Concept cards ── */
.hp-cards {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(220px, 1fr));
    gap: 14px;
}
.hp-card {
    background: var(--bg-card);
    border: 1px solid var(--border-light);
    border-radius: 12px;
    padding: 20px 18px;
}
.hp-card-icon { font-size: 1.5rem; margin-bottom: 10px; }
.hp-card h3 { font-size: 0.95rem; margin: 0 0 6px; color: var(--text-primary); }
.hp-card p  { font-size: 0.82rem; color: var(--text-muted); line-height: 1.55; margin: 0; }

/* ── Feature links ── */
.hp-features {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(240px, 1fr));
    gap: 10px;
}
.hp-feature {
    display: flex;
    align-items: center;
    gap: 12px;
    text-align: left;
    padding: 14px 16px;
    border-radius: 10px;
    border: 1px solid var(--border-light);
    background: var(--bg-card);
    cursor: pointer;
    transition: border-color 0.15s, transform 0.15s;
    color: inherit;
    font: inherit;
}
.hp-feature:hover {
    border-color: var(--accent);
    transform: translateY(-1px);
}
.hp-feature-icon { font-size: 1.2rem; flex-shrink: 0; }
.hp-feature-text { display: flex; flex-direction: column; gap: 2px; font-size: 0.8rem; color: var(--text-muted); }
.hp-feature-text strong { font-size: 0.9rem; color: var(--text-primary); font-weight: 600; }

.hp-foot {
    margin-top: 34px;
    padding-top: 18px;
    border-top: 1px solid var(--border-light);
    font-size: 0.76rem;
    color: var(--text-muted);
    line-height: 1.6;
}
/* "Powered by SignBridge" research credit -- sits just under the footer note,
   no top border/margin so it reads as part of the same block. */
.hp-credit { margin-top: 6px; padding-top: 0; border-top: none; }
.hp-credit a { color: var(--accent); text-decoration: none; font-weight: 600; }
.hp-credit a:hover { text-decoration: underline; }

/* Collapsible privacy statement (home footer + Settings). Muted like the footer;
   the summary reads as a subtle link with a rotating disclosure caret. */
.hp-privacy { margin-top: 12px; font-size: 0.78rem; color: var(--text-muted); line-height: 1.6; }
.hp-privacy > summary {
    cursor: pointer; font-weight: 600; color: var(--accent);
    list-style: none; display: inline-flex; align-items: center; gap: 5px;
}
.hp-privacy > summary::-webkit-details-marker { display: none; }
.hp-privacy > summary::after { content: "\25B8"; font-size: 0.8em; }        /* triangle ▸ */
.hp-privacy[open] > summary::after { content: "\25BE"; }                     /* ▾ */
.hp-privacy ul { margin: 10px 0 0; padding-left: 18px; display: flex; flex-direction: column; gap: 7px; }
.hp-privacy li { margin: 0; }
.hp-privacy strong { color: var(--text-secondary); font-weight: 600; }
.settings-privacy { margin-top: 14px; }
/* Footer: two stacked link lines (privacy summary + SignBridge credit). */
.hp-footlinks .hp-privacy { margin-top: 0; }
.hp-footlinks .hp-credit { margin-top: 10px; }

@media (max-width: 860px) {
    .hp-hero { grid-template-columns: 1fr; gap: 26px; padding: 28px 0 24px; }
    .hp-title { font-size: 2rem; }
    /* Stacked order = source order: heading/description card first, then the
     * WANT BORROW BOOK example art below it. (No order:-1 — that put the art
     * on top, which read backwards on phones.) */
    /* Stacked: centre the hero block. text-align alone would not move .hp-lede,
     * which is a max-width block and needs auto margins to centre itself. */
    .hp-hero-text { text-align: center; }
    .hp-lede { margin-left: auto; margin-right: auto; }
    .hp-cta { justify-content: center; }
}

/* ══════════════════════════════════════════════════════════════
   COLLAPSIBLE SIDEBAR (desktop only — on mobile it is a bottom bar)
   ══════════════════════════════════════════════════════════════ */
/* The collapse toggle and its .collapsed rules were removed with the side rail:
 * collapsing bought back 130px of width, and a menu bar has no width to buy. */



/* ── Lessons + learner code (Practice rework, Phase 1) ──
   Sits above the Sign Bank grid. The grid remains the free-pick escape hatch,
   so this bar must read as an addition, not a replacement. */
.lesson-bar {
    display: flex; align-items: center; gap: 12px; flex-wrap: wrap;
    padding: 14px 16px; margin-bottom: 12px;
    border: 1px solid var(--border-light); border-radius: 12px;
    background: var(--bg-card);
}
.lesson-bar-main { flex: 1 1 260px; min-width: 0; }
.lesson-bar-title { font-weight: 700; color: var(--text-primary); font-size: 0.95rem; }
.lesson-bar-sub { color: var(--text-secondary); font-size: 0.8rem; margin-top: 2px; }
.lesson-select {
    padding: 8px 10px; font-size: 0.82rem; min-width: 190px;
    border: 1px solid var(--border-light); border-radius: 8px;
    background: var(--bg-card); color: var(--text-primary);
}

.learner-row {
    display: flex; align-items: center; gap: 10px; flex-wrap: wrap;
    padding: 8px 16px; margin-bottom: 12px;
    font-size: 0.8rem; color: var(--text-secondary);
}
.learner-label { font-weight: 600; }
.learner-code code {
    font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
    letter-spacing: 0.08em; font-size: 0.92rem;
    color: var(--text-primary); background: var(--bg-chip);
    padding: 2px 8px; border-radius: 6px;
}
.learner-btn { padding: 5px 12px; font-size: 0.76rem; margin-left: auto; }
.learner-panel {
    padding: 14px 16px; margin: -4px 0 14px;
    border: 1px solid var(--border-light); border-radius: 12px;
    background: var(--bg-card);
}
.learner-help { font-size: 0.82rem; color: var(--text-secondary); margin: 0 0 10px; }
.learner-actions { display: flex; align-items: center; gap: 10px; flex-wrap: wrap; }
.learner-or { font-size: 0.78rem; color: var(--text-muted); }
.learner-input {
    padding: 9px 12px; font-size: 0.95rem; width: 160px;
    font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
    letter-spacing: 0.1em; text-transform: uppercase;
    border: 1px solid var(--border-light); border-radius: 8px;
    background: var(--bg-card); color: var(--text-primary);
}
.learner-msg { margin-top: 10px; font-size: 0.8rem; min-height: 1.1em; }
.learner-msg.ok  { color: var(--success, #34d399); }
.learner-msg.err { color: var(--danger, #dc2626); }
/* The post-lesson nudge: a prompt, not an error. */
.learner-msg.warn {
    color: var(--text-primary);
    font-weight: 600;
    margin-top: 12px;
    padding: 8px 10px;
    border-radius: 8px;
    border-left: 3px solid var(--accent);
    background: rgba(196, 135, 42, 0.14);
}

/* ── Learning path (Practice tab, gamified lessons) ──
   Duolingo's shape: a vertical stack of gated units, each a row of lesson nodes
   ending in a test node. Locked units are visibly inert rather than hidden, so
   the whole curriculum is legible from the start -- that visibility is the point,
   not decoration. */
.path { display: flex; flex-direction: column; gap: 14px; margin-bottom: 18px; }
.path-empty {
    padding: 18px; text-align: center; font-size: 0.85rem;
    color: var(--text-muted); background: var(--bg-card);
    border: 1px dashed var(--border-light); border-radius: 12px;
}
.phase {
    padding: 16px; border-radius: 14px;
    background: var(--bg-card); border: 1px solid var(--border-light);
}
.phase.is-locked { opacity: 0.62; }
.phase-head { display: flex; align-items: flex-start; gap: 12px; margin-bottom: 10px; }
.phase-head > div:first-child { flex: 1 1 auto; min-width: 0; }
.phase-title { font-weight: 700; font-size: 1rem; color: var(--text-primary); }
.phase-blurb { font-size: 0.78rem; color: var(--text-secondary); margin-top: 2px; }
.phase-badge {
    flex: 0 0 auto; font-size: 0.7rem; font-weight: 700; padding: 4px 10px;
    border-radius: 999px; background: var(--bg-chip); color: var(--text-secondary);
    white-space: nowrap;
}
.phase-badge.passed { background: rgba(52,211,153,0.2); color: #14663a; }
.phase-badge.ready  { background: rgba(196,135,42,0.2); color: #7a4a06; }
.phase-badge.locked { background: var(--bg-chip); color: var(--text-muted); }
.phase-bar {
    height: 8px; border-radius: 999px; overflow: hidden;
    background: var(--bg-chip); margin-bottom: 4px;
}
.phase-bar > span {
    display: block; height: 100%; border-radius: 999px;
    background: var(--success, #34d399); transition: width 0.3s ease;
}
.phase-meta { font-size: 0.72rem; color: var(--text-muted); margin-bottom: 10px; }
.phase-gate { font-size: 0.76rem; color: var(--text-secondary); margin-top: 10px; }

/* Lesson nodes. Wrapping row rather than Duolingo's snaking path: the snake is
   charming at 5 units and unreadable at 7, and these phases have up to 7. */
.node-row { display: flex; flex-wrap: wrap; gap: 10px; }
.node {
    display: flex; flex-direction: column; align-items: center; gap: 5px;
    width: 92px; padding: 8px 4px; border: none; background: none;
    cursor: pointer; border-radius: 10px;
}
.node:disabled { cursor: not-allowed; }
.node-ring {
    width: 52px; height: 52px; border-radius: 50%;
    display: flex; align-items: center; justify-content: center;
    font-weight: 800; font-size: 0.85rem;
    border: 3px solid var(--border-light);
    background: var(--bg-chip); color: var(--text-secondary);
}
.node-label {
    font-size: 0.68rem; line-height: 1.2; text-align: center;
    color: var(--text-secondary); word-break: break-word;
}
.node.todo .node-ring { }
.node.part .node-ring { border-color: var(--accent, #c4872a); color: var(--text-primary); }
.node.done .node-ring {
    border-color: var(--success, #34d399);
    background: var(--success, #34d399); color: #06280f;
}
.node.test .node-ring { border-style: dashed; }
.node.test.ready .node-ring {
    border-style: solid; border-color: var(--accent, #c4872a);
    background: rgba(196,135,42,0.18); color: var(--text-primary);
}
.node:not(:disabled):hover .node-ring { transform: translateY(-1px); }
.node:not(:disabled):active .node-ring { transform: translateY(1px); }

/* In-session HUD: replaces the score line with "Practice · Phase 1 · 3/6". */
.phase-hud { font-weight: 700; font-size: 0.82rem; color: var(--text-secondary); }

@media (max-width: 640px) {
    .node { width: 76px; }
    .node-ring { width: 44px; height: 44px; font-size: 0.78rem; }
}

/* Phrase chips in the in-session HUD (phases 2-3). A multi-sign unit has to show
   the whole phrase with the current sign marked, or the learner cannot tell where
   they are in it. Single-sign units already have their name in the practice
   header, so no chips there. */
.phrase-chips { display: inline-flex; gap: 5px; margin-left: 10px; vertical-align: middle; }
.phrase-chips .chip {
    font-size: 0.68rem; font-weight: 700; padding: 2px 8px; border-radius: 999px;
    background: var(--bg-chip); color: var(--text-muted);
}
.phrase-chips .chip.done { background: rgba(52,211,153,0.22); color: #14663a; }
.phrase-chips .chip.now  { background: var(--accent, #c4872a); color: #fff; }

/* ── The ONE camera-window status strip (logic: static/js/cam_status.js) ──
   Full-width bottom band. It inherited the framing bar's slot deliberately:
   framing messages are whole sentences, and the old centre pill was `nowrap`
   with max-width 92%, so they ellipsised. Right-padded to clear the expand
   button (z-index 7, bottom-right), which has always drawn over this slot.
   This is an overlay only -- it does not touch the camera's size, aspect ratio,
   zoom or object-fit. */
.cam-status {
    position: absolute;
    left: 0; right: 0; bottom: 0;
    z-index: 6;
    display: flex; align-items: center; gap: 10px;
    padding: 8px 52px 8px 14px;
    color: #fff;
    background: rgba(0, 0, 0, 0.68);
    font-size: clamp(0.72rem, 2.2vw, 0.86rem);
    font-weight: 600;
    text-align: left;
    border-bottom-left-radius: inherit;
    border-bottom-right-radius: inherit;
    pointer-events: none;
}
.cam-status .cs-dot {
    width: 10px; height: 10px; border-radius: 50%;
    background: #9aa0ad; flex: 0 0 auto;
}
.cam-status .cs-text { flex: 1 1 auto; }
.cam-status .cs-bar {
    width: 46px; height: 4px; border-radius: 999px; flex: 0 0 auto;
    background: rgba(255, 255, 255, 0.25); overflow: hidden;
}
.cam-status .cs-bar > span { display: block; height: 100%; width: 0; background: #fff; }

/* Tones -- one per rung of the priority ladder. Colour carries the state, the
   text says what to do. The dot pulses only while something is genuinely live,
   so a steady dot reads as a label and a moving one as an instruction. */
.cam-status.is-ready    { background: rgba(21, 128, 61, 0.9); font-weight: 700; }
.cam-status.is-ready    .cs-dot { background: #86efac; animation: csPulse 1.4s ease-in-out infinite; }
.cam-status.is-busy     { background: rgba(37, 99, 235, 0.9); }
.cam-status.is-busy     .cs-dot { background: #bfdbfe; animation: csPulse 0.7s ease-in-out infinite; }
.cam-status.is-wait     { background: rgba(180, 83, 9, 0.92); }
.cam-status.is-wait     .cs-dot { background: #fed7aa; }
.cam-status.is-warn     { background: rgba(196, 135, 42, 0.94); }
.cam-status.is-warn     .cs-dot { background: #fde68a; }
.cam-status.is-blocking { background: rgba(200, 38, 38, 0.94); }
.cam-status.is-blocking .cs-dot { background: #fecaca; }
.cam-status.is-info     .cs-dot { background: #cbd5e1; }

@keyframes csPulse { 0%,100% { opacity: 1; } 50% { opacity: 0.35; } }

/* The capture-review overlay (z-index 10) already covers the strip, since the
   strip is z-index 6. These are belt-and-braces for the case where the overlay
   is restyled; `body.capture-reviewing` is not currently set by anything. */
.prac-camera-box .prac-capture-review ~ .cam-status,
body.capture-reviewing .cam-status { display: none !important; }


/* ══════════════════════════════════════════════════════════════
   LESSON PROGRESS TRACK + WORD HAND-OFF
   The old HUD read "Practice · Phase 1 · 3/6", which gave the count but not the
   shape of it: you could not see which signs you had fumbled, and it replaced
   Practice's own score line. The track is additive instead -- Practice keeps its
   layout and a lesson gets one extra row.
   ══════════════════════════════════════════════════════════════ */
.path-track {
    display: flex;
    align-items: center;
    gap: 12px;
    flex-wrap: wrap;
    margin: 0 0 10px;
    padding: 8px 12px;
    border-radius: 10px;
    background: var(--bg-card);
    border: 1px solid var(--border-medium);
}
.ptrack-label {
    font-size: 0.78rem;
    font-weight: 700;
    letter-spacing: 0.04em;
    text-transform: uppercase;
    color: var(--text-muted);
    flex: 0 0 auto;
}
.ptrack-dots { display: flex; align-items: center; gap: 6px; flex-wrap: wrap; }
.ptrack-dot {
    width: 26px; height: 26px; border-radius: 50%;
    display: inline-flex; align-items: center; justify-content: center;
    font-size: 0.72rem; font-weight: 700;
    border: 2px solid var(--border-medium);
    background: transparent;
    color: var(--text-muted);
    transition: background 140ms ease, border-color 140ms ease, transform 140ms ease;
}
.ptrack-dot.ok    { background: var(--success); border-color: var(--success); color: #06280f; }
.ptrack-dot.close { background: var(--accent);  border-color: var(--accent);  color: #2a1a00; }
.ptrack-dot.miss  { background: var(--danger);  border-color: var(--danger);  color: #350a0a; }
/* Skipped: moved past without mastering, because the model would not give it to
   them. Yellow + dashed, deliberately unlike both done (solid green) and missed
   (solid red) -- it is neither, it is unfinished business. */
.ptrack-dot.skipped {
    background: rgba(250, 204, 21, 0.9);
    border-color: #ca8a04;
    border-style: dashed;
    color: #3a2c00;
}

/* In progress: some correct answers but not the cap yet. Deliberately NOT a
   tick -- a tick after one of two required correct answers made the chain
   contradict the rule printed next to it. */
.ptrack-dot.part {
    background: rgba(196, 135, 42, 0.25);
    border-color: var(--accent);
    color: var(--text-primary);
}
/* The current bubble is ringed rather than recoloured, so "where I am" and "how
   I did" stay independent -- a bubble can be both current and already missed. */
.ptrack-dot.now {
    transform: scale(1.18);
    box-shadow: 0 0 0 3px rgba(196, 135, 42, 0.35);
    border-color: var(--accent);
    color: var(--text-primary);
}
.ptrack-dot.now.ok, .ptrack-dot.now.close, .ptrack-dot.now.miss { color: inherit; }

/* Word hand-off. Item 5: the only previous signal that the lesson had moved on
   was the header text changing, which nobody reads while framing a sign. This
   takes the dead time while the next reference video resolves. */
.path-next {
    position: absolute;
    inset: 0;
    z-index: 9;
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    gap: 6px;
    background: rgba(8, 11, 18, 0.72);
    color: #fff;
    opacity: 0;
    pointer-events: none;
    transition: opacity 160ms ease;
}
.path-next.show { opacity: 1; animation: pnPop 420ms ease-out; }
.path-next .pn-kicker {
    font-size: 0.72rem; font-weight: 700; letter-spacing: 0.16em;
    text-transform: uppercase; color: #c7d2fe;
}
.path-next .pn-word {
    font-size: clamp(1.5rem, 7vw, 2.6rem); font-weight: 800; letter-spacing: 0.02em;
}
@keyframes pnPop {
    0%   { transform: scale(0.86); }
    60%  { transform: scale(1.04); }
    100% { transform: scale(1); }
}

/* Code rules in the learner panel. */
.learner-rules {
    margin: 10px 0 14px;
    padding-left: 18px;
    font-size: 0.85rem;
    line-height: 1.6;
    color: var(--text-secondary);
}
.learner-rules code {
    padding: 1px 4px; border-radius: 3px;
    background: var(--bg-chip);
    font-family: ui-monospace, Consolas, monospace;
}


/* The mastery rule, stated where the learner is actually looking. */
.ptrack-rule {
    margin-left: auto;
    font-size: 0.78rem;
    font-weight: 600;
    color: var(--text-muted);
    flex: 0 0 auto;
}
.phase-rule {
    margin-top: 4px;
    font-size: 0.8rem;
    font-weight: 600;
    color: var(--text-secondary);
}

/* ── Hand-off gate ───────────────────────────────────────────────────────
   Unlike .path-next (which fades on a timer) this one waits: it takes pointer
   events, so the camera behind it cannot be signed at by accident, and the
   learner studies the already-loaded reference video before pressing on. */
.path-gate {
    position: absolute;
    inset: 0;
    z-index: 11;
    display: none;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    gap: 4px;
    padding: 16px;
    text-align: center;
    background: rgba(8, 11, 18, 0.88);
    color: #fff;
}
.path-gate .pg-kicker {
    font-size: 0.82rem; font-weight: 700; color: #86efac;
}
.path-gate .pg-label {
    margin-top: 8px;
    font-size: 0.68rem; font-weight: 700; letter-spacing: 0.18em;
    text-transform: uppercase; color: #c7d2fe;
}
.path-gate .pg-word {
    font-size: clamp(1.4rem, 6.5vw, 2.4rem); font-weight: 800; line-height: 1.1;
}
.path-gate .pg-note {
    font-size: 0.78rem; color: #cbd5e1; margin-bottom: 12px;
}
.path-gate .pg-btn { font-size: 0.86rem; padding: 8px 16px; }


/* The escape hatch, next to the rule it lets you bypass. Understated on purpose:
   it is a rescue, not a shortcut anyone should reach for first. */
.ptrack-skip {
    flex: 0 0 auto;
    padding: 5px 10px;
    border-radius: 999px;
    border: 1px solid var(--border-medium);
    background: transparent;
    color: var(--text-muted);
    font-size: 0.74rem;
    font-weight: 600;
    cursor: pointer;
}
.ptrack-skip:hover {
    border-color: #ca8a04;
    color: var(--text-primary);
    background: rgba(250, 204, 21, 0.12);
}
/* The rule no longer pushes the button off the end of the row. */
.ptrack-rule { margin-left: auto; }
.phase-skipped { color: #ca8a04; font-weight: 700; }


/* Skip budget on the button. Spent reads as unavailable but stays clickable, so
   pressing it explains why rather than doing nothing. */
.ptrack-skip .pts-left { opacity: 0.75; font-weight: 700; }
.ptrack-skip.is-spent { opacity: 0.5; border-style: dashed; }
.ptrack-skip.is-spent:hover { background: rgba(200, 38, 38, 0.12); border-color: var(--danger); }


/* ══════════════════════════════════════════════════════════════
   OPERATOR / KIOSK SETUP
   For the person installing this on a counter, not the person using it. Touch
   targets are deliberately large throughout: this is done standing up, on a
   tablet, probably in a hurry, often by someone who will only ever see it once.
   ══════════════════════════════════════════════════════════════ */

/* Two button variants the app lacked. `ghost` is for the quiet third option on
   a screen that already has a primary and a secondary; `danger` is only for
   actions that destroy a working setup. */
.btn-ghost {
    background: transparent;
    border: 1px solid transparent;
    color: var(--text-secondary);
}
.btn-ghost:hover { background: var(--bg-toggle); color: var(--text-primary); }
.btn-danger {
    background: transparent;
    border: 1px solid var(--danger);
    color: var(--danger);
}
.btn-danger:hover { background: var(--danger); color: #fff; }

#operatorScreen { max-width: 980px; margin: 0 auto; padding: 24px 16px 64px; }

/* ── Welcome: the first thing an operator ever sees ──────────────────────── */
.op-hero {
    padding: 36px 32px;
    border-radius: 20px;
    background: var(--bg-card);
    border: 1px solid var(--border-light);
    box-shadow: var(--shadow-card);
}
.op-hero-eyebrow {
    display: inline-block;
    font-size: 0.74rem; font-weight: 700; letter-spacing: 0.14em;
    text-transform: uppercase; color: var(--accent);
    margin-bottom: 10px;
}
.op-hero-title {
    font-size: clamp(1.6rem, 4.5vw, 2.5rem);
    line-height: 1.15; font-weight: 700; margin: 0 0 12px;
    color: var(--text-primary);
}
.op-hero-lead {
    font-size: 1.02rem; line-height: 1.6; color: var(--text-secondary);
    max-width: 58ch; margin: 0 0 24px;
}
.op-hero-actions { display: flex; align-items: center; gap: 16px; flex-wrap: wrap; }
/* The one button on this page that matters. Sized for a thumb on a tablet. */
.op-cta { font-size: 1.05rem; padding: 14px 28px; border-radius: 12px; }
.op-hero-time { font-size: 0.86rem; color: var(--text-muted); }
.op-hero-resume {
    margin-top: 20px; padding: 12px 16px;
    border-radius: 10px; background: var(--bg-chip);
    border-left: 3px solid var(--accent);
    font-size: 0.9rem; color: var(--text-secondary);
}

/* Three cards saying what the next five minutes involve. An operator deciding
   whether to start now or come back deserves to know before they begin. */
.op-steps {
    display: grid; gap: 16px; margin: 24px 0;
    grid-template-columns: repeat(auto-fit, minmax(240px, 1fr));
}
.op-step-card {
    padding: 20px; border-radius: 14px;
    background: var(--bg-card); border: 1px solid var(--border-light);
}
.op-step-num {
    display: inline-flex; align-items: center; justify-content: center;
    width: 28px; height: 28px; border-radius: 50%;
    background: var(--accent); color: #fff;
    font-size: 0.86rem; font-weight: 700; margin-bottom: 10px;
}
.op-step-title { font-weight: 700; margin-bottom: 6px; color: var(--text-primary); }
.op-step-desc { font-size: 0.88rem; line-height: 1.55; color: var(--text-secondary); }

.op-welcome-foot {
    display: grid; gap: 16px; margin-top: 8px;
    grid-template-columns: repeat(auto-fit, minmax(280px, 1fr));
}
.op-foot-block {
    padding: 20px; border-radius: 14px;
    background: var(--bg-card); border: 1px solid var(--border-light);
}
.op-foot-muted { background: transparent; border-style: dashed; }
.op-foot-title { font-weight: 700; margin-bottom: 6px; }
.op-foot-desc { font-size: 0.88rem; line-height: 1.55; color: var(--text-secondary); margin: 0 0 12px; }
.op-foot-row { display: flex; gap: 10px; align-items: center; flex-wrap: wrap; margin-bottom: 8px; }
.op-code-input {
    flex: 1 1 160px; min-width: 0;
    padding: 12px 14px; border-radius: 10px;
    border: 1px solid var(--border-medium); background: var(--bg-card);
    color: var(--text-primary);
    font-family: ui-monospace, Consolas, monospace;
    font-size: 1.05rem; letter-spacing: 0.14em; text-transform: uppercase;
}

/* ── Wizard ──────────────────────────────────────────────────────────────── */
.op-wizard {
    background: var(--bg-card); border: 1px solid var(--border-light);
    border-radius: 20px; box-shadow: var(--shadow-card);
    padding: 20px 24px 24px;
}
.op-wizard-bar { display: flex; align-items: center; gap: 12px; margin-bottom: 20px; }
.op-dots { display: flex; align-items: center; gap: 7px; margin: 0 auto; }
.op-dot {
    width: 9px; height: 9px; border-radius: 50%;
    background: var(--border-medium);
    transition: background 160ms ease, transform 160ms ease;
}
.op-dot.is-done { background: var(--accent); opacity: 0.55; }
.op-dot.is-now { background: var(--accent); transform: scale(1.5); }
.op-dot-label { margin-left: 10px; font-size: 0.78rem; color: var(--text-muted); }

.op-q { font-size: clamp(1.25rem, 3.5vw, 1.7rem); margin: 0 0 8px; color: var(--text-primary); }
.op-help { font-size: 0.94rem; line-height: 1.6; color: var(--text-secondary); margin: 0 0 18px; max-width: 62ch; }
.op-sub {
    font-size: 0.78rem; font-weight: 700; letter-spacing: 0.08em;
    text-transform: uppercase; color: var(--text-muted);
    margin: 22px 0 10px;
}
.op-note { font-size: 0.84rem; color: var(--text-muted); line-height: 1.55; margin-top: 14px; }
.op-loading { padding: 40px; text-align: center; color: var(--text-muted); }
.op-planned-head {
    font-size: 0.78rem; font-weight: 700; letter-spacing: 0.08em;
    text-transform: uppercase; color: var(--text-muted); margin: 22px 0 10px;
}

/* Picture-first choice cards. Everything is a card so no screen needs reading
   twice, and every screen arrives with one already picked. */
.op-cards { display: grid; gap: 12px; grid-template-columns: repeat(auto-fit, minmax(170px, 1fr)); }
.op-cards-sm { grid-template-columns: repeat(auto-fit, minmax(140px, 1fr)); }
.op-card {
    display: flex; flex-direction: column; align-items: flex-start; gap: 4px;
    padding: 16px; min-height: 92px;
    border-radius: 14px; cursor: pointer; text-align: left;
    background: var(--bg-card); border: 2px solid var(--border-light);
    color: var(--text-primary);
    transition: border-color 140ms ease, background 140ms ease, transform 140ms ease;
}
.op-card:hover:not(.is-disabled) { border-color: var(--accent); transform: translateY(-1px); }
.op-card.is-picked { border-color: var(--accent); background: var(--bg-chip); }
/* Planned counters stay visible but unusable: an operator deciding whether to
   wait for one is better served by seeing it than by it being absent. */
.op-card.is-disabled { opacity: 0.45; cursor: not-allowed; border-style: dashed; }
.op-card-wide { min-height: 76px; }
.op-card-icon { font-size: 1.5rem; line-height: 1; }
.op-card-title { font-weight: 700; font-size: 0.98rem; }
.op-card-sub { font-size: 0.82rem; color: var(--text-secondary); line-height: 1.45; }

.op-field { margin: 16px 0; }
.op-field label {
    display: block; font-size: 0.82rem; font-weight: 600;
    color: var(--text-secondary); margin-bottom: 6px;
}
.op-text {
    width: 100%; max-width: 420px; padding: 12px 14px;
    border-radius: 10px; border: 1px solid var(--border-medium);
    background: var(--bg-card); color: var(--text-primary); font-size: 0.96rem;
}

.op-toggle {
    display: flex; align-items: flex-start; gap: 14px;
    padding: 16px; margin-bottom: 10px;
    border-radius: 12px; cursor: pointer;
    border: 1px solid var(--border-light); background: var(--bg-card);
}
.op-toggle input { width: 22px; height: 22px; flex: 0 0 auto; margin-top: 2px; accent-color: var(--accent); }
.op-toggle.is-locked { cursor: default; background: var(--bg-chip); }
.op-toggle-label { display: block; font-weight: 600; color: var(--text-primary); }
.op-toggle-desc { display: block; font-size: 0.86rem; color: var(--text-secondary); margin-top: 3px; }
.op-lock { font-size: 0.74rem; font-weight: 700; color: var(--accent); white-space: nowrap; }

/* The privacy claim. Boxed because it is a promise, not blurb -- and because
   the previous version of this app made a stronger claim than was true. */
.op-privacy-statement {
    margin-top: 20px; padding: 16px;
    border-radius: 12px; background: var(--bg-chip);
    border-left: 3px solid var(--accent);
    font-size: 0.88rem; line-height: 1.6; color: var(--text-secondary);
}

/* ── Camera check ────────────────────────────────────────────────────────── */
.op-check-wrap { display: grid; gap: 20px; grid-template-columns: minmax(260px, 1fr) minmax(260px, 1fr); }
@media (max-width: 720px) { .op-check-wrap { grid-template-columns: 1fr; } }
/* The box keeps the practice camera's own aspect handling; nothing here sets a
   width, height or object-fit of its own. */
.op-check-cam { width: 100%; }
.op-checklist { list-style: none; margin: 0; padding: 0; }
.op-check {
    display: flex; gap: 12px; align-items: flex-start;
    padding: 12px 14px; margin-bottom: 8px;
    border-radius: 10px; border: 1px solid var(--border-light);
    background: var(--bg-card);
}
.op-check-mark {
    flex: 0 0 auto; width: 24px; height: 24px; border-radius: 50%;
    display: inline-flex; align-items: center; justify-content: center;
    font-weight: 700; font-size: 0.82rem;
}
.op-check.is-pass .op-check-mark { background: var(--success); color: #06280f; }
.op-check.is-fail .op-check-mark { background: var(--danger); color: #fff; }
.op-check.is-wait .op-check-mark { background: var(--border-medium); color: var(--text-muted); }
.op-check.is-fail { border-color: var(--danger); }
.op-check-label { display: block; font-weight: 600; font-size: 0.92rem; }
.op-check-note { display: block; font-size: 0.84rem; color: var(--text-secondary); margin-top: 2px; }

.op-try-demo {
    width: 100%; max-width: 320px; border-radius: 14px;
    border: 1px solid var(--border-light); display: block; margin: 0 0 18px;
    transform: scaleX(-1);   /* mirrored to match how the signer sees themselves */
}
.op-pin-setup { margin-top: 24px; padding-top: 20px; border-top: 1px solid var(--border-light); }
.op-pin-fields { display: flex; gap: 12px; flex-wrap: wrap; }
.op-pin-input { max-width: 160px; letter-spacing: 0.3em; font-size: 1.1rem; }

.op-wizard-foot {
    display: flex; align-items: center; gap: 16px;
    margin-top: 26px; padding-top: 20px;
    border-top: 1px solid var(--border-light);
}
.op-next { margin-left: auto; font-size: 1rem; padding: 13px 26px; border-radius: 12px; }
.op-next:disabled { background: var(--disabled-bg); color: var(--disabled-text); cursor: not-allowed; }

.op-msg { font-size: 0.88rem; min-height: 1.2em; color: var(--text-secondary); }
.op-msg.is-ok { color: var(--success); font-weight: 600; }
.op-msg.is-err { color: var(--danger); font-weight: 600; }
.op-msg.is-warn { color: var(--accent); font-weight: 600; }

/* ── Finished ────────────────────────────────────────────────────────────── */
.op-done { text-align: center; padding: 40px 24px; }
.op-done-tick {
    width: 72px; height: 72px; margin: 0 auto 18px;
    border-radius: 50%; background: var(--success); color: #06280f;
    display: flex; align-items: center; justify-content: center;
    font-size: 2.2rem; font-weight: 700;
}
.op-done h2 { margin: 0 0 8px; }
.op-done .op-help { margin-left: auto; margin-right: auto; }
.op-code-box {
    display: inline-block; margin: 20px auto; padding: 18px 26px;
    border-radius: 14px; background: var(--bg-chip);
    border: 2px dashed var(--accent);
}
.op-code-box.is-warn { border-color: var(--danger); border-style: solid; font-size: 0.9rem; max-width: 420px; }
.op-code-label {
    font-size: 0.74rem; font-weight: 700; letter-spacing: 0.12em;
    text-transform: uppercase; color: var(--text-muted);
}
.op-code {
    font-family: ui-monospace, Consolas, monospace;
    font-size: 1.9rem; font-weight: 700; letter-spacing: 0.22em;
    color: var(--text-primary); margin: 6px 0;
}
.op-code-help { font-size: 0.84rem; color: var(--text-secondary); max-width: 32ch; }

/* ── Operator panel ──────────────────────────────────────────────────────── */
.op-panel {
    background: var(--bg-card); border: 1px solid var(--border-light);
    border-radius: 20px; box-shadow: var(--shadow-card); padding: 24px;
}
.op-panel-head { display: flex; align-items: center; justify-content: space-between; margin-bottom: 18px; }
.op-panel-head h2 { margin: 0; }
.op-warn {
    padding: 14px 16px; margin-bottom: 18px; border-radius: 12px;
    background: rgba(196, 135, 42, 0.14); border-left: 3px solid var(--accent);
    font-size: 0.9rem; color: var(--text-primary);
}
.op-status {
    display: grid; gap: 12px; margin-bottom: 18px;
    grid-template-columns: repeat(auto-fit, minmax(140px, 1fr));
}
.op-status > div { padding: 12px 14px; border-radius: 10px; background: var(--bg-chip); }
.op-status span {
    display: block; font-size: 0.72rem; font-weight: 700; letter-spacing: 0.08em;
    text-transform: uppercase; color: var(--text-muted); margin-bottom: 3px;
}
.op-status strong { font-size: 0.96rem; color: var(--text-primary); }
/* Plain numbers, no charts: three figures an operator can act on. */
.op-dash { display: flex; gap: 14px; flex-wrap: wrap; margin-bottom: 22px; }
.op-dash-n {
    flex: 1 1 110px; padding: 16px; border-radius: 12px; text-align: center;
    border: 1px solid var(--border-light);
}
.op-dash-n strong { display: block; font-size: 1.7rem; font-weight: 700; color: var(--text-primary); }
.op-dash-n span { font-size: 0.78rem; color: var(--text-muted); }

.op-row {
    display: flex; align-items: center; gap: 16px;
    padding: 16px 0; border-top: 1px solid var(--border-light);
}
.op-row-text { flex: 1 1 auto; min-width: 0; }
.op-row-label { font-weight: 600; color: var(--text-primary); }
.op-row-desc { font-size: 0.86rem; color: var(--text-secondary); margin-top: 2px; }

/* ── PIN pad ─────────────────────────────────────────────────────────────── */
/* A page-level overlay so the hidden gesture can raise it from inside a locked
   kiosk, where no navigation exists. z-index above the kiosk layout (1000). */
.op-pin-overlay {
    position: fixed; inset: 0; z-index: 2000;
    display: flex; align-items: center; justify-content: center;
    padding: 20px; background: rgba(12, 10, 8, 0.72);
}
.op-pin-card {
    width: 100%; max-width: 340px; padding: 26px 24px;
    border-radius: 20px; background: var(--bg-card);
    text-align: center; box-shadow: 0 20px 60px rgba(0,0,0,0.35);
}
.op-pin-title { font-size: 1.15rem; font-weight: 700; color: var(--text-primary); }
.op-pin-sub { font-size: 0.86rem; color: var(--text-secondary); margin: 6px 0 18px; }
.op-pin-dots { display: flex; gap: 10px; justify-content: center; margin-bottom: 20px; }
.op-pin-dot {
    width: 12px; height: 12px; border-radius: 50%;
    border: 2px solid var(--border-medium);
}
.op-pin-dot.is-filled { background: var(--accent); border-color: var(--accent); }
.op-pin-keys { display: grid; grid-template-columns: repeat(3, 1fr); gap: 10px; margin-bottom: 14px; }
/* 64px keys: this is tapped standing up, one-handed, on a tablet. */
.op-pin-key {
    height: 64px; border-radius: 12px; cursor: pointer;
    border: 1px solid var(--border-medium); background: var(--bg-card);
    color: var(--text-primary); font-size: 1.35rem; font-weight: 600;
}
.op-pin-key:hover { background: var(--bg-toggle); }
.op-pin-key.is-ok { background: var(--accent); color: #fff; border-color: var(--accent); }
.op-pin-msg { margin-bottom: 10px; }

/* ── In kiosk mode, hide the operator ENTRY POINTS a visitor must not find ── */
/* The home-page CTA goes; the operator SCREEN does not. It used to be in this
   rule, which silently killed the whole way out of a locked kiosk: the gear
   opened the PIN pad, the PIN was accepted, and then the panel rendered behind
   `display: none !important`.
   Hiding it here was never what protected it. #operatorScreen is display:none in
   the markup and is only ever shown by openPanel (after the PIN) or by the home
   CTA (hidden below), the sidebar is gone in kiosk mode, and entering the kiosk
   runs navigateTo's sweep which hides it. The PIN is the gate. */
.kiosk-mode .hp-cta-kiosk { display: none !important; }


/* ── Operator round 2 ────────────────────────────────────────────────────── */

/* What to do once the camera check is green. Without this the check told an
   operator that the spot was good and then left them to guess that marking it
   is the point. */
.op-check-done {
    margin-top: 14px; padding: 14px 16px;
    border-radius: 12px; background: rgba(76, 175, 128, 0.14);
    border-left: 3px solid var(--success);
    font-size: 0.88rem; line-height: 1.6; color: var(--text-primary);
}
.op-try-btn { margin: 4px 0 8px; }

/* The gloss and transcript toggles stay in the camera topbar's flex row, where
   they came from. They were briefly floated over the camera to keep it large,
   which put them in contention with the operator control and the transcript
   panel for the same corner -- three things in one place, and the transcript
   became impossible to close. The topbar costs about 30px of camera height,
   which is what normal Live has always spent, and nothing can overlap. */
.kiosk-mode #debugToggleBtn,
.kiosk-mode #transcriptToggleBtn { font-size: 1.15rem; opacity: 0.75; }


/* ══════════════════════════════════════════════════════════════
   KIOSK LANDING — one page per configured counter
   The first thing a visitor sees. Everything is oversized: it is read while
   walking up to a desk, possibly by a child, possibly by someone who has never
   seen one of these. One action, impossible to miss.
   ══════════════════════════════════════════════════════════════ */
.kl-wrap {
    max-width: 680px; margin: 0 auto; padding: 48px 24px;
    text-align: center;
}
.kl-icon { font-size: 4rem; line-height: 1; margin-bottom: 12px; }
.kl-title {
    font-size: clamp(1.9rem, 6vw, 3rem); font-weight: 700;
    line-height: 1.1; margin: 0 0 14px; color: var(--text-primary);
}
.kl-desc {
    font-size: clamp(1rem, 2.6vw, 1.2rem); line-height: 1.6;
    color: var(--text-secondary); margin: 0 auto 32px; max-width: 46ch;
}
/* The one button. 88px minimum is the signer-facing floor: this gets pressed by
   people standing up, in a hurry, sometimes by children. */
.kl-start {
    display: block; width: 100%; max-width: 440px; margin: 0 auto;
    min-height: 96px; padding: 20px 32px;
    border: none; border-radius: 18px; cursor: pointer;
    background: var(--accent-gradient); color: #fff;
    font-size: clamp(1.2rem, 3.4vw, 1.6rem); font-weight: 700;
    box-shadow: 0 10px 30px rgba(196, 135, 42, 0.32);
}
.kl-start:hover { filter: brightness(1.05); }
.kl-start:active { transform: translateY(1px); }
.kl-hint {
    font-size: 0.94rem; line-height: 1.6; color: var(--text-secondary);
    margin: 20px auto 0; max-width: 40ch;
}
.kl-examples { margin-top: 34px; }
.kl-examples-head {
    font-size: 0.74rem; font-weight: 700; letter-spacing: 0.14em;
    text-transform: uppercase; color: var(--text-muted); margin-bottom: 12px;
}
.kl-chip {
    display: inline-block; margin: 4px;
    padding: 9px 15px; border-radius: 999px;
    background: var(--bg-chip); border: 1px solid var(--border-light);
    font-family: ui-monospace, Consolas, monospace;
    font-size: 0.86rem; letter-spacing: 0.04em; color: var(--text-primary);
}


/* ── The kiosk landing owns the screen ───────────────────────────────────── */
/* Counter cards render into BOTH #liveScenarios and #demoScenarios, and
   #demoScenarios carries no inline display:none -- so hiding ids one at a time
   leaked cards under the Start button and would leak again the next time a
   container is added to .home-screen. Hiding the whole block declaratively
   cannot lose a race with a later render, which is why this is CSS and not JS.
   phaseConvo lives outside .home-screen, so a conversation is unaffected. */
body.kiosk-landing #heroSection,
body.kiosk-landing #demoIntro,
body.kiosk-landing .home-screen { display: none !important; }
body.kiosk-landing #kioskLanding { display: block !important; }

/* The brand on the landing page. Same treatment as the header wordmark -- warm
   display serif with the teal-to-violet gradient -- because on a locked kiosk
   the header is hidden and this is the only place the product is named. */
.kl-brand {
    font-family: 'Fraunces', Georgia, 'Times New Roman', serif;
    font-optical-sizing: auto;
    font-size: clamp(2.2rem, 7vw, 3.4rem);
    font-weight: 700;
    letter-spacing: 0.3px;
    line-height: 1.05;
    margin-bottom: 20px;
    color: var(--logo-solid);
}
@supports ((-webkit-background-clip: text) or (background-clip: text)) {
    .kl-brand {
        background: var(--logo-gradient);
        -webkit-background-clip: text;
        background-clip: text;
        -webkit-text-fill-color: transparent;
    }
}
/* The counter icon is secondary to the brand now, so it sits smaller. */
.kl-wrap .kl-icon { font-size: 2.6rem; margin-bottom: 8px; }

/* ── The way out of a locked kiosk ───────────────────────────────────────── */
/* Hidden unless the kiosk is locked: in normal use the header already has a
   settings button and this would be a second one.
   Understated on purpose. The PIN is the security boundary, so this only needs
   to be FINDABLE by a staff member who did not set the tablet up -- not
   advertised to every visitor. A kiosk outlives whoever configured it, and a
   hidden-only gesture leaves next week's shift with no way in. 44px keeps the
   touch target accessible even though it reads as small. */
.op-exit-btn { display: none; }

/* On the LANDING, floating top-right: that corner is empty there, and a visitor
   is not looking at it. */
body.kiosk-landing #opExitBtn {
    display: flex; align-items: center; justify-content: center;
    position: fixed; top: 10px; right: 10px; z-index: 40;
    width: 44px; height: 44px; padding: 0;
    border: none; border-radius: 12px; cursor: pointer;
    background: rgba(0, 0, 0, 0.14); color: rgba(60, 45, 30, 0.55);
    font-size: 1.05rem; line-height: 1;
    transition: background 160ms ease, color 160ms ease;
}
body.kiosk-landing #opExitBtn:hover,
body.kiosk-landing #opExitBtn:focus-visible {
    background: rgba(0, 0, 0, 0.3); color: #fff;
}

/* DURING a conversation, in the camera topbar's flex row. No position, no
   z-index: flex guarantees it cannot overlap the toggles, and because it is not
   floating it also cannot cover the transcript panel's minimize button -- which
   is what made the transcript impossible to close. */
.kiosk-mode #opExitBtnBar {
    display: inline-flex; align-items: center; justify-content: center;
    width: 32px; height: 32px; padding: 0;
    border: none; border-radius: 8px; cursor: pointer;
    background: var(--bg-chip); color: var(--text-muted);
    font-size: 0.95rem; line-height: 1;
}
.kiosk-mode #opExitBtnBar:hover,
.kiosk-mode #opExitBtnBar:focus-visible {
    background: var(--bg-toggle-hover); color: var(--text-primary);
}

/* The panel leads with what someone arriving here most likely wants, rather
   than burying it at the end of a settings list. */
.op-primary-actions {
    padding: 18px; margin-bottom: 18px;
    border-radius: 14px; background: var(--bg-chip);
    border: 1px solid var(--border-light);
}
.op-primary-actions .op-cta { width: 100%; max-width: 420px; }
.op-primary-actions .op-note { margin-top: 10px; }
.op-note code {
    padding: 1px 5px; border-radius: 4px; background: var(--bg-chip);
    font-family: ui-monospace, Consolas, monospace; font-weight: 700;
}


/* ── Nothing in the camera topbar offers a signer a settings route ───────── */
/* #cameraTitle is a LINK to the Settings page ("Change in Settings"). Useful on
   your own device, wrong on a counter tablet -- and which camera is in use is
   not a visitor's concern anyway. The navigateTo guard already refuses the
   destination; this removes the invitation. */
.kiosk-mode #cameraTitle { display: none !important; }
/* Backstop: the inline camera/zone panel, in case toggleSettings is ever
   reached by some path other than its (hidden) button. */
.kiosk-mode #settingsPanel { display: none !important; }


/* A kiosk that cannot see. Addressed to the visitor, who is the one standing
   there and cannot fix it; the staff instruction is secondary and small. Red
   rather than amber: this is not a hint, the kiosk does not work. */
.kl-fault {
    max-width: 560px; margin: 0 auto 26px;
    padding: 16px 18px; border-radius: 14px;
    background: rgba(255, 68, 68, 0.1);
    border: 1px solid var(--danger);
    font-size: 1rem; line-height: 1.6; color: var(--text-primary);
    text-align: left;
}
.kl-fault-staff {
    display: block; margin-top: 10px;
    font-size: 0.82rem; color: var(--text-secondary);
}


/* ── One message at a time on a kiosk conversation ───────────────────────── */
/* Three things spoke at once here: a pacing hint over the camera, "Camera will
   start when you begin" in the middle of it, and "Press ... to begin" below.
   On a kiosk the last two are not just noise, they are FALSE -- the landing's
   Start tap already began the conversation and opened the camera, so there is
   nothing left to press and nothing waiting to start.
   The pacing hint survives because it is the one that is still true while
   someone is signing. Scoped to .kiosk-mode: normal Live still needs both, since
   there the camera really is waiting for a button. */
.kiosk-mode #cameraPlaceholder,
.kiosk-mode .status-bar .prompt { display: none !important; }


/* ── Two controls, both of them the signer's ─────────────────────────────── */
/* Three competed in good framing: Restart, a highlighted "Start Signing", and
   New Conversation. The redundant one was "Start Signing" -- the segmenter was
   already listening (see _armSigningGate), so it was a manual trigger for
   something automatic.
   RESTART STAYS. Its handler clears the current utterance's glosses and caption
   and nothing else, so it is the signer's retry -- and with imperfect
   recognition that is their only correction. It is relabelled, because
   "Restart" reads as restarting the conversation.
   btnRestart itself is hidden because btnStart plays both roles (see its click
   handler); hiding it is not a policy, just the existing arrangement. */
.kiosk-mode #btnRestart { display: none !important; }
