/* ==========================================================================
   Reading practice — design system

   Every colour in the app comes from the tokens below and every measurement
   from the 4px step. If you need a value that isn't here, add a token rather
   than a literal, otherwise this file starts drifting again.

   Palette: aizuri — the indigo of Japanese woodblock printing — on washi
   paper. Celadon means learned or correct, ochre means due soon, clay means
   wrong or destructive. The accent is the indigo itself, not a pop colour.
   ========================================================================== */

:root {
    /* ground */
    --washi: #f4f2ec;
    --surface: #fffdf8;
    --surface-2: #edeae1;
    --surface-3: #e4e0d4;
    --hairline: #d9d5c9;

    /* ink */
    --ink: #191d2b;
    --muted: #5c6274;
    --faint: #7b8194;

    /* accent */
    --indigo: #2b4c9b;
    --indigo-hover: #1f3b7d;
    --indigo-soft: #e2e7f4;
    --indigo-line: #b9c6e6;
    --on-indigo: #fffdf8;

    /* semantic */
    --celadon: #3f7a5e;
    --celadon-soft: #e1ede6;
    --ochre: #8d6318;
    --ochre-soft: #f5ecdb;
    --clay: #a8402f;
    --clay-soft: #f5e3df;

    /* type */
    --ui: ui-sans-serif, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto,
        "Helvetica Neue", Arial, sans-serif;
    --read: "Iowan Old Style", "Palatino Linotype", Palatino, "Book Antiqua",
        Georgia, serif;
    --cjk: "Hiragino Mincho ProN", "Yu Mincho", YuMincho, "Songti SC",
        "Noto Serif CJK JP", serif;
    --mono: ui-monospace, SFMono-Regular, "SF Mono", Menlo, Consolas,
        "Liberation Mono", monospace;

    /* step scale — everything spatial is a multiple of 4px */
    --s1: 4px;
    --s2: 8px;
    --s3: 12px;
    --s4: 16px;
    --s5: 20px;
    --s6: 24px;
    --s8: 32px;
    --s10: 40px;
    --s12: 48px;

    --radius-sm: 6px;
    --radius: 10px;
    --radius-lg: 14px;
    --radius-pill: 999px;

    --shadow-sm: 0 1px 2px rgba(25, 29, 43, .06);
    --shadow-lg: 0 8px 20px rgba(25, 29, 43, .12), 0 32px 64px -32px rgba(25, 29, 43, .4);

    --rail-w: 68px;
    --tap: 44px;

    color-scheme: light;
}

@media (prefers-color-scheme: dark) {
    :root {
        --washi: #12151f;
        --surface: #1b1f2c;
        --surface-2: #232838;
        --surface-3: #2b3145;
        --hairline: #2e3446;

        --ink: #e9eaef;
        --muted: #98a0b4;
        --faint: #7d8599;

        --indigo: #93aeee;
        --indigo-hover: #aec3f5;
        --indigo-soft: #232c45;
        --indigo-line: #3b4560;
        --on-indigo: #12151f;

        --celadon: #7fb79a;
        --celadon-soft: #1d2e27;
        --ochre: #d8a24f;
        --ochre-soft: #2e2718;
        --clay: #e08a78;
        --clay-soft: #331f1c;

        --shadow-sm: 0 1px 2px rgba(0, 0, 0, .3);
        --shadow-lg: 0 8px 20px rgba(0, 0, 0, .4), 0 32px 64px -32px rgba(0, 0, 0, .9);

        color-scheme: dark;
    }
}

/* The in-app toggle must beat the media query in both directions. */
:root[data-theme="dark"] {
    --washi: #12151f;
    --surface: #1b1f2c;
    --surface-2: #232838;
    --surface-3: #2b3145;
    --hairline: #2e3446;
    --ink: #e9eaef;
    --muted: #98a0b4;
    --faint: #7d8599;
    --indigo: #93aeee;
    --indigo-hover: #aec3f5;
    --indigo-soft: #232c45;
    --indigo-line: #3b4560;
    --on-indigo: #12151f;
    --celadon: #7fb79a;
    --celadon-soft: #1d2e27;
    --ochre: #d8a24f;
    --ochre-soft: #2e2718;
    --clay: #e08a78;
    --clay-soft: #331f1c;
    --shadow-sm: 0 1px 2px rgba(0, 0, 0, .3);
    --shadow-lg: 0 8px 20px rgba(0, 0, 0, .4), 0 32px 64px -32px rgba(0, 0, 0, .9);
    color-scheme: dark;
}

:root[data-theme="light"] {
    --washi: #f4f2ec;
    --surface: #fffdf8;
    --surface-2: #edeae1;
    --surface-3: #e4e0d4;
    --hairline: #d9d5c9;
    --ink: #191d2b;
    --muted: #5c6274;
    --faint: #7b8194;
    --indigo: #2b4c9b;
    --indigo-hover: #1f3b7d;
    --indigo-soft: #e2e7f4;
    --indigo-line: #b9c6e6;
    --on-indigo: #fffdf8;
    --celadon: #3f7a5e;
    --celadon-soft: #e1ede6;
    --ochre: #8d6318;
    --ochre-soft: #f5ecdb;
    --clay: #a8402f;
    --clay-soft: #f5e3df;
    --shadow-sm: 0 1px 2px rgba(25, 29, 43, .06);
    --shadow-lg: 0 8px 20px rgba(25, 29, 43, .12), 0 32px 64px -32px rgba(25, 29, 43, .4);
    color-scheme: light;
}

/* --------------------------------------------------------------- reset -- */

*, *::before, *::after { box-sizing: border-box; }

html { background: var(--washi); }

body {
    margin: 0;
    background: var(--washi);
    color: var(--ink);
    font-family: var(--ui);
    font-size: 15px;
    line-height: 1.55;
    -webkit-font-smoothing: antialiased;
    overscroll-behavior-y: none;
}

h1, h2, h3, h4 { margin: 0; font-weight: 600; text-wrap: balance; }
p { margin: 0; }
ul, ol { margin: 0; padding: 0; list-style: none; }
img, svg { max-width: 100%; }
a { color: var(--indigo); }

button, input, select, textarea { font: inherit; color: inherit; }

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

.visually-hidden {
    position: absolute;
    width: 1px; height: 1px;
    margin: -1px; padding: 0;
    overflow: hidden;
    clip: rect(0 0 0 0);
    clip-path: inset(50%);
    white-space: nowrap;
}

.hidden { display: none !important; }
/* Components set display, which would otherwise beat the hidden attribute. */
[hidden] { display: none !important; }

@media (prefers-reduced-motion: reduce) {
    *, *::before, *::after {
        animation-duration: .01ms !important;
        animation-iteration-count: 1 !important;
        transition-duration: .01ms !important;
        scroll-behavior: auto !important;
    }
}

/* ------------------------------------------------------------ app shell -- */

.app {
    display: grid;
    grid-template-columns: var(--rail-w) minmax(0, 1fr);
    min-height: 100vh;
    min-height: 100dvh;
}

/* ------------------------------------------------------- public shell -- */

/* No rail for a visitor, so the one column is the whole width and the bar
   sits across the top of it. Rows are named because the grid is at least a
   screen tall: left to itself it shares the leftover height between the two
   of them and the bar comes out a third of the page deep. */
.app-public {
    grid-template-columns: minmax(0, 1fr);
    grid-template-rows: auto minmax(0, 1fr);
}

.public-bar {
    grid-column: 1;
    display: flex;
    align-items: center;
    flex-wrap: wrap;
    gap: var(--s2);
    padding: var(--s3) var(--s5);
    border-bottom: 1px solid var(--hairline);
    background: var(--surface);
}
.public-brand {
    display: inline-flex;
    align-items: center;
    gap: var(--s2);
    font-weight: 600;
    color: var(--ink);
    text-decoration: none;
    margin-right: var(--s2);
}
.public-brand svg {
    width: 20px; height: 20px;
    fill: none;
    stroke: currentColor;
    stroke-width: 1.6;
    stroke-linecap: round;
    stroke-linejoin: round;
    color: var(--indigo);
}
.public-nav { display: flex; flex-wrap: wrap; gap: var(--s1); }
/* Pushed to the far end, and wrapping as a pair rather than one at a time. */
.public-auth { display: flex; align-items: center; gap: var(--s2); margin-left: auto; }
.public-link {
    padding: var(--s1) var(--s3);
    border-radius: var(--radius-pill);
    font-size: 13px;
    color: var(--muted);
    text-decoration: none;
}
.public-link:hover { color: var(--ink); background: var(--surface-2); }
.public-link[aria-current="page"] { color: var(--indigo); background: var(--indigo-soft); }

/* ---------------------------------------------------------- scenarios -- */

.scenario-grid {
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(320px, 1fr));
    gap: var(--s3);
}
.scenario-card {
    display: flex;
    gap: var(--s4);
    padding: var(--s4);
    border: 1px solid var(--hairline);
    border-radius: var(--radius);
    background: var(--surface);
    text-decoration: none;
    color: var(--ink);
    transition: border-color .12s, background .12s;
}
.scenario-card:hover { border-color: var(--indigo-line); background: var(--indigo-soft); }
.scenario-card-icon {
    flex: none;
    display: grid;
    place-items: center;
    width: 40px; height: 40px;
    border-radius: var(--radius);
    background: var(--indigo-soft);
    color: var(--indigo);
}
.scenario-card:hover .scenario-card-icon { background: var(--surface); }
.scenario-card-icon svg,
.scenario-brief-scene svg {
    width: 22px; height: 22px;
    fill: none;
    stroke: currentColor;
    stroke-width: 1.6;
    stroke-linecap: round;
    stroke-linejoin: round;
}
.scenario-card-body { display: grid; gap: var(--s1); min-width: 0; }
.scenario-card-head { display: flex; align-items: center; gap: var(--s2); flex-wrap: wrap; }
.scenario-card-title { font-weight: 600; font-size: 15.5px; }
/* Carried on or done. Both quiet: the card is the thing being read, and the
   state is a note in the corner of it. */
.scenario-state { font-size: 11px; }
.scenario-state.is-open { background: var(--indigo-soft); color: var(--indigo); }
.scenario-state.is-done { background: var(--celadon-soft); color: var(--celadon); }

.scenario-card-place {
    font-family: var(--mono);
    font-size: 11px;
    letter-spacing: .04em;
    color: var(--muted);
}
.scenario-card-setting { font-size: 13px; line-height: 1.55; color: var(--muted); }
.scenario-card-goal { font-size: 12.5px; line-height: 1.5; color: var(--muted); margin-top: var(--s1); }
.scenario-card-goal b { color: var(--ink); font-weight: 600; }

/* The room on the left, the conversation on the right. */
/* One multiplier for everything the conversation is written in, so text size
   is a single number rather than a rule per element. The panel beside it does
   not scale: the room does not need to be bigger, the Japanese does. */
.scenario-stage { --scenario-zoom: 1; }
.scenario-zoom { display: flex; gap: var(--s1); }

.scenario-stage {
    flex: 1;
    display: grid;
    grid-template-columns: minmax(240px, 300px) minmax(0, 1fr);
    gap: var(--s5);
    padding: var(--s5);
    min-height: 0;
}

.scenario-brief {
    display: grid;
    gap: var(--s2);
    align-content: start;
    padding: var(--s4);
    border: 1px solid var(--hairline);
    border-radius: var(--radius);
    background: var(--surface);
}
/* Where the picture goes. Until there is one this is the icon on a wash, so
   the panel still reads as a place rather than as a gap. */
.scenario-brief-scene {
    display: grid;
    place-items: center;
    height: 96px;
    border-radius: var(--radius);
    background: var(--indigo-soft);
    color: var(--indigo);
    margin-bottom: var(--s2);
}
.scenario-brief-scene svg { width: 34px; height: 34px; }
.scenario-brief-setting { font-size: 13px; line-height: 1.6; color: var(--muted); }
.scenario-brief-goal { font-size: 13px; line-height: 1.55; margin-top: var(--s2); }
.scenario-brief-goal b { font-weight: 600; }

.scenario-phrases { display: grid; gap: var(--s2); margin-top: var(--s4); }
.scenario-phrase {
    display: grid;
    gap: 1px;
    padding: var(--s2) var(--s3);
    border: 1px solid var(--hairline);
    border-radius: var(--radius-sm);
    background: var(--surface-2);
    text-align: left;
    cursor: pointer;
}
.scenario-phrase:hover { border-color: var(--indigo-line); }
.scenario-phrase-ja { font-family: var(--cjk); font-size: 13.5px; line-height: 1.5; }
.scenario-phrase-romaji { font-family: var(--mono); font-size: 10.5px; color: var(--muted); }
.scenario-phrase-meaning { font-size: 11.5px; color: var(--muted); line-height: 1.4; }

.scenario-talk { display: flex; flex-direction: column; min-height: 0; gap: var(--s3); }
.scenario-thread {
    flex: 1;
    overflow-y: auto;
    display: flex;
    flex-direction: column;
    gap: var(--s4);
    padding: var(--s4);
    border: 1px solid var(--hairline);
    border-radius: var(--radius);
    background: var(--surface);
    min-height: 240px;
}

.scenario-line { display: grid; gap: var(--s1); max-width: 78%; }
.scenario-line.is-mine { margin-left: auto; justify-items: end; }
.scenario-bubble {
    display: grid;
    gap: 2px;
    padding: var(--s3) var(--s4);
    border-radius: var(--radius);
    background: var(--surface-2);
}
.scenario-line.is-mine .scenario-bubble {
    background: var(--indigo-soft);
    text-align: right;
}
.scenario-ja { font-family: var(--cjk); font-size: calc(16px * var(--scenario-zoom)); line-height: 1.6; }
.scenario-romaji { font-family: var(--mono); font-size: calc(11.5px * var(--scenario-zoom)); color: var(--muted); }
.scenario-meaning { font-size: calc(12.5px * var(--scenario-zoom)); color: var(--muted); line-height: 1.45; margin-top: 2px; }

/* Beside what you said, not instead of it. */
.scenario-fix {
    display: grid;
    gap: 1px;
    padding: var(--s2) var(--s3);
    border-left: 2px solid var(--ochre);
    background: var(--ochre-soft);
    border-radius: 0 var(--radius-sm) var(--radius-sm) 0;
    text-align: left;
    justify-self: end;
    max-width: 100%;
}
.scenario-fix-label {
    font-family: var(--mono);
    font-size: 10px;
    letter-spacing: .08em;
    text-transform: uppercase;
    color: var(--ochre);
}
.scenario-fix-ja { font-family: var(--cjk); font-size: calc(14px * var(--scenario-zoom)); line-height: 1.55; }
.scenario-fix-romaji { font-family: var(--mono); font-size: calc(11px * var(--scenario-zoom)); color: var(--muted); }
.scenario-fix-meaning { font-size: calc(12.5px * var(--scenario-zoom)); line-height: 1.45; }
.scenario-fix-note {
    font-size: 12px;
    line-height: 1.45;
    color: var(--muted);
    margin-top: var(--s1);
}

/* A word in a line, tappable the way a word in a passage is. The bubbles are
   tight, so this leans on the underline rather than on padding. */
.scenario-ja .word,
.scenario-fix-ja .word {
    padding: 0 1px;
    margin: 0;
    border-bottom: 1.5px solid transparent;
}
.scenario-ja .word:hover,
.scenario-fix-ja .word:hover { background: none; border-bottom-color: var(--indigo-line); }
.scenario-ja .word.is-saved,
.scenario-fix-ja .word.is-saved { border-bottom-color: var(--celadon); }
.scenario-ja .word.is-open,
.scenario-fix-ja .word.is-open { background: var(--indigo); color: var(--on-indigo); }

/* The gloss takes the scene's column while it is open. */
.scenario-gloss { display: grid; gap: var(--s2); align-content: start; }
.scenario-gloss-head {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: var(--s2);
    margin-bottom: var(--s2);
}

.scenario-compose { display: flex; gap: var(--s2); }
.scenario-input { flex: 1; font-family: var(--cjk); font-size: calc(15px * var(--scenario-zoom)); }
.scenario-send { flex: none; }
.scenario-hint { font-size: 12.5px; color: var(--muted); min-height: 1.2em; }

.scenario-end {
    display: grid;
    gap: var(--s3);
    justify-items: start;
    padding: var(--s5);
    border: 1px solid var(--celadon);
    border-radius: var(--radius);
    background: var(--celadon-soft);
}
.scenario-end-title { font-weight: 600; font-size: 16px; }
.scenario-end-body { font-size: 13.5px; line-height: 1.55; color: var(--muted); }

/* ------------------------------------------------------------ landing -- */

.landing { display: grid; gap: var(--s12); padding: var(--s10) var(--s5) var(--s12); }
.landing > * { width: 100%; max-width: 900px; margin-inline: auto; }

.landing-hero { display: grid; gap: var(--s4); justify-items: start; }
.landing-eyebrow {
    font-family: var(--mono);
    font-size: 11px;
    letter-spacing: .12em;
    text-transform: uppercase;
    color: var(--indigo);
}
.landing-title {
    font-size: clamp(30px, 4.6vw, 46px);
    line-height: 1.15;
    letter-spacing: -.02em;
    max-width: 18ch;
}
.landing-lede {
    font-family: var(--read);
    font-size: clamp(16px, 1.6vw, 19px);
    line-height: 1.6;
    color: var(--muted);
    max-width: 58ch;
}
.landing-actions { display: flex; flex-wrap: wrap; gap: var(--s3); margin-top: var(--s2); }
.landing-note { font-size: 12.5px; color: var(--faint); }

/* The reader's own markup, standing still. */
.landing-demo {
    display: grid;
    grid-template-columns: minmax(0, 1.4fr) minmax(260px, 1fr);
    align-items: center;
    gap: var(--s5);
    padding: var(--s6);
    border: 1px solid var(--hairline);
    border-radius: var(--radius-lg);
    background: var(--surface);
}
.landing-demo .word { cursor: default; }
.landing-demo .word:hover { background: none; }
/* Scoped through the parent so it outranks `.word { background: none }`,
   which is declared further down the file and was winning on order. */
.landing-demo .landing-demo-word { background: var(--indigo); color: var(--on-indigo); }
.landing-demo-text { display: grid; gap: var(--s3); align-content: start; }
.landing-demo-label,
.landing-demo-hint { font-size: 12.5px; color: var(--muted); }
.landing-demo-gloss {
    display: grid;
    gap: 2px;
    align-content: start;
    padding: var(--s4);
    border: 1px solid var(--hairline);
    border-radius: var(--radius);
    background: var(--surface-2);
}
.landing-demo-kanji { margin-top: var(--s4); background: var(--surface); }

.landing-steps {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(240px, 1fr));
    gap: var(--s5);
}
.landing-step { display: grid; gap: var(--s2); align-content: start; }
.landing-step-n {
    font-family: var(--mono);
    font-size: 12px;
    color: var(--indigo);
}
.landing-step-title { font-size: 17px; line-height: 1.3; }
.landing-step p { font-size: 14px; line-height: 1.6; color: var(--muted); }

.landing-section-title { font-size: 22px; line-height: 1.25; }
.landing-section-sub {
    font-size: 14.5px;
    line-height: 1.6;
    color: var(--muted);
    max-width: 56ch;
    margin-top: var(--s2);
}
.landing-links {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(260px, 1fr));
    gap: var(--s3);
    margin-top: var(--s5);
}
.landing-link {
    display: grid;
    gap: var(--s1);
    padding: var(--s4);
    border: 1px solid var(--hairline);
    border-radius: var(--radius);
    background: var(--surface);
    text-decoration: none;
    color: var(--ink);
    transition: border-color .12s, background .12s;
}
.landing-link:hover { border-color: var(--indigo-line); background: var(--indigo-soft); }
.landing-link-title { font-weight: 600; font-size: 15px; }
.landing-link-sub { font-size: 13px; line-height: 1.5; color: var(--muted); }

.landing-close {
    padding: var(--s8) var(--s6);
    border: 1px solid var(--indigo-line);
    border-radius: var(--radius-lg);
    background: var(--indigo-soft);
    display: grid;
    gap: var(--s2);
    justify-items: start;
}

/* The way in, at the foot of the page rather than in front of it. */
.public-cta {
    margin: var(--s10) var(--s5) var(--s8);
    padding: var(--s6);
    border: 1px solid var(--indigo-line);
    border-radius: var(--radius-lg);
    background: var(--indigo-soft);
    display: grid;
    gap: var(--s3);
    max-width: 62ch;
}
.public-cta-title { font-size: 19px; line-height: 1.3; }
.public-cta-body {
    font-family: var(--read);
    font-size: 15px;
    line-height: 1.6;
    color: var(--ink);
}
.public-cta-actions { display: flex; flex-wrap: wrap; gap: var(--s2); }
.public-cta-note { font-size: 12.5px; color: var(--muted); }

.rail {
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: var(--s1);
    padding: var(--s3) 0 calc(var(--s3) + env(safe-area-inset-bottom));
    background: var(--surface-2);
    border-right: 1px solid var(--hairline);
    position: sticky;
    top: 0;
    height: 100vh;
    height: 100dvh;
}

.rail-item {
    position: relative;
    width: 52px;
    padding: var(--s2) 0 var(--s1);
    border: 1px solid transparent;
    border-radius: var(--radius);
    background: none;
    color: var(--muted);
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: 3px;
    cursor: pointer;
    text-decoration: none;
    transition: background .12s, color .12s;
}

.rail-item svg {
    width: 20px; height: 20px;
    fill: none;
    stroke: currentColor;
    stroke-width: 1.6;
    stroke-linecap: round;
    stroke-linejoin: round;
}

/* Centred because a label that wraps is two lines, and the flex centring only
   places the box — inside it the lines were still flush left, so "Kanji" sat
   off to one side above "library" while the icon above them was centred. */
.rail-label { font-size: 10px; letter-spacing: .02em; text-align: center; }

.rail-item:hover { color: var(--ink); background: var(--surface-3); }

.rail-item[aria-current="page"] {
    background: var(--indigo-soft);
    border-color: var(--indigo-line);
    color: var(--indigo);
}

.rail-spacer { flex: 1; }

.rail-badge {
    position: absolute;
    top: 2px; right: 4px;
    min-width: 17px; height: 17px;
    padding: 0 4px;
    border-radius: var(--radius-pill);
    background: var(--clay);
    color: #fff;
    font-family: var(--mono);
    font-size: 9px;
    line-height: 17px;
    text-align: center;
    font-variant-numeric: tabular-nums;
}

/* One icon per theme; the other is hidden. */
.icon-moon { display: none; }
@media (prefers-color-scheme: dark) {
    :root:not([data-theme="light"]) .icon-sun { display: none; }
    :root:not([data-theme="light"]) .icon-moon { display: block; }
}
:root[data-theme="dark"] .icon-sun { display: none; }
:root[data-theme="dark"] .icon-moon { display: block; }
:root[data-theme="light"] .icon-sun { display: block; }
:root[data-theme="light"] .icon-moon { display: none; }

.main { min-width: 0; display: flex; flex-direction: column; }

/* ----------------------------------------------------------- page head -- */

.page-head {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: var(--s2);
    padding: var(--s3) var(--s5);
    border-bottom: 1px solid var(--hairline);
    background: var(--surface);
}

.page-title { font-size: 15px; margin-right: var(--s2); }

.page-sub {
    font-family: var(--mono);
    font-size: 11px;
    letter-spacing: .08em;
    text-transform: uppercase;
    color: var(--muted);
}

.grow { flex: 1; }

/* ---------------------------------------------------------- primitives -- */

.chip {
    display: inline-flex;
    align-items: center;
    gap: var(--s1);
    height: var(--s8);
    padding: 0 var(--s3);
    border: 1px solid var(--hairline);
    border-radius: var(--radius-pill);
    background: var(--surface);
    color: var(--ink);
    font-size: 13px;
    white-space: nowrap;
    cursor: pointer;
    text-decoration: none;
    transition: border-color .12s, background .12s, color .12s;
}

.chip:hover { border-color: var(--indigo-line); }
.chip[aria-pressed="true"], .chip.is-active {
    background: var(--indigo-soft);
    border-color: var(--indigo-line);
    color: var(--indigo);
    font-weight: 600;
}
.chip .caret { color: var(--faint); font-size: 10px; }
.chip-value { color: var(--muted); }
.chip[aria-pressed="true"] .chip-value, .chip.is-active .chip-value { color: inherit; }

.btn {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    gap: var(--s2);
    height: var(--s8);
    padding: 0 var(--s4);
    border: 1px solid var(--indigo);
    border-radius: var(--radius-pill);
    background: var(--indigo);
    color: var(--on-indigo);
    font-size: 13px;
    font-weight: 600;
    cursor: pointer;
    text-decoration: none;
    transition: background .12s, border-color .12s;
}

.btn:hover { background: var(--indigo-hover); border-color: var(--indigo-hover); }
.btn[disabled] { opacity: .45; cursor: not-allowed; }
.btn svg { width: 15px; height: 15px; fill: none; stroke: currentColor; stroke-width: 1.7; stroke-linecap: round; stroke-linejoin: round; }

.btn-ghost { background: none; color: var(--ink); border-color: var(--hairline); font-weight: 500; }
.btn-ghost:hover { background: var(--surface-2); border-color: var(--indigo-line); color: var(--indigo); }

.btn-danger { background: var(--clay); border-color: var(--clay); color: #fff; }
.btn-danger:hover { background: var(--clay); border-color: var(--clay); filter: brightness(.92); }

.btn-lg { height: var(--s10); padding: 0 var(--s6); font-size: 14px; }
.btn-block { width: 100%; }
.btn-on { background: var(--celadon-soft); border-color: var(--celadon); color: var(--celadon); }
.btn-on:hover { background: var(--celadon-soft); border-color: var(--celadon); color: var(--celadon); }

.icon-btn {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: var(--s8); height: var(--s8);
    border: 1px solid var(--hairline);
    border-radius: var(--radius-sm);
    background: var(--surface);
    color: var(--muted);
    cursor: pointer;
    transition: color .12s, border-color .12s;
}
.icon-btn:hover { color: var(--indigo); border-color: var(--indigo-line); }
.icon-btn svg { width: 16px; height: 16px; fill: none; stroke: currentColor; stroke-width: 1.7; stroke-linecap: round; stroke-linejoin: round; }
.icon-btn.is-danger:hover { color: var(--clay); border-color: var(--clay); }
.icon-btn[disabled] { opacity: .4; cursor: not-allowed; }

.card {
    border: 1px solid var(--hairline);
    border-radius: var(--radius-lg);
    background: var(--surface);
    box-shadow: var(--shadow-sm);
}

.label {
    display: block;
    font-family: var(--mono);
    font-size: 10.5px;
    letter-spacing: .1em;
    text-transform: uppercase;
    color: var(--muted);
    font-weight: 400;
}

.field { display: grid; gap: var(--s2); }
.label-hint { color: var(--faint); letter-spacing: .04em; text-transform: none; }
.field-hint { font-size: 12.5px; color: var(--muted); }

/* The entry a look-up found, shown for confirmation before it is kept. */
.lookup-card {
    padding: var(--s5);
    border: 1px solid var(--hairline);
    border-radius: var(--radius);
    background: var(--surface-2);
    display: grid;
    gap: var(--s1);
    margin-bottom: var(--s3);
}
.lookup-word { font-family: var(--cjk); font-size: 30px; line-height: 1.25; word-break: break-word; }
.lookup-word[data-latin="true"] { font-family: var(--read); }
.lookup-romaji { font-family: var(--mono); font-size: 12.5px; color: var(--muted); }
.lookup-meaning { font-family: var(--read); font-size: 17px; margin-top: var(--s2); }

.input, .select {
    width: 100%;
    height: var(--s10);
    padding: 0 var(--s3);
    border: 1px solid var(--hairline);
    border-radius: var(--radius);
    background: var(--surface);
    color: var(--ink);
    font-size: 14px;
}
.input::placeholder { color: var(--faint); }
.input:focus-visible, .select:focus-visible { border-color: var(--indigo); }
.select { appearance: none; padding-right: var(--s8); cursor: pointer; }

/* Safari on iOS zooms the whole page in when you focus a field whose text is
   smaller than 16px, and it does not zoom back out when you leave the field —
   so one tap on a search box leaves the app scrolled sideways until the reader
   pinches it back. 16px is the threshold, not a preference, which is why this
   is a flat value and not a token. Confined to touch pointers so the desk
   layout keeps its 14px fields.

   This sits directly under the base rule on purpose: the fields that set their
   own size later — the drill's 24px answer box — are meant to keep it, and
   only need to be equal to or above the threshold, not equal to this. */
@media (pointer: coarse) {
    .input, .select, textarea { font-size: 16px; }
}

.select-wrap { position: relative; }
.select-wrap::after {
    content: "";
    position: absolute;
    right: var(--s4); top: 50%;
    width: 7px; height: 7px;
    margin-top: -5px;
    border-right: 1.5px solid var(--muted);
    border-bottom: 1.5px solid var(--muted);
    transform: rotate(45deg);
    pointer-events: none;
}

.check {
    display: inline-flex;
    align-items: center;
    gap: var(--s2);
    height: var(--s10);
    padding: 0 var(--s3);
    border: 1px solid var(--hairline);
    border-radius: var(--radius);
    background: var(--surface);
    cursor: pointer;
    font-size: 14px;
}
.check input { accent-color: var(--indigo); width: 16px; height: 16px; }
.check:has(input:checked) { border-color: var(--indigo-line); background: var(--indigo-soft); color: var(--indigo); }

/* The box that means all the others. Set apart from them, because it is not
   another answer of the same kind — it says what the answers are. */
.check.is-all {
    border-style: dashed;
    margin-right: var(--s2);
}
.check.is-all:has(input:indeterminate) {
    border-color: var(--indigo-line);
    color: var(--indigo);
}
/* Half-ticked, so it should not read as ticked. */
.check.is-all input:indeterminate { accent-color: var(--indigo-line); }

.pill {
    display: inline-block;
    padding: 2px var(--s2);
    border-radius: var(--radius-pill);
    background: var(--indigo-soft);
    color: var(--indigo);
    font-family: var(--mono);
    font-size: 10px;
    letter-spacing: .07em;
    text-transform: uppercase;
}
.pill-muted { background: var(--surface-3); color: var(--muted); }
.pill-good { background: var(--celadon-soft); color: var(--celadon); }
.pill-warn { background: var(--ochre-soft); color: var(--ochre); }
.pill-bad { background: var(--clay-soft); color: var(--clay); }

.banner {
    display: flex;
    align-items: flex-start;
    gap: var(--s2);
    padding: var(--s3) var(--s4);
    border: 1px solid var(--hairline);
    border-radius: var(--radius);
    font-size: 13.5px;
}
.banner-error { background: var(--clay-soft); border-color: var(--clay); color: var(--clay); }
.banner-ok { background: var(--celadon-soft); border-color: var(--celadon); color: var(--celadon); }
.banner ul { display: grid; gap: var(--s1); }

.empty {
    display: grid;
    justify-items: center;
    gap: var(--s3);
    padding: var(--s12) var(--s5);
    text-align: center;
    color: var(--muted);
}
.empty svg { width: 40px; height: 40px; fill: none; stroke: currentColor; stroke-width: 1.4; stroke-linecap: round; stroke-linejoin: round; opacity: .5; }
.empty-title { font-size: 16px; font-weight: 600; color: var(--ink); }
.empty p { max-width: 44ch; font-size: 13.5px; }

/* --------------------------------------------------------------- toasts -- */

.toasts {
    position: fixed;
    left: 50%;
    bottom: calc(var(--s5) + env(safe-area-inset-bottom));
    transform: translateX(-50%);
    display: grid;
    gap: var(--s2);
    z-index: 90;
    width: min(420px, calc(100vw - var(--s8)));
    pointer-events: none;
}

.toast {
    display: flex;
    align-items: center;
    gap: var(--s2);
    padding: var(--s3) var(--s4);
    border: 1px solid var(--hairline);
    border-radius: var(--radius);
    background: var(--surface);
    box-shadow: var(--shadow-lg);
    font-size: 13.5px;
    pointer-events: auto;
    animation: toast-in .18s ease-out;
}
.toast-error { border-color: var(--clay); color: var(--clay); background: var(--clay-soft); }
.toast-ok { border-color: var(--celadon); color: var(--celadon); background: var(--celadon-soft); }
.toast.is-leaving { animation: toast-out .18s ease-in forwards; }
.toast-close { margin-left: auto; border: 0; background: none; color: inherit; cursor: pointer; opacity: .7; font-size: 16px; line-height: 1; }

@keyframes toast-in { from { opacity: 0; transform: translateY(8px); } }
@keyframes toast-out { to { opacity: 0; transform: translateY(8px); } }

/* --------------------------------------------------------------- modal -- */

.modal {
    position: fixed;
    inset: 0;
    z-index: 100;
    display: flex;
    align-items: center;
    justify-content: center;
    padding: var(--s5);
    background: rgba(25, 29, 43, .5);
}
.modal-card {
    width: min(460px, 100%);
    max-height: calc(100dvh - var(--s10));
    overflow-y: auto;
    padding: var(--s6);
    border: 1px solid var(--hairline);
    border-radius: var(--radius-lg);
    background: var(--surface);
    box-shadow: var(--shadow-lg);
    display: grid;
    gap: var(--s4);
}
.modal-title { font-size: 17px; }
.modal-actions { display: flex; gap: var(--s2); justify-content: flex-end; }

/* One saved word, opened from its row. Wider than the plain modal because it
   carries the character breakdown, which is a list rather than a sentence. */
.word-modal-card { width: min(520px, 100%); }
.word-detail-head {
    display: flex;
    align-items: flex-start;
    justify-content: space-between;
    gap: var(--s3);
}
.word-detail-word {
    font-family: var(--cjk);
    font-size: 28px;
    line-height: 1.25;
    word-break: break-word;
}
.word-detail-word[data-latin="true"] { font-family: var(--read); }

.word-stats {
    display: grid;
    gap: var(--s2);
    padding-top: var(--s4);
    border-top: 1px solid var(--hairline);
}
.word-stat-rows { display: grid; gap: var(--s1); }
.word-stat {
    display: grid;
    grid-template-columns: 1fr auto auto;
    align-items: center;
    gap: var(--s3);
    padding: var(--s2) var(--s3);
    border-radius: var(--radius);
    background: var(--surface-2);
    font-size: 13px;
}
.word-stat-name { font-weight: 500; }
.word-stat-when {
    font-family: var(--mono);
    font-size: 11.5px;
    color: var(--muted);
    white-space: nowrap;
}
/* A question that has never been put is not a question going badly, and five
   empty bars on a full-strength row would read as the latter. */
.word-stat.is-untouched { opacity: .62; }

/* The lists a word is in, and the box for putting it in another. The first
   row of the dropdown is the input itself — what you type on the left, and a +
   on the right that makes a list of that name. Anything you already have that
   matches shows underneath, to be picked instead. */
.word-tags {
    display: grid;
    gap: var(--s2);
    padding-top: var(--s4);
    border-top: 1px solid var(--hairline);
}
.tagbox { position: relative; display: grid; gap: var(--s2); }
.tagbox-chips { display: flex; flex-wrap: wrap; gap: var(--s1); font-size: 12.5px; color: var(--muted); }
.tagbox-chip {
    display: inline-flex;
    align-items: center;
    gap: var(--s1);
    padding: 3px var(--s1) 3px var(--s3);
    border-radius: var(--radius-pill);
    background: var(--indigo-soft);
    color: var(--indigo);
    font-size: 12.5px;
}
.tagbox-chip-drop {
    display: grid;
    place-items: center;
    width: 18px;
    height: 18px;
    border: 0;
    border-radius: 50%;
    background: none;
    color: inherit;
    cursor: pointer;
    opacity: .65;
}
.tagbox-chip-drop:hover { opacity: 1; background: var(--surface); }
.tagbox-chip-drop svg {
    width: 11px; height: 11px;
    fill: none; stroke: currentColor; stroke-width: 2.2; stroke-linecap: round;
}

.tagbox-field { position: relative; display: flex; align-items: center; }
.tagbox-input { flex: 1; padding-right: calc(var(--s8) + var(--s2)); }
/* Sits inside the input rather than beside it, because the row is one thing:
   what you typed, and the way to keep it. */
.tagbox-add {
    position: absolute;
    right: var(--s2);
    display: grid;
    place-items: center;
    width: var(--s7);
    height: var(--s7);
    border: 0;
    border-radius: var(--radius);
    background: var(--indigo-soft);
    color: var(--indigo);
    cursor: pointer;
}
.tagbox-add:hover { background: var(--indigo); color: var(--on-indigo); }
.tagbox-add[disabled] { opacity: .45; cursor: not-allowed; }
.tagbox-add svg {
    width: 15px; height: 15px;
    fill: none; stroke: currentColor; stroke-width: 2; stroke-linecap: round;
}

/* In the flow rather than floating over it. The panel this sits in scrolls
   and clips what overflows, so an absolutely positioned menu opening near the
   bottom was cut in half — and a dropdown you have to know to scroll for is
   worse than a list that simply pushes the page down. */
.tagbox-menu {
    margin-top: var(--s1);
    padding: var(--s1);
    list-style: none;
    max-height: 210px;
    overflow-y: auto;
    border: 1px solid var(--hairline);
    border-radius: var(--radius);
    background: var(--surface);
    box-shadow: var(--shadow-lg);
}
.tagbox-option {
    display: flex;
    align-items: baseline;
    justify-content: space-between;
    gap: var(--s3);
    width: 100%;
    padding: var(--s2) var(--s3);
    border: 0;
    border-radius: var(--radius);
    background: none;
    color: var(--ink);
    font-size: 13.5px;
    text-align: left;
    cursor: pointer;
}
.tagbox-option:hover { background: var(--indigo-soft); }
/* A list the word is already in. Shown so the search visibly found it, and
   greyed so it is plainly not the thing to click. */
.tagbox-option.is-in { color: var(--muted); cursor: default; }
.tagbox-option.is-in:hover { background: none; }
.tagbox-option-count { font-family: var(--mono); font-size: 10.5px; color: var(--muted); }

.tagbox-note { font-size: 12.5px; line-height: 1.45; min-height: 1.45em; color: var(--muted); }
.tagbox-note.is-ok { color: var(--celadon); }
.tagbox-note.is-warn { color: var(--ochre); }
.tagbox-note.is-bad { color: var(--clay); }

/* Choosing the lists while the word is still being looked at, rather than
   keeping it and then opening it again to file it. */
.add-lists {
    display: grid;
    gap: var(--s2);
    padding-top: var(--s4);
    border-top: 1px solid var(--hairline);
}
.add-lists .tagbox-field { position: relative; display: flex; align-items: center; }

/* The row opens the word, so it has to look like it does something. */
tr[data-word-row] { cursor: pointer; }
tr[data-word-row]:hover { background: var(--surface-2); }
tr[data-word-row]:focus-visible {
    outline: 2px solid var(--indigo);
    outline-offset: -2px;
}

/* --------------------------------------------------------------- table -- */

.table-wrap { overflow-x: auto; }

.table { width: 100%; border-collapse: collapse; font-size: 14px; }
.table th {
    text-align: left;
    padding: var(--s2) var(--s4);
    border-bottom: 1px solid var(--hairline);
    font-family: var(--mono);
    font-weight: 400;
    font-size: 10px;
    letter-spacing: .1em;
    text-transform: uppercase;
    color: var(--muted);
    white-space: nowrap;
}
.table td {
    padding: var(--s2) var(--s4);
    border-bottom: 1px solid var(--hairline);
    vertical-align: middle;
}
.table tbody tr:hover td { background: var(--surface-2); }
.table .num { font-family: var(--mono); font-variant-numeric: tabular-nums; }
.table .right { text-align: right; }

.pager {
    display: flex;
    align-items: center;
    gap: var(--s3);
    padding: var(--s3) var(--s5);
    border-top: 1px solid var(--hairline);
    font-size: 13px;
    color: var(--muted);
}

/* ================================================================ READ == */

.reader { display: grid; grid-template-columns: minmax(0, 1fr) 300px; flex: 1; min-height: 0; }
.reader-col { display: flex; flex-direction: column; min-width: 0; }

.passage { padding: var(--s8) var(--s8) var(--s10); min-width: 0; }
.passage-meta {
    display: flex;
    flex-wrap: wrap;
    gap: var(--s4);
    margin-bottom: var(--s5);
    font-family: var(--mono);
    font-size: 10.5px;
    letter-spacing: .09em;
    text-transform: uppercase;
    color: var(--muted);
}

.line {
    font-family: var(--cjk);
    font-size: 27px;
    line-height: 2.05;
    letter-spacing: .02em;
    max-width: 28em;
    /* Kinsoku: a Japanese line may not begin with 。or 、 */
    line-break: strict;
    overflow-wrap: break-word;
}
.line[data-latin="true"] { font-family: var(--read); line-height: 1.85; max-width: 34em; }

/* A conversation is written one turn to a line, and the line breaks are in the
   text. Only a conversation gets this: prose has no newlines to keep, and
   turning it on everywhere would mean any stray whitespace the writer left
   behind showed up on the page. */
.line[data-text-type="dialog"] { white-space: pre-wrap; }

.word {
    border: 0;
    background: none;
    font: inherit;
    color: inherit;
    text-align: left;
    padding: 1px 2px;
    margin: 0 1px;
    border-radius: var(--radius-sm);
    border-bottom: 1.5px solid transparent;
    cursor: pointer;
    transition: background .12s;
}

/* When the passage carries its own spacing and punctuation, the words must sit
   flush against it — any margin would invent gaps the sentence does not have. */
.line[data-tight="true"] .word { margin: 0; padding: 0; }

/* Holds a word together with the punctuation that follows it, so a line can
   never begin with 。or 、. Browsers ignore display:inline on a <button>, so
   the break has to be prevented around it instead. */
.word-group { white-space: nowrap; }
.word:hover { background: var(--indigo-soft); }
.word.is-seen { border-bottom-color: var(--indigo-line); }
.word.is-saved { border-bottom-color: var(--celadon); }
.word[aria-pressed="true"] { background: var(--indigo); color: var(--on-indigo); }

.reveal {
    max-width: 52em;
    margin-top: var(--s6);
    padding-top: var(--s5);
    border-top: 1px solid var(--hairline);
    display: grid;
    gap: var(--s5);
}
.reveal-head { display: flex; align-items: center; gap: var(--s3); margin-bottom: var(--s2); }
.reveal p { font-family: var(--read); font-size: 16.5px; line-height: 1.6; }
.reveal p.romaji { font-family: var(--mono); font-size: 13.5px; color: var(--muted); }
.is-blurred { filter: blur(5px); opacity: .5; user-select: none; transition: filter .18s, opacity .18s; }

.reveal-toggle {
    display: inline-flex;
    align-items: center;
    gap: 6px;
    height: 24px;
    padding: 0 var(--s2);
    font-size: 11px;
    border-radius: var(--radius-pill);
    border: 1px solid var(--hairline);
    background: var(--surface);
    color: var(--muted);
    cursor: pointer;
}
.reveal-toggle:hover { color: var(--indigo); border-color: var(--indigo-line); }

.gloss {
    border-left: 1px solid var(--hairline);
    background: var(--surface-2);
    padding: var(--s5);
    display: flex;
    flex-direction: column;
    gap: var(--s4);
    min-width: 0;
    /* Stays with you down a long text. As an ordinary column it scrolled away
       with the paragraphs, so on anything longer than a screen you could tap a
       word and be looking at an empty margin.
       `align-self: start` is what makes the sticking possible: a grid item is
       stretched to the row by default, and an item as tall as its container
       has nowhere to stick. The height then has to be given back, or the panel
       would shrink to its contents and leave the sidebar half painted. */
    position: sticky;
    top: 0;
    align-self: start;
    height: 100vh;
    height: 100dvh;
    overflow-y: auto;
}
/* The sheet's handle, and only a sheet has one — see the bottom-sheet rules
   under the 1020px breakpoint, where this is turned on. */
.gloss-grip { display: none; }

.gloss-empty { font-size: 13.5px; color: var(--muted); }
.gloss-word { font-family: var(--cjk); font-size: 30px; line-height: 1.25; word-break: break-word; }
.gloss-word[data-latin="true"] { font-family: var(--read); }
.gloss-romaji { font-family: var(--mono); font-size: 12.5px; color: var(--muted); margin-top: 2px; }
.gloss-translation { font-family: var(--read); font-size: 17px; margin-top: var(--s3); }
.gloss-tags { display: flex; flex-wrap: wrap; gap: var(--s1); margin-top: var(--s2); }
.gloss-actions { display: flex; gap: var(--s2); margin-top: var(--s5); }
.gloss-actions .btn { flex: 1; }
.gloss-foot {
    margin-top: auto;
    padding-top: var(--s4);
    border-top: 1px solid var(--hairline);
    font-size: 12.5px;
    color: var(--muted);
    display: grid;
    gap: var(--s3);
}
.gloss-foot b { color: var(--ink); font-family: var(--mono); font-variant-numeric: tabular-nums; }

/* ----------------------------------------------------------- alphabet -- */

/* Wraps because it has to. Everywhere else this holds two chips — Chart and
   Drill — and never needed to; the Kanji library puts five levels in it, and
   without this the row keeps its full width inside a narrower head and takes
   the whole page sideways with it. */
.chip-tabs { display: flex; flex-wrap: wrap; gap: var(--s1); }
/* Nothing to separate. A visitor is not shown the Chart/Drill chips, which
   leaves the rule sitting straight after the title as a stray tick of a line.
   The selector only matches once those chips are gone. */
.page-title + .head-rule { display: none; }

/* Separates the section chips from the controls that belong to that section. */
.head-rule {
    width: 1px;
    align-self: stretch;
    margin: 0 var(--s1);
    background: var(--hairline);
}
.chip[aria-current="page"] {
    background: var(--indigo-soft);
    border-color: var(--indigo-line);
    color: var(--indigo);
    font-weight: 600;
}

.kana-page { padding: var(--s6) var(--s5) var(--s10); display: grid; gap: var(--s8); }
.kana-intro {
    font-family: var(--read);
    font-size: 15px;
    line-height: 1.6;
    color: var(--muted);
    max-width: 62ch;
}
.kana-group-head { display: grid; gap: var(--s1); margin-bottom: var(--s4); }
.kana-hint {
    font-size: 13px;
    line-height: 1.5;
    color: var(--muted);
    max-width: 46ch;
}

/* One measure for the whole page. The chart headings are laid out on the same
   two blocks as the rows beneath them, so "Voiced" sits over the voiced half
   rather than merely near it. */
.kana-page { --kana-cell: clamp(58px, 5.6vw, 92px); }

.kana-grid {
    display: grid;
    grid-template-columns: repeat(var(--kana-cols, 5), var(--kana-cell));
    gap: var(--s2);
}

.kana-chart { display: grid; gap: var(--s2); }
.kana-chart-head,
.kana-row {
    display: grid;
    grid-template-columns:
        calc(5 * var(--kana-cell) + 4 * var(--s2))
        calc(5 * var(--kana-cell) + 4 * var(--s2));
    gap: var(--s2) var(--s10);
    justify-content: start;
}
.kana-chart-head { align-items: end; margin-bottom: var(--s3); }
/* Without this the は row's cells stretch to the height of ば plus ぱ beside
   them, and one row of the chart ends up twice as tall as the rest. */
.kana-row { align-items: start; }
/* は is the one row that voices two ways, ば and ぱ; they stack rather than
   running off to the right, so every other row keeps its place. */
.kana-voiced { display: grid; gap: var(--s2); align-content: start; }

.kana-cell {
    position: relative;
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    gap: 2px;
    min-height: 76px;
    padding: var(--s2);
    border: 1px solid var(--hairline);
    border-radius: var(--radius);
    background: var(--surface);
    cursor: pointer;
    transition: border-color .12s, background .12s, transform .08s;
}
.kana-cell:hover { border-color: var(--indigo-line); background: var(--indigo-soft); }
.kana-cell:active { transform: scale(.97); }
.kana-cell.is-gap {
    border-style: none;
    background: none;
    cursor: default;
    min-height: 0;
}
/* Recording a character that has never been played takes a moment. */
.kana-cell.is-loading { opacity: .55; cursor: progress; }
.kana-cell.is-played { border-color: var(--indigo); }

.kana-char { font-family: var(--cjk); font-size: 28px; line-height: 1.15; }
.kana-romaji {
    font-family: var(--mono);
    font-size: 11px;
    letter-spacing: .04em;
    color: var(--muted);
}
.kana-flag {
    position: absolute;
    top: 4px; right: 7px;
    font-family: var(--mono);
    font-size: 13px;
    line-height: 1;
    color: var(--ochre);
}

/* ----------------------------------------------------- alphabet drill -- */

.drill-settings {
    display: grid;
    gap: var(--s4);
    padding: var(--s4);
    border: 1px solid var(--hairline);
    border-radius: var(--radius);
    background: var(--surface);
}
.drill-set { border: 0; margin: 0; padding: 0; display: flex; flex-wrap: wrap; gap: var(--s2); }
.drill-set legend { padding: 0; margin-bottom: var(--s2); float: left; width: 100%; }
/* Its own row, always. `width: 100%` alone was being clamped by the 46ch
   measure .kana-hint carries, so whether the first checkbox tucked up beside
   the hint came down to whether it happened to fit in what was left — N5 did
   and "Read it" did not. The panel is narrow enough to keep the line length
   honest without the cap. */
.drill-set-hint { width: 100%; max-width: none; margin: calc(var(--s1) * -1) 0 var(--s1); }
/* How many characters a level contributes, beside its name — so the size of
   the deck you are choosing is visible before you choose it. */
.drill-set-count { font-family: var(--mono); font-size: 11px; color: var(--muted); }

.drill-task { display: flex; align-items: center; justify-content: center; gap: var(--s2); }
/* What is being asked and what you answer with are two things, not one block:
   the character needs room around it to be looked at. */
.drill-body { display: grid; gap: var(--s6); }
.drill-task .drill-prompt-sub { margin-top: 0; }
.option-kana { font-family: var(--cjk); font-size: 26px; line-height: 1.2; }

.drill-answer-actions { display: flex; gap: var(--s2); margin-top: var(--s2); }

/* Writing the word out. The field takes Japanese, so it gets the reading face
   and room for a whole word rather than the app's usual input sizing. */
.typed { display: grid; gap: var(--s3); }
.typed-input {
    width: 100%;
    height: auto;
    padding: var(--s3) var(--s4);
    font-family: var(--cjk);
    font-size: 24px;
    text-align: center;
}
.typed-note { font-size: 13.5px; line-height: 1.45; min-height: 1.45em; color: var(--muted); }
.typed-note.is-ok { color: var(--celadon); }
.typed-note.is-warn { color: var(--ochre); }
.typed-note.is-bad { color: var(--clay); }

/* The answer sits beside the square when there is room and underneath it when
   there is not. Without the wrap the square gives up all its width to the
   answer panel and there is nothing left to have drawn on. */
.draw-area {
    display: flex;
    flex-wrap: wrap;
    align-items: flex-start;
    justify-content: center;
    gap: var(--s3);
}
.draw-pad {
    position: relative;
    /* A basis rather than a width: as a flex item, width:100% resolves against
       the wrong box and the square collapses to half its size. */
    flex: 1 1 220px;
    min-width: 200px;
    max-width: 260px;
    aspect-ratio: 1;
    border: 1px solid var(--hairline);
    border-radius: var(--radius);
    background: var(--surface);
}
#draw-canvas {
    display: block;
    width: 100%;
    height: 100%;
    color: var(--ink);
    /* Named here so the canvas can read it and stay in step with the theme. */
    --guide: var(--hairline);
    border-radius: var(--radius);
    cursor: crosshair;
    /* Without this a finger scrolls the page instead of drawing on it. */
    touch-action: none;
}
.draw-answer {
    display: grid;
    align-content: center;
    justify-items: center;
    gap: var(--s2);
    padding: var(--s3);
    border: 1px solid var(--celadon);
    border-radius: var(--radius);
    background: var(--celadon-soft);
    flex: 0 0 auto;
    min-width: 108px;
}
.draw-target { font-family: var(--cjk); font-size: 56px; line-height: 1.1; color: var(--ink); }

/* The character writing itself. The stroke files colour themselves from these
   two properties, so the playback follows the theme like everything else. */
.draw-play { width: 108px; }
.kana-play {
    display: block;
    width: 100%;
    height: auto;
    fill: none;
    stroke: var(--ink);
    /* The source draws at this weight in a 1024 box; anything thinner stops
       looking like a brush and starts looking like a diagram. */
    stroke-width: 96;
    stroke-linecap: round;
    stroke-linejoin: round;
}
.draw-play:empty { display: none; }

.draw-verdict {
    font-size: 13.5px;
    line-height: 1.45;
    padding: var(--s2) var(--s3);
    border-radius: var(--radius);
    border: 1px solid var(--hairline);
}
.draw-verdict.is-ok { border-color: var(--celadon); background: var(--celadon-soft); color: var(--celadon); }
.draw-verdict.is-warn { border-color: var(--ochre); background: var(--ochre-soft); color: var(--ochre); }
.draw-verdict.is-bad { border-color: var(--clay); background: var(--clay-soft); color: var(--clay); }
/* Undo and Clear act on the square but are not part of it. Sitting close under
   its edge they read as a border of the pad rather than as controls. */
.draw-tools { display: flex; justify-content: center; gap: var(--s2); margin-top: var(--s4); }

/* The kanji you have kept. A list rather than a grid: each one carries a
   meaning and a reading, which a chart cell has no room for. */
.kanji-kept {
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(320px, 1fr));
    gap: var(--s3);
    /* Cards fill their row rather than each stopping at its own content, so a
       row of them ends on one line instead of a ragged edge. Rows are still
       sized to what is in them, so one character with a long note does not
       stretch every card on the page. */
}
/* The kanji library's cards are these cards — same character, same readings,
   same pills — so they share the rule rather than copying it. Only the corner
   differs, and that is in the library's own block below. */
.kanji-kept-item,
.kanji-card {
    display: flex;
    flex-direction: column;
    gap: var(--s3);
    padding: var(--s4);
    border: 1px solid var(--hairline);
    border-radius: var(--radius);
    background: var(--surface);
}
.kanji-kept-head { display: flex; align-items: flex-start; gap: var(--s3); }
.kanji-kept-char { font-family: var(--cjk); font-size: 44px; line-height: 1.05; flex: none; }
.kanji-kept-sense { min-width: 0; flex: 1; display: grid; gap: 2px; }
.kanji-kept-meaning { font-family: var(--read); font-size: 16px; line-height: 1.35; }
.kanji-kept-actions { flex: none; display: flex; flex-direction: column; gap: var(--s1); }
.kanji-kept-drop { flex: none; }
.kanji-kept-item .kanji-tags,
.kanji-card .kanji-tags,
.kanji-kept-item .kanji-note { margin-top: 0; }

/* ---------------------------------------------------- kanji library -- */

.klib-grid {
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(300px, 1fr));
    gap: var(--s3);
}
/* The pills sit at the foot of the card whatever the readings above them run
   to, so a row of cards has one line of pills rather than a staircase. */
.kanji-card .kanji-tags { margin-top: auto; }

/* Past the tenth, for a visitor. The card is still there — how much there is
   is part of what the page is saying — but not readable. This is a gate on
   the reading, not on the data: the characters are in the page either way,
   which is fine, because they are public reference and the page is meant to
   be indexable. */
.kanji-card.is-locked {
    filter: blur(5px);
    user-select: none;
    /* Nothing in it to press, and nothing to press it with: the card is out
       of the tab order and out of the accessibility tree, so a keyboard or a
       screen reader meets the same wall as everybody else. */
    pointer-events: none;
}

.klib-locked-note {
    grid-column: 1 / -1;
    padding: var(--s4) var(--s5);
    border: 1px solid var(--indigo-line);
    border-radius: var(--radius);
    background: var(--indigo-soft);
    font-size: 14px;
    line-height: 1.55;
}

.klib-actions { flex: none; display: flex; align-items: center; }
.klib-kept {
    display: grid;
    place-items: center;
    /* Exactly the size of the + it stands in for, so the card does not change
       shape when a character is added. */
    width: var(--s8);
    height: var(--s8);
    color: var(--celadon);
}
.klib-kept svg {
    width: 18px;
    height: 18px;
    fill: none;
    stroke: currentColor;
    stroke-width: 2;
    stroke-linecap: round;
    stroke-linejoin: round;
}
.kanji-kept-foot {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: var(--s3);
    /* Pushed to the foot of the card, so the rule above it lands at the same
       height on every card of a row however long the note above it runs. */
    margin-top: auto;
    padding-top: var(--s3);
    border-top: 1px solid var(--hairline);
}

.kana-marks { display: flex; flex-wrap: wrap; gap: var(--s3); }
.kana-mark {
    display: flex;
    align-items: center;
    gap: var(--s3);
    padding: var(--s3) var(--s4);
    border: 1px solid var(--hairline);
    border-radius: var(--radius);
    background: var(--surface);
    max-width: 380px;
}
.kana-mark-name { font-size: 14px; font-weight: 600; }
.kana-mark-note { font-size: 12.5px; color: var(--muted); line-height: 1.45; }
.kana-foot { font-size: 12.5px; color: var(--muted); }
.kana-foot b { color: var(--ochre); }

/* ----------------------------------------------------------- grammar -- */

/* The shelf. Cards rather than a list because a block is a place you go, and
   the blurb is doing the work of telling you whether it is the one you want. */
.grammar-page { padding: var(--s6) var(--s5) var(--s10); display: grid; gap: var(--s6); }
.grammar-grid {
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(320px, 1fr));
    gap: var(--s4);
    align-content: start;
}

.grammar-card {
    display: flex;
    align-items: flex-start;
    gap: var(--s4);
    padding: var(--s5);
    border: 1px solid var(--hairline);
    border-radius: var(--radius-lg);
    background: var(--surface);
    box-shadow: var(--shadow-sm);
    color: inherit;
    text-decoration: none;
    transition: border-color .12s, background .12s, transform .08s;
}
.grammar-card:hover { border-color: var(--indigo-line); background: var(--indigo-soft); }
.grammar-card:active { transform: scale(.995); }
.grammar-card:focus-visible { outline: 2px solid var(--indigo); outline-offset: 2px; }

.grammar-card-icon {
    flex: none;
    display: grid;
    place-items: center;
    width: 40px;
    height: 40px;
    border-radius: var(--radius);
    background: var(--indigo-soft);
    color: var(--indigo);
}
.grammar-card-icon svg {
    width: 22px;
    height: 22px;
    fill: none;
    stroke: currentColor;
    stroke-width: 1.7;
    stroke-linecap: round;
    stroke-linejoin: round;
}

.grammar-card-body { display: grid; gap: var(--s2); min-width: 0; }
.grammar-card-title { font-size: 17px; font-weight: 600; line-height: 1.3; }
.grammar-card-blurb {
    font-size: 13.5px;
    line-height: 1.55;
    color: var(--muted);
}
.grammar-card-covers { display: flex; flex-wrap: wrap; gap: var(--s1); }

/* Points the way without being a second thing to aim at — the whole card is
   the target. */
.grammar-card-go { flex: none; align-self: center; color: var(--muted); }
.grammar-card-go svg {
    width: 18px;
    height: 18px;
    fill: none;
    stroke: currentColor;
    stroke-width: 1.7;
    stroke-linecap: round;
    stroke-linejoin: round;
}
.grammar-card:hover .grammar-card-go { color: var(--indigo); }

/* ------------------------------------------------------------- verbs -- */

/* A measure, so the page does not stretch a centred derivation across a
   two-thousand-pixel monitor with the reading of it lost somewhere in the
   middle. */
.verb-page {
    padding: var(--s6) var(--s5) var(--s10);
    display: grid;
    gap: var(--s8);
    width: 100%;
    max-width: 1180px;
    margin-inline: auto;
}
.verb-bench-head { display: grid; gap: var(--s1); margin-bottom: var(--s4); }

/* The answer being built on the left, what the form is for on the right. At
   full width the stage put its content in the middle third and left the rest
   of the row empty, and the note had a screen's worth of nothing beside it.
   `align-items: start` keeps the note its own height rather than stretching it
   to match a stage that is taller. */
.verb-bench-body {
    display: grid;
    grid-template-columns: minmax(0, 1fr) minmax(0, 360px);
    gap: var(--s6);
    align-items: start;
}

/* The pickers. Twenty verbs and sixteen forms is more than fits on a phone in
   one line, so they scroll sideways rather than wrapping into a wall — the row
   stays a row, and a row reads as something to choose from. */
.verb-picker { display: grid; gap: var(--s2); margin-bottom: var(--s5); }
.verb-picker-row {
    display: flex;
    gap: var(--s2);
    overflow-x: auto;
    padding-bottom: var(--s2);
    scrollbar-width: thin;
}
.verb-chip {
    flex: none;
    padding: var(--s2) var(--s4);
    border: 1px solid var(--hairline);
    border-radius: var(--radius-pill);
    background: var(--surface);
    color: var(--ink);
    font-family: var(--cjk);
    font-size: 16px;
    cursor: pointer;
    white-space: nowrap;
    transition: border-color .12s, background .12s, color .12s;
}
.verb-chip.is-form { font-family: inherit; font-size: 13px; }

/* Which class a verb is in is the only thing deciding how it conjugates, so
   the picker says it in front of the run of verbs it applies to rather than
   leaving twenty of them in one undifferentiated row. */
.verb-picker-label {
    flex: none;
    align-self: center;
    padding: 0 var(--s2);
    font-family: var(--mono);
    font-size: 10px;
    letter-spacing: .1em;
    text-transform: uppercase;
    color: var(--muted);
    white-space: nowrap;
}
.verb-picker-label.has-rule {
    margin-left: var(--s2);
    padding-left: var(--s4);
    border-left: 1px solid var(--hairline);
}
.verb-chip:hover { border-color: var(--indigo-line); }
.verb-chip.is-on {
    background: var(--indigo-soft);
    border-color: var(--indigo);
    color: var(--indigo);
    font-weight: 600;
}

/* The stage: where the rule happens. Given room, because it is the reason the
   page exists. */
.verb-stage {
    display: grid;
    gap: var(--s5);
    justify-items: center;
    padding: var(--s8) var(--s5);
    text-align: center;
}
.verb-stage-source { display: grid; gap: var(--s1); justify-items: center; }
.verb-stage-word { font-family: var(--cjk); font-size: 26px; line-height: 1.2; }
.verb-stage-meta { font-size: 12.5px; color: var(--muted); }

.verb-build {
    display: flex;
    flex-wrap: wrap;
    align-items: flex-start;
    justify-content: center;
    gap: var(--s2);
}
.verb-build-part {
    display: grid;
    gap: var(--s1);
    justify-items: center;
    padding: var(--s3) var(--s4);
    border-radius: var(--radius);
    border: 1px solid var(--hairline);
    background: var(--surface-2);
}
.verb-build-text { font-family: var(--cjk); font-size: clamp(26px, 3.4vw, 38px); line-height: 1.1; }
.verb-build-label {
    font-family: var(--mono);
    font-size: 10.5px;
    letter-spacing: .06em;
    color: var(--muted);
    white-space: nowrap;
}
.verb-build-join {
    align-self: center;
    font-size: 20px;
    color: var(--muted);
    padding-top: var(--s3);
}

/* Colour carries which piece did what: the stem stayed, the marked one moved.
   The border does the work as well as the text so it survives being read by
   somebody who cannot tell the two hues apart. */
.verb-build-part.is-stem { border-color: var(--hairline); }
.verb-build-part.is-shift,
.verb-build-part.is-fuse {
    border-color: var(--ochre);
    background: var(--surface);
}
.verb-build-part.is-shift .verb-build-text,
.verb-build-part.is-fuse .verb-build-text { color: var(--ochre); }
.verb-build-part.is-shift .verb-build-label,
.verb-build-part.is-fuse .verb-build-label { color: var(--ochre); }
.verb-build-part.is-suffix,
.verb-build-part.is-ending { border-color: var(--indigo-line); }
.verb-build-part.is-suffix .verb-build-text,
.verb-build-part.is-ending .verb-build-text { color: var(--indigo); }
.verb-build-part.is-irregular {
    border-color: var(--clay);
    border-style: dashed;
}
.verb-build-part.is-irregular .verb-build-text { color: var(--clay); }

.verb-stage-result { display: flex; align-items: center; gap: var(--s3); }
.verb-stage-arrow { font-size: 20px; color: var(--muted); }
.verb-stage-answer {
    border: 0;
    background: none;
    color: var(--ink);
    font-family: var(--cjk);
    font-size: clamp(30px, 4vw, 44px);
    line-height: 1.15;
    cursor: pointer;
    padding: var(--s1) var(--s2);
    border-radius: var(--radius);
    transition: background .12s;
}
.verb-stage-answer:hover { background: var(--indigo-soft); }
.verb-stage-answer.is-loading { opacity: .55; cursor: progress; }

.verb-stage-rule { font-size: 14px; line-height: 1.6; max-width: 52ch; }
.verb-stage-warning {
    font-size: 13px;
    line-height: 1.55;
    color: var(--ochre);
    max-width: 52ch;
    padding: var(--s3) var(--s4);
    border: 1px solid var(--ochre);
    border-radius: var(--radius);
}

/* What the chosen form is, when to reach for it, and it doing its job. The
   picker above says the name of a thing, which is the least useful fact about
   it; this says the rest. It sits beside the stage on a wide screen and under
   it on a narrow one. */
.verb-form-note {
    display: grid;
    gap: var(--s2);
    align-content: start;
    padding: var(--s4) var(--s5);
    border: 1px solid var(--hairline);
    border-left: 3px solid var(--indigo);
    border-radius: var(--radius);
    background: var(--surface);
}
.verb-form-note-head {
    display: flex;
    align-items: baseline;
    flex-wrap: wrap;
    gap: var(--s2) var(--s3);
}
.verb-form-note-name { font-size: clamp(16px, 1.5vw, 19px); font-weight: 600; }
.verb-form-note-jp { font-family: var(--cjk); font-size: 15px; color: var(--indigo); }
.verb-form-note-hint { font-size: 13px; color: var(--muted); }
.verb-form-note-what { font-size: 14px; line-height: 1.6; }
.verb-form-note-when { font-size: 13.5px; line-height: 1.6; color: var(--muted); }
.verb-form-note-when b {
    color: var(--ink);
    font-family: var(--mono);
    font-size: 10.5px;
    letter-spacing: .09em;
    text-transform: uppercase;
}

/* The form doing its job in a whole sentence, which is the only place a form
   means anything. Tappable, like every other bit of Japanese on these pages. */
.verb-form-note-example {
    display: grid;
    gap: 2px;
    margin-top: var(--s1);
    padding: var(--s3) var(--s4);
    border: 0;
    border-radius: var(--radius);
    background: var(--surface-2);
    color: var(--ink);
    text-align: left;
    cursor: pointer;
    transition: background .12s;
}
.verb-form-note-example:hover { background: var(--indigo-soft); }
.verb-form-note-example.is-loading { opacity: .55; cursor: progress; }
.verb-form-note-example.is-played { outline: 1px solid var(--indigo); }
.verb-form-note-jp-line { font-family: var(--cjk); font-size: 16px; line-height: 1.6; }
.verb-form-note-romaji { font-family: var(--mono); font-size: 11.5px; color: var(--muted); }
.verb-form-note-meaning { font-size: 12.5px; color: var(--muted); }

/* The old chart, kept but folded away. It is the thing to check once you know
   what you are looking for, not the thing to meet first. */
.verb-reference {
    padding: var(--s4) var(--s5);
    border: 1px solid var(--hairline);
    border-radius: var(--radius);
    background: var(--surface);
}
.verb-reference summary { cursor: pointer; font-size: 14px; font-weight: 600; }
.verb-reference > *:not(summary) { margin-top: var(--s3); }

.verb-rows { display: grid; gap: 2px; max-width: 560px; }
.verb-row {
    display: grid;
    grid-template-columns: 4.5rem repeat(6, minmax(0, 1fr));
    gap: var(--s2);
    align-items: center;
    padding: var(--s2) var(--s3);
    border-radius: var(--radius);
    background: var(--surface-2);
    font-family: var(--cjk);
    font-size: 15px;
}
.verb-row.is-head {
    background: none;
    font-family: var(--mono);
    font-size: 10.5px;
    letter-spacing: .09em;
    text-transform: uppercase;
    color: var(--muted);
}
.verb-row-ending { font-weight: 600; color: var(--indigo); }
.verb-row-te { color: var(--ochre); }

/* Kept for the drill's prompt, which shows a verb's reading and class under
   the word it is asking about. */
.verb-prompt-meta {
    font-size: 13px;
    color: var(--muted);
    text-align: center;
    margin-top: var(--s2);
}

/* --------------------------------------------------------- particles -- */

.confusions {
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(280px, 1fr));
    gap: var(--s4);
}
.confusion {
    display: grid;
    gap: var(--s3);
    align-content: start;
    padding: var(--s4);
    border: 1px solid var(--ochre);
    border-radius: var(--radius);
    background: var(--surface);
}
.confusion-title { font-family: var(--cjk); font-size: 18px; color: var(--ochre); }
.confusion-rule { font-size: 13px; line-height: 1.55; color: var(--muted); }
.confusion-examples { display: grid; gap: var(--s2); }
.confusion-example { display: grid; gap: 2px; }

.particle-list {
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(300px, 1fr));
    gap: var(--s4);
    align-items: start;
}
.particle-card {
    display: grid;
    gap: var(--s3);
    align-content: start;
    padding: var(--s4);
    border: 1px solid var(--hairline);
    border-radius: var(--radius);
    background: var(--surface-2);
}
.particle-head { display: flex; align-items: center; gap: var(--s3); }
.particle-mark {
    flex: none;
    display: grid;
    place-items: center;
    width: 44px;
    height: 44px;
    border-radius: var(--radius);
    background: var(--indigo-soft);
    color: var(--indigo);
    font-family: var(--cjk);
    font-size: 22px;
}
.particle-head-body { display: grid; gap: 1px; min-width: 0; flex: 1; }
.particle-role { font-size: 14px; font-weight: 600; }
.particle-gloss { font-size: 12.5px; color: var(--muted); }
.particle-reading {
    font-family: var(--mono);
    font-size: 11.5px;
    color: var(--muted);
}
.particle-when { font-size: 13px; line-height: 1.55; color: var(--muted); }
.particle-note {
    font-size: 12.5px;
    line-height: 1.5;
    color: var(--ochre);
    border-left: 3px solid var(--ochre);
    padding-left: var(--s3);
}

.particle-examples { display: grid; gap: var(--s2); }
.particle-example {
    display: grid;
    gap: 2px;
    padding: var(--s3);
    border: 0;
    border-radius: var(--radius);
    background: var(--surface);
    color: var(--ink);
    text-align: left;
    cursor: pointer;
    transition: background .12s;
}
.particle-example:hover { background: var(--indigo-soft); }
.particle-example.is-loading { opacity: .55; cursor: progress; }
.particle-example.is-played { outline: 1px solid var(--indigo); }
.particle-sentence { font-family: var(--cjk); font-size: 15px; line-height: 1.6; }
.particle-romaji { font-family: var(--mono); font-size: 11.5px; color: var(--muted); }
.particle-meaning { font-size: 12.5px; color: var(--muted); line-height: 1.45; }

/* The gap in the drill's sentence. Empty it is a rule to write on; filled it
   is the answer sitting where it belongs. */
.particle-prompt { line-height: 1.7; }
.particle-blank {
    display: inline-block;
    min-width: 2ch;
    margin: 0 .1em;
    border-bottom: 3px solid var(--indigo-line);
    text-align: center;
}
.particle-blank.is-filled { border-bottom-color: var(--celadon); color: var(--celadon); }
/* Filled in for you after a wrong answer, which is a different thing from
   having filled it in yourself. */
.particle-blank.is-corrected { border-bottom-color: var(--clay); color: var(--clay); }
.particle-prompt-meaning {
    font-size: 14px;
    color: var(--muted);
    text-align: center;
    margin-top: var(--s2);
}
.particle-why { font-size: 13px; line-height: 1.55; color: var(--muted); }

/* --------------------------------------------------- demonstratives -- */

/* The sections are grid items, and a grid item's default min-width is its
   min-content — which, for the section holding the twelve-word table, is the
   whole table. Without this the table refuses to shrink on a phone and drags
   the page sideways instead of scrolling inside its own box. Scoped to this
   page: the kana and particle charts are grid items of the same wrapper and
   have no reason to be touched. */
.koso-page .kana-group { min-width: 0; }

/* The four distances are the page's one idea, so they get colour and every
   word on the page inherits it: a こ card, a こ heading and a こ cell are all
   the same hue. The reader is meant to stop reading the labels quite quickly
   and go by the colour, which only works if it never means anything else. */
.koso-distance[data-distance="ko"], .koso-word[data-distance="ko"] { --koso: var(--indigo); }
.koso-distance[data-distance="so"], .koso-word[data-distance="so"] { --koso: var(--celadon); }
.koso-distance[data-distance="a"], .koso-word[data-distance="a"] { --koso: var(--ochre); }
.koso-distance[data-distance="do"], .koso-word[data-distance="do"] { --koso: var(--clay); }

/* The distances follow .kana-group-head, which already carries the gap.
   The words follow the series rule instead, so they supply their own. */
.koso-distances,
.koso-words {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(210px, 1fr));
    gap: var(--s3);
}
.koso-words { margin-top: var(--s4); }

.koso-distance {
    display: flex;
    align-items: center;
    gap: var(--s3);
    padding: var(--s3) var(--s4);
    border: 1px solid var(--hairline);
    border-left: 3px solid var(--koso);
    border-radius: var(--radius);
    background: var(--surface);
}
.koso-prefix {
    font-family: var(--cjk);
    font-size: 30px;
    line-height: 1;
    flex: none;
    color: var(--koso);
}
.koso-distance-body { display: grid; gap: 2px; min-width: 0; }
.koso-distance-label { font-size: 14px; font-weight: 600; }
.koso-distance-when { font-size: 12.5px; line-height: 1.45; color: var(--muted); }

/* All twelve words at once. A table because it is one — the reading is across
   a row, and the row header says which shape the row is. */
.koso-grid-wrap { overflow-x: auto; }
.koso-grid {
    width: 100%;
    min-width: 560px;
    border-collapse: collapse;
}
.koso-grid th, .koso-grid td {
    padding: var(--s3);
    text-align: left;
    vertical-align: top;
    border-bottom: 1px solid var(--hairline);
}
.koso-grid thead th { border-bottom-width: 2px; }
.koso-grid tbody tr:last-child th,
.koso-grid tbody tr:last-child td { border-bottom: 0; }
.koso-grid-prefix { font-family: var(--cjk); font-size: 20px; line-height: 1.2; display: block; }
.koso-grid-head {
    display: block;
    font-family: var(--mono);
    font-size: 10.5px;
    letter-spacing: .08em;
    text-transform: uppercase;
    color: var(--muted);
    font-weight: 400;
}
/* The column colours again, applied to whole columns so a glance down the
   table finds the one it wants. nth-child is safe here because the header and
   the body are generated from the same list, in the same order. */
.koso-grid tr > *:nth-child(2) .koso-grid-prefix,
.koso-grid tr > *:nth-child(2) .koso-grid-word { color: var(--indigo); }
.koso-grid tr > *:nth-child(3) .koso-grid-prefix,
.koso-grid tr > *:nth-child(3) .koso-grid-word { color: var(--celadon); }
.koso-grid tr > *:nth-child(4) .koso-grid-prefix,
.koso-grid tr > *:nth-child(4) .koso-grid-word { color: var(--ochre); }
.koso-grid tr > *:nth-child(5) .koso-grid-prefix,
.koso-grid tr > *:nth-child(5) .koso-grid-word { color: var(--clay); }

.koso-grid-role {
    font-family: var(--mono);
    font-size: 11px;
    font-weight: 400;
    color: var(--muted);
    white-space: nowrap;
}
.koso-grid-word { font-family: var(--cjk); font-size: 19px; line-height: 1.3; display: block; }
.koso-grid-reading { font-family: var(--mono); font-size: 11px; color: var(--muted); display: block; }
.koso-grid-gloss { font-size: 12.5px; color: var(--muted); display: block; margin-top: 2px; }

/* The rule for a whole series — the one sentence that stops the shape being
   used in the wrong slot, so it is louder than the prose around it. */
.koso-rule {
    margin-top: var(--s3);
    padding: var(--s3) var(--s4);
    border-left: 3px solid var(--indigo);
    border-radius: 0 var(--radius) var(--radius) 0;
    background: var(--indigo-soft);
    font-size: 13.5px;
    line-height: 1.55;
}
.koso-warning {
    margin-top: var(--s3);
    font-size: 12.5px;
    line-height: 1.5;
    color: var(--muted);
    max-width: 62ch;
}

.koso-word {
    display: grid;
    gap: var(--s2);
    align-content: start;
    padding: var(--s3) var(--s4);
    border: 1px solid var(--hairline);
    border-top: 3px solid var(--koso);
    border-radius: var(--radius);
    background: var(--surface);
}
.koso-word-head { display: flex; align-items: baseline; gap: var(--s3); }
.koso-word-mark {
    font-family: var(--cjk);
    font-size: 24px;
    line-height: 1.2;
    color: var(--koso);
    flex: none;
}
.koso-word-body { display: grid; gap: 1px; min-width: 0; }
.koso-word-gloss { font-size: 14px; font-weight: 600; }
.koso-word-reading { font-family: var(--mono); font-size: 11px; color: var(--muted); }
.koso-word-when { font-size: 12.5px; line-height: 1.5; color: var(--muted); }

.koso-examples {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(240px, 1fr));
    gap: var(--s2);
    margin-top: var(--s4);
}

/* Right and wrong, one above the other. The wrong one is worth printing
   because it is the sentence the reader is about to write. */
.koso-good { font-family: var(--cjk); font-size: 15px; line-height: 1.6; }
.koso-bad {
    font-family: var(--cjk);
    font-size: 14px;
    line-height: 1.6;
    color: var(--clay);
    /* Struck through in its own colour rather than a softer one: --clay-soft
       is a background token and in the dark theme it is nearly the page
       colour, which left the line invisible on the half of the app that
       needs it most. */
    text-decoration: line-through;
    text-decoration-thickness: 1px;
}

/* ----------------------------------------------------------- numbers -- */

/* Every tappable thing on the numbers page is one of these: a figure, its
   reading, and a hit area big enough to mean it. They differ in what else they
   carry, so the shared part is here and the rest is per block. */
.num-cell,
.num-rank,
.num-change,
.num-row {
    display: flex;
    border: 1px solid var(--hairline);
    border-radius: var(--radius);
    background: var(--surface);
    color: var(--ink);
    cursor: pointer;
    text-align: left;
    transition: border-color .12s, background .12s, transform .08s;
}
.num-cell:hover,
.num-rank:hover,
.num-change:hover,
.num-row:hover { border-color: var(--indigo-line); background: var(--indigo-soft); }
.num-cell:active,
.num-rank:active,
.num-change:active,
.num-row:active { transform: scale(.98); }
.num-cell.is-loading,
.num-rank.is-loading,
.num-change.is-loading,
.num-row.is-loading { opacity: .55; cursor: progress; }
.num-cell.is-played,
.num-rank.is-played,
.num-change.is-played,
.num-row.is-played { border-color: var(--indigo); }

.num-reading { font-family: var(--cjk); font-size: 14px; line-height: 1.35; }
.num-note { font-size: 12.5px; color: var(--muted); line-height: 1.45; }

/* The five sound changes are marked in every table they appear in, so a
   learner scanning the hundreds sees at a glance which three to be careful
   about. Colour alone would not say it, so the marked ones also carry a rule
   down their leading edge. */
.num-reading.is-change,
.num-row.is-change .num-reading { color: var(--ochre); font-weight: 600; }
.num-row.is-change { border-left: 3px solid var(--ochre); }

.num-grid {
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(96px, 1fr));
    gap: var(--s2);
}
.num-cell {
    flex-direction: column;
    align-items: center;
    justify-content: center;
    gap: 2px;
    min-height: 96px;
    padding: var(--s3) var(--s2);
    text-align: center;
}
.num-figure { font-family: var(--mono); font-size: 24px; font-weight: 600; }
.num-kanji { font-family: var(--cjk); font-size: 15px; color: var(--muted); }
.num-alt { font-family: var(--cjk); font-size: 12px; color: var(--muted); }

.num-ranks {
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(240px, 1fr));
    gap: var(--s2);
}
.num-rank { align-items: center; gap: var(--s3); padding: var(--s3) var(--s4); }
.num-rank-kanji { font-family: var(--cjk); font-size: 30px; line-height: 1; flex: none; }
.num-rank-body { display: grid; gap: 1px; min-width: 0; }
.num-rank-figure {
    font-family: var(--mono);
    font-size: 13px;
    font-variant-numeric: tabular-nums;
    color: var(--muted);
}

.num-changes {
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(200px, 1fr));
    gap: var(--s2);
}
.num-change {
    flex-direction: column;
    gap: 2px;
    padding: var(--s3) var(--s4);
    border-left: 3px solid var(--ochre);
}
.num-change-figure {
    font-family: var(--mono);
    font-size: 20px;
    font-weight: 600;
    font-variant-numeric: tabular-nums;
}
.num-change-not {
    font-family: var(--cjk);
    font-size: 12.5px;
    color: var(--muted);
    text-decoration: line-through;
}

.num-tables {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
    gap: var(--s6);
    align-items: start;
}
.num-table { display: grid; gap: var(--s1); align-content: start; }
.num-table .label { margin-bottom: var(--s1); }
.num-row {
    align-items: baseline;
    gap: var(--s3);
    padding: var(--s2) var(--s3);
}
.num-row-figure {
    flex: none;
    min-width: 4.5ch;
    font-family: var(--mono);
    font-size: 13px;
    font-variant-numeric: tabular-nums;
    color: var(--muted);
}

.num-counters {
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(260px, 1fr));
    gap: var(--s4);
}
.num-counter {
    display: grid;
    gap: var(--s3);
    align-content: start;
    padding: var(--s4);
    border: 1px solid var(--hairline);
    border-radius: var(--radius);
    background: var(--surface-2);
}
.num-counter-head { display: flex; align-items: center; gap: var(--s3); }
.num-counter-kanji { font-family: var(--cjk); font-size: 28px; line-height: 1; flex: none; }
.num-counter-body { display: grid; gap: 1px; min-width: 0; }
.num-counter-what { font-size: 12.5px; color: var(--muted); line-height: 1.45; }
.num-counter-rows { display: grid; gap: var(--s1); }

/* The characters a word is written with, under the word itself. */
.kanji-title { padding-top: var(--s4); border-top: 1px solid var(--hairline); }
.kanji-list { display: grid; gap: var(--s3); margin-top: var(--s3); }
.kanji-item {
    border: 1px solid var(--hairline);
    border-radius: var(--radius);
    background: var(--surface);
    padding: var(--s3) var(--s4) var(--s4);
}
.kanji-head { display: flex; gap: var(--s3); align-items: flex-start; }
.kanji-char {
    font-family: var(--cjk);
    font-size: 34px;
    line-height: 1.1;
    flex: none;
}
.kanji-sense { min-width: 0; }
.kanji-meaning { font-family: var(--read); font-size: 15px; line-height: 1.35; }
.kanji-readings {
    font-family: var(--cjk);
    font-size: 12.5px;
    color: var(--muted);
    margin-top: 2px;
    word-break: break-word;
}
.kanji-readings b {
    font-family: var(--mono);
    font-size: 10.5px;
    text-transform: uppercase;
    letter-spacing: .06em;
    color: var(--muted);
    margin-right: var(--s2);
}
.kanji-tags { display: flex; flex-wrap: wrap; gap: var(--s1); margin-top: var(--s3); }
.kanji-note {
    font-size: 12.5px;
    line-height: 1.5;
    color: var(--muted);
    margin-top: var(--s3);
}
.kanji-keep { width: 100%; margin-top: var(--s3); }
.kanji-loading {
    display: flex;
    align-items: center;
    gap: var(--s2);
    font-size: 12.5px;
    color: var(--muted);
    margin-top: var(--s3);
}
.kanji-limit { font-size: 12px; color: var(--muted); margin-top: var(--s3); }

.legend { display: flex; flex-wrap: wrap; gap: var(--s3); font-size: 12px; color: var(--muted); }
.legend i { display: inline-block; width: 18px; border-bottom: 1.5px solid; margin-right: var(--s1); vertical-align: 4px; }

.picker { position: relative; }
.picker-panel {
    position: absolute;
    z-index: 30;
    top: calc(100% + var(--s1));
    left: 0;
    width: 220px;
    padding: var(--s2);
    border: 1px solid var(--hairline);
    border-radius: var(--radius);
    background: var(--surface);
    box-shadow: var(--shadow-lg);
    display: grid;
    gap: var(--s1);
    max-height: 280px;
    overflow-y: auto;
}
.picker-item {
    display: flex;
    align-items: center;
    gap: var(--s2);
    padding: var(--s1) var(--s2);
    border-radius: var(--radius-sm);
    font-size: 13.5px;
    cursor: pointer;
}
.picker-item:hover { background: var(--surface-2); }
.picker-item input { accent-color: var(--indigo); }

/* =============================================================== DRILL == */

/* The drill claims the height left under the head, so it has to use it. It
   used to pin the card to the top of that box and leave the rest empty. */
.drill { flex: 1; display: grid; place-items: start center; padding: var(--s8) var(--s5); }
.drill-inner {
    width: 100%;
    /* Grows with the window between about 1100 and 1600px, along with the
       prompt and the answer buttons below, so the card scales as one thing.
       440 was chosen for a laptop and looks marooned on anything larger. */
    max-width: clamp(440px, 40vw, 560px);
    display: grid;
    gap: var(--s4);
    /* Centred by auto margins rather than by place-items, because the tallest
       card — the alphabet drill with its deck panel open and the drawing
       square — is taller than a laptop screen. Auto margins take the free
       space when there is some and collapse to nothing when there is not, so
       a card too big for the window starts at the top where it can be reached
       instead of hanging off both ends. */
    margin-block: auto;
}

/* The card is the object on this screen. The counter above it and the buttons
   below it are not, and they were sitting flush against it — the spacing had
   been added per footer as each drill needed it, and the typed footer that the
   verbs and numbers drills use had never been given any. Spacing the whole
   stack from the container covers every drill and every one of the footers,
   including the ones added next. */
#state-card { display: grid; gap: var(--s5); }

.drill-head { display: flex; align-items: center; gap: var(--s3); }
.progress { flex: 1; height: 4px; border-radius: 2px; background: var(--surface-3); overflow: hidden; }
.progress > i { display: block; height: 100%; width: 0; background: var(--indigo); transition: width .25s; }
.count { font-family: var(--mono); font-size: 11px; color: var(--muted); font-variant-numeric: tabular-nums; }

.drill-card { padding: var(--s6) var(--s5); text-align: center; display: grid; gap: var(--s5); }
.drill-prompt { font-family: var(--cjk); font-size: clamp(40px, 3.6vw, 56px); line-height: 1.2; word-break: break-word; }
.drill-prompt[data-latin="true"] { font-family: var(--read); }
.drill-prompt-sub { font-family: var(--mono); font-size: 11px; letter-spacing: .08em; text-transform: uppercase; color: var(--muted); margin-top: var(--s1); }

.options { display: grid; grid-template-columns: 1fr 1fr; gap: var(--s2); }
.option {
    /* Floor is the touch target; the cap holds two lines of a long meaning
       without the button becoming a slab. */
    min-height: clamp(var(--tap), 4vw, 64px);
    padding: var(--s2) var(--s3);
    border: 1px solid var(--hairline);
    border-radius: var(--radius);
    background: var(--surface-2);
    color: var(--ink);
    font-family: var(--read);
    font-size: clamp(15.5px, 1.4vw, 17.5px);
    cursor: pointer;
    display: flex;
    align-items: center;
    justify-content: center;
    gap: var(--s2);
    text-align: center;
    transition: border-color .12s, background .12s;
}
.option:hover:not([disabled]) { border-color: var(--indigo-line); }
.option kbd {
    font-family: var(--mono);
    font-size: 10px;
    color: var(--muted);
    border: 1px solid var(--hairline);
    border-radius: 3px;
    padding: 1px 4px;
    background: var(--surface);
    flex: none;
}
.option.is-right { background: var(--celadon-soft); border-color: var(--celadon); color: var(--celadon); }
.option.is-wrong { background: var(--clay-soft); border-color: var(--clay); color: var(--clay); }
.option[disabled] { cursor: default; }

.drill-answer { display: grid; gap: var(--s2); padding-top: var(--s4); border-top: 1px solid var(--hairline); }
.drill-answer-romaji { font-family: var(--mono); font-size: 13px; color: var(--muted); }
.drill-answer-meaning { font-family: var(--read); font-size: 16px; }

.drill-foot { display: flex; gap: var(--s2); }

/* The draw card is taller than the others and its own contents are spaced
   further apart, so the gap that suits the rest reads as tight under it. */
.draw-foot { margin-top: var(--s2); }

.drill-help { display: grid; justify-items: center; gap: var(--s3); }
.drill-help-toggle {
    width: 26px; height: 26px;
    border: 1px solid var(--hairline);
    border-radius: 50%;
    background: none;
    color: var(--faint);
    font-size: 13px;
    line-height: 1;
    cursor: pointer;
    display: grid;
    place-items: center;
    transition: color .12s, border-color .12s;
}
.drill-help-toggle:hover { color: var(--indigo); border-color: var(--indigo-line); }
.drill-help-toggle[aria-expanded="true"] {
    color: var(--indigo);
    border-color: var(--indigo-line);
    background: var(--indigo-soft);
}

.scale {
    display: flex;
    flex-wrap: wrap;
    gap: var(--s2);
    justify-content: center;
    font-family: var(--mono);
    font-size: 10.5px;
    color: var(--muted);
}
.scale span { padding: 2px var(--s2); border-radius: var(--radius-pill); background: var(--surface-2); }

/* ========================================================== DICTIONARY == */

.dict-tools {
    display: flex;
    flex-wrap: wrap;
    gap: var(--s2);
    align-items: center;
    padding: var(--s3) var(--s5);
    border-bottom: 1px solid var(--hairline);
    background: var(--surface);
}
.dict-search { flex: 1; min-width: 180px; height: var(--s8); border-radius: var(--radius-pill); padding: 0 var(--s4); }
.dict-select { width: auto; height: var(--s8); border-radius: var(--radius-pill); font-size: 13px; padding: 0 var(--s6) 0 var(--s3); }

/* The filter pills opt down to 13px, which puts them back under the zoom
   threshold the base rule just cleared. They are the most-tapped controls on
   the page, so they cannot be the exception. */
@media (pointer: coarse) {
    .dict-select { font-size: 16px; }
}

.dict-word { font-family: var(--cjk); font-size: 19px; }
.dict-word[data-latin="true"] { font-family: var(--read); }
.dict-romaji { font-family: var(--mono); font-size: 12px; color: var(--muted); }
.dict-verb { display: block; font-size: 11px; color: var(--faint); }

/* The lists a word is in, in the row rather than only behind a click. Sized
   and coloured like the chips in the word panel, so the same fact looks the
   same in both places. */
.dict-lists {
    display: flex;
    flex-wrap: wrap;
    gap: 2px var(--s1);
    max-width: 20ch;
}
.dict-list {
    display: inline-block;
    margin: 1px var(--s1) 1px 0;
    padding: 2px var(--s2);
    border-radius: var(--radius-pill);
    background: var(--indigo-soft);
    color: var(--indigo);
    font-size: 11.5px;
    line-height: 1.5;
    white-space: nowrap;
}
.dict-meaning { font-family: var(--read); font-size: 15px; }

.bars { display: inline-flex; gap: 2px; vertical-align: middle; }
.bars i { width: 5px; height: 13px; border-radius: 1px; background: var(--surface-3); }
.bars i.on { background: var(--celadon); }

.due { font-family: var(--mono); font-size: 11.5px; font-variant-numeric: tabular-nums; white-space: nowrap; }
.due-now { color: var(--clay); }
.due-soon { color: var(--ochre); }
.due-later { color: var(--muted); }

/* ============================================================= LIBRARY == */

.tag-bar {
    display: flex;
    flex-wrap: wrap;
    gap: var(--s2);
    padding: var(--s3) var(--s5);
    border-bottom: 1px solid var(--hairline);
}

.lib-grid {
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(280px, 1fr));
    gap: var(--s4);
    padding: var(--s5);
    align-content: start;
}

.lib-card {
    display: flex;
    flex-direction: column;
    gap: var(--s3);
    padding: var(--s5);
    border: 1px solid var(--hairline);
    border-radius: var(--radius-lg);
    background: var(--surface);
    box-shadow: var(--shadow-sm);
}
.lib-card:hover { border-color: var(--indigo-line); }

.lib-card-link { text-decoration: none; color: inherit; display: grid; gap: var(--s2); }
.lib-title { font-size: 17px; line-height: 1.3; }
.lib-preview {
    font-family: var(--cjk);
    font-size: 15px;
    line-height: 1.7;
    color: var(--muted);
    display: -webkit-box;
    -webkit-line-clamp: 3;
    -webkit-box-orient: vertical;
    overflow: hidden;
}
.lib-preview[data-latin="true"] { font-family: var(--read); line-height: 1.5; }

.lib-meta,
.lib-tags {
    display: flex;
    flex-wrap: wrap;
    gap: var(--s2) var(--s3);
    font-family: var(--mono);
    font-size: 10.5px;
    letter-spacing: .07em;
    text-transform: uppercase;
    color: var(--faint);
}
.lib-tags { text-transform: none; letter-spacing: 0; }
.lib-tags .pill { text-decoration: none; }

.lib-foot { display: flex; align-items: center; gap: var(--s2); margin-top: auto; }
.lib-rating { font-size: 13px; color: var(--ochre); font-variant-numeric: tabular-nums; }
.lib-rating-count { color: var(--faint); }
.lib-rating-none { color: var(--faint); }
.lib-owner { font-size: 12px; color: var(--faint); }

.lib-progress { display: flex; align-items: center; gap: var(--s2); }
.lib-progress-bar { display: flex; align-items: center; gap: var(--s3); margin-bottom: var(--s5); max-width: 52em; }

/* ---- the paragraph reader ---- */

.lib-passage { padding-bottom: var(--s12); }
.lib-hint { max-width: 52em; margin-bottom: var(--s6); }

.para {
    max-width: 52em;
    padding: var(--s4) var(--s4) var(--s5);
    margin-bottom: var(--s4);
    border: 1px solid transparent;
    border-radius: var(--radius);
    cursor: pointer;
    transition: border-color .12s, background .12s;
}
.para:hover { border-color: var(--hairline); background: var(--surface); }
.para.is-read { border-color: var(--hairline); }

/* Only a words load dims the paragraph, because that call replaces the text.
   Translating leaves it readable — you may still be reading it while it runs. */
.para.is-loading { cursor: progress; }
.para.is-loading .para-body { opacity: .5; }

.para-status {
    display: inline-flex;
    align-items: center;
    gap: var(--s2);
    font-family: var(--mono);
    font-size: 10.5px;
    letter-spacing: .08em;
    text-transform: uppercase;
    color: var(--indigo);
}

.spinner {
    width: 13px;
    height: 13px;
    flex: none;
    border: 2px solid var(--indigo-line);
    border-top-color: var(--indigo);
    border-radius: 50%;
    animation: spin .6s linear infinite;
}
@keyframes spin { to { transform: rotate(360deg); } }

/* With motion reduced the ring cannot spin, so the wording carries it and the
   ring pulses gently instead of sitting there looking frozen. */
@media (prefers-reduced-motion: reduce) {
    .spinner { animation: none; border-top-color: var(--indigo); opacity: .8; }
}

.para-head {
    display: flex;
    align-items: center;
    gap: var(--s3);
    margin-bottom: var(--s2);
}
.para-number {
    font-family: var(--mono);
    font-size: 11px;
    color: var(--faint);
    min-width: 2ch;
    font-variant-numeric: tabular-nums;
}
/* Script toggle: icon only, with its name on hover and on focus. */
.para-script {
    position: relative;
    width: 32px;
    height: 28px;
    border-radius: var(--radius-pill);
}
.para-script svg { width: 19px; height: 19px; }
.para-script .icon-to-kanji { display: none; }
.para-script[aria-pressed="true"] {
    background: var(--indigo-soft);
    border-color: var(--indigo-line);
    color: var(--indigo);
}
.para-script[aria-pressed="true"] .icon-to-kana { display: none; }
.para-script[aria-pressed="true"] .icon-to-kanji { display: block; }

.para-script-label {
    position: absolute;
    bottom: calc(100% + 6px);
    left: 50%;
    transform: translateX(-50%);
    padding: var(--s1) var(--s2);
    border-radius: var(--radius-sm);
    background: var(--ink);
    color: var(--washi);
    font-family: var(--ui);
    font-size: 11px;
    letter-spacing: 0;
    text-transform: none;
    white-space: nowrap;
    opacity: 0;
    pointer-events: none;
    transition: opacity .12s;
    z-index: 20;
}
.para-script:hover .para-script-label,
.para-script:focus-visible .para-script-label { opacity: 1; }

/* A rewritten paragraph is still tappable: the conversion comes back split
   into words, each mapping back to its gloss. */
.para.is-hiragana { border-color: var(--indigo-line); }

.para-body { cursor: pointer; }
.para .word { cursor: pointer; }

.para-gloss {
    display: grid;
    gap: var(--s4);
    margin-top: var(--s4);
    padding-top: var(--s4);
    border-top: 1px solid var(--hairline);
}
.para-transcription { font-family: var(--mono); font-size: 13.5px; color: var(--muted); }
.para-translation { font-family: var(--read); font-size: 16.5px; line-height: 1.6; }

/* ---- ratings ---- */

.rating { display: inline-flex; align-items: center; gap: 2px; }
.rating-star {
    border: 0;
    background: none;
    color: var(--surface-3);
    font-size: 18px;
    line-height: 1;
    padding: 2px;
    cursor: pointer;
    transition: color .12s;
}
.rating-star[aria-pressed="true"] { color: var(--ochre); }
.rating:hover .rating-star { color: var(--ochre); }
.rating-star:hover ~ .rating-star { color: var(--surface-3); }
.rating-summary {
    margin-left: var(--s2);
    font-family: var(--mono);
    font-size: 11px;
    color: var(--muted);
    font-variant-numeric: tabular-nums;
}

/* ---- the upload form ---- */

.lib-form { max-width: 720px; }
.lib-body { height: auto; min-height: 320px; padding: var(--s3); line-height: 1.6; resize: vertical; }
.lib-share { width: fit-content; }
.field-hint.is-over { color: var(--clay); font-weight: 600; }

.chip-icon { width: 15px; height: 15px; fill: none; stroke: currentColor; stroke-width: 1.7; stroke-linecap: round; stroke-linejoin: round; }

/* ============================================================= ACCOUNT == */

.account { display: grid; grid-template-columns: 232px minmax(0, 1fr); flex: 1; min-height: 0; }

.tabs {
    display: flex;
    flex-direction: column;
    gap: var(--s1);
    padding: var(--s4);
    border-right: 1px solid var(--hairline);
    background: var(--surface-2);
}
.tab {
    display: grid;
    gap: 2px;
    padding: var(--s3);
    border: 1px solid transparent;
    border-radius: var(--radius);
    color: var(--ink);
    text-decoration: none;
}
.tab:hover { background: var(--surface-3); }
.tab[aria-current="page"] { background: var(--indigo-soft); border-color: var(--indigo-line); }
.tab-name { font-size: 14px; font-weight: 600; }
.tab[aria-current="page"] .tab-name { color: var(--indigo); }
.tab-meta { font-size: 12px; color: var(--muted); }

.panel { padding: var(--s6) var(--s6) var(--s10); min-width: 0; display: grid; gap: var(--s5); align-content: start; }
.panel-head { display: flex; align-items: center; gap: var(--s3); flex-wrap: wrap; }
.panel-title { font-size: 17px; }

.stats { display: flex; flex-wrap: wrap; gap: var(--s2); }
.stat {
    padding: var(--s3) var(--s4);
    border: 1px solid var(--hairline);
    border-radius: var(--radius);
    background: var(--surface);
    min-width: 128px;
}
.stat-value { font-size: 22px; font-weight: 600; font-variant-numeric: tabular-nums; }
.stat-value small { font-size: 13px; font-weight: 400; color: var(--muted); }

.form-grid { display: grid; gap: var(--s4); max-width: 480px; }
.check-row { display: flex; flex-wrap: wrap; gap: var(--s2); }

.saved-list { display: grid; gap: var(--s2); }
.saved-item {
    display: flex;
    align-items: flex-start;
    gap: var(--s3);
    padding: var(--s4);
    border: 1px solid var(--hairline);
    border-radius: var(--radius);
    background: var(--surface);
    color: inherit;
}
.saved-item:hover { border-color: var(--indigo-line); background: var(--surface-2); }

/* The card opens the text; Share is a separate destination, so the two are
   separate links rather than one nested inside the other. */
.saved-open {
    display: flex;
    flex: 1;
    min-width: 0;
    text-decoration: none;
    color: inherit;
}
.saved-share { flex: none; align-self: center; }
.saved-body { min-width: 0; flex: 1; display: grid; gap: var(--s1); }
.saved-text { font-family: var(--cjk); font-size: 17px; line-height: 1.7; }
.saved-text[data-latin="true"] { font-family: var(--read); }
.saved-translation { font-family: var(--read); font-size: 14px; color: var(--muted); }
.saved-meta {
    font-family: var(--mono);
    font-size: 10px;
    letter-spacing: .08em;
    text-transform: uppercase;
    color: var(--faint);
    display: flex;
    flex-wrap: wrap;
    gap: var(--s3);
}

/* ================================================================ AUTH == */

.auth-page { display: grid; place-items: center; min-height: 100vh; min-height: 100dvh; padding: var(--s6); }
.auth-card { width: min(420px, 100%); padding: var(--s8); display: grid; gap: var(--s5); }
.auth-brand { font-family: var(--mono); font-size: 11px; letter-spacing: .14em; text-transform: uppercase; color: var(--muted); }
.auth-title { font-size: 26px; letter-spacing: -.01em; }
.auth-sub { color: var(--muted); font-size: 14px; margin-top: var(--s1); }
.auth-switch { font-size: 13.5px; color: var(--muted); text-align: center; }

/* ========================================================== RESPONSIVE == */

@media (max-width: 1020px) {
    .reader { grid-template-columns: minmax(0, 1fr); }
    /* The stage needs the whole width once there is not much of it. */
    .verb-bench-body { grid-template-columns: minmax(0, 1fr); }
    .gloss {
        border-left: 0;
        border-top: 1px solid var(--hairline);
        position: sticky;
        /* Undo the full-height sidebar above: down here it is a sheet held to
           the bottom of the screen, and a column that is one viewport tall
           held to the top is the opposite of that. */
        top: auto;
        /* And sat at the bottom of its row rather than stretched across it. A
           stretched item that cannot fill its row — which is any item with a
           max-height, so this one always — is laid out at the row's start, and
           the shorter the sheet the further it floated above the tab bar.
           Anchoring the end instead keeps it against the bottom edge at either
           of its two heights, while still taking up room in the flow so the
           passage never runs underneath it. */
        align-self: end;
        height: auto;
        bottom: 0;
        max-height: 46vh;
        overflow-y: auto;
        padding-bottom: calc(var(--s5) + env(safe-area-inset-bottom));
        box-shadow: 0 -8px 24px -18px rgba(0, 0, 0, .6);
        /* Between its two heights. Both ends are real lengths, so this
           animates; the sheet dropping out of the way rather than vanishing is
           what says it is still there to bring back. */
        transition: max-height .22s ease;
    }
    /* Once the gloss is a bottom sheet its height is capped, and the legend and
       the kept-count are reference material rather than something you act on.
       The word itself gets that space instead. */
    .gloss-foot { display: none; }

    /* The grip carries the heading down here, so the heading itself would be
       one of two. */
    .gloss > .label { display: none; }

    .gloss-grip {
        display: flex;
        align-items: center;
        gap: var(--s3);
        /* Out to the sheet's edges: a handle you have to aim for is not much
           of a handle. The negative margins undo the sheet's padding, and
           sticking to the top keeps it in reach after the gloss has been
           scrolled. */
        margin: calc(var(--s5) * -1) calc(var(--s5) * -1) 0;
        padding: var(--s3) var(--s5);
        position: sticky;
        top: 0;
        z-index: 1;
        border: 0;
        background: var(--surface-2);
        color: var(--ink);
        font-family: var(--cjk);
        font-size: 13px;
        text-align: left;
        cursor: pointer;
        /* No double-tap zoom to wait on before the tap counts. */
        touch-action: manipulation;
    }
    .gloss-grip-bar {
        flex: none;
        width: 32px;
        height: 4px;
        border-radius: var(--radius-pill);
        background: var(--hairline);
    }
    .gloss-grip-label {
        overflow: hidden;
        white-space: nowrap;
        text-overflow: ellipsis;
    }
    .gloss-grip-label[data-latin="true"] { font-family: var(--read); }

    /* Down: capped at the grip, which is measured as it goes so the figure is
       right whatever the type does. Scrolling goes with it — there is nothing
       to scroll to, and a sheet that moved under the finger while it was shut
       would just be a smaller version of the problem. */
    .gloss.is-peeked {
        max-height: var(--gloss-peek, 44px);
        overflow: hidden;
    }
    .passage { padding: var(--s6) var(--s5) var(--s8); }
}

@media (max-width: 860px) {
    .account { grid-template-columns: minmax(0, 1fr); }
    .tabs {
        flex-direction: row;
        overflow-x: auto;
        border-right: 0;
        border-bottom: 1px solid var(--hairline);
        padding: var(--s3) var(--s4);
    }
    .tab { flex: none; }
    .tab-meta { display: none; }
    .panel { padding: var(--s5) var(--s4) var(--s10); }
}

/* Two five-cell blocks need about 800px between them and the rail. Below that
   the voiced row drops beneath the row it comes from — still next to its
   origin, just downwards rather than across. */
@media (max-width: 860px) {
    .kana-chart-head, .kana-row { grid-template-columns: minmax(0, 1fr); gap: var(--s1); }
    .kana-chart { gap: var(--s4); }
    .kana-chart-head { gap: var(--s4); margin-bottom: 0; align-items: start; }
    .kana-voiced:not(:empty) { margin-bottom: var(--s3); }
}

@media (max-width: 720px) {
    /* One value for the tab bar's height: the bar is set to it, and everything
       that must clear the bar offsets by it. Guessing separately is how the
       gloss buttons ended up underneath it. */
    :root { --rail-w: 0px; --tabbar-h: calc(64px + env(safe-area-inset-bottom)); }

    .app { grid-template-columns: minmax(0, 1fr); }

    .rail {
        position: fixed;
        left: 0; right: 0; bottom: 0;
        top: auto;
        height: var(--tabbar-h);
        flex-direction: row;
        justify-content: space-around;
        gap: 0;
        border-right: 0;
        border-top: 1px solid var(--hairline);
        padding: var(--s1) var(--s2) calc(var(--s1) + env(safe-area-inset-bottom));
        z-index: 80;
    }
    /* Five items — Read, Library, Grammar & Vocab, Kanji, You — so each takes
       an equal share of whatever there is rather than a fixed width. The cap
       is what the longest label measures at the narrowest phone: below it the
       label truncates, and above it the tabs drift apart on a tablet. */
    .rail-item {
        width: auto;
        flex: 1;
        max-width: 96px;
        min-height: var(--tap);
        padding-left: 0;
        padding-right: 0;
        gap: 2px;
    }
    /* Only the rail has room for the whole name. See app.html. */
    .rail-label-tail { display: none; }
    .rail-label {
        font-size: 9.5px;
        max-width: 100%;
        overflow: hidden;
        text-overflow: ellipsis;
        white-space: nowrap;
    }
    .rail-spacer { display: none; }

    /* The public bar wraps to three rows on a phone — brand, links, buttons —
       which is most of the first screen before any of the page it is on. Two:
       the brand and the way in on one, the reference links under them. */
    .public-bar { padding: var(--s2) var(--s4); }
    .public-nav { order: 3; flex-basis: 100%; }
    .public-brand { font-size: 14px; }
    /* The brand and both buttons come to a few pixels more than a 375px
       phone has, and the pair would drop to a row of its own. A few pixels
       is all this is buying back. */
    .public-auth .chip,
    .public-auth .btn { height: var(--s8); padding: 0 var(--s3); font-size: 12.5px; }
}

/* On a phone the name and both buttons will not share a row, and the row they
   push onto costs more of the first screen than the name is worth. The mark
   stays, and the page title under it says where you are anyway. */
@media (max-width: 480px) {
    .public-brand span { display: none; }
    .public-cta { margin: var(--s8) var(--s4) var(--s6); padding: var(--s5); }

    /* No tab bar down there for a visitor, so nothing to leave room for. */
    .app-public .main { padding-bottom: 0; }

    /* The brief asks for 240px of its own, which on a phone leaves the
       conversation the rest and takes the page sideways. One column, with
       the room described above the conversation that happens in it. */
    .scenario-stage {
        grid-template-columns: minmax(0, 1fr);
        gap: var(--s4);
        padding: var(--s4);
    }
    .scenario-brief-scene { height: 72px; }
    /* Capped so the box you type in is on screen without scrolling past the
       whole conversation to reach it. */
    .scenario-thread { max-height: 58vh; }
    .scenario-line { max-width: 92%; }
    .scenario-grid { grid-template-columns: minmax(0, 1fr); }

    /* The gloss column asks for 260px, which on a phone leaves the passage
       beside it about ninety — one word per line, which is not what the
       reader looks like and so not worth showing. */
    .landing { gap: var(--s10); padding: var(--s8) var(--s4) var(--s10); }
    .landing-demo { grid-template-columns: minmax(0, 1fr); padding: var(--s4); }
    .landing-close { padding: var(--s6) var(--s5); }

    /* The grid keeps its five columns and gives up cell width instead: a chart
       that wraps mid-row stops being a chart. */
    .kana-page { padding: var(--s5) var(--s4) var(--s8); gap: var(--s6); }
    .kana-grid { gap: var(--s1); grid-template-columns: repeat(var(--kana-cols, 5), minmax(0, 1fr)); }
    .kana-cell { min-height: 62px; padding: var(--s1); }
    .kana-char { font-size: 22px; }
    .kana-romaji { font-size: 10px; }
    .kana-mark { max-width: none; }

    .main { padding-bottom: var(--tabbar-h); }

    /* The sheet sits on the tab bar, not behind it. */
    .gloss {
        bottom: var(--tabbar-h);
        padding-bottom: var(--s5);
        max-height: calc(52vh - var(--tabbar-h));
    }

    .line { font-size: 23px; }
    .drill-prompt { font-size: 34px; }
    .options { grid-template-columns: 1fr; }
    .passage { padding: var(--s5) var(--s4) var(--s6); }
    .page-head { padding: var(--s3) var(--s4); }
    .lib-grid { grid-template-columns: minmax(0, 1fr); padding: var(--s4); gap: var(--s3); }
    .tag-bar { padding: var(--s3) var(--s4); }
    .para { padding: var(--s3); }
    .dict-tools { padding: var(--s3) var(--s4); }

    /* Eight columns will not fit a phone. The table becomes one block per word:
       the word and its reading lead, the meaning follows, and the details sit
       on a single line underneath. Nothing is dropped — it is re-stacked, and
       the header row is redundant once each value is on its own line. */
    .dict-table thead { display: none; }
    .dict-table,
    .dict-table tbody,
    .dict-table tr,
    .dict-table td { display: block; }

    .dict-table tr {
        position: relative;
        padding: var(--s4);
        border-bottom: 1px solid var(--hairline);
    }
    /* Only the first two lines sit beside the buttons; the detail line below
       them gets the full width instead of being needlessly indented. */
    .dict-table .c-word,
    .dict-table .c-reading { padding-right: 84px; }
    .dict-table tbody tr:hover td { background: none; }

    .dict-table td { padding: 0; border: 0; }
    .dict-table .c-word { margin-bottom: 2px; }
    .dict-table .c-word .dict-word { font-size: 22px; }
    .dict-table .c-meaning { margin-top: var(--s1); font-size: 16px; }

    /* Part of speech, strength and next review share one line. */
    .dict-table .c-part,
    .dict-table .c-lists,
    .dict-table .c-owner,
    .dict-table .c-strength,
    .dict-table .c-due {
        display: inline-block;
        vertical-align: middle;
        margin: var(--s2) var(--s3) 0 0;
    }
    /* The cap is for a table column; stacked there is the whole width. */
    .dict-lists { max-width: none; display: inline-flex; }
    .dict-table .c-part .dict-verb { display: inline; }
    .dict-table .c-part .dict-verb::before { content: " · "; }

    .dict-table .c-actions {
        position: absolute;
        top: var(--s4);
        right: var(--s4);
        display: flex;
        gap: var(--s2);
    }

    .table-wrap { overflow-x: visible; }
    .toasts { bottom: calc(var(--tabbar-h) + var(--s3)); }
}
