/* Tokyo Night */
:root {
    --bg-primary: #1a1b26;
    --bg-secondary: #24283b;
    --bg-raised: #1f2335;
    --bg-hover: #414868;
    --text-primary: #c0caf5;
    --text-secondary: #a9b1d6;
    --accent: #7aa2f7;
    --accent-hover: #89b4fa;
    --border: #2f334d;
    --success: #9ece6a;
    --error: #f7768e;
    --warning: #e0af68;

    --radius: 6px;
    --radius-sm: 4px;
    --radius-pill: 999px;
    --shadow-1: 0 1px 2px rgb(0 0 0 / 0.3);
    --shadow-2: 0 8px 24px -12px rgb(0 0 0 / 0.6);
    --ease: 140ms cubic-bezier(0.2, 0, 0, 1);

    --font-mono: 'Monaco', 'Menlo', 'Ubuntu Mono', monospace;
    --font-ui: ui-sans-serif, system-ui, -apple-system, 'Segoe UI', sans-serif;

    /* Overridden on narrow screens: anything under 16px makes iOS Safari zoom
       the page when the control takes focus. */
    --editor-font-size: 14px;
}

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

html {
    /* Stop iOS inflating text when the phone is turned to landscape. */
    -webkit-text-size-adjust: 100%;
}

body {
    font-family: var(--font-mono);
    background-color: var(--bg-primary);
    color: var(--text-primary);
    line-height: 1.6;
    font-size: 14px;
}

::selection {
    background: var(--accent);
    color: var(--bg-primary);
}

/* svh, not vh: on mobile browsers vh is the height *without* the address bar,
   so 100vh is taller than what you can actually see. */
#app {
    min-height: 100vh;
    min-height: 100svh;
    padding: 20px;
}

.menu-container,
.drill-container {
    max-width: 1280px;
    margin: 0 auto;
}

/* ---------- Menu ---------- */

.menu-top {
    display: flex;
    align-items: center;
    gap: 16px;
    margin-bottom: 20px;
}

.menu-title {
    font-size: 20px;
    color: var(--accent);
    white-space: nowrap;
    text-shadow: 0 0 14px color-mix(in srgb, var(--accent) 22%, transparent);
}

.search {
    flex: 1;
    max-width: 420px;
    margin-left: auto;
    padding: 9px 14px;
    background: var(--bg-secondary);
    border: 1px solid var(--border);
    border-radius: var(--radius);
    color: var(--text-primary);
    font-family: inherit;
    font-size: 14px;
    transition: border-color var(--ease), box-shadow var(--ease);
}

.search:focus {
    outline: none;
    border-color: var(--accent);
    box-shadow: 0 0 0 3px color-mix(in srgb, var(--accent) 18%, transparent);
}

.search::placeholder {
    color: var(--text-secondary);
    opacity: 0.6;
}

.breadcrumbs {
    margin-bottom: 20px;
    padding: 10px 14px;
    background: var(--bg-secondary);
    border: 1px solid var(--border);
    border-radius: var(--radius);
    box-shadow: var(--shadow-1);
}

.breadcrumb {
    color: var(--text-secondary);
}

.breadcrumb.clickable {
    color: var(--accent);
    cursor: pointer;
    text-decoration: underline;
}

.breadcrumb.clickable:hover {
    color: var(--accent-hover);
}

.panes-container {
    display: flex;
    gap: 16px;
    margin-bottom: 20px;
    overflow-x: auto;
}

.pane {
    flex: 1;
    min-width: 250px;
    background: var(--bg-secondary);
    border: 1px solid var(--border);
    border-radius: var(--radius);
    padding: 15px;
    max-height: 460px;
    display: flex;
    flex-direction: column;
    box-shadow: var(--shadow-1);
}

.pane h3 {
    color: var(--accent);
    margin-bottom: 12px;
    font-size: 15px;
    flex-shrink: 0;
}

.pane-list {
    overflow-y: auto;
    flex: 1;
}

/* Themed scrollbars everywhere something scrolls inside a panel — the default
   light-grey ones read as a rendering glitch against Tokyo Night. */
.pane-list,
.drill-side,
.drill-main,
.search-results,
.output-pane,
.answer-content.answer-side,
gleam-editor .cm-scroller {
    scrollbar-width: thin;
    scrollbar-color: var(--bg-hover) transparent;
}

.pane-item {
    display: flex;
    align-items: center;
    gap: 8px;
    padding: 8px 10px;
    margin-bottom: 4px;
    border-radius: var(--radius-sm);
    cursor: pointer;
    color: var(--text-secondary);
    border: 1px solid transparent;
    transition: background var(--ease), border-color var(--ease),
        color var(--ease);
}

.pane-item:hover {
    background: var(--bg-hover);
    color: var(--text-primary);
}

/* Navigation position (category / subcategory panes). */
.pane-item.current {
    background: var(--bg-hover);
    color: var(--text-primary);
    border-color: var(--border);
}

/* Membership in the drill selection (problems pane, search results). */
.pane-item.selected {
    background: var(--bg-hover);
    color: var(--text-primary);
    border-color: var(--accent);
}

.pane-item.selected::before {
    content: '\2713';
    color: var(--accent);
    font-size: 12px;
}

.pane-empty {
    color: var(--text-secondary);
    opacity: 0.5;
    font-style: italic;
    padding: 8px 10px;
}

.badge {
    margin-left: auto;
    font-size: 12px;
    line-height: 1;
    padding: 3px 6px;
    border-radius: var(--radius-pill);
    flex-shrink: 0;
}

.badge-due {
    color: var(--warning);
    border: 1px solid var(--warning);
    background: color-mix(in srgb, var(--warning) 14%, transparent);
}

.badge-learning {
    color: var(--accent);
    border: 1px solid var(--accent);
    background: color-mix(in srgb, var(--accent) 14%, transparent);
}

/* Scheduled cards are the quiet majority: outlined, not filled, so a screen
   of them does not compete with the handful that are actually due. */
.badge-scheduled {
    color: var(--text-secondary);
    border: 1px solid var(--border);
    opacity: 0.75;
}

.badge-paused {
    color: var(--text-secondary);
    border-color: var(--border);
    opacity: 0.8;
}

/* Park/resume on a browse row. Small and quiet; the row's real action is
   selection, and this must read as an aside. */
.suspend-toggle {
    background: none;
    border: none;
    color: var(--text-secondary);
    cursor: pointer;
    font-size: 12px;
    padding: 2px 6px;
    margin-left: 4px;
    border-radius: var(--radius-sm);
}

.suspend-toggle:hover {
    background: var(--bg-hover);
    color: var(--text-primary);
}

.detail-suspend {
    margin: 8px 0;
}

.search-results {
    background: var(--bg-secondary);
    border: 1px solid var(--border);
    border-radius: var(--radius);
    padding: 10px;
    margin-bottom: 20px;
    max-height: 460px;
    overflow-y: auto;
    box-shadow: var(--shadow-1);
}

.search-hit {
    display: flex;
    align-items: center;
    gap: 10px;
    padding: 8px 10px;
    margin-bottom: 4px;
    border-radius: var(--radius-sm);
    cursor: pointer;
    color: var(--text-secondary);
    border: 1px solid transparent;
    transition: background var(--ease), border-color var(--ease),
        color var(--ease);
}

.search-hit:hover {
    background: var(--bg-hover);
    color: var(--text-primary);
}

.search-hit.selected {
    border-color: var(--accent);
    color: var(--text-primary);
    background: var(--bg-hover);
}

.search-hit-title {
    color: inherit;
}

.search-hit-context {
    margin-left: auto;
    font-size: 12px;
    opacity: 0.7;
    flex-shrink: 0;
}

.search-hit.selected .badge,
.search-hit .badge {
    margin-left: 0;
}

/* --- the Selected pane --- */

.selected-item {
    cursor: pointer;
}

.selected-title {
    flex: 1;
    min-width: 0;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

/* Two letters that say which language a selection came from, since the pane
   mixes them freely. */
.lang-tag {
    flex-shrink: 0;
    font-size: 10px;
    line-height: 1;
    letter-spacing: 0.08em;
    text-transform: uppercase;
    padding: 3px 5px;
    border-radius: var(--radius-sm);
    color: var(--text-secondary);
    border: 1px solid var(--border);
}

.selected-remove {
    flex-shrink: 0;
    color: var(--text-secondary);
    font-size: 14px;
    line-height: 1;
}

.selected-item:hover .selected-remove {
    color: var(--error);
}

.iteration-control {
    display: flex;
    align-items: center;
    gap: 10px;
    margin-bottom: 16px;
}

.iteration-control label {
    color: var(--text-secondary);
}

.iteration-control input {
    width: 70px;
    padding: 8px;
    background: var(--bg-secondary);
    border: 1px solid var(--border);
    border-radius: var(--radius-sm);
    color: var(--text-primary);
    font-family: inherit;
    font-size: 14px;
}

.iteration-control input:focus {
    outline: 2px solid var(--accent);
    outline-offset: 1px;
}

.menu-actions {
    display: flex;
    gap: 10px;
    flex-wrap: wrap;
}

/* ---------- Buttons ---------- */

.btn-primary,
.btn-secondary {
    padding: 10px 20px;
    border-radius: var(--radius-sm);
    cursor: pointer;
    font-family: inherit;
    font-size: 14px;
    border: 1px solid var(--border);
    transition: background var(--ease), border-color var(--ease),
        color var(--ease), box-shadow var(--ease), transform var(--ease);
}

.btn-primary:active:not(:disabled),
.btn-secondary:active {
    transform: translateY(1px);
}

.btn-primary {
    background: var(--accent);
    color: var(--bg-primary);
    border-color: var(--accent);
    font-weight: bold;
    box-shadow: 0 6px 18px -12px var(--accent);
}

.btn-primary:hover:not(:disabled) {
    background: var(--accent-hover);
    box-shadow: 0 8px 20px -10px var(--accent);
}

.btn-primary:disabled {
    opacity: 0.4;
    cursor: not-allowed;
    box-shadow: none;
}

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

.btn-secondary:hover {
    background: var(--bg-hover);
}

.btn-primary:focus-visible,
.btn-secondary:focus-visible,
.pane-item:focus-visible,
.search-hit:focus-visible,
.search:focus-visible,
.keymap-option:focus-visible,
.selected-item:focus-visible,
.solution-button:focus-visible,
.results-details summary:focus-visible,
details.approach summary:focus-visible {
    outline: 2px solid var(--accent);
    outline-offset: 2px;
}

/* ---------- Drill ---------- */

.drill-header {
    display: flex;
    align-items: center;
    gap: 16px;
    margin-bottom: 16px;
}

.drill-title {
    font-size: 18px;
    color: var(--accent);
    flex: 1;
    text-align: center;
}

.keymap-picker {
    display: flex;
    border: 1px solid var(--border);
    border-radius: var(--radius-sm);
    overflow: hidden;
    flex-shrink: 0;
}

.keymap-option {
    background: var(--bg-secondary);
    color: var(--text-secondary);
    border: none;
    padding: 6px 12px;
    cursor: pointer;
    font-family: inherit;
    font-size: 12px;
    transition: background var(--ease), color var(--ease);
}

.keymap-option + .keymap-option {
    border-left: 1px solid var(--border);
}

.keymap-option:hover {
    background: var(--bg-hover);
    color: var(--text-primary);
}

.keymap-option.active {
    background: var(--accent);
    color: var(--bg-primary);
    font-weight: bold;
}

.progress-text {
    color: var(--text-secondary);
}

/* Scoped to the drill header: the menu reuses .progress-text for its "N
   selected" line, which has nothing to fill a bar with. The text sits above a
   hairline track whose filled width is --progress, set per-render by
   view/drill.gleam. */
.drill-header .progress-text {
    white-space: nowrap;
    padding-bottom: 6px;
    position: relative;
}

.drill-header .progress-text::before,
.drill-header .progress-text::after {
    content: '';
    position: absolute;
    bottom: 0;
    left: 0;
    height: 2px;
    border-radius: var(--radius-pill);
}

.drill-header .progress-text::before {
    width: 100%;
    background: var(--border);
}

.drill-header .progress-text::after {
    width: var(--progress, 0%);
    background: var(--accent);
    box-shadow: 0 0 8px color-mix(in srgb, var(--accent) 60%, transparent);
    transition: width var(--ease);
}

.drill-grid {
    display: grid;
    grid-template-columns: minmax(280px, 380px) 1fr;
    gap: 16px;
    align-items: start;
}

.drill-side {
    display: flex;
    flex-direction: column;
    gap: 16px;
    max-height: calc(100vh - 140px);
    max-height: calc(100svh - 140px);
    overflow-y: auto;
}


.panel {
    background: var(--bg-secondary);
    border: 1px solid var(--border);
    border-radius: var(--radius);
    padding: 14px 16px;
    box-shadow: var(--shadow-1);
}

.panel-title {
    font-size: 12px;
    text-transform: uppercase;
    letter-spacing: 0.08em;
    color: var(--accent);
    margin-bottom: 10px;
}

.problem-category {
    color: var(--text-secondary);
    font-size: 12px;
    margin-bottom: 10px;
    opacity: 0.8;
}

/* Prose reads in the UI font; everything code-shaped stays monospace. Prompts
   run several sentences and monospace costs real width on a phone. */
.problem-prompt,
.approach-text,
.answer-note {
    font-family: var(--font-ui);
    font-size: 14px;
}

.problem-prompt {
    color: var(--text-primary);
}

details.approach summary.panel-title {
    cursor: pointer;
    margin-bottom: 0;
    list-style: revert;
}

details.approach[open] summary.panel-title {
    margin-bottom: 10px;
}

.approach-text {
    color: var(--text-primary);
}

.approach-nudge {
    color: var(--text-primary);
    margin-bottom: 10px;
}

.approach-steps {
    padding-left: 20px;
    margin: 0 0 10px;
}

.approach-steps li + li {
    margin-top: 6px;
}

.approach-pseudocode {
    background: var(--bg-raised);
    border: 1px solid var(--border);
    border-radius: var(--radius-sm);
    padding: 10px 12px;
    overflow-x: auto;
    white-space: pre;
    font-family: inherit;
    font-size: 13px;
    color: var(--text-primary);
    margin-bottom: 10px;
}

.hint-controls {
    display: flex;
    align-items: center;
    gap: 10px;
    flex-wrap: wrap;
}

.hint-button {
    font-size: 12px;
    padding: 6px 12px;
}

.hint-warning {
    color: var(--warning);
    font-family: var(--font-ui);
    font-size: 11px;
    font-style: italic;
}

.solution-button.revealed {
    border-color: var(--success);
    color: var(--success);
}

.answer-label {
    color: var(--success);
    font-size: 12px;
    text-transform: uppercase;
    letter-spacing: 0.08em;
    margin-bottom: 8px;
    opacity: 0.9;
}

/* Vim status bar panel */
.cm-vim-panel {
    background: var(--bg-raised);
    color: var(--text-secondary);
    font-family: inherit;
    font-size: 12px;
    letter-spacing: 0.04em;
    padding: 2px 10px;
    white-space: pre;
}

/* Block cursor while in normal mode. .cm-cursor is a zero-width element with a
   left border, so widening the border is what makes it a block; the fade lets
   the character underneath stay readable. */
.cm-vim-normal .cm-cursor-primary {
    border-left-width: 1ch;
    opacity: 0.45;
}

.cm-vim-normal .cm-content {
    caret-color: transparent;
}

/* CodeMirror's own panels (Ctrl+F search) and autocomplete tooltip, restyled
   from their light defaults to match the rest of the chrome. */
.cm-panels {
    background: var(--bg-raised);
    color: var(--text-primary);
    border-color: var(--border);
}

.cm-panel.cm-search {
    font-family: var(--font-ui);
    font-size: 12px;
}

.cm-panel.cm-search input,
.cm-panel.cm-search button {
    background: var(--bg-secondary);
    color: var(--text-primary);
    border: 1px solid var(--border);
    border-radius: var(--radius-sm);
    font-family: inherit;
    font-size: 12px;
}

.cm-panel.cm-search label {
    color: var(--text-secondary);
}

.cm-searchMatch {
    background: color-mix(in srgb, var(--accent) 30%, transparent);
}

.cm-searchMatch-selected {
    background: color-mix(in srgb, var(--warning) 40%, transparent);
}

.cm-selectionMatch {
    background: color-mix(in srgb, var(--accent) 18%, transparent);
}

.cm-tooltip-autocomplete {
    background: var(--bg-raised);
    border: 1px solid var(--border);
    border-radius: var(--radius-sm);
    font-family: inherit;
}

.cm-tooltip-autocomplete ul li {
    color: var(--text-primary);
}

.cm-tooltip-autocomplete ul li[aria-selected] {
    background: var(--bg-hover);
    color: var(--text-primary);
}

.signature {
    background: var(--bg-raised);
    border: 1px solid var(--border);
    border-radius: var(--radius-sm);
    padding: 10px 12px;
    overflow-x: auto;
    white-space: pre;
    color: var(--text-primary);
    font-family: inherit;
    font-size: 13px;
}

.case-list {
    list-style: none;
}

.case-list .case {
    padding: 4px 0;
    color: var(--text-secondary);
    font-size: 13px;
    overflow-wrap: anywhere;
}

.case-list .case.pass .case-icon {
    color: var(--success);
}

.case-list .case.fail .case-icon {
    color: var(--error);
}

.drill-main {
    display: flex;
    flex-direction: column;
    gap: 12px;
    min-width: 0;
}

.editor-frame {
    border: 1px solid var(--border);
    border-radius: var(--radius);
    overflow: hidden;
    box-shadow: var(--shadow-2);
}

.editor-frame:focus-within {
    border-color: color-mix(in srgb, var(--accent) 55%, var(--border));
}

gleam-editor {
    display: block;
    min-height: 380px;
}

gleam-editor .cm-editor {
    min-height: 380px;
}

.run-bar {
    display: flex;
    align-items: center;
    gap: 10px;
    flex-wrap: wrap;
}

.run-unavailable {
    color: var(--text-secondary);
    font-style: italic;
    font-size: 13px;
}

/* Why the runtime is unavailable, next to the Retry button. First lines only —
   the full story belongs to the results pane, not the toolbar. */
.run-error {
    color: var(--error);
    font-family: var(--font-ui);
    font-size: 12px;
    white-space: pre-line;
}

.next-button {
    margin-left: auto;
}

.results {
    background: var(--bg-secondary);
    border: 1px solid var(--border);
    border-radius: var(--radius);
    padding: 14px 16px;
    display: flex;
    flex-direction: column;
    gap: 10px;
    box-shadow: var(--shadow-1);
}

/* A pass should register before you have read a word of it, so the summary
   line gets a tinted slab and a glow in its own colour rather than being one
   more line of coloured text. */
.results-summary {
    font-weight: bold;
    margin: -14px -16px 0;
    padding: 10px 16px;
    border-bottom: 1px solid var(--border);
    border-radius: var(--radius) var(--radius) 0 0;
}

.results-summary.pass {
    color: var(--success);
    background: color-mix(in srgb, var(--success) 12%, transparent);
    border-bottom-color: color-mix(in srgb, var(--success) 35%, transparent);
    box-shadow: inset 0 0 24px -12px var(--success);
}

.results-summary.fail {
    color: var(--error);
    background: color-mix(in srgb, var(--error) 12%, transparent);
    border-bottom-color: color-mix(in srgb, var(--error) 35%, transparent);
    box-shadow: inset 0 0 24px -12px var(--error);
}

.results .case.fail {
    border-left: 3px solid var(--error);
    padding-left: 10px;
}

.case-label {
    color: var(--text-primary);
    margin-bottom: 4px;
    overflow-wrap: anywhere;
}

.case-diff {
    font-size: 13px;
    overflow-x: auto;
}

/* `min-width`, not `width`: "expected " is nine characters and a fixed 8ch
   box swallowed its trailing space, so the value butted straight up against
   the label while "got" alongside it kept a gap. */
.case-diff-tag {
    display: inline-block;
    min-width: 9ch;
    color: var(--text-secondary);
}

.case-diff code {
    color: var(--warning);
    overflow-wrap: anywhere;
}

.results-message {
    background: var(--bg-raised);
    border: 1px solid var(--border);
    border-radius: var(--radius-sm);
    padding: 10px 12px;
    overflow-x: auto;
    white-space: pre;
    font-family: inherit;
    font-size: 12px;
    color: var(--text-secondary);
}

/* Whatever the attempt printed. Same slab as .results-message, but the text is
   the user's own, so it keeps the primary colour rather than reading as chrome. */
.results-stdout {
    background: var(--bg-raised);
    border: 1px solid var(--border);
    border-radius: var(--radius-sm);
    padding: 10px 12px;
    margin-top: 6px;
    overflow-x: auto;
    white-space: pre;
    font-family: inherit;
    font-size: 12px;
    color: var(--text-primary);
    max-height: 220px;
    overflow-y: auto;
}

/* The Output side panel reuses the stdout slab; the margin belonged to its
   life under the results and reads as a gap up in the panel. */
.output-pane {
    margin-top: 0;
}

.output-empty {
    color: var(--text-secondary);
    font-style: italic;
    font-size: 13px;
    font-family: var(--font-ui);
}

/* The previous run's output while a new one is in flight: still the latest
   thing the program said, but visibly not this run's. */
.output-stale {
    opacity: 0.55;
}

.results-details summary {
    cursor: pointer;
    color: var(--text-secondary);
    font-size: 13px;
    font-family: var(--font-ui);
}

.answer-content {
    background: var(--bg-secondary);
    border: 1px solid var(--success);
    border-radius: var(--radius);
    padding: 15px;
    overflow-x: auto;
    box-shadow: 0 8px 24px -18px var(--success);
}

/* Label row: the technique name on the left, its Big-O badge on the right. */
.answer-header {
    display: flex;
    align-items: baseline;
    justify-content: space-between;
    gap: 10px;
    flex-wrap: wrap;
}

.answer-complexity {
    font-family: var(--font-ui);
    font-size: 12px;
    color: var(--text-secondary);
    background: var(--bg-raised);
    border: 1px solid var(--border);
    border-radius: var(--radius-sm);
    padding: 2px 8px;
    white-space: nowrap;
}

/* Sits between the label and the code: what this particular approach does, as
   prose, so the code below it can carry only the comments that explain a line. */
.answer-note {
    color: var(--text-secondary);
    /* Notes are one paragraph per line — the shared technique blurb, then what
       this variant does with it. pre-line is what keeps them apart. */
    white-space: pre-line;
    margin-bottom: 12px;
    padding-bottom: 12px;
    border-bottom: 1px solid var(--border);
}

.answer-content pre {
    color: var(--success);
    font-family: inherit;
    white-space: pre-wrap;
}

.hidden {
    display: none;
}

/* Editor and revealed solution side by side; the row is permanent so the keyed
   editor frame never remounts when the answer appears next to it. */
.work-row {
    display: flex;
    gap: 16px;
    align-items: stretch;
    min-width: 0;
}

.work-row .editor-frame {
    flex: 1 1 54%;
    min-width: 0;
}

.answer-content.answer-side {
    flex: 1 1 46%;
    min-width: 280px;
    max-height: calc(100svh - 240px);
    overflow-y: auto;
    margin: 0;
}

/* Collapsed, the prompt column is just the toggle standing on end. */
.drill-grid.side-collapsed {
    grid-template-columns: auto 1fr;
}

.drill-grid.side-collapsed .side-toggle {
    writing-mode: vertical-rl;
    padding: 14px 6px;
}

/* Desktop only: let the editor take the column's height, LeetCode-style, with
   results scrolling inside the main column instead of pushing the page down.
   The CM theme already sets height: 100%, so the old min-height becomes a
   floor rather than the size. */
@media (min-width: 901px) {
    .drill-main {
        max-height: calc(100vh - 140px);
        max-height: calc(100svh - 140px);
        overflow-y: auto;
    }

    .work-row {
        flex: 1 1 auto;
        min-height: 0;
    }

    .work-row .editor-frame {
        display: flex;
        flex-direction: column;
    }

    .work-row .editor-frame gleam-editor {
        flex: 1;
        height: 100%;
    }
}

/* ---------- Tablet ---------- */

@media (max-width: 900px) {
    #app {
        padding: 12px;
    }

    .menu-top {
        flex-wrap: wrap;
    }

    .search {
        max-width: none;
        width: 100%;
        margin-left: 0;
    }

    .panes-container {
        flex-direction: column;
        overflow-x: visible;
    }

    .pane {
        max-height: 300px;
        min-width: unset;
    }

    .drill-grid {
        grid-template-columns: 1fr;
    }

    .drill-grid.side-collapsed {
        grid-template-columns: 1fr;
    }

    .drill-grid.side-collapsed .side-toggle {
        writing-mode: horizontal-tb;
        padding: 8px 14px;
    }

    .work-row {
        flex-direction: column;
    }

    .answer-content.answer-side {
        min-width: 0;
        max-height: none;
    }

    /* There is still room here for the panels to run their natural height. The
       phone block below deliberately takes this back. */
    .drill-side {
        max-height: none;
        overflow-y: visible;
    }

    .drill-header {
        flex-wrap: wrap;
    }

    .drill-title {
        text-align: left;
        width: 100%;
        order: 2;
    }

    gleam-editor,
    gleam-editor .cm-editor {
        min-height: 300px;
    }

    .menu-actions .btn-primary,
    .menu-actions .btn-secondary {
        width: 100%;
    }
}

/* ---------- Phone ---------- */

@media (max-width: 700px) {
    /* Anything under 16px makes iOS Safari zoom the page on focus, and it does
       not zoom back out. This is the whole reason the editor's size is a
       custom property rather than a constant in editor_ffi.mjs. */
    :root {
        --editor-font-size: 16px;
    }

    #app {
        padding: 10px;
    }

    .search,
    .iteration-control input {
        font-size: 16px;
    }

    .menu-title {
        font-size: 18px;
    }

    .pane {
        max-height: 240px;
    }

    /* Two rows, not four flex children fighting over a 390px line. Every row
       costs editor space, so the keymap picker shares row two with the title
       rather than taking a third. */
    .drill-header {
        display: grid;
        grid-template-columns: auto 1fr;
        grid-template-areas:
            'exit progress'
            'title keymap';
        gap: 8px 12px;
        align-items: center;
        margin-bottom: 12px;
    }

    .drill-header > .btn-secondary {
        grid-area: exit;
        justify-self: start;
        padding: 10px 14px;
    }

    .drill-title {
        grid-area: title;
        width: auto;
        font-size: 16px;
        overflow-wrap: anywhere;
    }

    .keymap-picker {
        grid-area: keymap;
        justify-self: end;
    }

    .progress-text {
        grid-area: progress;
        justify-self: end;
        font-size: 12px;
        min-width: 150px;
    }

    /* Without this the prompt, approach, signature and test list stack to full
       height and the editor starts below the fold on every single problem. */
    .drill-side {
        max-height: 34svh;
        overflow-y: auto;
        scrollbar-gutter: stable;
    }

    /* A panel clipped by the scroller reads as broken rendering rather than as
       "there is more below". Sticky flex item, so the fade rides the bottom
       edge while the panels scroll under it. Phone only: at this height the
       panels always overflow, so the fade is never covering a short list. */
    .drill-side::after {
        content: '';
        position: sticky;
        bottom: -1px;
        flex-shrink: 0;
        height: 24px;
        margin-top: -40px;
        pointer-events: none;
        background: linear-gradient(transparent, var(--bg-primary));
    }

    gleam-editor,
    gleam-editor .cm-editor {
        min-height: min(45svh, 300px);
    }

    /* Run and Next stay under the thumb no matter how long the results get. */
    .run-bar {
        position: sticky;
        bottom: 0;
        z-index: 2;
        padding: 10px 0;
        margin-bottom: -10px;
        background: var(--bg-primary);
        border-top: 1px solid var(--border);
    }
}

/* ---------- Touch ---------- */

/* Gated on the input device, not the width: a phone in landscape is 844px wide
   and still has fingers, while a narrow desktop window does not. */
@media (hover: none) {
    button,
    .pane-item,
    .search-hit,
    summary {
        -webkit-tap-highlight-color: transparent;
    }

    /* 44px is the floor every touch guideline agrees on. Blanket rule so a new
       button cannot be added below it by accident. */
    button {
        min-height: 44px;
    }

    .pane-item,
    .search-hit {
        padding: 12px;
        min-height: 44px;
    }

    .keymap-option {
        padding: 0 16px;
        font-size: 13px;
    }


    /* Padding rather than flex centring: display:flex on a <summary> drops the
       disclosure triangle. */
    .results-details summary,
    details.approach summary.panel-title {
        min-height: 44px;
        padding: 11px 0;
    }
}

@media (prefers-reduced-motion: reduce) {
    * {
        transition: none !important;
        animation: none !important;
    }
}

/* ---------- Quiz ---------- */

.quiz-choices {
    display: flex;
    flex-direction: column;
    gap: 10px;
}

.quiz-choice {
    display: flex;
    align-items: flex-start;
    gap: 12px;
    width: 100%;
    text-align: left;
    font-family: inherit;
    font-size: 14px;
    line-height: 1.6;
    color: var(--text-primary);
    background: var(--bg-secondary);
    border: 1px solid var(--border);
    border-radius: var(--radius);
    padding: 12px 14px;
    cursor: pointer;
    transition: border-color var(--ease), background var(--ease);
}

.quiz-choice:hover:not(:disabled) {
    border-color: var(--accent);
    background: var(--bg-raised);
}

.quiz-choice.picked {
    border-color: var(--accent);
    background: color-mix(in srgb, var(--accent) 10%, var(--bg-secondary));
}

/* Only ever applied after grading — before that it would give the answer. */
.quiz-choice.correct {
    border-color: var(--success);
    background: color-mix(in srgb, var(--success) 12%, var(--bg-secondary));
    color: var(--success);
}

.quiz-choice.wrong {
    border-color: var(--error);
    background: color-mix(in srgb, var(--error) 12%, var(--bg-secondary));
    color: var(--error);
}

/* Graded options are disabled, but they are the record of what you answered,
   so they must not read as unavailable. */
.quiz-choice:disabled {
    cursor: default;
    opacity: 1;
}

.quiz-marker {
    flex: none;
    width: 22px;
    height: 22px;
    display: grid;
    place-items: center;
    border-radius: var(--radius-pill);
    border: 1px solid currentColor;
    font-size: 12px;
    opacity: 0.75;
}

.quiz-choice-text {
    flex: 1;
}

.quiz-explanation {
    color: var(--text-secondary);
    line-height: 1.7;
}

.quiz-page {
    color: var(--text-secondary);
    font-size: 12px;
    opacity: 0.7;
}

/* ---------- Report ---------- */

.report-container {
    max-width: 1280px;
    margin: 0 auto;
}

.report-body {
    display: flex;
    flex-direction: column;
    gap: 16px;
    margin-top: 20px;
}

.report-total {
    display: flex;
    align-items: baseline;
    gap: 14px;
}

.report-total-score {
    font-size: 40px;
    font-weight: bold;
    color: var(--text-primary);
}

.report-total-percent {
    font-size: 20px;
    color: var(--text-secondary);
}

.report-sections {
    list-style: none;
    display: flex;
    flex-direction: column;
    gap: 8px;
}

.report-section {
    display: grid;
    grid-template-columns: minmax(160px, 1fr) minmax(80px, 2fr) auto auto;
    align-items: center;
    gap: 14px;
    padding: 6px 0;
}

.report-section-name {
    color: var(--text-primary);
}

.report-section.weak .report-section-name,
.report-section.weak .report-section-percent {
    color: var(--warning);
}

.report-bar {
    height: 8px;
    border-radius: var(--radius-pill);
    background: var(--bg-hover);
    position: relative;
    overflow: hidden;
}

.report-bar::after {
    content: '';
    position: absolute;
    inset: 0 auto 0 0;
    width: var(--score, 0%);
    background: var(--success);
    border-radius: inherit;
}

.report-section.weak .report-bar::after {
    background: var(--warning);
}

.report-section-score,
.report-section-percent {
    color: var(--text-secondary);
    font-size: 13px;
    text-align: right;
    min-width: 44px;
}

.report-advice {
    list-style: none;
    display: flex;
    flex-direction: column;
    gap: 6px;
    color: var(--warning);
}

@media (max-width: 700px) {
    .report-section {
        grid-template-columns: 1fr auto auto;
    }

    .report-bar {
        grid-column: 1 / -1;
        order: 1;
    }
}

/* ---------- Dashboard estimate and queue preview ---------- */

/* A card count is not a workload: a problem typed from memory is minutes, so
   the estimate sits directly under the counts it qualifies. */
.study-estimate {
    display: flex;
    align-items: center;
    justify-content: center;
    gap: 14px;
    flex-wrap: wrap;
    margin-top: -8px;
    font-size: 13px;
    color: var(--text-secondary);
}

.study-estimate-time {
    color: var(--text-primary);
}

.study-estimate-streak::before {
    content: "\00b7";
    margin-right: 14px;
    color: var(--border);
}

.study-preview {
    background: var(--bg-secondary);
    border: 1px solid var(--border);
    border-radius: var(--radius);
}

.study-preview-summary {
    padding: 12px 16px;
    cursor: pointer;
    color: var(--text-secondary);
    font-size: 13px;
    list-style: none;
}

.study-preview-summary::-webkit-details-marker {
    display: none;
}

.study-preview-summary::before {
    content: "\25b8";
    display: inline-block;
    margin-right: 8px;
    transition: transform var(--ease);
}

.study-preview[open] .study-preview-summary::before {
    transform: rotate(90deg);
}

.study-preview-list {
    max-height: 320px;
    overflow-y: auto;
    padding: 0 16px 12px;
}

.study-preview-item {
    display: grid;
    grid-template-columns: auto 1fr auto;
    align-items: center;
    gap: 10px;
    padding: 6px 0;
    border-top: 1px solid var(--border);
    font-size: 13px;
}

.study-preview-tag {
    color: var(--text-secondary);
    font-size: 11px;
    text-transform: uppercase;
    letter-spacing: 0.06em;
    min-width: 2.2em;
}

.study-preview-title {
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

.study-preview-state {
    color: var(--text-secondary);
    font-size: 11px;
}

/* ---------- Session summary ---------- */

.summary-container {
    max-width: 900px;
    margin: 0 auto;
}

.summary-body {
    display: flex;
    flex-direction: column;
    gap: 16px;
}

.summary-totals {
    display: grid;
    grid-template-columns: repeat(3, 1fr);
    gap: 10px;
}

.summary-tile {
    display: flex;
    flex-direction: column;
    gap: 4px;
    padding: 16px;
    background: var(--bg-secondary);
    border: 1px solid var(--border);
    border-radius: var(--radius);
    text-align: center;
}

.summary-tile-value {
    font-size: 22px;
    color: var(--accent);
}

.summary-tile-label {
    font-size: 12px;
    color: var(--text-secondary);
}

.summary-rows {
    display: flex;
    flex-direction: column;
    gap: 2px;
    margin-top: 10px;
}

.summary-row {
    display: grid;
    grid-template-columns: 1fr auto auto auto;
    align-items: center;
    gap: 14px;
    padding: 9px 0;
    border-bottom: 1px solid var(--border);
}

.summary-row:last-child {
    border-bottom: none;
}

.summary-row-title {
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

.summary-row-time,
.summary-row-next {
    color: var(--text-secondary);
    font-size: 13px;
    font-variant-numeric: tabular-nums;
}

.summary-row-grade {
    font-size: 12px;
    padding: 2px 8px;
    border-radius: var(--radius-pill);
    background: var(--bg-primary);
    border: 1px solid var(--border);
}

/* The grade pressed, not the grade recorded: a failed run is coerced to Again
   server-side, and that shows up in the interval rather than here. */
.summary-row-grade.grade-again { color: var(--warning); border-color: var(--warning); }
.summary-row-grade.grade-hard  { color: var(--text-secondary); }
.summary-row-grade.grade-good  { color: var(--success); border-color: var(--success); }
.summary-row-grade.grade-easy  { color: var(--accent); border-color: var(--accent); }

.summary-remaining {
    color: var(--text-secondary);
    line-height: 1.7;
    margin-top: 8px;
}

@media (max-width: 700px) {
    .summary-totals {
        grid-template-columns: 1fr;
    }

    .summary-row {
        grid-template-columns: 1fr auto;
        row-gap: 4px;
    }

    .summary-row-title {
        grid-column: 1 / -1;
        white-space: normal;
    }
}

/* ---------- Settings ---------- */

.settings-screen {
    max-width: 780px;
    margin: 0 auto;
    padding: 28px 20px 64px;
    display: flex;
    flex-direction: column;
    gap: 28px;
}

.settings-section {
    display: flex;
    flex-direction: column;
    gap: 4px;
}

/* Which store a setting lands in is not cosmetic -- account settings follow
   you between browsers and device ones do not -- so each group says so. */
.settings-section-note {
    color: var(--text-secondary);
    font-size: 13px;
    margin-bottom: 10px;
}

.settings-rows {
    display: flex;
    flex-direction: column;
    gap: 2px;
}

.settings-row {
    display: flex;
    align-items: flex-start;
    justify-content: space-between;
    gap: 24px;
    padding: 14px 0;
    border-bottom: 1px solid var(--border);
}

.settings-row:last-child {
    border-bottom: none;
}

.settings-label {
    display: flex;
    flex-direction: column;
    gap: 4px;
    min-width: 0;
}

.settings-label-text {
    color: var(--text-primary);
}

.settings-help {
    color: var(--text-secondary);
    font-size: 12px;
    line-height: 1.6;
    max-width: 46ch;
}

.settings-input {
    flex: 0 0 auto;
    width: 96px;
    padding: 8px 10px;
    background: var(--bg-secondary);
    color: var(--text-primary);
    border: 1px solid var(--border);
    border-radius: var(--radius);
    font-family: inherit;
    font-size: 14px;
    text-align: right;
}

.settings-input:focus-visible {
    outline: 1px solid var(--accent);
    outline-offset: 1px;
}

.settings-timezone {
    display: flex;
    align-items: center;
    gap: 10px;
    flex-wrap: wrap;
    justify-content: flex-end;
}

.settings-timezone-value {
    color: var(--text-secondary);
    font-size: 13px;
}

@media (max-width: 700px) {
    .settings-row {
        flex-direction: column;
        gap: 10px;
    }

    .settings-input {
        width: 100%;
        text-align: left;
    }

    .settings-timezone {
        justify-content: flex-start;
    }
}

/* ---------- First-run picker ---------- */

.picker-screen {
    max-width: 620px;
    margin: 0 auto;
    padding: 48px 20px 64px;
    display: flex;
    flex-direction: column;
    gap: 18px;
}

.picker-question {
    font-size: 20px;
    color: var(--text-primary);
}

.picker-explainer {
    color: var(--text-secondary);
    line-height: 1.7;
}

.picker-options {
    display: flex;
    flex-direction: column;
    gap: 8px;
    margin-top: 6px;
}

/* A row rather than a chip: this is a considered choice, not a filter being
   flicked on and off, so it gets the width to say so. */
.picker-option {
    display: flex;
    align-items: center;
    gap: 12px;
    width: 100%;
    padding: 14px 16px;
    background: var(--bg-secondary);
    color: var(--text-primary);
    border: 1px solid var(--border);
    border-radius: var(--radius);
    font-family: inherit;
    font-size: 14px;
    text-align: left;
    cursor: pointer;
    transition: border-color var(--ease), background var(--ease);
}

.picker-option:hover {
    border-color: var(--accent);
}

.picker-option.picked {
    border-color: var(--accent);
    background: var(--bg-raised);
}

.picker-check {
    display: grid;
    place-items: center;
    flex: 0 0 auto;
    width: 20px;
    height: 20px;
    border: 1px solid var(--border);
    border-radius: 4px;
    color: var(--bg-primary);
    font-size: 12px;
}

.picker-option.picked .picker-check {
    background: var(--accent);
    border-color: var(--accent);
}

.picker-actions {
    margin-top: 10px;
}

.picker-start {
    width: 100%;
    padding: 14px;
}

.picker-start:disabled {
    opacity: 0.45;
    cursor: not-allowed;
}

.picker-hint {
    color: var(--text-secondary);
    font-size: 13px;
    text-align: center;
}

@media (max-width: 700px) {
    .picker-screen {
        padding: 28px 16px 48px;
    }
}

/* ==========================================================================
   Accounts and scheduling
   ========================================================================== */

.auth-screen {
    min-height: 100dvh;
    display: grid;
    place-items: center;
    padding: 24px;
}

.auth-card {
    display: flex;
    flex-direction: column;
    gap: 14px;
    width: min(380px, 100%);
    padding: 28px;
    background: var(--bg-raised);
    border: 1px solid var(--border);
    border-radius: var(--radius);
    box-shadow: var(--shadow-2);
}

.auth-title {
    font-size: 22px;
    letter-spacing: -0.01em;
}

.auth-subtitle {
    color: var(--text-secondary);
    font-size: 14px;
    margin-top: -6px;
}

.auth-field {
    display: flex;
    flex-direction: column;
    gap: 5px;
}

.auth-label {
    font-size: 12px;
    color: var(--text-secondary);
}

.auth-input {
    font-family: var(--font-ui);
    font-size: 16px; /* Under 16px iOS Safari zooms the page on focus. */
    padding: 9px 11px;
    color: var(--text-primary);
    background: var(--bg-primary);
    border: 1px solid var(--border);
    border-radius: var(--radius-sm);
}

.auth-input:focus-visible {
    outline: 2px solid var(--accent);
    outline-offset: 1px;
}

.auth-error {
    color: var(--error);
    font-size: 13px;
}

.auth-submit {
    font-family: var(--font-ui);
    font-size: 15px;
    font-weight: 600;
    padding: 10px;
    color: var(--bg-primary);
    background: var(--accent);
    border: none;
    border-radius: var(--radius-sm);
    cursor: pointer;
    transition: background var(--ease);
}

.auth-submit:hover:not(:disabled) {
    background: var(--accent-hover);
}

.auth-submit:disabled {
    opacity: 0.55;
    cursor: default;
}

.auth-switch,
.footer-link,
.link-button {
    font-family: var(--font-ui);
    font-size: 13px;
    color: var(--text-secondary);
    background: none;
    border: none;
    padding: 4px;
    cursor: pointer;
    text-decoration: underline;
    text-underline-offset: 3px;
}

.auth-switch:hover,
.footer-link:hover,
.link-button:hover {
    color: var(--text-primary);
}

/* --- study screen --- */

.study-screen,
.stats-screen {
    max-width: 780px;
    margin: 0 auto;
    padding: 28px 20px 48px;
    display: flex;
    flex-direction: column;
    gap: 22px;
}

.study-header {
    display: flex;
    align-items: baseline;
    justify-content: space-between;
    gap: 12px;
    flex-wrap: wrap;
}

.study-title {
    font-size: 24px;
    letter-spacing: -0.01em;
}

.study-account {
    display: flex;
    align-items: center;
    gap: 12px;
}

.study-email {
    font-size: 13px;
    color: var(--text-secondary);
}

.study-counts {
    display: grid;
    grid-template-columns: repeat(3, 1fr);
    gap: 12px;
}

.study-count {
    display: flex;
    flex-direction: column;
    gap: 2px;
    padding: 16px;
    background: var(--bg-raised);
    border: 1px solid var(--border);
    border-radius: var(--radius);
}

.study-count-value {
    font-size: 30px;
    font-weight: 600;
    line-height: 1.1;
}

.study-count-due .study-count-value {
    color: var(--warning);
}

.study-count-new .study-count-value {
    color: var(--accent);
}

.study-count-label {
    font-size: 12px;
    color: var(--text-secondary);
}

/* The language mood dial: pressed chips feed today's queue, muted ones sit
   out. Deliberately quiet — a filter, not a call to action. */
.language-chips {
    display: flex;
    gap: 8px;
    flex-wrap: wrap;
    justify-content: center;
    margin: 4px 0 2px;
}

.language-chip {
    background: var(--bg-secondary);
    color: var(--text-primary);
    border: 1px solid var(--accent);
    border-radius: 999px;
    padding: 4px 12px;
    font-family: var(--font-ui);
    font-size: 12px;
    cursor: pointer;
}

.language-chip.muted {
    border-color: var(--border);
    color: var(--text-secondary);
    opacity: 0.55;
    text-decoration: line-through;
}

.study-hidden-hint {
    color: var(--text-secondary);
    font-family: var(--font-ui);
    font-size: 12px;
    text-align: center;
}

.study-summary {
    color: var(--text-secondary);
    font-size: 14px;
}

.study-actions {
    display: flex;
    gap: 10px;
    flex-wrap: wrap;
}

.study-start {
    font-family: var(--font-ui);
    font-size: 15px;
    font-weight: 600;
    padding: 11px 22px;
    color: var(--bg-primary);
    background: var(--accent);
    border: none;
    border-radius: var(--radius-sm);
    cursor: pointer;
}

.study-start:disabled {
    opacity: 0.5;
    cursor: default;
}

.study-secondary {
    font-family: var(--font-ui);
    font-size: 14px;
    padding: 11px 16px;
    color: var(--text-primary);
    background: var(--bg-secondary);
    border: 1px solid var(--border);
    border-radius: var(--radius-sm);
    cursor: pointer;
}

.study-secondary:hover {
    background: var(--bg-hover);
}

.study-section-title {
    font-size: 13px;
    font-weight: 600;
    text-transform: uppercase;
    letter-spacing: 0.06em;
    color: var(--text-secondary);
    margin-bottom: 10px;
}

/* --- forecast --- */

.forecast-bars {
    display: flex;
    align-items: flex-end;
    gap: 6px;
    height: 90px;
}

.forecast-day {
    flex: 1;
    display: flex;
    flex-direction: column;
    justify-content: flex-end;
    align-items: center;
    gap: 4px;
    height: 100%;
}

.forecast-bar {
    width: 100%;
    background: var(--accent);
    border-radius: 2px 2px 0 0;
    opacity: 0.75;
    min-height: 2px;
}

.forecast-count,
.forecast-label {
    font-size: 10px;
    color: var(--text-secondary);
    line-height: 1;
}

.forecast-month {
    height: 70px;
    gap: 2px;
}

/* --- the support footer, last thing on the study screen --- */

.study-footer {
    margin-top: 6px;
    padding-top: 14px;
    border-top: 1px solid var(--border);
    display: flex;
    flex-wrap: wrap;
    justify-content: center;
    align-items: baseline;
    gap: 8px;
    font-size: 12px;
    color: var(--text-secondary);
    opacity: 0.75;
}

.study-footer:hover {
    opacity: 1;
}

.footer-sep {
    color: var(--text-secondary);
    opacity: 0.5;
}

/* The shared link rule sizes for the auth screens; the footer runs smaller. */
.study-footer .footer-link,
.help-footnote .footer-link {
    font-size: inherit;
    padding: 0;
}

/* --- grading bar --- */

.grade-bar {
    display: flex;
    gap: 8px;
    margin-left: auto;
}

.grade-hint {
    margin-left: auto;
    font-size: 13px;
    color: var(--text-secondary);
    font-style: italic;
}

.grade-button {
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: 1px;
    min-width: 74px;
    padding: 6px 12px;
    font-family: var(--font-ui);
    color: var(--text-primary);
    background: var(--bg-secondary);
    border: 1px solid var(--border);
    border-radius: var(--radius-sm);
    cursor: pointer;
    transition: background var(--ease), border-color var(--ease);
}

.grade-button:hover {
    background: var(--bg-hover);
}

.grade-label {
    font-size: 14px;
    font-weight: 600;
    line-height: 1.2;
}

/* The interval is the point of the button, but it is reference information:
   readable at a glance, never louder than the verdict above it. */
.grade-interval {
    font-size: 11px;
    color: var(--text-secondary);
    line-height: 1;
}

.grade-again {
    border-color: color-mix(in srgb, var(--error) 55%, var(--border));
}

.grade-again .grade-label {
    color: var(--error);
}

.grade-good {
    border-color: color-mix(in srgb, var(--success) 55%, var(--border));
}

.grade-good .grade-label {
    color: var(--success);
}

.grade-hard .grade-label {
    color: var(--warning);
}

.grade-easy .grade-label {
    color: var(--accent);
}

/* --- notice banner --- */

.notice {
    display: flex;
    align-items: center;
    gap: 10px;
    padding: 10px 14px;
    font-size: 13px;
    color: var(--warning);
    background: color-mix(in srgb, var(--warning) 12%, transparent);
    border: 1px solid color-mix(in srgb, var(--warning) 40%, transparent);
    border-radius: var(--radius-sm);
}

.notice-text {
    flex: 1;
}

.notice-dismiss {
    font-size: 18px;
    line-height: 1;
    color: inherit;
    background: none;
    border: none;
    cursor: pointer;
    padding: 0 4px;
}

/* --- stats --- */

.stats-tiles {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(140px, 1fr));
    gap: 12px;
}

.stats-tile {
    display: flex;
    flex-direction: column;
    gap: 2px;
    padding: 16px;
    background: var(--bg-raised);
    border: 1px solid var(--border);
    border-radius: var(--radius);
}

.stats-tile-value {
    font-size: 26px;
    font-weight: 600;
    line-height: 1.1;
}

.stats-tile-label {
    font-size: 12px;
    color: var(--text-secondary);
}

.stats-section {
    background: var(--bg-raised);
    border: 1px solid var(--border);
    border-radius: var(--radius);
    padding: 16px;
}

/* Half a year of study days. Wraps into columns of seven so each column is a
   week, the way a calendar reads. */
.heatmap {
    display: grid;
    grid-template-rows: repeat(7, 1fr);
    grid-auto-flow: column;
    grid-auto-columns: 1fr;
    gap: 3px;
}

.heatmap-cell {
    aspect-ratio: 1;
    border-radius: 2px;
    background: var(--bg-secondary);
}

.heatmap-1 { background: color-mix(in srgb, var(--accent) 30%, var(--bg-secondary)); }
.heatmap-2 { background: color-mix(in srgb, var(--accent) 52%, var(--bg-secondary)); }
.heatmap-3 { background: color-mix(in srgb, var(--accent) 74%, var(--bg-secondary)); }
.heatmap-4 { background: var(--accent); }

.state-bars {
    display: flex;
    flex-direction: column;
    gap: 8px;
}

.state-row {
    display: grid;
    grid-template-columns: 84px 1fr 44px;
    align-items: center;
    gap: 10px;
    font-size: 13px;
}

.state-label {
    color: var(--text-secondary);
}

.state-track {
    height: 8px;
    background: var(--bg-secondary);
    border-radius: var(--radius-pill);
    overflow: hidden;
}

.state-fill {
    height: 100%;
    border-radius: var(--radius-pill);
    background: var(--accent);
}

.state-fill-1 { background: var(--accent); }
.state-fill-2 { background: var(--success); }
.state-fill-3 { background: var(--warning); }

.state-count {
    text-align: right;
    color: var(--text-secondary);
    font-variant-numeric: tabular-nums;
}

@media (max-width: 560px) {
    .study-counts {
        grid-template-columns: repeat(3, 1fr);
    }

    .study-count-value {
        font-size: 24px;
    }

    .grade-bar {
        width: 100%;
        margin-left: 0;
    }

    .grade-button {
        flex: 1;
        min-width: 0;
    }
}

/* ==========================================================================
   Guest mode
   ========================================================================== */

/* The standing reminder. Deliberately quiet: it states where the data lives,
   it is not an alert, and it is on screen the entire time. */
.guest-strip {
    display: flex;
    align-items: center;
    gap: 10px;
    flex-wrap: wrap;
    padding: 8px 12px;
    font-size: 13px;
    color: var(--text-secondary);
    background: var(--bg-raised);
    border: 1px solid var(--border);
    border-radius: var(--radius-sm);
}

.guest-strip-text {
    flex: 1;
    min-width: 200px;
}

.guest-strip-action {
    font-family: var(--font-ui);
    font-size: 13px;
    font-weight: 600;
    color: var(--accent);
    background: none;
    border: none;
    padding: 2px 4px;
    cursor: pointer;
    text-decoration: underline;
    text-underline-offset: 3px;
    white-space: nowrap;
}

.guest-strip-action:hover {
    color: var(--accent-hover);
}

/* Shown once, and only once there is a real number to put in it. */
.upgrade-prompt {
    display: flex;
    flex-direction: column;
    gap: 8px;
    padding: 16px;
    background: var(--bg-raised);
    border: 1px solid var(--accent);
    border-radius: var(--radius);
    box-shadow: var(--shadow-2);
}

.upgrade-prompt-title {
    font-size: 16px;
    font-weight: 600;
}

.upgrade-prompt-body {
    font-size: 13px;
    color: var(--text-secondary);
}

.upgrade-prompt-cta {
    font-family: var(--font-ui);
    font-size: 15px;
    font-weight: 600;
    padding: 11px 22px;
    color: var(--bg-primary);
    background: var(--accent);
    border: none;
    border-radius: var(--radius-sm);
    cursor: pointer;
}

.upgrade-prompt-cta:hover {
    background: var(--accent-hover);
}

.upgrade-prompt-actions {
    display: flex;
    gap: 8px;
    margin-top: 4px;
    flex-wrap: wrap;
}

/* Not dismissible, and louder than the notice banner: progress is actively
   being lost while this is on screen. */
.storage-warning {
    display: flex;
    align-items: center;
    gap: 10px;
    flex-wrap: wrap;
    padding: 12px 14px;
    font-size: 13px;
    color: var(--error);
    background: color-mix(in srgb, var(--error) 12%, transparent);
    border: 1px solid var(--error);
    border-radius: var(--radius-sm);
}

.storage-warning .guest-strip-action {
    color: var(--error);
}

/* ==========================================================================
   The TUI layer: status bar, cursor, help overlay
   ========================================================================== */

/* Focused pane: where j/k acts. Quiet accent, tmux active-pane style. */
.pane.focused {
    border-color: var(--accent);
}

.pane.focused h3::after {
    content: ' \25c2';
    opacity: 0.7;
}

/* The cursor row. Distinct from `.current` (where you ARE) and `.selected`
   (what you picked): this is where the keyboard is pointing. */
.pane-item.cursor,
.search-hit.cursor {
    outline: 1px solid var(--accent);
    outline-offset: -1px;
    color: var(--text-primary);
}

.statusbar {
    position: fixed;
    inset: auto 0 0 0;
    z-index: 40;
    display: flex;
    align-items: stretch;
    gap: 2px;
    height: 30px;
    padding: 0 8px 0 0;
    background: var(--bg-raised);
    border-top: 1px solid var(--border);
    font-size: 12px;
    overflow: hidden;
}

.statusbar-context {
    display: flex;
    align-items: center;
    padding: 0 12px;
    background: var(--accent);
    color: var(--bg-primary);
    font-weight: 600;
    letter-spacing: 0.08em;
}

/* The , leader chip: quiet until armed, accent while the one-shot waits. */
.leader-chip {
    font-family: inherit;
    font-size: 12px;
    color: var(--text-secondary);
    border: 1px solid var(--border);
    border-radius: var(--radius-sm);
    padding: 0 6px;
    margin: 0 8px;
}

.leader-chip.leader-armed {
    color: var(--accent);
    border-color: var(--accent);
    background: color-mix(in srgb, var(--accent) 12%, transparent);
}

.statusbar-hints {
    display: flex;
    align-items: stretch;
    overflow: hidden;
}

.keyhint {
    display: flex;
    align-items: center;
    gap: 6px;
    padding: 0 10px;
    background: none;
    border: none;
    font-family: var(--font-mono);
    font-size: 12px;
    color: var(--text-secondary);
    cursor: pointer;
    white-space: nowrap;
}

.keyhint:hover {
    background: var(--bg-hover);
    color: var(--text-primary);
}

.keyhint-keys {
    color: var(--warning);
}

/* The status bar is a keyboard artifact; on touch there is no keyboard and
   the 30px are better spent on content. */
body {
    padding-bottom: 30px;
}

@media (hover: none) {
    .statusbar {
        display: none;
    }

    body {
        padding-bottom: 0;
    }
}

/* --- the ? cheatsheet --- */

.help-overlay {
    position: fixed;
    inset: 0;
    z-index: 50;
    display: grid;
    place-items: center;
    padding: 24px;
    background: rgb(10 12 20 / 0.7);
}

.help-card {
    width: min(560px, 100%);
    max-height: 80vh;
    overflow-y: auto;
    padding: 22px 26px;
    background: var(--bg-raised);
    border: 1px solid var(--border);
    border-radius: var(--radius);
    box-shadow: var(--shadow-2);
}

.help-title {
    font-size: 15px;
    color: var(--accent);
    letter-spacing: 0.06em;
    margin-bottom: 14px;
}

.help-rows {
    display: flex;
    flex-direction: column;
    gap: 6px;
}

.help-row {
    display: grid;
    grid-template-columns: 130px 1fr;
    gap: 14px;
    font-size: 13px;
}

.help-keys {
    color: var(--warning);
    text-align: right;
}

.help-desc {
    color: var(--text-secondary);
}

.help-footnote {
    margin-top: 16px;
    padding-top: 12px;
    border-top: 1px solid var(--border);
    font-size: 12px;
    color: var(--text-secondary);
    opacity: 0.8;
}

/* A second footnote is a continuation, not a new section: one rule above it. */
.help-footnote + .help-footnote {
    margin-top: 8px;
    padding-top: 0;
    border-top: none;
}

/* ==========================================================================
   Insights: tiers, calibration, problem lists, the review timeline
   ========================================================================== */

.stats-tile-hero .stats-tile-value {
    color: var(--success);
}

.stats-tile-delta {
    font-size: 12px;
    color: var(--success);
}

.tier-fluent { background: var(--success); }
.tier-solid { background: var(--accent); }
.tier-grinding { background: var(--warning); }
.tier-learning { background: var(--bg-hover); }

.grade-name-again { color: var(--error); }
.grade-name-hard { color: var(--warning); }
.grade-name-good { color: var(--success); }
.grade-name-easy { color: var(--accent); }

.calibration-verdict {
    margin-top: 12px;
    font-size: 13px;
    color: var(--text-primary);
}

.calibration-verdict.quiet {
    color: var(--text-secondary);
    font-style: italic;
}

.problem-rows {
    display: flex;
    flex-direction: column;
    gap: 4px;
}

.problem-row {
    display: flex;
    align-items: center;
    gap: 10px;
    width: 100%;
    padding: 8px 10px;
    font-family: var(--font-mono);
    font-size: 13px;
    text-align: left;
    color: var(--text-secondary);
    background: none;
    border: 1px solid transparent;
    border-radius: var(--radius-sm);
    cursor: pointer;
    transition: background var(--ease), color var(--ease);
}

.problem-row:hover {
    background: var(--bg-hover);
    color: var(--text-primary);
}

.problem-row.cursor {
    outline: 1px solid var(--accent);
    outline-offset: -1px;
    color: var(--text-primary);
}

.problem-row-title {
    flex: 1;
    min-width: 0;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

.problem-row-time {
    font-variant-numeric: tabular-nums;
    color: var(--text-primary);
}

.problem-row-due {
    font-size: 12px;
    opacity: 0.8;
    flex-shrink: 0;
}

.trend { flex-shrink: 0; }
.trend-down { color: var(--success); }
.trend-up { color: var(--warning); }

/* --- the review timeline --- */

.detail-card {
    width: min(640px, 100%);
}

.detail-context {
    font-size: 12px;
    color: var(--text-secondary);
    margin-bottom: 14px;
}

/* The journey headline: first honest solve to latest. The whole point. */
.detail-journey {
    font-size: 22px;
    font-weight: 600;
    color: var(--success);
    margin-bottom: 14px;
    font-variant-numeric: tabular-nums;
}

.timeline {
    display: flex;
    align-items: flex-end;
    gap: 8px;
    height: 120px;
    padding-bottom: 4px;
    overflow-x: auto;
}

.timeline-slot {
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: flex-end;
    gap: 4px;
    height: 100%;
    min-width: 34px;
}

.timeline-bar {
    width: 18px;
    border-radius: 2px 2px 0 0;
    min-height: 6px;
}

/* A reveal wears a ring: the bar's height is still real time spent, but it
   was spent reading the answer. */
.timeline-bar.revealed {
    outline: 2px dashed var(--text-secondary);
    outline-offset: 1px;
}

.grade-fill-again { background: var(--error); }
.grade-fill-hard { background: var(--warning); }
.grade-fill-good { background: var(--success); }
.grade-fill-easy { background: var(--accent); }

.timeline-caption {
    font-size: 10px;
    color: var(--text-secondary);
    white-space: nowrap;
}

.detail-intervals {
    margin-top: 12px;
    font-size: 12px;
    color: var(--text-secondary);
}
