/* ============================================
   MEMFLEX - Spaced Repetition Flashcard App
   Mobile-first responsive CSS
   ============================================ */

/* CSS Variables — "calm & fresh" sage theme, light (default) + dark.
   All colors flow through these semantic tokens; the dark variant only
   overrides tokens, never component rules. data-theme is set on <html>
   by the inline boot script (localStorage 'memflex_theme', falling back
   to prefers-color-scheme). */
:root {
    color-scheme: light;

    /* Sage greens */
    --primary-color: #5e8d6a;          /* buttons, links, accents */
    --primary-dark: #4b7456;
    --on-primary: #ffffff;             /* text on primary/secondary buttons */
    --secondary-color: #87967f;        /* muted sage-gray actions */
    --secondary-dark: #71816b;

    /* Feedback hues — softened: terracotta instead of alarm-red,
       honey amber instead of taxi yellow */
    --success-color: #3e8e57;
    --danger-color: #c95d4d;
    --danger-dark: #b04a3b;
    --warning-color: #c9892e;

    /* Warm paper neutrals */
    --bg-color: #f4f1e8;               /* app background — warm cream */
    --bg-secondary: #fbf9f2;           /* panels, menus, inputs */
    --card-bg: #ffffff;                /* cards float on top */
    --text-color: #34403a;             /* deep green-gray, softer than black */
    --text-muted: #7c887e;
    --border-color: #e3ded0;           /* hairlines + input borders */
    --hover-bg: rgba(94, 141, 106, 0.08);   /* gentle sage wash on hover */
    --overlay: rgba(52, 64, 58, 0.45);       /* modal scrim */

    /* Softer geometry */
    --border-radius: 18px;
    --border-radius-sm: 12px;
    --shadow: 0 2px 10px rgba(98, 116, 102, 0.10);
    --shadow-lg: 0 12px 32px rgba(98, 116, 102, 0.16);

    --header-height: 60px;
    --transition: all 0.3s ease;
}

/* Dark & cozy: green-tinted charcoal instead of the old navy. Same sage
   family, lifted for contrast on dark surfaces. */
[data-theme="dark"] {
    color-scheme: dark;

    --primary-color: #7fae8b;
    --primary-dark: #689676;
    --on-primary: #15201a;             /* dark ink on mint buttons */
    --secondary-color: #5f6f5f;
    --secondary-dark: #71816b;

    --success-color: #82c397;
    --danger-color: #e08573;
    --danger-dark: #d0705e;
    --warning-color: #ddab57;

    --bg-color: #161c17;
    --bg-secondary: #1e261f;
    --card-bg: #28332a;
    --text-color: #e4eae2;
    --text-muted: #95a496;
    --border-color: #36433a;
    --hover-bg: rgba(143, 191, 154, 0.10);
    --overlay: rgba(8, 12, 9, 0.6);

    --shadow: 0 2px 10px rgba(0, 0, 0, 0.35);
    --shadow-lg: 0 12px 32px rgba(0, 0, 0, 0.45);
}

/* Reset & Base */
*, *::before, *::after {
    box-sizing: border-box;
    margin: 0;
    padding: 0;
}

html {
    font-size: 16px;
    -webkit-tap-highlight-color: transparent;
}

body {
    /* ui-rounded gives the friendly rounded face on iOS/Safari (SF Rounded);
       everywhere else falls back to the regular system stack. */
    font-family: ui-rounded, -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, sans-serif;
    background-color: var(--bg-color);
    color: var(--text-color);
    line-height: 1.6;
    min-height: 100vh;
    overflow-x: hidden;
}

#app {
    min-height: 100vh;
    display: flex;
    flex-direction: column;
}

/* Header */
.app-header {
    position: fixed;
    top: 0;
    left: 0;
    right: 0;
    height: var(--header-height);
    background: var(--bg-secondary);
    display: flex;
    align-items: center;
    justify-content: space-between;
    padding: 0 16px;
    z-index: 100;
    box-shadow: var(--shadow);
}

.app-header h1 {
    font-size: 1.25rem;
    font-weight: 700;
    letter-spacing: 0.01em;
    color: var(--primary-color);
}
/* Little sprout mascot-mark next to the app name — sage/growth motif that
   recurs in empty states and the finish screen. */
.app-header h1::before {
    content: '🌱 ';
    font-size: 1.05em;
}

.icon-btn {
    background: transparent;
    border: none;
    color: var(--text-color);
    width: 44px;
    height: 44px;
    display: flex;
    align-items: center;
    justify-content: center;
    cursor: pointer;
    border-radius: var(--border-radius-sm);
    transition: var(--transition);
}

.icon-btn:hover, .icon-btn:focus {
    background: var(--hover-bg);
}

.icon-btn svg {
    width: 24px;
    height: 24px;
}

/* The two header controls are anchored to the VIEWPORT rather than laid out by
   the header's flexbox. Belt-and-braces with the .side-menu transform fix below:
   that removes the thing that made the page scroll sideways, and this makes these
   two immune to it ever happening again from some other source. A horizontally
   scrolled page used to carry the header's right-hand end — and both of these —
   out of view, while every style on them measured perfectly correct, because they
   WERE correct. Verified in view at 390 / 885 / 1440 px.
   z-index sits above .app-header (100) and below .menu-overlay (250) and
   .side-menu (300), so an open menu still covers them as before. */
.app-header #theme-btn,
.app-header #menu-btn {
    position: fixed;
    top: calc(env(safe-area-inset-top, 0px) + ((var(--header-height) - 44px) / 2));
    z-index: 200;
}
.app-header #menu-btn  { right: 12px; }
.app-header #theme-btn { right: 60px; }

/* Theme toggle sits with the menu button on the right; the auto margin keeps
   the title pinned left even though the header is space-between. */
#theme-btn {
    margin-left: auto;
    font-size: 1.15rem;
}

.hidden {
    display: none !important;
}

/* Main Content */
#main-content {
    margin-top: var(--header-height);
    flex: 1;
    padding: 20px;
    padding-bottom: 40px;
}

/* Screens */
.screen {
    display: none;
    animation: fadeIn 0.3s ease;
}

.screen.active {
    display: block;
}

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

/* Home Screen */
.welcome-section {
    text-align: center;
    padding: 24px 0;
}

.welcome-section h2 {
    font-size: 1.75rem;
    margin-bottom: 8px;
    color: var(--primary-color);
}

.welcome-section p {
    color: var(--text-muted);
}

/* Role-based visibility — body class set by app.js after login. */
body:not(.role-admin)   .role-admin   { display: none !important; }
body.role-student       .role-teacher { display: none !important; }

/* Side-menu "signed in as" row. */
.menu-current-user {
    padding: 8px 16px 12px;
    border-bottom: 1px solid var(--border-color);
    font-size: 0.9rem;
    color: var(--text-muted);
}
.menu-current-user strong { color: var(--text-color); }

/* Read-only card editor (deck shared without edit permission). */
#card-editor-screen.read-only::before {
    content: "Read-only — you don't have edit permission on this deck.";
    display: block;
    background: var(--warning-color, #d97706);
    color: var(--on-primary);
    padding: 10px 14px;
    border-radius: var(--border-radius-sm);
    margin-bottom: 16px;
    font-weight: 500;
    text-align: center;
}
#card-editor-screen.read-only #card-form input[readonly],
#card-editor-screen.read-only #card-form textarea[readonly] {
    background: var(--bg-secondary);
    color: var(--text-muted);
    cursor: not-allowed;
    opacity: 0.85;
}
#card-editor-screen.read-only #card-form select[disabled] {
    opacity: 0.6;
    cursor: not-allowed;
}
/* Hide every button that mutates state on a read-only deck. */
#card-editor-screen.read-only button[type="submit"],
#card-editor-screen.read-only #delete-card-btn,
#card-editor-screen.read-only .upload-btn,
#card-editor-screen.read-only #audio-question-record-btn,
#card-editor-screen.read-only #audio-question-clear-btn,
#card-editor-screen.read-only #audio-answer-record-btn,
#card-editor-screen.read-only #audio-answer-clear-btn,
#card-editor-screen.read-only #add-hotspot-btn,
#card-editor-screen.read-only #cancel-hotspot-btn,
#card-editor-screen.read-only #hs-retrace-btn,
#card-editor-screen.read-only #hs-delete-btn,
#card-editor-screen.read-only .hotspot-delete,
#card-editor-screen.read-only .hs-audio-btn,
#card-editor-screen.read-only #hotspot-autogen-audio,
#card-editor-screen.read-only .screen-edit-toggle,
#card-editor-screen.read-only #weight-section {
    display: none !important;
}
/* Block clicks on the hotspot canvas so users can't open the edit-text
   prompt by tapping a polygon. */
#card-editor-screen.read-only #hotspot-editor-canvas {
    pointer-events: none;
}

/* Share modal */
.modal {
    position: fixed;
    inset: 0;
    background: var(--overlay);
    display: flex;
    align-items: center;
    justify-content: center;
    z-index: 9000;
    padding: 16px;
}
.modal.hidden { display: none; }
.modal-card {
    background: var(--bg-secondary);
    border-radius: var(--border-radius);
    box-shadow: var(--shadow-lg);
    width: 100%;
    max-width: 520px;
    max-height: 90vh;
    overflow-y: auto;
}
.modal-head {
    display: flex;
    align-items: center;
    justify-content: space-between;
    padding: 12px 16px;
    border-bottom: 1px solid var(--border-color);
}
.modal-head h3 { margin: 0; }
.modal-body { padding: 16px; }
.share-add-row {
    display: flex;
    flex-wrap: wrap;
    gap: 8px;
    align-items: center;
}
.share-add-row select { padding: 6px 8px; }
.share-list {
    display: flex;
    flex-direction: column;
    gap: 6px;
    margin-bottom: 12px;
}
.share-list .share-row {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 8px;
    padding: 6px 8px;
    background: var(--card-bg);
    border-radius: 4px;
}
.share-row .share-target { font-weight: 500; }
.share-row .share-perm   { color: var(--text-muted); font-size: 0.85rem; }

/* Cascade-to-children checkbox in the share modal, shown only for container
   decks (those that have child phase decks under them). */
.share-cascade-row {
    display: flex;
    align-items: center;
    gap: 8px;
    margin-top: 12px;
    padding: 8px 10px;
    background: var(--card-bg);
    border-radius: var(--border-radius-sm);
    font-size: 0.9rem;
}
.share-cascade-row.hidden { display: none; }
.share-cascade-row input[type="checkbox"] { margin: 0; }

/* Groups screen */
.groups-list {
    display: flex;
    flex-direction: column;
    gap: 16px;
    margin-top: 16px;
}
.group-card {
    background: var(--bg-secondary);
    border-radius: var(--border-radius);
    padding: 12px;
}
.group-card-head {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 8px;
    margin-bottom: 8px;
}
.group-members {
    display: flex;
    flex-direction: column;
    gap: 4px;
    margin-bottom: 8px;
}
.group-member-row {
    display: flex;
    align-items: center;
    justify-content: space-between;
    padding: 4px 8px;
    background: var(--card-bg);
    border-radius: 4px;
}
.group-add-member {
    display: flex;
    gap: 8px;
    align-items: center;
}
.form-row { display: flex; gap: 8px; }
.form-row input { flex: 1; }

/* Users management table */
.users-table {
    width: 100%;
    border-collapse: collapse;
    margin-top: 16px;
}
.users-table th,
.users-table td {
    text-align: left;
    padding: 8px 12px;
    border-bottom: 1px solid var(--border-color);
}
.users-table select { padding: 4px 6px; }

/* Login Screen */
.login-container {
    max-width: 400px;
    margin: 40px auto 0;
    padding: 0 8px;
}

.login-form {
    background: var(--bg-secondary);
    padding: 24px;
    border-radius: var(--border-radius);
    box-shadow: var(--shadow);
    display: flex;
    flex-direction: column;
    gap: 16px;
}

.login-form .form-group {
    display: flex;
    flex-direction: column;
    gap: 6px;
    margin: 0;
}

.login-form label {
    font-weight: 500;
    font-size: 0.95rem;
    color: var(--text-muted);
}

.login-form input {
    width: 100%;
    padding: 12px 14px;
    border: 2px solid var(--border-color);
    border-radius: var(--border-radius-sm);
    background: var(--card-bg);
    color: var(--text-color);
    font-size: 1rem;
}

.login-form input:focus {
    outline: none;
    border-color: var(--primary-color);
}

.login-form button[type="submit"] {
    margin-top: 4px;
    width: 100%;
}

.login-error {
    background: color-mix(in srgb, var(--danger-color) 12%, transparent);
    color: var(--danger-color);
    border: 1px solid color-mix(in srgb, var(--danger-color) 40%, transparent);
    padding: 10px 12px;
    border-radius: var(--border-radius-sm);
    font-size: 0.9rem;
}

.login-success {
    background: color-mix(in srgb, var(--success-color) 12%, transparent);
    color: var(--success-color);
    border: 1px solid color-mix(in srgb, var(--success-color) 40%, transparent);
    padding: 10px 12px;
    border-radius: var(--border-radius-sm);
    font-size: 0.9rem;
}

.login-links {
    display: flex;
    justify-content: space-between;
    gap: 12px;
    font-size: 0.88rem;
}
.login-links a { color: var(--primary-color); }

#register-form h3, #forgot-form h3, #reset-form h3 { margin: 0; }

.student-selector {
    background: var(--bg-secondary);
    padding: 16px;
    border-radius: var(--border-radius);
    margin-bottom: 24px;
}

.student-selector label {
    display: block;
    margin-bottom: 8px;
    font-weight: 500;
}

.student-selector select {
    width: 100%;
    padding: 12px;
    border: 1px solid var(--border-color);
    border-radius: var(--border-radius-sm);
    background: var(--card-bg);
    color: var(--text-color);
    font-size: 1rem;
    margin-bottom: 12px;
}

.deck-list-container h3 {
    margin-bottom: 16px;
    font-size: 1.25rem;
}

/* Search/filter row sits between the heading and the deck list. */
.deck-filter-row {
    position: relative;
    margin-bottom: 16px;
}

.deck-filter {
    width: 100%;
    padding: 10px 36px 10px 14px;   /* room on the right for the clear button */
    font-size: 1rem;
    background: var(--bg-secondary);
    color: var(--text-color);
    border: 1px solid var(--border-color);
    border-radius: var(--border-radius);
    outline: none;
    -webkit-appearance: none;        /* iOS strips its native search styling */
}

.deck-filter:focus {
    border-color: var(--primary-color);
}

.deck-filter-clear {
    position: absolute;
    right: 6px;
    top: 50%;
    transform: translateY(-50%);
    width: 26px;
    height: 26px;
    line-height: 1;
    font-size: 0.9rem;
    color: var(--text-muted);
    background: transparent;
    border: none;
    cursor: pointer;
}

.deck-filter-clear:hover { color: var(--text-color); }
.deck-filter-clear.hidden { display: none; }

/* Temporary mic diagnostics line (debugging the "stuck on Listening" freeze). */
.mic-debug {
    font-family: ui-monospace, Menlo, Consolas, monospace;
    /* Diagnostics must be readable at a glance on a phone — full-contrast
       text on the card surface, slightly larger than before. */
    font-size: 0.8rem;
    font-weight: 600;
    line-height: 1.4;
    color: var(--text-color);
    background: var(--card-bg);
    border: 1px solid var(--border-color);
    border-radius: 6px;
    padding: 6px 10px;
    margin: 4px 0;
    white-space: pre-wrap;
    word-break: break-word;
}
.mic-debug.hidden { display: none; }

/* In-deck card search panel (toggled by the Search button in .deck-actions). */
.card-search-panel {
    margin-bottom: 16px;
}
.card-search-panel.hidden { display: none; }
.card-search-panel #card-search-results:not(:empty) {
    margin-top: 10px;
}

/* Breadcrumb appears at the top of the deck list when drilled into a
   container. Shows ← Back + the container's name. */
.deck-breadcrumb {
    display: flex;
    align-items: center;
    gap: 12px;
    margin-bottom: 12px;
    padding: 8px 4px;
}
.deck-breadcrumb.hidden { display: none; }
.deck-breadcrumb-label {
    font-weight: 600;
    color: var(--text-color);
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

/* Container deck rows: subtle visual cue that they drill in instead of opening
   a study session. Border on the left + slightly different background. */
.deck-item.is-container {
    border-left: 3px solid var(--primary-color);
    background: color-mix(in srgb, var(--primary-color) 6%, var(--card-bg));
}
.deck-item.is-container .deck-item-arrow {
    font-size: 1.5rem;
    color: var(--primary-color);
}

.deck-list {
    display: flex;
    flex-direction: column;
    gap: 14px;
    margin-bottom: 20px;
}

.deck-item {
    background: var(--card-bg);
    padding: 18px 20px;
    border-radius: var(--border-radius);
    border-left: 4px solid transparent;
    box-shadow: var(--shadow);
    cursor: pointer;
    transition: var(--transition);
    display: flex;
    justify-content: space-between;
    align-items: center;
}

/* Rotating pastel accents so the deck list reads as a friendly collection
   instead of identical gray rows. Container/last-used keep their own accent. */
.deck-item:nth-child(4n+1):not(.is-container):not(.last-used) { border-left-color: #8fbf8f; }
.deck-item:nth-child(4n+2):not(.is-container):not(.last-used) { border-left-color: #e0b884; }
.deck-item:nth-child(4n+3):not(.is-container):not(.last-used) { border-left-color: #92b4d4; }
.deck-item:nth-child(4n+4):not(.is-container):not(.last-used) { border-left-color: #c5a3cf; }

.deck-item:hover, .deck-item:focus {
    background: color-mix(in srgb, var(--primary-color) 7%, var(--card-bg));
    transform: translateX(4px);
    box-shadow: var(--shadow-lg);
}

.deck-item-info h4 {
    font-size: 1rem;
    margin-bottom: 4px;
}

.deck-item-info span {
    font-size: 0.875rem;
    color: var(--text-muted);
}

.deck-item-arrow {
    color: var(--text-muted);
    font-size: 1.5rem;
}

/* Buttons — friendly pills with a small lift on hover and a squish on press. */
.btn {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    gap: 8px;
    padding: 12px 24px;
    border: none;
    border-radius: 999px;
    font-size: 1rem;
    font-weight: 600;
    font-family: inherit;
    cursor: pointer;
    transition: var(--transition);
    text-decoration: none;
}

.btn:hover, .btn:focus {
    transform: translateY(-1px);
    box-shadow: var(--shadow);
}

.btn:active {
    transform: translateY(0) scale(0.97);
    box-shadow: none;
}

/* Disabled buttons read as inert: greyed out, no hover lift, no-go cursor.
   Used e.g. while hot-spot detection is running and Save is blocked. */
.btn:disabled, .btn[disabled] {
    opacity: 0.45;
    filter: grayscale(45%);
    cursor: not-allowed;
}
.btn:disabled:hover, .btn[disabled]:hover,
.btn:disabled:focus, .btn[disabled]:focus,
.btn:disabled:active, .btn[disabled]:active {
    transform: none;
    box-shadow: none;
}

.btn-primary {
    background: var(--primary-color);
    color: var(--on-primary);
}

.btn-primary:hover, .btn-primary:focus {
    background: var(--primary-dark);
}

.btn-secondary {
    background: var(--secondary-color);
    color: var(--on-primary);
}

.btn-secondary:hover, .btn-secondary:focus {
    background: var(--secondary-dark);
}

.btn-danger {
    background: var(--danger-color);
    color: var(--on-primary);
}

.btn-danger:hover, .btn-danger:focus {
    background: var(--danger-dark);
}

.btn-large {
    padding: 16px 32px;
    font-size: 1.125rem;
    width: 100%;
}

.btn-small {
    padding: 8px 16px;
    font-size: 0.875rem;
}

.btn-icon {
    font-size: 1.25rem;
}

/* Deck Screen */
.deck-header {
    text-align: center;
    padding: 8px 0 16px;
    border-bottom: 1px solid var(--border-color);
    margin-bottom: 24px;
}

/* Edit / Search / Settings icons in their own right-aligned row above the
   stats (the deck name itself now lives only in the app header, and these
   replace the old full-width labelled buttons). Kept on a separate line so
   they never overlap the "N cards / N mastered" text below. */
.deck-header-tools {
    display: flex;
    justify-content: flex-end;
    gap: 4px;
    margin-bottom: 6px;
}
.deck-header-tool {
    width: 40px;
    height: 40px;
    color: var(--text-muted);
}
.deck-header-tool svg { width: 22px; height: 22px; }
.deck-header-tool.active { color: var(--primary-color); }

.deck-stats {
    display: flex;
    justify-content: center;
    gap: 24px;
    color: var(--text-muted);
}

.deck-actions {
    display: flex;
    flex-direction: column;
    gap: 12px;
    margin-bottom: 24px;
}

.test-options {
    background: var(--bg-secondary);
    padding: 16px;
    border-radius: var(--border-radius);
    margin-bottom: 24px;
}

.test-options h4 {
    margin-bottom: 16px;
    font-size: 1.1rem;
}

.settings-row {
    display: flex;
    gap: 24px;
    margin-bottom: 16px;
    flex-wrap: wrap;
}

.settings-row .settings-group {
    flex: 1;
    min-width: 160px;
}

.settings-group {
    margin-bottom: 16px;
}

/* Inline radio used in the Add Audio & Script modal: "Which side has the foreign
   language? [Question] [Answer]" with both options on one line. */
.radio-inline {
    display: inline-flex;
    align-items: center;
    gap: 6px;
    margin-right: 12px;
    font-weight: normal;
}

.add-audio-status {
    margin: 12px 0;
    min-height: 1.2em;
    font-size: 0.9rem;
    color: var(--text-muted);
}

/* Generate Cards (AI) wizard */
.cardgen-input { width: 100%; box-sizing: border-box; resize: vertical; }
.cardgen-name { width: 100%; box-sizing: border-box; }
.cardgen-advanced { margin: 10px 0; font-size: 0.9rem; }
.cardgen-advanced summary { cursor: pointer; color: var(--text-muted); }
.cardgen-review-list { max-height: 40vh; overflow-y: auto; margin: 8px 0; }
.cardgen-row {
    display: flex;
    align-items: center;
    gap: 8px;
    padding: 4px 0;
    border-bottom: 1px solid var(--border-color);
}
.cardgen-native { flex: 0 0 35%; font-weight: 600; word-break: break-word; }
.cardgen-arrow { color: var(--text-muted); }
.cardgen-target-input { flex: 1 1 auto; min-width: 0; }
.cardgen-script { color: var(--text-muted); white-space: nowrap; }

.parent-deck-filter { width: 100%; box-sizing: border-box; margin-bottom: 6px; }

/* API Spend panel (AI Models & Tokens modal, admin) */
.usage-breakdown { margin: 8px 0; }
.usage-total { font-weight: 600; margin: 6px 0 12px; }
.usage-row {
    display: flex; justify-content: space-between; gap: 12px;
    padding: 3px 0; border-bottom: 1px solid var(--border-color); font-size: 0.9rem;
}
.usage-subrow { border-bottom: none; padding: 0 0 3px; }
.usage-detail { color: var(--text-muted); font-size: 0.78rem; }

/* Leech mnemonic hint (💡) under the study card. */
.hint-row { display: flex; flex-direction: column; align-items: center; gap: 8px; margin: 6px 0 2px; }
.mnemonic-display {
    max-width: 34rem; padding: 10px 14px; border-radius: 10px;
    background: #fff8e1; border: 1px solid #f0d98a;   /* soft-amber "hint" card */
    color: #4a3b00;                                    /* dark amber — readable on the amber box */
    font-size: 0.92rem; line-height: 1.4; text-align: center;
}
/* Paging row under a study hint: ‹ 2/3 › ＋Another */
.hint-nav { display: flex; align-items: center; gap: 8px; }
.hint-counter { font-size: 0.85rem; color: #666; min-width: 2.5rem; text-align: center; }

/* Editor list of a card's saved memory hints, each deletable one-by-one. */
#card-mnemonics-list { display: flex; flex-direction: column; gap: 6px; }
.mnemonic-item {
    display: flex; align-items: center; gap: 8px;
    padding: 8px 10px; border-radius: 8px;
    background: #fff8e1; border: 1px solid #f0d98a;
}
.mnemonic-item-text { flex: 1; color: #4a3b00; font-size: 0.9rem; line-height: 1.4; }

.settings-label {
    display: block;
    font-weight: 500;
    margin-bottom: 8px;
    font-size: 0.9rem;
}

.settings-hint {
    color: var(--text-muted);
    font-size: 0.8rem;
    margin: 4px 0 8px;
}

.radio-group {
    display: flex;
    flex-direction: column;
    gap: 10px;
}

.radio-group label {
    display: flex;
    align-items: center;
    gap: 8px;
    cursor: pointer;
    font-size: 0.9rem;
}

.radio-group input[type="radio"] {
    width: 18px;
    height: 18px;
    accent-color: var(--primary-color);
}

/* Sliders */
.slider-row {
    display: flex;
    align-items: center;
    gap: 10px;
}

.slider-row input[type="range"] {
    flex: 1;
    accent-color: var(--primary-color);
    height: 6px;
}

.slider-hint {
    font-size: 0.75rem;
    color: var(--text-muted);
    white-space: nowrap;
}

/* Advanced settings */
.advanced-settings {
    margin-top: 8px;
    border-top: 1px solid var(--border-color);
    padding-top: 12px;
}

.advanced-settings summary {
    cursor: pointer;
    color: var(--text-muted);
    font-size: 0.9rem;
    margin-bottom: 12px;
}

.advanced-settings summary:hover {
    color: var(--text-color);
}

.advanced-settings input[type="range"] {
    width: 100%;
    accent-color: var(--primary-color);
}

.section-header {
    display: flex;
    justify-content: space-between;
    align-items: center;
    margin-bottom: 12px;
}

.card-list {
    display: flex;
    flex-direction: column;
    gap: 8px;
    max-height: 300px;
    overflow-y: auto;
}

.card-list-item {
    background: var(--card-bg);
    padding: 12px 16px;
    border-radius: var(--border-radius-sm);
    cursor: pointer;
    transition: var(--transition);
    display: flex;
    align-items: center;
    gap: 12px;
}

/* Brief highlight when returning from slides mode so the user's eye lands
   on the card they were just viewing. Fades out via the inherited
   transition; JS removes the class after ~1.8s. */
.card-list-item-highlight {
    outline: 2px solid var(--primary-color);
    outline-offset: 2px;
    background: color-mix(in srgb, var(--primary-color) 12%, var(--card-bg));
}
.card-list-item .card-list-text { flex: 1; min-width: 0; }
.card-list-item .card-list-meta {
    display: flex;
    align-items: center;
    gap: 8px;
    flex-shrink: 0;
}
.card-list-item .card-star {
    color: var(--warning-color);
    font-size: 1.25rem;
    line-height: 1;
}
.card-list-item .card-weights {
    font-size: 0.75rem;
    color: var(--text-muted);
    font-variant-numeric: tabular-nums;
    background: var(--bg-secondary);
    padding: 2px 6px;
    border-radius: 4px;
    white-space: nowrap;
    border: 1px solid transparent;
    cursor: pointer;
}
.card-list-item .card-weights:hover {
    border-color: var(--primary-color);
    color: var(--text-color);
}
.card-list-item.mastered .card-weights {
    color: var(--success-color);
}
/* Projected long-term "time away" re-weight (§3.5.7) — dry run. Shown as a
   full-width line under the card text: projected odds + the visible delta since
   the previous session (date) + the weight adjustment. Italic/accent = preview. */
.card-list-item .card-proj-detail {
    font-size: 0.72rem;
    font-style: italic;
    color: var(--primary-color);
    font-variant-numeric: tabular-nums;
    line-height: 1.35;
    margin-top: 3px;
}
.card-list-item .card-proj-detail .proj-side { font-weight: 600; }
.card-list-item .card-proj-detail .proj-delta { opacity: 0.85; }
/* Editor weight panel: visible delta + adjustment line under the projected odds. */
.weight-proj-detail {
    font-size: 0.8rem;
    font-style: italic;
    color: var(--primary-color);
    font-variant-numeric: tabular-nums;
    margin-top: 2px;
}
/* "Show/Hide projected" toggle in the active state. */
#toggle-projected-btn.active {
    background: var(--primary-color);
    color: #fff;
}
/* "Use" state: the projection has been APPLIED to real weights — warmer accent so
   it reads as an action taken, not just a preview. */
#toggle-projected-btn.proj-use {
    background: var(--danger-color, #c0392b);
}
/* Usability story modal — one block per detected confusion episode. */
.ui-story-episode {
    border-left: 3px solid var(--primary-color);
    padding: 6px 10px;
    margin: 8px 0;
    background: var(--bg-secondary);
    border-radius: 0 6px 6px 0;
}
.ui-story-type {
    font-weight: 600;
    text-transform: capitalize;
}
.ui-story-evidence { font-size: 0.9rem; margin-top: 2px; }
.ui-story-fix { font-size: 0.9rem; margin-top: 4px; color: var(--text-color); }
.card-list-item .card-weight-menu {
    display: flex;
    gap: 4px;
    flex-wrap: wrap;
}
.card-list-item .card-weight-menu .btn {
    padding: 2px 8px;
    font-size: 0.8rem;
}

.card-list-item:hover {
    background: color-mix(in srgb, var(--primary-color) 8%, var(--card-bg));
}

.card-list-item .question-preview {
    font-weight: 500;
    margin-bottom: 4px;
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}

.card-list-item .answer-preview {
    font-size: 0.875rem;
    color: var(--text-muted);
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}

.deck-footer-actions {
    display: flex;
    gap: 8px;
    margin-top: 24px;
    flex-wrap: wrap;
}

/* Study Screen */
.study-progress {
    margin-bottom: 24px;
}

.progress-bar {
    height: 8px;
    background: var(--bg-secondary);
    border-radius: 4px;
    overflow: hidden;
    margin-bottom: 8px;
}

.progress-fill {
    height: 100%;
    background: var(--success-color);
    width: 0%;
    transition: width 0.5s ease;
}

.progress-stats {
    font-size: 0.875rem;
    color: var(--text-muted);
}

.study-progress-row {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 12px;
}
.study-progress-row .timer {
    /* Pull the .timer font-size override below into the top bar layout. */
    font-size: 1.25rem;
    font-weight: 700;
    min-width: 0;
}

/* Flashcard */
/* Flashcard - no fixed height, grows with content, no overlap */
.flashcard-container {
    perspective: 1000px;
    margin-bottom: 0;
    position: relative;
    z-index: 0;
}

.flashcard {
    width: 100%;
    position: relative;
    cursor: pointer;
}

/* Tap-to-flip indicator — a small circular arrow pinned to the card's corner. */
.flip-hint {
    position: absolute;
    top: 10px;
    right: 10px;
    z-index: 2;
    width: 1.8em;
    height: 1.8em;
    display: flex;
    align-items: center;
    justify-content: center;
    font-size: 1.05rem;
    line-height: 1;
    color: var(--text-muted);
    background: var(--card-bg);
    border-radius: 50%;
    box-shadow: var(--shadow);
    opacity: 0.85;
    pointer-events: none;   /* taps fall through to the card */
}

/* Suppress the flip animation when restoring the shown side on prev/next, so a
   navigated card simply appears already on the answer side. */
.flashcard.no-anim .flashcard-inner { transition: none !important; }

.flashcard-inner {
    position: relative;
    width: 100%;
    transform-style: preserve-3d;
    /* Slight overshoot at the end of the flip — playful, not mechanical. */
    transition: transform 0.65s cubic-bezier(0.34, 1.15, 0.5, 1);
}

.flashcard.flipped .flashcard-inner {
    transform: rotateY(180deg);
}

/* Front uses normal flow so it sizes the container.
   Back is absolute so it overlays the front when flipped. */
.flashcard-front {
    position: relative;
    width: 100%;
    min-height: 200px;
    -webkit-backface-visibility: hidden;
    backface-visibility: hidden;
    background: var(--card-bg);
    border-radius: var(--border-radius);
    padding: 24px;
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    box-shadow: var(--shadow-lg);
}

.flashcard-back {
    position: absolute;
    top: 0;
    left: 0;
    width: 100%;
    min-height: 100%;
    -webkit-backface-visibility: hidden;
    backface-visibility: hidden;
    transform: rotateY(180deg);
    background: var(--bg-secondary);
    border-radius: var(--border-radius);
    padding: 24px;
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    box-shadow: var(--shadow-lg);
}

.card-content {
    font-size: 1.25rem;
    text-align: center;
    word-wrap: break-word;
    max-width: 100%;
    /* Preserve explicit line breaks so enrichment-style "{romaji}\n\n{kana}"
       renders on two lines instead of collapsing into a single space. */
    white-space: pre-line;
}

/* Billboard treatment for pure-text cards in study mode: the card fills the
   viewport like an image card and the question text scales up to be the
   visual focus. clamp() keeps it readable from phone to desktop.
   60vh (not 82vh like image cards) leaves room on an iPhone for the input
   row above and the aside below to remain visible without scrolling. */
.flashcard-front:not(.has-image) {
    min-height: 60vh;
    padding: clamp(16px, 4vw, 48px);
}
.flashcard-front:not(.has-image) .card-content {
    font-size: clamp(1.75rem, 5vw, 4rem);
    line-height: 1.25;
    font-weight: 500;
}
/* Keep the back side (revealed on manual flip) visually consistent. */
.flashcard-back .card-content {
    font-size: clamp(1.75rem, 5vw, 4rem);
    line-height: 1.25;
    font-weight: 500;
}

/* iPhone portrait: image cards at 82vh + the prompt strip + the aside that
   wraps below leaves no room for the input or any breathing space. Pull the
   image card down a notch so the surrounding chrome fits. */
@media (orientation: portrait) and (max-width: 700px) {
    .flashcard-front.has-image { min-height: 65vh; padding: 8px; }
    .flashcard-front:not(.has-image) { min-height: 50vh; }
}

.card-image {
    max-width: 100%;
    margin-top: 16px;
    position: relative;
}

.card-image img {
    display: block;
    max-width: 100%;
    max-height: 60vh;
    border-radius: var(--border-radius-sm);
}

/* Study-mode image zoom: when the card front has an image we let it dominate
   the viewport and pin the question prompt off to the side (landscape) or to
   a thin strip below (portrait). The hotspot canvas tracks the image via the
   ResizeObserver in hotspot.js, so the new layout just works. */
.flashcard-front.has-image {
    min-height: 82vh;
    padding: 12px;
    gap: 12px;
    flex-direction: column;   /* image on top, text below — desktop matches phone */
    align-items: stretch;
    justify-content: center;
}
.flashcard-front.has-image .card-image {
    margin-top: 0;
    flex: 1 1 auto;
    min-height: 0;
    min-width: 0;
    display: flex;
    align-items: center;
    justify-content: center;
}
.flashcard-front.has-image .card-image img {
    max-width: 100%;
    max-height: 100%;
    object-fit: contain;
}
.flashcard-front.has-image .card-content {
    flex: 0 0 auto;
    display: flex;
    align-items: center;
    justify-content: center;
    text-align: center;
}

/* Only phones in landscape (short viewports) put the text beside the image;
   desktop browsers are wide-but-tall and use the stacked column above. */
@media (orientation: landscape) and (max-height: 600px) {
    .flashcard-front.has-image {
        flex-direction: row;
    }
    .flashcard-front.has-image .card-image  { order: 1; }
    .flashcard-front.has-image .card-content {
        order: 2;
        flex-basis: 220px;
        max-width: 30%;
        font-size: 1.1rem;
    }
}

@media (orientation: portrait) {
    .flashcard-front.has-image {
        flex-direction: column;
    }
    .flashcard-front.has-image .card-image   { order: 1; }
    .flashcard-front.has-image .card-content {
        order: 2;
        font-size: 1rem;
        padding: 4px 0 0;
    }
}

/* Slides mode + hotspot card: pin the graphic to the very top of the card
   instead of vertically centering it. The user wants the image to be the
   first thing they see when browsing a hotspot deck, with the prompt text
   directly below. Overrides both portrait and landscape rules above. */
.flashcard-front.has-image.hotspot-slide {
    flex-direction: column;
    justify-content: flex-start;
}
.flashcard-front.has-image.hotspot-slide .card-image {
    order: 1;
    flex: 0 0 auto;
}
.flashcard-front.has-image.hotspot-slide .card-content {
    order: 2;
    flex: 0 0 auto;
    max-width: 100%;
    font-size: clamp(1rem, 3vw, 1.5rem);
    padding: 8px 0 0;
}

/* Image cards: make the graphic as big as possible while keeping the input+mic
   (above) and the OK / odds / Stop bar (below) on screen. We drop the fixed
   82vh/65vh card floor and cap the IMAGE directly to the viewport minus the
   surrounding chrome. ONE knob — the reserve below: SMALLER number = TALLER
   image. (The reserve covers header + progress bar + input row + prompt + the
   bottom odds/Stop bar, so it's a few hundred px, not tens.) Scoped to image
   cards on the active study screen; text cards are untouched. */
#study-screen.active.image-card .flashcard-front.has-image {
    min-height: 0;
}
#study-screen.active.image-card .flashcard-front.has-image .card-image img {
    /* Driven by the "Graphic size" slider (Advanced Settings) via JS, which sets
       --image-card-max to e.g. 65dvh. Default 65dvh if unset. */
    max-height: var(--image-card-max, 65dvh);
}

/* Multiple Choice */
.mc-options {
    width: 100%;
    margin-top: 16px;
}

.mc-option {
    display: block;
    width: 100%;
    padding: 12px 16px;
    margin-bottom: 8px;
    background: var(--bg-secondary);
    border: 2px solid transparent;
    border-radius: var(--border-radius-sm);
    text-align: left;
    color: var(--text-color);
    font-size: 1rem;
    cursor: pointer;
    transition: var(--transition);
}

.mc-option:hover, .mc-option:focus {
    border-color: var(--primary-color);
}

.mc-option.selected {
    border-color: var(--primary-color);
    background: color-mix(in srgb, var(--primary-color) 16%, transparent);
}

.mc-option.correct {
    border-color: var(--success-color);
    background: color-mix(in srgb, var(--success-color) 16%, transparent);
}

.mc-option.incorrect {
    border-color: var(--danger-color);
    background: color-mix(in srgb, var(--danger-color) 16%, transparent);
}

/* Answer Section */
.answer-section {
    display: flex;
    gap: 18px;   /* clear daylight between the text box and the mic button */
    margin-bottom: 16px;
    position: relative;
    z-index: 1;
}

.answer-section input {
    flex: 1;
    padding: 14px 16px;
    border: 2px solid var(--border-color);
    border-radius: var(--border-radius-sm);
    background: var(--bg-secondary);
    color: var(--text-color);
    font-size: 1rem;
}

.answer-section input:focus {
    outline: none;
    border-color: var(--primary-color);
}

/* Feedback */
.feedback-section {
    text-align: center;
    padding: 24px;
    background: var(--bg-secondary);
    border-radius: var(--border-radius);
    margin-bottom: 16px;
    position: relative;
    z-index: 1;
}

.feedback-message {
    font-size: 1.5rem;
    font-weight: 600;
    margin-bottom: 12px;
}

.feedback-message.correct {
    color: var(--success-color);
    /* Loud and large — this is the win moment. */
    font-size: clamp(2rem, 4vw, 3rem);
    font-weight: 700;
    line-height: 1.1;
}
.user-answer.correct {
    font-size: 1.4rem;
    font-weight: 600;
}

.user-answer {
    font-size: 1.1rem;
    font-weight: 500;
    margin-bottom: 8px;
    padding: 8px 12px;
    border-radius: var(--border-radius-sm);
}

.user-answer.correct {
    color: var(--success-color);
    background: color-mix(in srgb, var(--success-color) 12%, transparent);
}

.user-answer.incorrect {
    color: var(--danger-color);
    background: color-mix(in srgb, var(--danger-color) 12%, transparent);
}

/* On a wrong answer the card flips to reveal the correct answer, so the
   user's (wrong) answer becomes the prominent text in the feedback panel. */
.user-answer.big {
    font-size: 1.6rem;
    font-weight: 700;
    padding: 10px 14px;
}

.feedback-message.incorrect {
    color: var(--danger-color);
}

.correct-answer {
    /* The revealed right answer — show it loud and green so it's the
       thing the eye lands on, especially after a wrong guess. */
    color: var(--success-color);
    font-size: 1.6rem;
    font-weight: 700;
    margin-bottom: 16px;
}

/* Study Controls — live inside .study-meta-row; stacked vertically in default
   mode, switched to horizontal when feedback is showing. */
.study-controls {
    display: flex;
    flex-direction: column;
    align-items: stretch;
    gap: 8px;
    margin-bottom: 0;
    position: relative;
    z-index: 1;
    background: var(--bg-secondary);
    border-radius: var(--border-radius);
    padding: 12px;
    flex: 1 1 auto;          /* grow to fill the meta column so Stop can sit at the bottom */
}

.study-controls-right {
    display: flex;
    flex-direction: column;
    gap: 8px;
    flex: 1;                 /* fill .study-controls vertically */
}

/* Stop drops to the bottom-right of the question box; Show + OK stay up top. */
.study-controls-right > #stop-study-btn {
    margin-top: auto;
}

/* Prev | Stop | Next share one row, sharing width evenly. The OK (Submit)
   button stacks ABOVE this row, and Edit below it — study-controls-right stays
   a column so that vertical order holds. In plain test mode Prev/Next are
   hidden, leaving just Stop in the row. */
.slide-nav-row {
    display: flex;
    flex-direction: row;
    gap: 8px;
    align-items: center;
    justify-content: center;   /* center Prev|Stop|Next as a group → Stop over Edit */
}
.slide-nav-row > .btn {
    flex: 0 1 auto;            /* natural width, centered cluster (not full-width) */
    min-width: 4.5em;
}
/* Center the whole control stack (Submit / nav row / Edit) so Submit sits over
   Stop and Stop over Edit. */
.study-controls-right {
    align-items: center;
}
/* Edit now rides in the card-nav row (Prev | Next | Edit); no extra offset so it
   aligns with its row-mates. */
.study-meta-row.slides-layout #edit-slide-btn {
    margin-top: 0;
}

.timer {
    font-size: 1.5rem;
    font-weight: bold;
    color: var(--warning-color);
    min-width: 60px;
}

/* Study Card Row — ONE stacked column in every state (question, correct,
   wrong, MC, hotspot, slides):
       [ card                                       ]
       [ feedback (only when an answer was judged)  ]
       [ odds / progress .................. buttons ]
   Unified 2026-06-06: there used to be a 220px right-side column during the
   question phase and a different placement per feedback type; one layout now
   serves them all, so nothing jumps around when feedback appears. */
.study-card-row {
    display: grid;
    grid-template-columns: 1fr;
    grid-template-rows: auto auto auto;
    gap: 12px;
    margin-bottom: 12px;
    align-items: stretch;
    position: relative;
    z-index: 0;
    /* Keep the study view a centered phone-width column on wide screens, so the
       image, prompt, and button panel look the same on desktop as on iPhone. */
    max-width: 620px;
    margin-left: auto;
    margin-right: auto;
}

.study-card-row .flashcard-container {
    grid-column: 1;
    grid-row: 1;
    min-width: 0;
}

/* Hidden feedback is display:none, so its row collapses to zero height and
   the meta row sits directly under the card during the question phase. */
.study-card-row > #feedback-section {
    grid-column: 1;
    grid-row: 2;
}

/* Bottom bar: probability info on the left, action buttons on the right —
   identical in the question phase and both feedback states. */
.study-meta-row {
    grid-column: 1;
    grid-row: 3;
    display: flex;
    flex-direction: row;
    align-items: stretch;
    gap: 12px;
    min-width: 0;
}
.study-meta-row .probability-sidebar {
    flex: 1 1 auto;
    flex-direction: row;
    flex-wrap: wrap;
    align-items: center;
    justify-content: flex-start;
    gap: 12px;
    padding: 8px 12px;
}
.study-meta-row .study-controls {
    flex: 0 0 auto;
    padding: 8px;
}
.study-meta-row .study-controls-right {
    /* Stacked, centered: each control group is its own centered line, so a slide
       reads spot-nav row / card-nav+Edit row / Stop. In a test, Submit then Stop
       stack the same way (the empty hidden rows collapse). */
    flex-direction: column;
    justify-content: center;
    align-items: center;
}
.study-meta-row .study-controls-right > #stop-study-btn {
    margin-top: 0;   /* sit right below the card-nav row, not pinned to the bottom */
}

/* Slide-show flows: stack with the Stop/Prev/Next controls on top and the card
   odds BELOW them. column-reverse puts the last DOM child (controls) first. */
.study-meta-row.slides-layout {
    flex-direction: column-reverse;
    align-items: stretch;
}
.study-meta-row.slides-layout .study-controls,
.study-meta-row.slides-layout .probability-sidebar {
    flex: 0 0 auto;
}

/* Card frame in the study flow: hugs its content in EVERY state. Both faces
   share one grid cell, so the frame height is the max of the two sides —
   submitting an answer or flipping the card never resizes it. The 50–65vh
   billboard floors are gone: the big question font (unchanged) sizes the
   card, keeping the odds/buttons bar high on screen instead of pushed below
   the fold. The 3D flip still works (.flashcard-inner keeps preserve-3d, the
   faces keep backface-visibility:hidden; only their in-flow/absolute roles
   are replaced by the shared grid cell). Image cards keep their dominant
   image layout in the question phase. */
.study-card-row .flashcard-inner {
    display: grid;
}
.study-card-row .flashcard-front,
.study-card-row .flashcard-back {
    grid-area: 1 / 1;
    position: relative;
    top: auto;
    left: auto;
}
.study-card-row .flashcard-front:not(.has-image),
.study-card-row .flashcard-back {
    min-height: 0;
    padding: 18px 20px;   /* snug but not cramped around the billboard text */
}
/* .flip-reveal — STATE class showFeedback sets for typed/spoken feedback
   (correct AND wrong; never MC/hotspot). During feedback even image cards
   compact — the answer text is the focus. */
.study-card-row.flip-reveal .flashcard-front {
    min-height: 0;
}
/* The revealed correct answer reads green — "here's the right one" — against
   the red user answer below (visible whenever the back face is shown). */
.study-card-row.flip-reveal #card-answer {
    color: var(--success-color);
}

/* Long/short label toggle for tight-fit buttons. */
.label-short { display: none; }
@media (max-width: 600px) {
    .label-long  { display: none; }
    .label-short { display: inline; }
    .study-card-row       { gap: 8px; }
    /* Tighten the inner padding on small screens. */
    .probability-sidebar { padding: 8px 6px; gap: 4px; }
    .study-controls       { padding: 8px; gap: 6px; }
    .feedback-section     { padding: 8px; }
    .feedback-message     { font-size: 1rem; margin-bottom: 6px; }
    .user-answer          { font-size: 0.9rem; padding: 4px 6px; }
    .correct-answer       { font-size: 1.15rem; margin-bottom: 8px; }
    .study-meta-row       { gap: 8px; }
}

.probability-sidebar {
    width: 100%;
    background: var(--bg-secondary);
    border-radius: var(--border-radius);
    padding: 12px;
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    gap: 6px;
}

.prob-label {
    font-size: 0.65rem;
    text-transform: uppercase;
    letter-spacing: 0.05em;
    color: var(--text-muted);
}

.prob-value {
    font-size: 1.25rem;
    font-weight: 700;
    color: var(--warning-color);
}

.prob-bar-container {
    width: 100%;
    height: 6px;
    background: var(--card-bg);
    border-radius: 3px;
    overflow: hidden;
}

.prob-bar {
    height: 100%;
    background: var(--warning-color);
    width: 0%;
    transition: width 0.5s ease;
    border-radius: 3px;
}

.prob-detail {
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: 1px;
}

.prob-sublabel {
    font-size: 0.6rem;
    text-transform: uppercase;
    color: var(--text-muted);
}

.prob-subvalue {
    font-size: 0.85rem;
    font-weight: 600;
    color: var(--text-color);
}

/* Image Upload & Hotspot Editor */
.image-input-row,
.audio-input-row {
    display: flex;
    align-items: center;
    gap: 8px;
    flex-wrap: wrap;
}

.audio-input-row audio {
    max-width: 100%;
    height: 32px;
}

/* Small mic button tucked into the lower-right of a Q/A textarea (replaces the
   old "Audio (optional)" label + big Record button). */
.textarea-with-mic { position: relative; }
.textarea-with-mic textarea { padding-bottom: 40px; }   /* keep text clear of the audio icons */
/* The Q/A audio controls — same 3 icons as a hotspot row (play/record/file),
   docked as a pill in the bottom-right corner of the textarea. */
.qa-audio-icons {
    position: absolute;
    right: 6px;
    bottom: 6px;
    display: flex;
    align-items: center;
    gap: 1px;
    background: var(--bg-secondary);
    border-radius: 18px;
    box-shadow: var(--shadow);
    padding: 2px 5px;
}
.mic-icon-btn {
    position: absolute;
    right: 8px;
    bottom: 8px;
    width: 34px;
    height: 34px;
    padding: 0;
    border: none;
    border-radius: 50%;
    background: var(--bg-secondary);
    box-shadow: var(--shadow);
    font-size: 1rem;
    line-height: 1;
    display: flex;
    align-items: center;
    justify-content: center;
    cursor: pointer;
}
.mic-icon-btn:active { transform: scale(0.92); }
.mic-icon-btn.recording {
    background: var(--danger-color);
    color: #fff;
    animation: micPulse 1.1s ease-in-out infinite;
}
@keyframes micPulse {
    0%, 100% { box-shadow: 0 0 0 0 color-mix(in srgb, var(--danger-color) 55%, transparent); }
    50%      { box-shadow: 0 0 0 7px color-mix(in srgb, var(--danger-color) 0%, transparent); }
}

.image-input-row input[type="url"] {
    flex: 1;
}

.image-or {
    color: var(--text-muted);
    font-size: 0.85rem;
}

.upload-btn {
    cursor: pointer;
    white-space: nowrap;
}

.image-preview-container {
    margin-top: 12px;
}

/* A card picture held back from a TARGET-language prompt (see _imageRidesWithPrompt
   in app.js): the <img> stays loaded in the DOM so revealing it with the answer is
   instant, but it must not be on screen while the learner is meant to be reading the
   word. */
#card-image.held-back {
    display: none !important;
}


/* Image controls while a hot-spot detect job is running: the picture on the card is
   still the ORIGINAL (labels showing) until the worker writes back, so editing it
   now would race that write. Locked, and visibly so. */
.locked-processing,
.btn.locked-processing {
    opacity: 0.45;
    pointer-events: none;
    cursor: not-allowed;
}

/* Deck-list badge for a card whose detection hasn't landed yet. Such a card is NOT
   served in a test (it would show the answer printed on the image). */
.card-detect-badge {
    display: inline-block;
    margin-left: 6px;
    padding: 1px 7px;
    border-radius: 999px;
    font-size: 0.72rem;
    font-weight: 600;
    background: color-mix(in srgb, var(--warning-color) 18%, var(--card-bg));
    color: var(--warning-color);
    white-space: nowrap;
}
.card-detect-badge.is-error {
    background: color-mix(in srgb, var(--danger-color) 18%, var(--card-bg));
    color: var(--danger-color);
}

/* ⭐ Mark-mastered, in the odds panel during study/slides. Deliberately quiet — it
   zeroes a weight, so it shouldn't be the most prominent thing on the screen. */
.master-now {
    background: none;
    border: none;
    padding: 2px 4px;
    font-size: 1.05rem;
    line-height: 1;
    cursor: pointer;
    opacity: 0.55;
    transition: opacity 0.15s, transform 0.15s;
}
.master-now:hover:not(:disabled) { opacity: 1; transform: scale(1.15); }
.master-now:disabled { opacity: 0.25; cursor: default; }
.master-now.done { opacity: 1; }

/* "Click here to assign one." — the inline fix link on an AI setup error. */
.ai-fix-link {
    color: var(--primary-color);
    font-weight: 600;
    text-decoration: underline;
    cursor: pointer;
}

/* 🤖 AI models & tokens. One row per AI function: a long label wraps inside its own
   cell instead of shoving the select onto the next line, so every row lines up. */
.ai-fn-row {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 10px;
    padding: 5px 0;
}
.ai-fn-row label {
    flex: 1 1 auto;
    min-width: 0;
    margin: 0;
}
.ai-fn-row select {
    flex: 0 1 auto;
    max-width: 58%;
}

/* One block per provider (status, paste-a-token, remove). */
.ai-provider {
    padding: 10px 0;
    border-top: 1px solid var(--border-color);
}
.ai-provider:first-child { border-top: none; }
.ai-provider-head { margin-bottom: 6px; }
.ai-token-ok      { color: var(--primary-color); font-size: 0.85rem; }
.ai-token-missing { color: var(--text-muted);    font-size: 0.85rem; }
.ai-provider-row {
    display: flex;
    flex-wrap: wrap;      /* the Remove button drops to its own line on a phone */
    gap: 6px;
    align-items: center;
    margin-bottom: 4px;
}
.ai-provider-row input {
    flex: 1 1 12ch;       /* stays usable next to the buttons at 390px */
    min-width: 0;
}

/* "Image (optional):" + its ✨ find-a-picture button on one line. */
.image-label-row {
    display: flex;
    align-items: center;
    gap: 6px;
}
.image-label-row label {
    margin: 0;
}

/* ✨ Free card-picture picker: candidate tiles (AI generations + Commons clipart).
   auto-fill keeps 2 across on an iPhone and more on a desktop without a media
   query. Tiles are <button>s so they're tappable + keyboard-reachable. */
.image-picker-grid {
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(140px, 1fr));
    gap: 10px;
    margin: 10px 0;
}
.image-cand {
    display: flex;
    flex-direction: column;
    align-items: stretch;
    gap: 4px;
    padding: 6px;
    background: var(--card-bg);
    border: 2px solid var(--border-color);
    border-radius: var(--border-radius-sm);
    cursor: pointer;
    font: inherit;
    color: var(--text-color);
    text-align: center;
    transition: border-color 0.15s, transform 0.15s, box-shadow 0.15s;
}
.image-cand:hover:not(:disabled) {
    border-color: var(--primary-color);
    background: var(--hover-bg);
    transform: translateY(-2px);
    box-shadow: var(--shadow);
}
.image-cand:disabled {
    opacity: 0.5;
    cursor: default;
}
.image-cand.picking {
    /* The one being downloaded stays highlighted while its siblings grey out. */
    opacity: 1;
    border-color: var(--primary-color);
    box-shadow: var(--shadow-lg);
}
.image-cand img {
    width: 100%;
    aspect-ratio: 1 / 1;
    object-fit: contain;
    /* Clipart is mostly DARK line art on transparency, and the picked file gets
       flattened onto white when stored (_flatten_on_white) — so preview it on a light
       plate in BOTH themes. A theme-following plate went near-black in dark mode and
       swallowed the black-glyph tiles whole. */
    background: #f2f2f0;
    border-radius: var(--border-radius-sm);
}
.image-cand-loading {
    font-size: 0.75rem;
    color: var(--text-muted);
    opacity: 0.7;
}
.image-cand-cap {
    font-size: 0.78rem;
    font-weight: 600;
    line-height: 1.2;
    overflow-wrap: anywhere;
}
.image-cand-lic {
    font-size: 0.68rem;
    opacity: 0.65;
    line-height: 1.15;
    overflow-wrap: anywhere;
}

.image-preview-wrapper {
    position: relative;
    /* Centered, image-sized frame — matches the slide-show viewer's placement.
       A zoom is clipped to this box (overflow:hidden), so it can't spill over
       the controls and the surrounding text/buttons stay put, like in slides. */
    display: block;
    width: fit-content;
    max-width: 100%;
    margin: 0 auto;
    overflow: hidden;
    /* When the editor opens scrolled to the image, leave room for the fixed header. */
    scroll-margin-top: calc(var(--header-height, 60px) + 12px);
}

/* Editing hot spots: hide the card Question/Answer fields so the screen reads
   like the slide-show view (image → hot-spot text → buttons). */
#card-form.spot-mode .qa-group { display: none; }

.image-preview-wrapper img {
    display: block;
    max-width: 100%;
    /* Match the slide-show viewer's framing: same cap as image cards there,
       and respects the Advanced Settings "Graphic size" slider. */
    max-height: var(--image-card-max, 65dvh);
    border-radius: var(--border-radius-sm);
}

/* Editor zoom scales the image + its overlay canvas about their shared centre. */
.image-preview-wrapper img,
#hotspot-editor-canvas { transform-origin: center center; }

/* Fullscreen tracing overlay: takes over the screen while the user draws
   a hotspot so the image is as large as the viewport allows. */
.hotspot-fullscreen-overlay {
    position: fixed;
    inset: 0;
    z-index: 9999;
    background: rgba(0, 0, 0, 0.92);
    display: flex;
    align-items: center;
    justify-content: center;
    overflow: hidden;          /* clip the magnified image to the viewport */
    touch-action: none;        /* own pinch/pan instead of browser page-zoom */
    padding: max(16px, env(safe-area-inset-top))
             16px
             max(16px, env(safe-area-inset-bottom));
}
.hotspot-fullscreen-overlay .image-preview-wrapper {
    max-width: 100%;
    max-height: 100%;
}
.hotspot-fullscreen-overlay .image-preview-wrapper img {
    max-width: 100%;
    /* Override the 60vh limit so the image uses the full viewport height. */
    max-height: calc(100vh - 32px);
    object-fit: contain;
}
.hotspot-fullscreen-cancel {
    position: absolute;
    top: max(12px, env(safe-area-inset-top));
    right: max(12px, env(safe-area-inset-right));
    width: 44px;
    height: 44px;
    border-radius: 50%;
    border: none;
    background: rgba(255, 255, 255, 0.15);
    color: #fff;
    font-size: 20px;
    cursor: pointer;
    z-index: 1;
}
.hotspot-fullscreen-cancel:hover { background: rgba(255, 255, 255, 0.28); }

.image-preview-wrapper canvas {
    position: absolute;
    top: 0;
    left: 0;
    pointer-events: auto;
    touch-action: none;
}

.screen-edit-toggle {
    margin-bottom: 10px;
}

.radio-group-row {
    flex-direction: row !important;
    gap: 16px !important;
}

.hotspot-panel {
    /* A zoomed editor image is CSS-transformed, so it paints above in-flow
       siblings. Lift the review controls (◀ ▶ stepper, label box, etc.) above
       it and give them an opaque backdrop so they stay usable while zoomed. */
    position: relative;
    z-index: 2;
    background: var(--bg-color);
    border-top: 1px solid var(--border-color);
    padding-top: 10px;
    margin-top: 10px;
}

.hotspot-controls {
    display: flex;
    gap: 8px;
    margin-bottom: 8px;
    flex-wrap: wrap;
}

/* Fixed action bar in hot-spot mode becomes a D-pad: Save on top, ◀ Re-trace ▶
   across the middle, Add · Delete-spot · Cancel on the bottom, then a slim
   Delete-card row. The hot-spot-only buttons are hidden otherwise. */
.sticky-actions:not(.spot-mode) #hs-prev,
.sticky-actions:not(.spot-mode) #hs-retrace-btn,
.sticky-actions:not(.spot-mode) #hs-next,
.sticky-actions:not(.spot-mode) #add-hotspot-btn,
.sticky-actions:not(.spot-mode) #cancel-hotspot-btn,
.sticky-actions:not(.spot-mode) #hs-delete-btn { display: none; }

.sticky-actions.spot-mode {
    /* Docked inline (image → buttons → text), so NOT sticky and no negative
       full-bleed margins. */
    position: static;
    display: grid;
    grid-template-columns: 1fr 1fr 1fr;
    grid-template-areas:
        ".       save     ."
        "left    retrace  right"
        "add     delspot  cancel"
        "delcard delcard  delcard";
    gap: 8px;
    align-items: center;
    max-width: 440px;
    margin: 12px auto;
    padding: 0;
    border-top: none;
    background: transparent;
}
.sticky-actions.spot-mode .btn { width: 100%; margin: 0; }
.sticky-actions.spot-mode .dpad-save    { grid-area: save; }
.sticky-actions.spot-mode .dpad-left    { grid-area: left; }
.sticky-actions.spot-mode .dpad-center  { grid-area: retrace; }
.sticky-actions.spot-mode .dpad-right   { grid-area: right; }
.sticky-actions.spot-mode .dpad-add     { grid-area: add; }
.sticky-actions.spot-mode .dpad-del     { grid-area: delspot; }
.sticky-actions.spot-mode .dpad-cancel  { grid-area: cancel; }
.sticky-actions.spot-mode .dpad-delcard { grid-area: delcard; justify-self: center; width: auto; }

.hotspot-list {
    margin-top: 8px;
}

.hotspot-item {
    display: flex;
    align-items: center;
    gap: 8px;
    padding: 6px 8px;
    background: var(--card-bg);
    border-radius: var(--border-radius-sm);
    margin-bottom: 4px;
}

.hotspot-num {
    background: var(--primary-color);
    color: var(--on-primary);
    width: 22px;
    height: 22px;
    border-radius: 50%;
    display: flex;
    align-items: center;
    justify-content: center;
    font-size: 0.75rem;
    font-weight: 600;
    flex-shrink: 0;
}

.hotspot-text {
    flex: 1;
    min-width: 0;                 /* let it shrink so the audio buttons fit */
    font-size: 0.85rem;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

.hotspot-delete {
    padding: 2px 8px !important;
    font-size: 1rem;
}

/* Per-spot audio controls in the list: play (when set) · record · choose file. */
.hs-audio-btn {
    flex-shrink: 0;
    width: 28px;
    height: 28px;
    padding: 0;
    border: none;
    background: transparent;
    border-radius: 50%;
    font-size: 1rem;
    line-height: 1;
    display: flex;
    align-items: center;
    justify-content: center;
    cursor: pointer;
}
.hs-audio-btn:active { transform: scale(0.9); }
.hotspot-mic.recording,
.hs-audio-btn.recording {
    background: var(--danger-color);
    color: #fff;
    animation: micPulse 1.1s ease-in-out infinite;
}
#hotspot-autogen-audio { margin-top: 8px; }

/* Study mode hotspot canvas */
#card-image {
    position: relative;
}

#hotspot-viewer-canvas {
    position: absolute;
    top: 0;
    left: 0;
    cursor: pointer;
}

/* Zoom & pan (study viewer, slides + test). The image and its overlay canvas
   are transformed together about their shared centre, so hotspots scale and
   hit-test correctly with no extra math. The container clips the magnified
   image so it can't spill over the prompt or controls. */
.flashcard-front.has-image .card-image {
    overflow: hidden;
    /* Own touch gestures on the image (pinch/drag) instead of letting the
       browser page-zoom or scroll — needed so a pinch can START at 1×. Taps
       still flip / answer. */
    touch-action: none;
}
#card-image img,
#hotspot-viewer-canvas { transform-origin: center center; }
#card-image.zoomed,
#card-image.zoomed #hotspot-viewer-canvas { cursor: grab; }

/* Move the ↻ flip hint clear of the top-right corner on image cards. */
.flip-hint.flip-hint-left { left: 10px; right: auto; }

/* Slides hotspot card: the spot label shown in the prompt area. The card's own
   text (e.g. "Kidney") is a small muted caption; the spot is the focus. */

/* Hotspot tooltip */
#hotspot-tooltip {
    position: fixed;
    background: var(--bg-secondary);
    color: var(--text-color);
    padding: 8px 16px;
    border-radius: var(--border-radius-sm);
    border: 1px solid var(--primary-color);
    box-shadow: var(--shadow-lg);
    font-size: 0.9rem;
    z-index: 400;
    transform: translateX(-50%);
    pointer-events: none;
    opacity: 0;
    transition: opacity 0.2s;
}

#hotspot-tooltip.active {
    opacity: 1;
}

/* Weight Controls */
.weight-display {
    display: flex;
    gap: 24px;
    margin-bottom: 12px;
    font-size: 0.95rem;
    color: var(--text-muted);
}

.weight-display strong {
    color: var(--warning-color);
}

.weight-buttons {
    display: flex;
    gap: 8px;
    flex-wrap: wrap;
}

/* Celebration: a brief confetti burst on a correct answer. Transparent,
   pointer-events:none, lives above everything but doesn't intercept taps. */
#celebration-overlay {
    position: fixed;
    inset: 0;
    overflow: hidden;
    pointer-events: none;
    z-index: 500;
}
.confetti {
    position: absolute;
    top: -16px;
    width: 9px;
    height: 13px;
    border-radius: 2px;
    opacity: 0.95;
    animation: confettiFall 1.1s ease-in forwards;
}
/* Every few confetti pieces is a little leaf/sparkle instead of a square —
   ties the celebration to the sprout motif. */
.confetti.emoji {
    background: transparent !important;
    width: auto;
    height: auto;
    font-size: 15px;
    line-height: 1;
}
@keyframes confettiFall {
    0%   { transform: translateY(0)     rotate(0deg);   opacity: 1; }
    100% { transform: translateY(110vh) rotate(720deg); opacity: 0; }
}

@keyframes celebrationPop {
    0% { transform: scale(0.3); opacity: 0; }
    50% { transform: scale(1.2); }
    100% { transform: scale(1); opacity: 1; }
}

@keyframes starSpin {
    0% { transform: rotate(0deg) scale(0.3); }
    50% { transform: rotate(180deg) scale(1.3); }
    100% { transform: rotate(360deg) scale(1); }
}

/* Slides-mode navigation: Prev / Play / Next, centered under the card.
   Hidden in test mode. */
.slide-controls {
    display: flex;
    justify-content: center;
    align-items: center;
    gap: 12px;
    margin: 16px 0;
    flex-wrap: wrap;
}

.slide-controls.hidden { display: none; }

.slide-controls .btn {
    min-width: 96px;
}

.study-complete {
    text-align: center;
    padding: 32px;
}

.study-complete h2 {
    color: var(--success-color);
    font-size: 2rem;
    margin-bottom: 16px;
}

.complete-stats {
    display: flex;
    justify-content: center;
    gap: 32px;
    margin: 24px 0;
}

.stat {
    text-align: center;
}

.stat-value {
    display: block;
    font-size: 2rem;
    font-weight: bold;
    color: var(--primary-color);
}

.stat-label {
    font-size: 0.875rem;
    color: var(--text-muted);
}

/* Forms */
.form-group {
    margin-bottom: 20px;
}

.form-group label {
    display: block;
    margin-bottom: 8px;
    font-weight: 500;
}

/* Radios/checkboxes must NOT be stretched to full width + big padding (that
   detaches the control from its label text). Only text-like inputs get sized. */
.form-group input:not([type="radio"]):not([type="checkbox"]),
.form-group textarea,
.form-group select {
    width: 100%;
    padding: 12px 16px;
    border: 2px solid var(--border-color);
    border-radius: var(--border-radius-sm);
    background: var(--bg-secondary);
    color: var(--text-color);
    font-size: 1rem;
    font-family: inherit;
}

.form-group input:not([type="radio"]):not([type="checkbox"]):focus,
.form-group textarea:focus,
.form-group select:focus {
    outline: none;
    border-color: var(--primary-color);
}

.form-group small {
    display: block;
    margin-top: 4px;
    color: var(--text-muted);
    font-size: 0.875rem;
}

.form-actions {
    display: flex;
    gap: 12px;
    flex-wrap: wrap;
}

/* Sticky action bar for card editor */
.sticky-actions {
    position: sticky;
    bottom: 0;
    display: flex;
    gap: 12px;
    flex-wrap: wrap;
    padding: 16px;
    background: var(--bg-color);
    border-top: 1px solid var(--border-color);
    margin: 0 -16px -32px;
    z-index: 50;
}

.sticky-spacer {
    height: 80px;
}

/* Import Section */
.import-section {
    margin-top: 32px;
    padding-top: 24px;
    border-top: 1px solid var(--border-color);
}

.import-section h3 {
    margin-bottom: 12px;
}

.import-section p {
    color: var(--text-muted);
    margin-bottom: 12px;
}

.import-section textarea {
    width: 100%;
    padding: 12px;
    margin-bottom: 12px;
    background: var(--bg-secondary);
    border: 2px solid var(--border-color);
    border-radius: var(--border-radius-sm);
    color: var(--text-color);
    font-family: monospace;
    font-size: 0.875rem;
}

/* Modal */
.modal {
    position: fixed;
    top: 0;
    left: 0;
    right: 0;
    bottom: 0;
    background: var(--overlay);
    display: flex;
    align-items: center;
    justify-content: center;
    z-index: 200;
    padding: 16px;
}

.modal-content {
    background: var(--bg-secondary);
    padding: 24px;
    border-radius: var(--border-radius);
    width: 100%;
    max-width: 400px;
}

.modal-content h3 {
    margin-bottom: 16px;
}

.modal-content input {
    width: 100%;
    padding: 12px 16px;
    margin-bottom: 16px;
    border: 2px solid var(--border-color);
    border-radius: var(--border-radius-sm);
    background: var(--card-bg);
    color: var(--text-color);
    font-size: 1rem;
}

.modal-actions {
    display: flex;
    gap: 12px;
}

/* Side Menu */
/* Parked with a TRANSFORM, not `right: -280px`. A negative offset leaves the
   panel occupying real layout 280px past the right edge, which made the whole
   page horizontally scrollable — and scrolling right carried the header's right
   end, with the ☰ and 🌙, off the visible area. Every style on those buttons
   measured correct because they WERE correct; the page was simply scrolled.
   A transform moves the panel without giving it layout, so nothing overflows.
   Cost a long debugging session on 2026-07-28 — don't reintroduce the offset. */
.side-menu {
    position: fixed;
    top: 0;
    right: 0;
    width: 280px;
    height: 100%;
    background: var(--bg-secondary);
    z-index: 300;
    transform: translateX(100%);
    /* visibility, not just the transform: a transformed-away panel is still a
       RENDERED box sitting 280px past the right edge (measured: right=1720 in a
       1440 viewport), which is what made the page scroll sideways and carried the
       header's right-hand end — the burger and theme toggle — out of view.
       visibility:hidden makes the closed menu inert: not painted, not hit-tested,
       contributing nothing to scrollable overflow. It is delayed by the animation
       duration on close so the slide-out is still visible. */
    visibility: hidden;
    transition: transform 0.3s ease, visibility 0s linear 0.3s;
    display: flex;
    flex-direction: column;
}

.side-menu.open {
    transform: translateX(0);
    visibility: visible;
    transition: transform 0.3s ease, visibility 0s;
}

.menu-header {
    display: flex;
    justify-content: space-between;
    align-items: center;
    padding: 16px;
    border-bottom: 1px solid var(--border-color);
}

.menu-list {
    list-style: none;
    flex: 1;
    padding: 16px 0;
}

.menu-list li a {
    display: block;
    padding: 16px 24px;
    color: var(--text-color);
    text-decoration: none;
    transition: var(--transition);
}

.menu-list li a:hover {
    background: var(--card-bg);
}

.menu-footer {
    padding: 16px;
    border-top: 1px solid var(--border-color);
    text-align: center;
    color: var(--text-muted);
    font-size: 0.875rem;
}

.menu-overlay {
    position: fixed;
    top: 0;
    left: 0;
    right: 0;
    bottom: 0;
    background: var(--overlay);
    z-index: 250;
    opacity: 0;
    visibility: hidden;
    transition: var(--transition);
}

.menu-overlay.visible {
    opacity: 1;
    visibility: visible;
}

/* Empty State — friendly, not clinical. The copy (app.js) leads with an
   emoji; give it room and a slightly larger voice. */
.empty-state {
    text-align: center;
    padding: 56px 24px;
    color: var(--text-muted);
    font-size: 1.05rem;
    line-height: 1.7;
}

.empty-state p {
    margin-bottom: 16px;
}

/* Responsive - Tablet and up */
@media (min-width: 768px) {
    #main-content {
        max-width: 600px;
        margin-left: auto;
        margin-right: auto;
    }
    
    .deck-actions {
        flex-direction: row;
    }
    
    .deck-actions .btn {
        flex: 1;
    }
    
    .flashcard-front, .flashcard-back {
        min-height: 300px;
    }
    
    .card-content {
        font-size: 1.5rem;
    }
}

/* Responsive - Desktop */
@media (min-width: 1024px) {
    #main-content {
        max-width: 800px;
    }
    
    .flashcard-front, .flashcard-back {
        min-height: 350px;
    }
}

/* iOS Safe Area */
@supports (padding-top: env(safe-area-inset-top)) {
    .app-header {
        padding-top: env(safe-area-inset-top);
        height: calc(var(--header-height) + env(safe-area-inset-top));
    }
    
    #main-content {
        margin-top: calc(var(--header-height) + env(safe-area-inset-top));
        padding-bottom: calc(32px + env(safe-area-inset-bottom));
    }
}

/* Scrollbar Styling */
::-webkit-scrollbar {
    width: 8px;
}

::-webkit-scrollbar-track {
    background: var(--bg-color);
}

::-webkit-scrollbar-thumb {
    background: var(--border-color);
    border-radius: 4px;
}

::-webkit-scrollbar-thumb:hover {
    background: var(--secondary-color);
}

/* ── Speaking / spoken answers ─────────────────────────────────────────────── */

.speech-mic {
    flex: 0 0 auto;
    padding: 14px 16px;
    font-size: 1.1rem;
    line-height: 1;
}

/* Amber "getting ready" — mic armed but the recognizer isn't capturing yet.
   Distinct from red so the learner waits to speak (and doesn't lose the first
   word into the ~1s not-ready window). Slower, calmer pulse than 'listening'. */
.speech-mic.warming {
    background: var(--warning-color);
    color: #fff;
    animation: mic-warm 1.4s ease-in-out infinite;
}

@keyframes mic-warm {
    0%, 100% { opacity: 0.65; }
    50%      { opacity: 1; }
}

/* Pulsing red glow while the mic is hot (actually capturing — speak now). */
.speech-mic.listening {
    background: var(--danger-color);
    color: #fff;
    animation: mic-pulse 1.1s ease-in-out infinite;
}

@keyframes mic-pulse {
    0%, 100% { box-shadow: 0 0 0 0 color-mix(in srgb, var(--danger-color) 60%, transparent); }
    50%      { box-shadow: 0 0 0 8px transparent; }
}

.speech-status {
    margin: -6px 0 14px;
    font-size: 0.85rem;
    color: var(--text-muted);
    min-height: 1.1em;
}

/* Pronunciation (accent) feedback panel inside the feedback section. */
.pronunciation-panel {
    margin: 12px 0;
    padding: 12px 14px;
    background: var(--bg-secondary);
    border-radius: var(--border-radius-sm);
    border-left: 4px solid var(--success-color);
}

.pronunciation-panel.pron-low {
    border-left-color: var(--danger-color);
}

.pron-score {
    display: flex;
    align-items: baseline;
    gap: 4px;
}

.pron-score #pron-score-num {
    font-size: 2rem;
    font-weight: 700;
    color: var(--success-color);
}

.pronunciation-panel.pron-low #pron-score-num {
    color: var(--danger-color);
}

.pron-score-unit { color: var(--text-muted); font-size: 0.9rem; }
.pron-score-label {
    margin-left: 8px;
    text-transform: uppercase;
    letter-spacing: 0.05em;
    font-size: 0.7rem;
    color: var(--text-muted);
}

.pron-actions {
    display: flex;
    gap: 8px;
    margin-top: 10px;
}

/* Checkbox-style settings rows in the Speaking panel. */
.settings-checkbox {
    display: flex;
    align-items: center;
    gap: 8px;
    cursor: pointer;
}

.settings-checkbox input { width: auto; }

#speech-settings select,
#deck-prongate-input {
    padding: 8px 10px;
    border: 2px solid var(--border-color);
    border-radius: var(--border-radius-sm);
    background: var(--bg-secondary);
    color: var(--text-color);
}

/* The Settings gear toggles this collapsible test-setup + management panel. */
.deck-settings-panel { margin-top: 10px; }

/* Soft sage focus ring on every text control — friendlier than a hard
   outline, consistent across both themes. */
input:focus-visible,
textarea:focus-visible,
select:focus-visible {
    box-shadow: 0 0 0 3px color-mix(in srgb, var(--primary-color) 22%, transparent);
}

/* Most-recently-used deck floated to the top of the list */
.deck-item.last-used { border-left: 3px solid var(--primary-color); }
/* The .deck-item qualifier is REQUIRED: the badge is a <span> inside
   .deck-item-info, whose generic `.deck-item-info span` rule (muted gray,
   0.875rem) would otherwise out-rank this class and wash the label out. */
.deck-item .deck-last-badge {
    display: inline-block;
    margin-left: 8px;
    padding: 2px 10px;
    /* Readable at arm's length: bigger than the old 0.62rem micro-caps, and
       on the darker primary shade so the label text has real contrast in
       both themes (white-on-dark-sage / ink-on-mint). */
    font-size: 0.78rem;
    font-weight: 700;
    text-transform: uppercase;
    letter-spacing: 0.03em;
    background: var(--primary-dark);
    color: var(--on-primary);
    border-radius: 999px;
    vertical-align: middle;
    white-space: nowrap;
}

/* Labeled diagram → hotspots entry (Edit Deck modal) */
.beta-tag {
    display: inline-block;
    margin-left: 6px;
    padding: 1px 7px;
    font-size: 0.62rem;
    font-weight: 700;
    text-transform: uppercase;
    letter-spacing: 0.04em;
    background: var(--primary-dark);
    color: var(--on-primary);
    border-radius: 999px;
    vertical-align: middle;
}
.settings-radio { display: inline-flex; align-items: center; margin: 3px 12px 3px 0; font-size: 0.9rem; cursor: pointer; }
.settings-radio input { margin-right: 6px; }
/* Upload-mode picker (No detection / Quick / Accurate) in the card editor's
   image section — governs what the single Upload button does. */
.image-mode-row {
    margin: 8px 0;
    padding: 8px 10px;
    border: 1px dashed var(--border-color, #ccc);
    border-radius: 8px;
}
.image-mode-row .settings-label { display: block; margin-bottom: 4px; }
/* One option per line, radio glued to its own label (align-items keeps the
   control and text on the same baseline so it's unambiguous which is which). */
/* Each option: radio pinned to the start of its (possibly wrapping) label, with
   the text hang-indented beside it so the control is unambiguously its own. */
.image-mode-row .settings-radio {
    display: flex;
    align-items: flex-start;
    gap: 8px;
    margin: 8px 0;
    line-height: 1.3;
    cursor: pointer;
}
.image-mode-row .settings-radio input {
    flex: none;
    width: 18px;
    height: 18px;
    margin: 1px 0 0;          /* nudge onto the first text line */
    accent-color: var(--primary-color);
}
.image-mode-row .settings-radio span { flex: 1; }
/* Seed-model picker is a dependent sub-option of the Free radio — indent it. */
.image-mode-row .settings-suboption { margin: 4px 0 4px 1.6em; font-size: 0.85rem; }
.image-mode-row .settings-hint { margin-top: 6px; }
#detect-hotspots-btn { margin-top: 8px; }
#diagram-status:not(:empty) { margin-top: 6px; }
/* Elapsed-time counter next to the detection spinner (00:01, 00:02…). */
.detect-timer { font-variant-numeric: tabular-nums; font-weight: 700; opacity: 0.85; }
/* While detecting, dim the picker; the spinner shows in the status line. */
.image-mode-row.busy { opacity: 0.6; }

/* Loud, unmissable banner for config problems (e.g. SAM edge detection
   unreachable) — surfaced from the detection status line. */
.detect-warn-big {
    display: block;
    margin-top: 10px;
    padding: 12px 16px;
    background: #fde7e9;
    color: #b00020;
    border: 2px solid #e53935;
    border-radius: 10px;
    font-weight: 800;
    font-size: 1.05rem;
    line-height: 1.35;
}

/* Hot-spot review bar — step through spots one by one and edit the current. */
.hotspot-review {
    display: flex;
    align-items: center;
    gap: 8px;
    margin-bottom: 8px;
}
.hotspot-review .hs-counter {
    min-width: 3.5em;
    text-align: center;
    font-variant-numeric: tabular-nums;
    font-weight: 700;
}
.hotspot-review .hs-text { flex: 1; min-width: 0; }
.hotspot-review button:disabled { opacity: 0.4; }
.hotspot-item { cursor: pointer; }
.hotspot-item.selected {
    background: rgba(255, 152, 0, 0.18);
    border-radius: 6px;
    outline: 2px solid #ff9800;
}
.spinner {
    display: inline-block;
    width: 0.9em; height: 0.9em;
    margin-right: 4px;
    vertical-align: -0.12em;
    border: 2px solid var(--border-color, #ccc);
    border-top-color: var(--primary-color, #4a90d9);
    border-radius: 50%;
    animation: spin 0.7s linear infinite;
}
@keyframes spin { to { transform: rotate(360deg); } }
