/* Deckist: the card box. White cards on a quiet desk, Literata for the words on a card and for
   headings, Instrument Sans for everything else, one moss green. The brief is docs/DESIGN.md.

   The tokens live here, once, for every page that loads this file: the public pages, the web
   client, the auth cards. marketing.css draws the masthead, the footer and the public pages on
   top of the same tokens, so there is no second palette for anything to leak out of. */
:root {
  color-scheme: light dark;
  --desk: #e9eae6; --desk-2: #dfe0db; --card: #ffffff; --card-2: #f4f5f2;
  --ink: #17191c; --ink-2: #454850;
  /* The brief's #6a6d75 reads 4.28:1 on the desk; #5f626a reads 5.05 there, the same value
     the apps' Theme.swift carries. */
  --muted: #5f626a;
  --hair: rgba(23, 25, 28, 0.14); --hair-soft: rgba(23, 25, 28, 0.07);
  --moss: #176b45; --moss-hi: #1f8a59; --moss-soft: #e3f1e9; --moss-hover: #125a39; --on-moss: #ffffff;
  /* Hard is darker than the brief's #a56b0a, which reads 4.46:1 on a card and 3.69 on the desk. */
  --again: #b4241c; --hard: #8c5a07; --good: #176b45; --easy: #1d5fb4;
  --on-ink: #ffffff; --on-again: #ffffff;
  --lift: 0 18px 30px -14px rgba(23, 25, 28, 0.32), 0 3px 8px rgba(23, 25, 28, 0.08);
  --serif: "Literata", "Iowan Old Style", Georgia, serif;
  --sans: "Instrument Sans", -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;
  --ease: cubic-bezier(0.22, 1, 0.36, 1);
  --ease-slide: cubic-bezier(0.7, 0, 0.2, 1);
  --radius: 10px;
  /* The mark's overlap (shared/_glyph draws it in --accent) and the client's own names for the
     same tokens, so its rules read as they always did. */
  --accent: var(--moss); --accent-ink: var(--on-moss); --accent-soft: var(--moss-soft);
  --ground: var(--desk); --ink-soft: var(--ink-2); --ink-faint: var(--muted);
  --line: var(--hair); --line-soft: var(--hair-soft);
  --bad: var(--again); --warn: var(--hard);
}
@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) {
    --desk: #141619; --desk-2: #0e1012; --card: #1d2024; --card-2: #24282d;
    --ink: #e9eae5; --ink-2: #b4b7bd; --muted: #8f939b;
    --hair: rgba(233, 234, 229, 0.14); --hair-soft: rgba(233, 234, 229, 0.07);
    --moss: #3fae78; --moss-hi: #58c48f; --moss-soft: #1b2f25; --moss-hover: #58c48f; --on-moss: #0e1a13;
    --again: #ee6b63; --hard: #e6a940; --good: #3fae78; --easy: #6ea8f0;
    --on-ink: #141619; --on-again: #141619;
    --lift: 0 18px 34px -12px rgba(0, 0, 0, 0.7), 0 3px 8px rgba(0, 0, 0, 0.4);
  }
}
:root[data-theme="dark"] {
  --desk: #141619; --desk-2: #0e1012; --card: #1d2024; --card-2: #24282d;
  --ink: #e9eae5; --ink-2: #b4b7bd; --muted: #8f939b;
  --hair: rgba(233, 234, 229, 0.14); --hair-soft: rgba(233, 234, 229, 0.07);
  --moss: #3fae78; --moss-hi: #58c48f; --moss-soft: #1b2f25; --moss-hover: #58c48f; --on-moss: #0e1a13;
  --again: #ee6b63; --hard: #e6a940; --good: #3fae78; --easy: #6ea8f0;
  --on-ink: #141619; --on-again: #141619;
  --lift: 0 18px 34px -12px rgba(0, 0, 0, 0.7), 0 3px 8px rgba(0, 0, 0, 0.4);
}

/* base ---------------------------------------------------------------------------------- */
*, *::before, *::after { box-sizing: border-box; }
html, body { margin: 0; padding: 0; }
html { -webkit-text-size-adjust: 100%; }
body {
  background: var(--desk); color: var(--ink);
  font-family: var(--sans); font-size: 16px; line-height: 1.6;
  font-variant-numeric: tabular-nums; font-optical-sizing: auto;
  -webkit-font-smoothing: antialiased; -moz-osx-font-smoothing: grayscale;
}
/* A link's underline is the ink at 55%: 3.7:1 on the desk and 4:1 on a card in light, 5:1 and
   more in dark. The hairline it was (1.3:1) left a link in a sentence looking like the sentence
   (hardening pass, 2026-10-17; docs/DESIGN.md). */
a { color: var(--ink); text-decoration-line: underline; text-decoration-color: color-mix(in srgb, var(--ink) 55%, transparent);
  text-decoration-thickness: 1px; text-underline-offset: 0.2em;
  transition: text-decoration-color 0.3s var(--ease), color 0.3s var(--ease); }
a:hover { text-decoration-color: var(--moss); }
h1, h2, h3 { font-family: var(--serif); font-weight: 500; letter-spacing: -0.015em; line-height: 1.04;
  margin: 0; text-wrap: balance; color: var(--ink); }
h3 { line-height: 1.2; }
p { margin: 0 0 1em; text-wrap: pretty; }
::selection { background: var(--moss); color: var(--on-moss); }
:focus-visible { outline: 2px solid var(--moss); outline-offset: 3px; }
kbd, code { font-family: var(--sans); }
.wrap { max-width: 1040px; margin: 0 auto; padding: 0 16px; }
.narrow { max-width: 720px; }

/* blocks -------------------------------------------------------------------------------- */
main { padding: 2rem 0 5rem; }
.panel { background: var(--card); border: 1px solid var(--hair); border-radius: var(--radius);
  padding: 1.25rem 1.35rem; }
.stack { display: grid; gap: 1rem; }
.row { display: flex; align-items: center; gap: 0.75rem; }
.spread { display: grid; grid-template-columns: minmax(0, 1fr) auto; align-items: center; gap: 1rem; }
.spread > * { min-width: 0; }
.muted { color: var(--ink-2); text-wrap: pretty; } /* a sentence beside a pill ends on more than one word */
.faint { color: var(--muted); }
.small { font-size: 14px; }
.tel { white-space: nowrap; }
.mono { font-family: var(--sans); font-variant-numeric: tabular-nums; }

.flash { border-radius: 8px; padding: 0.75rem 1rem; margin: 0 0 1.2rem; border: 1px solid var(--hair);
  background: var(--card); font-size: 15px; color: var(--ink); }
.flash.notice { border-color: color-mix(in srgb, var(--moss) 45%, var(--hair)); }
.flash.alert { border-color: color-mix(in srgb, var(--again) 50%, var(--hair)); }

/* buttons: one height, set once, so a row of them lines up without nudging. Ink is the primary
   fill; the moss is kept for Start Free (marketing.css) and nothing else a button does. */
.btn { --h: 44px; height: var(--h); display: inline-flex; align-items: center; justify-content: center;
  gap: 0.45rem; white-space: nowrap; padding: 0 1.1rem; border-radius: 7px; border: 1px solid var(--hair);
  background: var(--card); color: var(--ink); font: 600 15px/1 var(--sans); letter-spacing: -0.005em;
  cursor: pointer; text-decoration: none;
  transition: background-color 0.2s var(--ease), border-color 0.2s var(--ease), color 0.2s var(--ease), transform 0.12s var(--ease); }
.btn:hover { border-color: color-mix(in srgb, var(--ink) 45%, transparent); }
.btn:active { transform: translateY(1px); }
/* The primary action is the moss on every surface: the site's Start Free, the apps' Study and
   Show Answer, and these. Ink was the web client's alone, and the native pass showed the gap. */
.btn.primary { background: var(--moss); border-color: var(--moss); color: var(--on-moss); }
.btn.primary:hover { background: var(--moss-hover); border-color: var(--moss-hover); }
.btn.ghost { background: transparent; border-color: transparent; color: var(--ink-2); }
.btn.ghost:hover { background: var(--card-2); color: var(--ink); border-color: var(--hair-soft); }
.btn.danger { color: var(--again); }
.btn.danger:hover { border-color: var(--again); }
.btn.danger-fill { background: var(--again); border-color: var(--again); color: var(--on-again); }
.btn.danger-fill:hover { background: color-mix(in srgb, var(--again) 86%, #000000); }
.btn.lg { --h: 52px; font-size: 16px; padding: 0 1.4rem; }
.btn.small { --h: 36px; font-size: 14px; padding: 0 0.8rem; border-radius: 6px; }

/* forms: inputs share the button height so they line up in a row */
label { display: block; font: 600 13.5px/1.35 var(--sans); letter-spacing: 0; color: var(--ink-2); margin: 0 0 0.35rem; }
input[type=text], input[type=email], input[type=password], input[type=number], input[type=search], textarea, select {
  width: 100%; height: 44px; padding: 0 0.8rem; border: 1px solid var(--hair); border-radius: 7px;
  background: var(--card); color: var(--ink); font: 16px/1.4 var(--sans); }
input::placeholder, textarea::placeholder { color: var(--muted); opacity: 1; }
textarea { height: auto; min-height: 96px; padding: 0.65rem 0.8rem; line-height: 1.5; resize: vertical; }
input:focus, textarea:focus, select:focus { outline: 2px solid color-mix(in srgb, var(--moss) 70%, transparent);
  outline-offset: 1px; border-color: var(--moss); }
.field { margin: 0 0 1rem; }
/* A hint anywhere, not only inside .field: a standalone <p class="hint"> was unstyled and
   printed at the body size beside 14px text. */
.hint { font-size: 14px; line-height: 1.5; color: var(--muted); margin: 0.35rem 0 0; }
.field .err { font-size: 14px; color: var(--again); margin: 0.35rem 0 0; display: block; }

/* The listbox: the styled select (the form-controls skill, copied from rostr). A native
   <select>'s menu is drawn by the operating system and no CSS reaches it, so the real one
   stays in the DOM to submit (and to be the control when the script has not run) while
   everything visible is ours. The closed control is an input in every way that shows:
   the height, the border, the radius, the face. */
.listbox { position: relative; }
.listbox[data-listbox-ready] .listbox-native { position: absolute; opacity: 0; pointer-events: none;
  width: 1px; height: 1px; padding: 0; border: 0; }
.listbox-button { display: none; }
.listbox[data-listbox-ready] .listbox-button { display: flex; }
.listbox-button { width: 100%; height: 44px; align-items: center; justify-content: space-between; gap: 0.6rem;
  padding: 0 0.7rem 0 0.8rem; border: 1px solid var(--hair); border-radius: 7px; background: var(--card);
  color: var(--ink); font: 16px/1.4 var(--sans); text-align: left; cursor: pointer;
  transition: border-color 0.2s var(--ease); }
.listbox-button:hover { border-color: color-mix(in srgb, var(--ink) 45%, transparent); }
.listbox-button[aria-expanded="true"], .listbox-button:focus-visible { border-color: var(--moss);
  outline: 2px solid color-mix(in srgb, var(--moss) 70%, transparent); outline-offset: 1px; }
/* The chosen value is one line, always, so the control keeps the height of the inputs. */
.listbox-value { min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
/* A disclosure arrow as tall as the text it sits with (1em). */
.listbox-chevron { display: grid; place-items: center; flex: none; color: var(--ink-2); }
.listbox-chevron svg { display: block; width: 1em; height: 1em; }
/* The menu sizes to its longest option, never narrower than the control, capped so a
   long label cannot span the screen; it scrolls inside itself past sixteen rows' worth. */
.listbox-list { position: absolute; z-index: 30; left: 0; top: calc(100% + 0.3rem); margin: 0; padding: 0.3rem;
  min-width: 100%; width: max-content; max-width: min(22rem, 90vw); max-height: 17rem; overflow: auto;
  list-style: none; background: var(--card); border: 1px solid var(--hair); border-radius: 10px;
  box-shadow: var(--lift); overscroll-behavior: contain; }
.listbox-option { display: flex; align-items: center; gap: 0.5rem; padding: 0.5rem 0.65rem 0.5rem 0.5rem;
  border-radius: 7px; color: var(--ink); font-size: 15px; line-height: 1.35; white-space: nowrap; cursor: pointer; }
.listbox-option.active { background: var(--moss-soft); }
.listbox-tick { flex: none; width: 1em; height: 1em; color: var(--moss); visibility: hidden; }
.listbox-option[aria-selected="true"] { font-weight: 600; }
.listbox-option[aria-selected="true"] .listbox-tick { visibility: visible; }

/* The deck form: a name and a language, at the width a name needs. */
.deck-form { max-width: 32rem; }

/* page heads: a way back (when there is one) over a heading, the page's actions beside them.
   No kicker: the ledger's numbered labels are gone, and "Your collection" over "Decks" said
   nothing the heading does not. */
.page-head { margin: 0 0 1.4rem; }
/* A heading beside its actions wraps rather than squeezes: on a phone a long deck name keeps
   its line and the actions drop beneath it, never on top of it. */
.page-head.spread { display: flex; flex-wrap: wrap; align-items: flex-end; justify-content: space-between; gap: 0.9rem 1.5rem; }
.page-head.spread > :first-child { flex: 1 1 auto; }
.page-head h1 { font-size: clamp(1.9rem, 1.5rem + 1.4vw, 2.5rem); line-height: 1.08; }
.page-head .lede { margin: 0.55rem 0 0; color: var(--ink-2); max-width: 64ch; }
.crumb { display: inline-flex; align-items: center; gap: 0.35rem; margin: 0 0 0.4rem; font-size: 14px;
  color: var(--ink-2); text-decoration: none; }
.crumb:hover { color: var(--ink); }
.crumb svg { width: 1em; height: 1em; flex: none; }

/* deck list: a grid, so every column lines up without space-between guesswork */
.decks { display: grid; gap: 0.6rem; }
/* Sections of the deck list (Deck#category) and the filter box above them. */
.deck-search { margin: 0 0 1.2rem; }
.deck-search input { width: 100%; max-width: 28rem; }
.deck-group + .deck-group { margin-top: 1.6rem; }
.deck-cat { font: 600 13px/1.2 var(--sans); letter-spacing: 0.06em; text-transform: uppercase; color: var(--muted); margin: 0 0 0.6rem; }
.deck-cat-n { font-weight: 500; margin-left: 0.35rem; }
.deck-group[hidden], .deck[hidden] { display: none; }
.deck { display: grid; grid-template-columns: minmax(0, 1fr) auto auto; align-items: center; gap: 1rem;
  background: var(--card); border: 1px solid var(--hair); border-radius: var(--radius);
  padding: 0.95rem 1.15rem; text-decoration: none; color: var(--ink);
  transition: border-color 0.25s var(--ease), transform 0.25s var(--ease); }
.deck:hover { border-color: color-mix(in srgb, var(--ink) 40%, transparent); }
.deck .nm { font-family: var(--serif); font-size: 1.2rem; font-weight: 500; letter-spacing: -0.01em; line-height: 1.25; }
.deck .ct { font-size: 14px; color: var(--muted); white-space: nowrap; }
.deck-main { display: grid; gap: 0.2rem; min-width: 0; }
.deck-meta { font-size: 14px; color: var(--muted); text-wrap: pretty; }
/* Mastery, 0 to 100 (Reviewing::Mastery): a ring filled in moss to the score, the number inside. */
.mastery { --m: 0; display: inline-grid; place-items: center; width: 44px; height: 44px; border-radius: 50%;
  background: conic-gradient(var(--moss) calc(var(--m) * 1%), var(--hair) 0); flex: none; }
.mastery::before { content: ""; grid-area: 1 / 1; width: 34px; height: 34px; border-radius: 50%; background: var(--card); }
.mastery-n { grid-area: 1 / 1; position: relative; font: 600 13px/1 var(--sans); font-variant-numeric: tabular-nums; color: var(--ink); }
/* A narrow catalog row gives the name and description the full width and puts the count and
   pill on the line below, instead of a 100px description column. */
@media (max-width: 520px) {
  a.deck { grid-template-columns: minmax(0, 1fr) auto; }
  a.deck > span:first-child { grid-column: 1 / -1; }
}

.pill { display: inline-flex; align-items: center; font: 600 12.5px/1 var(--sans); letter-spacing: 0.01em;
  padding: 0.4rem 0.6rem; border-radius: 5px; border: 1px solid var(--hair); color: var(--ink-2);
  background: var(--card); white-space: nowrap; }
.pill.due { background: var(--moss-soft); border-color: color-mix(in srgb, var(--moss) 40%, transparent); color: var(--moss); }

table { width: 100%; border-collapse: collapse; }
th, td { text-align: left; padding: 0.65rem 0.75rem; border-bottom: 1px solid var(--hair-soft); vertical-align: middle; }
th { font: 600 12.5px/1.3 var(--sans); letter-spacing: 0.02em; color: var(--muted); border-bottom-color: var(--hair); }
tbody tr:last-child td { border-bottom: 0; }

/* the reviewer: an index card on the desk ----------------------------------------------- */
.study { max-width: 720px; margin: 0 auto; }
/* `hidden` must win over the grids and flexes below (.answers, .study-tools). Only a stale,
   gitignored Tailwind build in one checkout (app/assets/builds/tailwind.css, whose preflight
   says the same) was hiding them; a fresh checkout showed the rating keys before the answer. */
.study [hidden] { display: none !important; }
.face { position: relative; background: var(--card); border: 1px solid var(--hair); border-radius: 12px;
  min-height: 280px; display: flex; flex-direction: column; align-items: center; justify-content: center;
  gap: 1rem; padding: 2.6rem 1.6rem; text-align: center; }
.face .q { font-family: var(--serif); font-size: clamp(1.6rem, 1.3rem + 1.2vw, 2.1rem); font-weight: 450;
  letter-spacing: -0.012em; line-height: 1.25; color: var(--ink); }
.face hr { width: 64px; border: none; border-top: 1px solid var(--hair); margin: 0.2rem 0; }
.face .a { font-family: var(--serif); font-size: 1.25rem; color: var(--ink-2); line-height: 1.45; }
.face .a:not([hidden]) { animation: answer-in 0.45s var(--ease) both; }
/* Bold on a card: what its author wrote between **, and a vocabulary word on its own back. */
.face .q strong, .face .a strong { font-weight: 650; color: var(--ink); }
/* How the card is said, in IPA, never read aloud: the answer's own size and second ink, so it
   reads at a glance (Derick, 2026-10-26: "that needs to be bigger and more clear"), in the body
   face. It is a button: pressing it opens the guide to its symbols, which the circled question
   mark beside it says. The face's gap is closed a little so it reads as its side's own line. */
.face .ipa { display: inline-flex; align-items: center; gap: 0.45rem; margin: -0.35rem 0 0; padding: 0.2rem 0.55rem;
  border: 1px solid transparent; border-radius: 7px; background: transparent; cursor: pointer;
  font-family: var(--sans); font-size: 1.15rem; line-height: 1.4; letter-spacing: 0.01em; color: var(--ink-2);
  overflow-wrap: anywhere; text-align: center; transition: border-color 0.2s var(--ease), background-color 0.2s var(--ease); }
.face .ipa[hidden] { display: none; }
.face .ipa .ipa-text { text-decoration: underline dotted color-mix(in srgb, var(--ink-2) 55%, transparent);
  text-decoration-thickness: 1.5px; text-underline-offset: 0.3em; }
.face .ipa .ico { flex: none; width: 1.05em; height: 1.05em; color: var(--muted); }
.face .ipa:hover { border-color: var(--hair); background: var(--card-2); }
.face .ipa:hover .ico { color: var(--moss); }
.face .ipa:focus { outline: none; }
.face .ipa:focus-visible { outline: 2px solid var(--moss); outline-offset: 2px; }
.face .ipa:not([hidden]) { animation: answer-in 0.45s var(--ease) both; }
.face .ipa.with-question { margin-top: -0.45rem; animation: none; }

/* The pronunciation guide: a card on the desk, lifted, over a dimmed page. Each symbol in the
   card's pronunciation, large, beside what it sounds like and a word that has it. */
/* Centered on every screen: a phone otherwise drew it pinned to the top left corner. */
.guide { position: fixed; inset: 0; margin: auto; height: fit-content;
  width: min(30rem, calc(100vw - 32px)); max-height: min(40rem, calc(100dvh - 32px)); padding: 0;
  border: 1px solid var(--hair); border-radius: 12px; background: var(--card); color: var(--ink);
  box-shadow: var(--lift); overflow: hidden; }
.guide::backdrop { background: rgba(14, 16, 18, 0.42); }
.guide[open] { animation: answer-in 0.3s var(--ease) both; }
/* The head and Done stay put and only the rows scroll, so on a phone the pronunciation being
   explained is always in view above them. */
.guide-card { display: flex; flex-direction: column; max-height: min(40rem, calc(100dvh - 34px));
  padding: 1.4rem 1.4rem 1.2rem; }
.guide-head { text-align: center; margin: 0 0 1rem; }
.guide-head h2 { margin: 0; font: 600 13.5px/1.35 var(--sans); letter-spacing: 0.02em; color: var(--muted); }
.guide-said { margin: 0.35rem 0 0; font: 400 1.6rem/1.3 var(--sans); color: var(--ink); overflow-wrap: anywhere; }
.guide-lang { margin: 0.25rem 0 0; font-size: 14px; color: var(--ink-2); text-wrap: balance; }
.guide-rows { display: grid; grid-template-columns: 3.2rem minmax(0, 1fr); align-content: start; gap: 0; margin: 0;
  min-height: 0; overflow-y: auto; overscroll-behavior: contain; border-top: 1px solid var(--hair-soft);
  border-bottom: 1px solid var(--hair-soft); }
.guide-rows > :nth-last-child(-n + 2) { border-bottom: 0; }
/* A long word's sounds scroll; the last rows fade while more are below, so the list never looks finished early. */
.guide-rows.more { mask-image: linear-gradient(#000 calc(100% - 3.5rem), transparent);
  -webkit-mask-image: linear-gradient(#000 calc(100% - 3.5rem), transparent); }
.guide-rows dt, .guide-rows dd { margin: 0; padding: 0.6rem 0; border-bottom: 1px solid var(--hair-soft); }
.guide-rows dt { font: 400 1.5rem/1.2 var(--sans); color: var(--ink); text-align: center; align-self: stretch;
  display: flex; align-items: center; justify-content: center; }
.guide-rows dt.mark { color: var(--moss); font-size: 2.4rem; font-weight: 700; }
/* A stress tick before a syllable, at the letters' own size, so high and low read as high and low. */
.guide-rows dt.mark.stress { font-size: 1.5rem; }
.guide-syllable { color: var(--muted); font-weight: 400; }
.guide-rows dd { display: flex; flex-direction: column; justify-content: center; gap: 0.1rem; padding-left: 0.6rem;
  font-size: 15px; line-height: 1.45; color: var(--ink); text-wrap: pretty; }
.guide-example { font-size: 14px; color: var(--muted); font-style: italic; }
.guide-foot { display: flex; justify-content: center; margin-top: 1.1rem; }
.guide-foot .btn { min-width: 8rem; }
@media (prefers-reduced-motion: reduce) { .guide[open] { animation: none; } }

/* The card editor: Bold under the text it acts on, and the reverse card's switch, the words on
   the left and the house switch at the row's right edge. */
.bold-bar { display: flex; flex-wrap: wrap; align-items: center; gap: 0.35rem 0.75rem; margin-top: 0.45rem; }
.bold-bar .hint { margin: 0; }
.bold-bar .btn .keys { margin-left: 0.5em; }
/* ⌘B only where there is a keyboard to press it on. */
@media (hover: none) { .bold-bar .btn .keys { display: none; } }
.switch-row { display: grid; grid-template-columns: minmax(0, 1fr) auto; align-items: center; gap: 1rem;
  margin: 0; padding: 0.7rem 0.75rem 0.7rem 0.9rem; border: 1px solid var(--hair); border-radius: 7px;
  background: var(--card); cursor: pointer; }
.switch-words { display: flex; flex-direction: column; gap: 0.1rem; min-width: 0; }
.switch-name { font: 600 15px/1.3 var(--sans); color: var(--ink); }
.switch-words .hint { margin: 0; font-weight: 400; }
@keyframes answer-in { from { opacity: 0; transform: translateY(6px); } to { opacity: 1; transform: none; } }
/* Answer buttons sit BELOW the card, so revealing an answer never moves them. Each takes its
   rating's color, which is the only place on the screen those four colors appear. */
.answers { display: grid; grid-template-columns: repeat(4, minmax(0, 1fr)); gap: 0.55rem; margin-top: 1rem; }
.answers .btn { flex-direction: column; gap: 0.25rem; height: auto; min-height: 60px; padding: 0.65rem 0.4rem; }
.answers .btn .k { font-size: 15px; font-weight: 650; }
.answers .btn .iv { font-size: 13px; font-weight: 500; color: var(--muted); }
.answers .btn[data-rating="1"] .k { color: var(--again); }
.answers .btn[data-rating="2"] .k { color: var(--hard); }
.answers .btn[data-rating="3"] .k { color: var(--good); }
.answers .btn[data-rating="4"] .k { color: var(--easy); }
.answers .btn[data-rating="1"]:hover { border-color: var(--again); }
.answers .btn[data-rating="2"]:hover { border-color: var(--hard); }
.answers .btn[data-rating="3"]:hover { border-color: var(--good); }
.answers .btn[data-rating="4"]:hover { border-color: var(--easy); }
/* Keys as keycaps, one per key (developer.md, "Keys as keycaps"). Shown only where there is a
   keyboard; [hidden] has to win over the inline-flex. */
.keys { display: inline-flex; gap: 0.2em; margin-left: 0.4em; vertical-align: middle; }
.keys[hidden] { display: none; }
.keys kbd { font: 600 11.5px/1 var(--sans); padding: 0.22em 0.45em; min-width: 1.6em; text-align: center;
  border: 1px solid color-mix(in srgb, currentColor 35%, transparent); border-bottom-width: 2px;
  border-radius: 4px; background: transparent; color: inherit; opacity: 0.85; }
.progress { display: flex; flex-wrap: wrap; justify-content: center; gap: 0.3rem 1.4rem; margin: 0 0 1rem; font-size: 14px; color: var(--muted); }
.progress > span { white-space: nowrap; }
.progress b { color: var(--ink); font-weight: 650; }
.progress .round { color: var(--ink); font-weight: 650; }
.round-marker .face .q, .round-marker .face .a { margin: 0; }
.empty { text-align: center; padding: 3rem 1.2rem; color: var(--ink-2); }
.empty .big { font-family: var(--serif); font-size: 1.7rem; font-weight: 500; color: var(--ink); margin: 0 0 0.45rem;
  letter-spacing: -0.015em; line-height: 1.15; }
.empty .muted { margin: 0 auto; max-width: 44ch; }

/* Reading a card aloud, and answering out loud (speech_controller.js). Speak sits in the
   card's corner, out of the words' way: the card keeps a band at its top and bottom for it,
   and revealing the answer grows the card downward, so the button never moves. */
.face:has(> .speak:not([hidden])) { padding-top: 3.6rem; padding-bottom: 3.6rem; }
.face .speak { position: absolute; top: 0.75rem; right: 0.75rem; padding: 0 0.75rem; }
.speak .ico { display: block; flex: none; width: 1em; height: 1em; }
.tool .ico { display: block; flex: none; }
/* Speak and Stop share one cell, so the button is as wide as the wider of the two and the
   word changing never moves its edge. */
.speak-face { display: inline-grid; }
.speak-as { grid-area: 1 / 1; display: inline-flex; align-items: center; gap: 0.4rem; }
.speak .speak-off, .speak[data-state="stop"] .speak-on { visibility: hidden; }
.speak[data-state="stop"] .speak-off { visibility: visible; }
.speak .keys { margin-left: 0; }
.speak.is-speaking { color: var(--moss); border-color: color-mix(in srgb, var(--moss) 55%, transparent); }
.speak:disabled { opacity: 0.45; cursor: default; }
.speak:disabled:hover { border-color: var(--hair); }
.speak:disabled:active { transform: none; }

/* Read aloud and Answer aloud: an icon beside its name, saved the moment it is flipped. The
   icon sits in a circle, outlined while off, filled with the moss while on, exactly as the
   apps draw it (IconToggle; Derick, 2026-10-27: "the wording and the mechanism should be
   similar"). The checkbox is still there for a screen reader and the keyboard, drawn by
   nothing: the circle is the switch. Side by side, centered, on every width. */
.study-tools { display: flex; justify-content: center; flex-wrap: wrap; gap: 0.5rem 1.6rem; margin-top: 1.25rem; }
.tool { position: relative; display: inline-flex; align-items: center; gap: 0.6rem; min-height: 44px; margin: 0;
  color: var(--ink); font: 600 15px/1.2 var(--sans); letter-spacing: -0.005em; cursor: pointer; white-space: nowrap; }
.tool-icon { display: grid; place-items: center; width: 40px; height: 40px; border-radius: 50%; flex: none;
  border: 1px solid color-mix(in srgb, var(--ink) 30%, transparent); background: var(--card); color: var(--ink);
  transition: background-color 0.2s var(--ease), border-color 0.2s var(--ease), color 0.2s var(--ease); }
.tool-icon .ico { width: 18px; height: 18px; color: inherit; }
.tool:hover .tool-icon { border-color: color-mix(in srgb, var(--ink) 45%, transparent); }
.tool:has(.switch:checked) .tool-icon { background: var(--moss); border-color: var(--moss); color: var(--on-moss); }
.tool:has(.switch:checked) .tool-icon .ico { fill: currentColor; }
.tool .switch { position: absolute; inset: 0; width: 100%; height: 100%; opacity: 0; margin: 0; cursor: pointer; }
.tool:has(.switch:focus-visible) .tool-icon { outline: 2px solid var(--moss); outline-offset: 2px; }
.tool-name { white-space: nowrap; }

/* The switch is the checkbox itself, drawn: the moss when on, in both schemes, and a focus
   ring only for the keyboard (a switch flipped with the pointer hands its focus back). */
.switch { appearance: none; -webkit-appearance: none; position: relative; flex: none; width: 40px; height: 24px;
  margin: 0; padding: 0; border: 0; border-radius: 999px; cursor: pointer;
  background: color-mix(in srgb, var(--ink) 24%, var(--card)); transition: background-color 0.2s var(--ease); }
.switch::after { content: ""; position: absolute; top: 3px; left: 3px; width: 18px; height: 18px;
  border-radius: 50%; background: #ffffff; box-shadow: 0 1px 2px rgba(0, 0, 0, 0.3);
  transition: transform 0.2s var(--ease); }
.switch:checked { background: var(--moss); }
.switch:checked::after { transform: translateX(16px); }
.switch:focus { outline: none; }
.switch:focus-visible { outline: 2px solid var(--moss); outline-offset: 2px; }

/* Two lines under the switches: what the microphone is doing (while it listens, what to
   say for the side showing, beside the switch's own microphone in the moss), and under it
   what just happened ("Last card: Good"). Their room is kept when they have nothing to say
   (two lines' worth each on a phone, where the longest wraps between its clauses), so
   flipping a switch moves nothing below them. */
.voice-line { margin: 0.65rem 0 0; font-size: 14px; line-height: 1.5; color: var(--muted);
  text-align: center; text-wrap: balance; visibility: hidden; }
.voice-line.on { visibility: visible; }
.voice-line p { margin: 0; min-height: 1.5em; }
.voice-line.live .voice-say { color: var(--ink-2); }
.voice-say .ico { display: none; width: 1em; height: 1em; margin-right: 0.4em; vertical-align: -0.14em; }
.voice-line[data-mic="on"] .mic-on { display: inline-block; color: var(--moss); }
.voice-line[data-mic="off"] .mic-off { display: inline-block; }
.voice-news { color: var(--ink-2); }
.voice-line .part { display: inline-block; }
@media (max-width: 640px) { .voice-line p { min-height: 3em; } }
@media (prefers-reduced-motion: reduce) {
  .switch, .switch::after { transition: none; }
}

.two { display: grid; gap: 1rem; grid-template-columns: minmax(0, 1fr); }
@media (min-width: 760px) { .two { grid-template-columns: repeat(2, minmax(0, 1fr)); } }
/* Two things side by side share a row, so they share its height. */
.two { align-items: stretch; }
.two > * { height: 100%; min-width: 0; }

.prose { max-width: 70ch; }
.prose h2 { font-size: 1.3rem; margin: 2rem 0 0.5rem; }
.prose p, .prose li { color: var(--ink-2); }
.prose strong { color: var(--ink); }

/* the auth card's lede and links ------------------------------------------------------- */
.auth-lede { color: var(--ink-2); font-size: 15px; margin: 0 0 1.3rem; }
.auth-foot { margin: 1rem 0 0; font-size: 14.5px; color: var(--ink-2); }
.auth-foot.small { font-size: 13.5px; color: var(--muted); }
/* The show/hide toggle on a password field (the form-controls skill). */
.pw-row { position: relative; }
.pw-row input { width: 100%; padding-right: 2.9rem; }
.pw-eye { position: absolute; right: 0.15rem; top: 50%; transform: translateY(-50%); min-width: 44px; min-height: 44px;
  border: 0; background: transparent; cursor: pointer; color: var(--muted); display: grid; place-items: center; padding: 0; }
.pw-eye:hover { color: var(--ink); }
.pw-eye [hidden] { display: none; }

/* the dev sign-in block ----------------------------------------------------------------- */
/* Settled design, ported from Rostr. It is additive: it lives OUTSIDE the sign-in card as a
   sibling, so it cannot widen it or move it a pixel. One column definition drives the header
   and every row so the labels sit over their columns. */
.auth-wrap { max-width: 25rem; margin: 2.5rem auto 0; }
.dev-cast { --dev-col: 8rem; --dev-gap: 0.9rem; --dev-pad: 0.7rem;
  --tape-angle: 45deg; --tape-band: 14px;
  --dev-stripe: color-mix(in srgb, var(--moss) 14%, transparent);
  max-width: 34rem; margin: 1.25rem auto 1rem; overflow: hidden;
  background: var(--card); border: 2px dashed color-mix(in srgb, var(--moss) 55%, transparent);
  border-radius: 12px; }
.dev-cast-tape { display: flex; align-items: center; justify-content: center; margin: 0;
  padding: 0.4rem 0.7rem; font-size: 0.74rem; font-weight: 700; letter-spacing: 0.1em;
  line-height: 1; text-transform: uppercase;
  background: repeating-linear-gradient(var(--tape-angle),
    var(--dev-stripe) 0 calc(var(--tape-band) / 2),
    transparent calc(var(--tape-band) / 2) var(--tape-band)); }
.dev-cast-tape-top { border-bottom: 2px dashed color-mix(in srgb, var(--moss) 45%, transparent); }
.dev-cast-tape-bottom { border-top: 2px dashed color-mix(in srgb, var(--moss) 45%, transparent); }
.dev-cast-tape-label { display: inline-flex; align-items: center; gap: 0.55rem;
  padding: 0.5rem 1.15rem; background: var(--card); color: var(--moss);
  border-left: 2px dashed color-mix(in srgb, var(--moss) 45%, transparent);
  border-right: 2px dashed color-mix(in srgb, var(--moss) 45%, transparent); }
.dev-cast-tape-label::before, .dev-cast-tape-label::after { content: ""; flex: none;
  width: 0.4rem; height: 0.4rem; border-radius: 50%; background: var(--moss); }
.dev-cast-body { padding: 0.45rem; }
.dev-cast-head, .dev-cast-row { display: grid;
  grid-template-columns: var(--dev-col) 1fr 1.25rem; column-gap: var(--dev-gap);
  width: 100%; padding: 0.6rem var(--dev-pad); border: 1px solid transparent;
  border-radius: 8px; background: transparent; text-align: left; font: inherit; color: inherit; }
.dev-cast-head { align-items: center; cursor: pointer; }
.dev-cast-row { align-items: start; cursor: pointer; }
.dev-cast-head:hover, .dev-cast-row:hover { background: var(--card-2); border-color: var(--hair); }
.dev-cast-head-title { font-size: 0.88rem; font-weight: 650; color: var(--ink); }
.dev-cast-head-sub { min-width: 0; font-size: 0.8rem; color: var(--ink-2);
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.dev-cast-chevron { display: grid; place-items: center; color: var(--ink-2); }
.dev-cast-chevron svg { display: block; transition: transform 180ms var(--ease); }
.dev-cast.is-collapsed .dev-cast-chevron svg { transform: rotate(180deg); }
/* 0fr to 1fr is the only way to transition to a content-determined height without measuring
   it in JavaScript; the child needs min-height:0 to shrink. */
.dev-cast-panel { display: grid; grid-template-rows: 1fr;
  transition: grid-template-rows 180ms ease, opacity 140ms ease, visibility 0s; }
.dev-cast-panel > .dev-cast-table { min-height: 0; overflow: hidden; }
.dev-cast.is-collapsed .dev-cast-panel { grid-template-rows: 0fr; opacity: 0; visibility: hidden;
  transition: grid-template-rows 180ms ease, opacity 140ms ease, visibility 0s 180ms; }
.dev-cast-table { display: grid; gap: 0.3rem; padding-top: 0.4rem; border-top: 1px solid var(--hair); }
.dev-cast-who { display: grid; gap: 0.1rem; min-width: 0; }
.dev-cast-name { font-family: var(--serif); font-weight: 500; font-size: 1rem; color: var(--ink); }
.dev-cast-role { font-size: 0.76rem; color: var(--ink-2); text-wrap: pretty; }
/* The address, so a task that says "sign in as Maya" is answerable by reading the block
   instead of guessing a local part. */
.dev-cast-email { font-size: 12px; line-height: 1.3; color: var(--muted); overflow-wrap: anywhere; }
.dev-cast-blurb { font-size: 0.84rem; line-height: 1.45; color: var(--ink-2); text-wrap: pretty; }
.dev-cast-group { grid-column: 1 / -1; font: 650 11.5px/1 var(--sans); letter-spacing: 0.04em;
  color: var(--muted); padding: 0.75rem var(--dev-pad) 0.1rem; }
@media (prefers-reduced-motion: reduce) {
  .dev-cast-panel, .dev-cast-chevron svg { transition: none; } }
@media (max-width: 32rem) {
  .dev-cast-head, .dev-cast-row { grid-template-columns: 1fr 1.25rem; }
  .dev-cast-row { row-gap: 0.25rem; }
  .dev-cast-blurb { grid-column: 1 / -1; }
  .dev-cast-who { grid-auto-flow: row; } }

/* file input: the browser's own button, brought into the app's language */
input[type=file] { width: 100%; font: inherit; font-size: 15px; color: var(--ink-2);
  padding: 0.6rem 0.65rem; border: 1px dashed color-mix(in srgb, var(--ink) 28%, transparent); border-radius: 8px;
  background: var(--card-2); }
input[type=file]::file-selector-button { font: 600 14px/1 var(--sans); margin-right: 0.75rem; padding: 0.55rem 0.85rem;
  border-radius: 6px; border: 1px solid var(--hair); background: var(--card); color: var(--ink); cursor: pointer; }
input[type=file]::file-selector-button:hover { border-color: color-mix(in srgb, var(--ink) 45%, transparent); }

/* cloze and image occlusion ------------------------------------------------------------- */
/* The blank a cloze card shows, and the answer it reveals, both read as the thing the card is
   about rather than as punctuation in a sentence. */
.face .q, .face .a { white-space: pre-wrap; }
.occl-wrap { width: 100%; }
/* A picture an imported Anki note carried, under its text. */
/* As large as the screen allows without pushing Show Answer off it: a chord diagram has to be
   read exactly (Derick, p58, "slightly bigger without making them less usable"). */
.card-pic { display: block; max-width: 100%; max-height: min(27rem, 46dvh); height: auto; border-radius: 8px; object-fit: contain; }
.card-pic[hidden] { display: none; }
/* A Toon Words card under the answer: the cartoon (4:3, edge to edge, the art draws no frame),
   then the sound-alike in small capitals, the caption in the card face, and a quiet credit. */
.toon { margin: 1.4rem 0 0; display: grid; gap: 0.6rem; justify-items: center; }
.toon[hidden] { display: none; }
.toon-pic { display: block; width: 100%; max-width: 30rem; height: auto; object-fit: contain; border-radius: 8px; }
.toon figcaption { display: grid; gap: 0.2rem; justify-items: center; text-align: center; max-width: 30rem; }
.toon-link { font-size: 12.5px; font-weight: 600; letter-spacing: 0.06em; text-transform: uppercase; color: var(--muted); }
.toon-caption { font-family: var(--serif); font-size: 1.05rem; color: var(--ink); text-wrap: balance; }
.toon-credit { font-size: 12.5px; color: var(--muted); }
.occl { position: relative; width: 100%; border-radius: 10px; overflow: hidden; background: var(--card-2); }
.occl img { display: block; width: 100%; height: 100%; object-fit: contain; }
/* The masks ride on top in the image's own coordinate space, so one set of fractions draws
   correctly at every size on every surface. */
.occl svg { position: absolute; inset: 0; width: 100%; height: 100%; }

/* note type picker: one row of keys, not three competing buttons */
.typebar { display: flex; gap: 0.3rem; margin: 0.9rem 0 1.2rem; padding: 0.25rem; background: var(--desk);
  border: 1px solid var(--hair); border-radius: 9px; width: fit-content; max-width: 100%; flex-wrap: wrap; }
.typebtn { padding: 0.5rem 0.85rem; border-radius: 6px; font-size: 14.5px; font-weight: 600;
  color: var(--ink-2); text-decoration: none; white-space: nowrap; }
.typebtn:hover { color: var(--ink); }
.typebtn.on { background: var(--card); color: var(--ink); box-shadow: 0 0 0 1px var(--hair); }

/* cloze editor: every card the note will make, because "three cards" is a number and three
   questions is a decision */
.cloze-preview { display: grid; gap: 0.35rem; margin: 0 0 1rem; }
.cloze-preview:empty { margin: 0; }
.cloze-card { display: grid; grid-template-columns: 2.4rem minmax(0, 1fr); gap: 0.6rem; align-items: baseline;
  background: var(--card-2); border: 1px solid var(--hair); border-radius: 8px; padding: 0.55rem 0.75rem; }
.cloze-ord { font: 650 12.5px/1.4 var(--sans); color: var(--moss); }
.cloze-q { font-family: var(--serif); font-size: 15px; color: var(--ink-2); white-space: pre-wrap; overflow-wrap: break-word; }
/* Buttons of different lengths in one group, at one width: side by side they share the row
   equally, and when the longest does not fit they stack, each the full width. */
.same-w { display: flex; flex-wrap: wrap; gap: 0.6rem; }
.same-w > form, .same-w > .btn { flex: 1 1 0; min-width: max-content; margin: 0; }
.same-w .btn { width: 100%; }
/* Two buttons that sit together share one width (catalog A1): the wider label sets it, set
   once here rather than per page. */
.pair { flex-wrap: wrap; }
.pair > .btn, .pair > form > .btn { min-width: 7.5rem; }
.pair > .btn.small { min-width: 7.75rem; }
/* The cloze buttons keep their labels whole; the hint drops under them. */
.cloze-tools { flex-wrap: wrap; }

/* the occlusion editor */
.occl-edit { position: relative; cursor: crosshair; border: 1px solid var(--hair); margin: 0 0 0.3rem;
  touch-action: none; user-select: none; }
.occl-edit img { display: block; width: 100%; }
.occl-edit svg rect { fill: var(--moss); opacity: 0.8; cursor: pointer; }
.occl-edit svg rect:hover { opacity: 0.95; }

/* settings ------------------------------------------------------------------------------ */
.statline { display: flex; align-items: center; gap: 0.75rem; flex-wrap: wrap; }
.meter { height: 8px; border-radius: 99px; background: var(--card-2); border: 1px solid var(--hair); overflow: hidden; }
/* display:block: the statistics page draws the fill as a <span>, and an inline span ignores
   width and height, so every bar there was empty. */
.meter-fill { display: block; height: 100%; background: var(--moss); border-radius: 99px;
  transition: width 0.5s var(--ease); }
.meter-fill.r-again { background: var(--again); }
.meter-fill.r-hard { background: var(--hard); }
.meter-fill.r-good { background: var(--good); }
.meter-fill.r-easy { background: var(--easy); }
/* A count, not a sentence: as wide as four digits and the stepper. */
input.input-num { width: 7rem; flex: none; }
input[type=range] { flex: 1; min-width: 10rem; accent-color: var(--moss); height: auto; padding: 0; border: none; background: none; }
output { font: 650 16px/1 var(--sans); min-width: 3.2rem; color: var(--ink); }

/* Devices: one row per device, on columns, so every Sign Out shares an edge. */
.device-list { display: grid; gap: 0.15rem; }
.device-row { display: grid; grid-template-columns: minmax(0, 1fr) 5.5rem 9.5rem 6.5rem; align-items: center;
  gap: 0.75rem; padding: 0.45rem 0; border-top: 1px solid var(--hair-soft); }
.device-row:first-child { border-top: 0; }
.device-name { min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; font-weight: 600; }
.device-row form { margin: 0; }
.device-row .btn { width: 100%; }
@media (max-width: 520px) {
  .device-row { grid-template-columns: minmax(0, 1fr) 6.5rem; }
  .device-row > span:nth-child(2), .device-row > span:nth-child(3) { display: none; }
}
/* Settings, Export: one row a deck, the way Devices lists one a device. A long list scrolls in
   its own box rather than pushing Devices a page away. */
.export-list { display: grid; gap: 0.15rem; max-height: 18rem; overflow-y: auto; }
.export-row { display: grid; grid-template-columns: minmax(0, 1fr) 6.5rem 6.5rem; align-items: center;
  gap: 0.75rem; padding: 0.45rem 0; border-top: 1px solid var(--hair-soft); }
.export-row:first-child { border-top: 0; }
.export-name { min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; font-weight: 600; }
.export-row .btn { width: 100%; }
@media (max-width: 520px) { .export-row { grid-template-columns: minmax(0, 1fr) 6.5rem; } .export-row > span:nth-child(2) { display: none; } }
.confirm-row { display: grid; grid-template-columns: minmax(0, 1fr) auto; gap: 0.6rem; }
@media (max-width: 520px) { .confirm-row { grid-template-columns: 1fr; } }

/* statistics ---------------------------------------------------------------------------- */
.tiles { display: grid; gap: 0.75rem; grid-template-columns: repeat(2, minmax(0, 1fr)); margin: 0 0 1rem; }
@media (min-width: 760px) { .tiles { grid-template-columns: repeat(4, minmax(0, 1fr)); } }
.tile { background: var(--card); border: 1px solid var(--hair); border-radius: var(--radius);
  padding: 1rem 1.1rem; display: flex; flex-direction: column; gap: 0.2rem; min-width: 0; }
.tile-n { font: 500 2rem/1.05 var(--serif); letter-spacing: -0.02em; font-variant-numeric: tabular-nums lining-nums; }
.tile-l { font-size: 14px; color: var(--ink-2); }

/* The heatmap. Both axes scroll inside ONE container: putting overflow-x on a wrapper would
   make the other axis a scroll container too, and a sticky heading would end up stuck to a
   box that never scrolls vertically. */
.hmwrap { overflow: auto; padding: 2px 3px 0.2rem 0; }
/* As wide as the panel, so a year fills a laptop's page and half a year is drawn large; narrower
   than its 11px minimum, it scrolls sideways. */
.hmwrap { container-type: inline-size; }
/* A row is as tall as a week's column is wide (square cells for a year), up to 30px, so eight
   weeks fill the width as wide short blocks rather than towering squares. */
.hmgrid { display: grid; gap: 3px; width: 100%; min-width: min-content; justify-content: start;
  grid-template-rows: auto;
  grid-auto-rows: max(11px, min(30px, calc((100cqi - 2.2rem - var(--weeks, 53) * 3px) / var(--weeks, 53)))); }
.hmgrid > .hm-month { height: auto; }
.hm-corner { position: sticky; top: 0; left: 0; z-index: 3; background: var(--card); }
/* A month's label sits on the week it starts and runs on over the next, which is blank. No
   background, or the next week's empty label would cover its last letters. */
.hm-month { position: sticky; top: 0; z-index: 2; white-space: nowrap; overflow: visible;
  font: 600 10px/11px var(--sans); color: var(--muted); }
.hmgrid [hidden] { display: none; }
.hm-day { position: sticky; left: 0; z-index: 2; background: var(--card); align-self: center;
  font: 600 10px/11px var(--sans); color: var(--muted); padding-right: 0.3rem; }
.hm { display: block; width: 100%; height: 100%; border-radius: 3px; background: var(--card-2); }
.hm-0 { background: color-mix(in srgb, var(--ink) 7%, var(--card)); }
.hm-1 { background: color-mix(in srgb, var(--moss) 30%, var(--card)); }
.hm-2 { background: color-mix(in srgb, var(--moss) 52%, var(--card)); }
.hm-3 { background: color-mix(in srgb, var(--moss) 76%, var(--card)); }
.hm-4 { background: var(--moss); }
/* Outside the year is a different blank from a day you did not study. */
.hm-out { background: transparent; border: 1px dashed var(--hair); }
.hm-today { outline: 1.5px solid var(--ink); outline-offset: 1px; }
.legend { display: flex; align-items: center; gap: 3px; }
.legend .hm { width: 11px; }
.legend .hm { width: 10px; height: 10px; }
.legend-l { font-size: 12.5px; color: var(--muted); margin: 0 0.3rem; }

/* The week ahead: seven days, each a stack of the answers its cards last got, new cards at the bottom.
   NOT `.bar` (the nav bar). */
.wk { display: grid; grid-template-columns: repeat(7, minmax(0, 1fr)); gap: 0.6rem; align-items: end; }
.wk-day { display: grid; grid-template-columns: minmax(0, 1fr); grid-template-rows: auto 180px auto auto; gap: 0.3rem; min-width: 0; text-align: center; }
.wk-n { font-size: 15px; font-weight: 600; color: var(--ink); font-variant-numeric: tabular-nums; }
.wk-stack { display: flex; flex-direction: column; justify-content: flex-end; width: min(100%, 4.5rem); height: 180px; margin: 0 auto;
  border-bottom: 1px solid var(--hair); }
.wk-seg { display: block; width: 100%; min-height: 3px; }
.wk-seg:first-child { border-radius: 4px 4px 0 0; }
.wk-seg.r-again, .wk-key.r-again { background: var(--again); }
.wk-seg.r-hard, .wk-key.r-hard { background: var(--hard); }
.wk-seg.r-good, .wk-key.r-good { background: var(--good); }
.wk-seg.r-easy, .wk-key.r-easy { background: var(--easy); }
.wk-new { background: color-mix(in srgb, var(--moss) 14%, var(--card)); box-shadow: inset 0 0 0 1px color-mix(in srgb, var(--moss) 55%, var(--card)); }
.wk-label { font-size: 14px; font-weight: 600; color: var(--ink); }
.wk-date { font-size: 12.5px; color: var(--muted); white-space: nowrap; }
.wk-legend { display: flex; flex-wrap: wrap; gap: 0.4rem 1rem; margin: 0.9rem 0 0; font-size: 13px; color: var(--muted); }
.wk-legend span { display: inline-flex; align-items: center; gap: 0.4rem; white-space: nowrap; }
.wk-key { display: inline-block; width: 0.8rem; height: 0.8rem; border-radius: 2px; }
/* The collection by how well it is known. */
.mbar { display: flex; height: 14px; border-radius: 7px; overflow: hidden; background: var(--hair); }
.mbar-seg { display: block; height: 100%; }
.mbar-mature { background: var(--moss); }
.mbar-young { background: color-mix(in srgb, var(--moss) 45%, var(--card)); }
.mbar-new { background: color-mix(in srgb, var(--ink) 14%, var(--card)); }
@media (max-width: 560px) {
  .wk { gap: 0.3rem; }
  .wk-day { grid-template-rows: auto 140px auto auto; }
  .wk-stack { height: 140px; }
  .wk-date { font-size: 11px; }
}

/* The forecast. NOT `.bar`: that was the nav bar, and a later rule of the same name once
   repainted the whole app's header. */
.fbars { display: flex; align-items: flex-end; gap: 2px; height: 110px;
  border-bottom: 1px solid var(--hair); padding-bottom: 1px; }
.fcol { flex: 1; display: flex; flex-direction: column; justify-content: flex-end; height: 100%; min-width: 0; }
.fbar { background: color-mix(in srgb, var(--moss) 45%, var(--card)); border-radius: 2px 2px 0 0; min-height: 0; }
.fcol:hover .fbar:not(.fbar-new), .fbar-today { background: var(--moss); }
/* New cards sit on top of the reviews, in the outline of the same green: work that will
   arrive, not work already scheduled. */
.fbar-new { background: color-mix(in srgb, var(--moss) 14%, var(--card)); box-shadow: inset 0 0 0 1px color-mix(in srgb, var(--moss) 55%, var(--card)); }
.fbar-new + .fbar { border-radius: 0; }
.flegend { display: flex; align-items: center; gap: 0.45rem; margin: 0.5rem 0 0; font-size: 12.5px; color: var(--muted); }
.fkey { display: inline-block; width: 0.8rem; height: 0.8rem; border-radius: 2px; background: color-mix(in srgb, var(--moss) 45%, var(--card)); }
.fkey-new { margin-left: 0.6rem; background: color-mix(in srgb, var(--moss) 14%, var(--card)); box-shadow: inset 0 0 0 1px color-mix(in srgb, var(--moss) 55%, var(--card)); }
.fbarlabels { display: flex; justify-content: space-between; margin-top: 0.4rem; font-size: 12.5px; color: var(--muted); }

/* `.nw` was used on the statistics page and on the deck table and was never defined anywhere,
   so "7 days over" broke across two lines inside a column that had room for it. A wrap nobody
   asked for is a layout bug. */
.nw { white-space: nowrap; }

/* the deck page: the deck's own facts, its composition, and a sortable card table ------------ */
.deckbar { display: flex; flex-wrap: wrap; align-items: baseline; gap: 0.35rem 1.5rem;
  margin: 0 0 0.85rem; font-size: 15px; color: var(--ink-2); }
.deckbar .fact b { font-weight: 650; color: var(--ink); }
.deckbar .fact.hot b { color: var(--moss); }

/* Composition as one bar. Four counts in a row read as four numbers; a bar reads as a shape,
   which is the thing you are actually asking about. */
.mixbar { display: flex; height: 8px; border-radius: 999px; overflow: hidden; background: var(--hair-soft); gap: 2px; }
.mix { display: block; border-radius: 999px; }
.mix-new { background: color-mix(in srgb, var(--ink) 30%, var(--card)); }
.mix-learning { background: var(--hard); }
.mix-young { background: color-mix(in srgb, var(--moss) 50%, var(--card)); }
.mix-mature { background: var(--moss); }
.mixkey { display: flex; flex-wrap: wrap; gap: 0.25rem 1.2rem; margin: 0.55rem 0 0; }
.mixkey-item { display: inline-flex; align-items: center; gap: 0.4rem; white-space: nowrap; }
.mixkey-item .mix { width: 9px; height: 9px; flex: 0 0 auto; }

/* Sorting lives on the heading, and the active column has to look active or the current order
   is something you have to remember rather than read. */
.sorth { display: inline-flex; align-items: center; gap: 0.25rem; color: inherit; text-decoration: none; white-space: nowrap; }
.sorth:hover { color: var(--ink); }
.sorth.on { color: var(--ink); font-weight: 700; }
.sorth-caret { font-size: 1em; }

table.cards td { vertical-align: baseline; }
table.cards td:first-child { font-family: var(--serif); font-size: 15.5px; color: var(--ink); text-wrap: pretty; }
table.cards .answer { display: block; font-family: var(--sans); color: var(--muted); font-size: 13.5px;
  margin-top: 0.15rem; overflow-wrap: break-word; text-wrap: pretty; }
.ord { display: inline-block; margin-left: 0.35rem; padding: 0 0.35rem; border-radius: 4px;
  background: var(--card-2); border: 1px solid var(--hair);
  font: 600 11.5px/1.5 var(--sans); color: var(--muted); vertical-align: 1px; }
.over { color: var(--ink); font-weight: 650; }
.pip { display: inline-block; width: 8px; height: 8px; border-radius: 50%; margin-right: 0.4rem; vertical-align: 0; }
.pip-new { background: color-mix(in srgb, var(--ink) 30%, var(--card)); }
.pip-learning { background: var(--hard); }
.pip-young { background: color-mix(in srgb, var(--moss) 50%, var(--card)); }
.pip-mature { background: var(--moss); }

/* The card table's pages, and the deck's actions on one width. */
.pager { display: grid; grid-template-columns: 1fr auto 1fr; align-items: center; gap: 0.75rem; margin: 0.8rem 0 0; }
.pager > :last-child { justify-self: end; }
.deck-actions { --action-w: 12.5rem; display: grid; grid-template-columns: repeat(3, var(--action-w)) 1fr;
  gap: 0.75rem; align-items: center; margin-top: 1.6rem; }
.deck-actions form { margin: 0; }
.deck-actions .btn { width: var(--action-w); }
.deck-actions .deck-delete { grid-column: -2 / -1; justify-self: end; }
/* Four actions are wider than a tablet, so the three share the row and Delete Deck takes one of its own. */
@media (max-width: 900px) {
  .deck-actions { grid-template-columns: repeat(3, minmax(0, 1fr)); }
  .deck-actions .btn { width: 100%; }
  .deck-actions .deck-delete { grid-column: 1 / -1; justify-self: stretch; }
}
@media (max-width: 640px) {
  .deck-actions { grid-template-columns: 1fr; }
  .deck-actions .deck-delete { grid-column: auto; justify-self: stretch; }
  .deck-actions .btn { width: 100%; }
}
/* Reviews, Lapses and Strength give way on a phone, so Card, Due and Edit fit without the
   table scrolling sideways. */
@media (max-width: 640px) { .hide-sm { display: none; } }

/* On a phone the table is wider than the screen. It scrolls in its own box, never by making
   the page scroll sideways. */
@media (max-width: 760px) {
  .deckbar { gap: 0.2rem 1rem; font-size: 14px; }
  table.cards { min-width: 0; }
  .panel:has(table.cards) { overflow-x: auto; }
}

/* getting started, and the tours -------------------------------------------------------- */
.start { border: 1px solid var(--hair); border-radius: var(--radius); background: var(--card);
  padding: 1.2rem 1.3rem; margin: 0 0 1.4rem; }
.start-head { margin: 0 0 1rem; display: grid; grid-template-columns: minmax(0, 1fr) auto auto;
  grid-template-areas: "title count close"; align-items: center; gap: 0.75rem; }
.start-head h2 { grid-area: title; margin: 0; }
.start-head .faint { grid-area: count; }
.start-close-form { grid-area: close; margin: 0; }
/* The close: a stroke cross with a 44px target (a week out of the way, the third for good). */
.start-close { display: inline-grid; place-items: center; width: 44px; height: 44px; margin: -10px -12px -10px 0;
  border: 0; border-radius: 8px; background: none; color: var(--muted); cursor: pointer; }
.start-close:hover, .start-close:focus-visible { color: var(--ink); background: color-mix(in srgb, var(--ink) 7%, transparent); }
.start-close svg { width: 20px; height: 20px; }
.start-head h2 { font-size: 1.3rem; }
.start-head .faint { font-size: 14px; }
.startgrid { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 1rem 1.5rem; }
.startstep { display: flex; gap: 0.75rem; align-items: flex-start; min-width: 0; }
.startstep-t { margin: 0 0 0.15rem; font-weight: 650; font-size: 15.5px; color: var(--ink); }
.startstep-b { margin: 0 0 0.55rem; font-size: 14.5px; color: var(--ink-2); line-height: 1.5; }
.startstep.done .startstep-t, .startstep.done .startstep-b { color: var(--muted); }
.startstep.done .startstep-t { text-decoration: line-through; text-decoration-thickness: 1px; }
.tick { flex: 0 0 auto; display: inline-flex; align-items: center; justify-content: center;
  width: 22px; height: 22px; border-radius: 6px; border: 1.5px solid var(--hair); color: var(--on-moss);
  margin-top: 0.1rem; }
.tick svg { width: 14px; height: 14px; }
.startstep.done .tick { background: var(--moss); border-color: var(--moss); }
/* Four steps in two columns are 2+2 at every width this grid is used at. One column below the
   breakpoint, never a ragged last row. */
@media (max-width: 760px) { .startgrid { grid-template-columns: 1fr; } }
/* On a phone the count goes under the heading, so the heading keeps the width it needs. */
@media (max-width: 560px) { .start-head { grid-template-columns: minmax(0, 1fr) auto; grid-template-areas: "title close" "count count"; gap: 0.3rem; } }

.tour { position: fixed; inset: 0; z-index: 200; }
.tour-veil { position: absolute; inset: 0; background: rgba(12, 14, 16, 0.55); }
/* The cutout is a transparent box with an enormous shadow, which is the one way to punch a
   hole that works without an SVG mask and without the page underneath losing its stacking. */
.tour-hole { position: absolute; border-radius: 10px; pointer-events: none;
  box-shadow: 0 0 0 9999px rgba(12, 14, 16, 0.55); outline: 2px solid var(--moss);
  transition: top 0.25s var(--ease), left 0.25s var(--ease), width 0.25s var(--ease), height 0.25s var(--ease); }
.tour-card { position: absolute; width: min(23rem, calc(100vw - 24px)); background: var(--card);
  border: 1px solid var(--hair); border-radius: 12px; padding: 1.05rem 1.15rem; box-shadow: var(--lift); }
.tour-kicker { margin: 0 0 0.35rem; font: 600 13px/1.4 var(--sans); color: var(--muted); }
.tour-card h3 { margin: 0 0 0.35rem; font-size: 1.15rem; }
.tour-card p[data-tour-target="body"] { margin: 0; font-size: 15px; color: var(--ink-2); line-height: 1.5; }
.tour-actions { display: flex; align-items: center; gap: 0.5rem; margin-top: 1rem; }
@media (prefers-reduced-motion: reduce) {
  .tour-hole { transition: none; }
  .face .a:not([hidden]) { animation: none; }
}

/* headings inside a card, and between cards */
.panel-h { font-size: 1.3rem; line-height: 1.2; margin: 0 0 0.45rem; }
.sub-h { font-size: 1.35rem; line-height: 1.2; margin: 2.2rem 0 0.75rem; }
.stamp { font-size: 15px; color: var(--ink-2); }
.typebar { margin-top: 0; }

/* Settings: the five panels in one column, and a rail that names them beside it. */
.settings { display: grid; grid-template-columns: minmax(0, 11rem) minmax(0, 46rem); gap: 1rem clamp(2rem, 5vw, 4rem); align-items: start; }
.settings > * { min-width: 0; }
.settings .panel { margin-bottom: 1rem; }
.settings .panel:last-child { margin-bottom: 0; }
.rail { position: sticky; top: 6rem; display: grid; gap: 0.15rem; padding-top: 0.35rem; margin-left: -0.7rem; }
.rail a { padding: 0.45rem 0.7rem; border-radius: 7px; color: var(--ink-2); text-decoration: none; font-size: 15px; font-weight: 550; }
.rail a:hover { background: var(--card); color: var(--ink); }
.rail a.danger { color: var(--again); }
.settings .panel { scroll-margin-top: 6rem; }
@media (max-width: 900px) { .settings { grid-template-columns: minmax(0, 1fr); } .rail { display: none; } }

/* Two panels side by side: the form, and what it is safe to do with it. */
.side-by { display: grid; grid-template-columns: minmax(0, 1.2fr) minmax(0, 0.8fr); gap: 1rem; align-items: stretch; }
.side-by > * { min-width: 0; }
@media (max-width: 900px) { .side-by { grid-template-columns: minmax(0, 1fr); } }

/* Each deck's work, in the statistics' Your decks panel. A long name ends in an ellipsis rather
   than wrapping its row (the full name is the link's title). */
.decks-at { margin-top: 0.6rem; }
.decks-at td:first-child { max-width: 0; width: 100%; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.decks-at th.num, .decks-at td.num { text-align: right; white-space: nowrap; font-variant-numeric: tabular-nums; width: 1%; padding-left: 1rem; }
@media (max-width: 560px) { .decks-at th.num, .decks-at td.num { padding-left: 0.5rem; } .decks-at td, .decks-at th { padding-left: 0.4rem; padding-right: 0.4rem; } }

/* The picture card editor's count ("2 cards. Click a box to remove it.") keeps its line from the
   start, so the picture does not jump down under the pointer as the first box is drawn. */
.occl-status { min-height: 1.5em; }

/* Today's priorities, above the decks: one session that mixes the decks marked first or after. */
.priorities { display: grid; grid-template-columns: minmax(0, 1fr) auto; align-items: center; gap: 0.2rem 1rem;
  margin: 0 0 1rem; text-decoration: none; color: var(--ink); border-left: 4px solid var(--moss); }
.priorities-h { font-family: var(--serif); font-size: 1.25rem; font-weight: 600; }
.priorities-d { grid-column: 1; color: var(--muted); font-size: 14px; text-wrap: pretty; }
.priorities .btn { grid-column: 2; grid-row: 1 / span 2; }
.pri { display: inline-block; margin-left: 0.3rem; padding: 0 0.4rem; border-radius: 4px; vertical-align: 2px;
  font: 600 11.5px/1.6 var(--sans); color: var(--moss); background: color-mix(in srgb, var(--moss) 12%, var(--card)); white-space: nowrap; }
/* The deck a card in Today's priorities came from, quiet above the question. */
.study .from { margin: 0 0 0.6rem; text-align: center; font-size: 12.5px; font-weight: 600; letter-spacing: 0.04em;
  text-transform: uppercase; color: var(--muted); }
.study .from[hidden] { display: none; }

/* Report a Problem under the study card, and the admin's live boxes (report_controller.js,
   livefix_controller.js). Quiet: a link-sized button, a status line in the card's own colors. */
.report { margin: 1.1rem auto 0; max-width: 40rem; text-align: center; }
.linkish { background: none; border: 0; padding: 0.5rem 0.25rem; min-height: 44px; color: var(--muted); font: inherit;
  text-decoration: underline; text-underline-offset: 3px; text-decoration-color: color-mix(in srgb, var(--muted) 45%, transparent); cursor: pointer; }
.linkish:hover { color: var(--ink); }
.report-form { display: grid; gap: 0.45rem; text-align: left; margin-top: 0.4rem; }
.btn-row { display: flex; flex-wrap: wrap; gap: 0.5rem; }
.report-status, .problem-status { margin: 0 0 0.3rem; font-size: 14px; color: var(--ink-2); }
.is-fixed { color: var(--moss); }
.is-dismissed { color: var(--ink-2); }
.report-sent { color: var(--moss); margin: 0.4rem 0 0; }
.livefix { display: grid; gap: 0.55rem; margin: 0.8rem 0; }
.study-fix { max-width: 40rem; margin: 1.4rem auto 0; padding: 0.9rem 1rem; border: 1px dashed var(--hair); border-radius: var(--radius); }
.livefix-head { margin: 0; color: var(--muted); font-weight: 600; }
.livefix-row { display: grid; gap: 0.2rem; }
.livefix-name { font-size: 13px; font-weight: 600; color: var(--ink-2); }
.livefix textarea { min-height: 2.6rem; field-sizing: content; width: 100%; box-sizing: border-box; }
.check-card { min-width: 0; }
.livefix-status { margin: 0; min-height: 1.3em; color: var(--muted); }
.problems { display: grid; gap: 0.9rem; }
.problem p { margin: 0 0 0.35rem; }
.problem-said { color: var(--ink); }
.problem-reply { color: var(--ink-2); }
.problem-answer { display: grid; gap: 0.45rem; margin-top: 0.6rem; }
.problem.answered { opacity: 0.85; }
.is-approved { color: var(--ink); }

/* Check Cards (Admin::CardChecksController): one card a panel, its state in the head. */
.check-decks { display: flex; flex-wrap: wrap; gap: 0.5rem; margin: 0 0 1rem; }
.check-decks .chip { display: inline-flex; gap: 0.4rem; align-items: center; min-height: 44px; padding: 0 0.9rem;
  border: 1px solid var(--hair); border-radius: 999px; color: var(--ink); text-decoration: none; background: var(--card); }
.check-decks .chip.is-on { border-color: var(--moss); box-shadow: inset 0 0 0 1px var(--moss); }
.check-approve { margin: 0 0 1rem; }
.check-approve-row { display: grid; grid-template-columns: minmax(0, 1fr) auto; gap: 0.5rem 1rem; align-items: center; padding: 0.4rem 0; border-top: 1px solid var(--line, rgba(128,128,128,.25)); }
.check-approve-row p { margin: 0; }
.check-approve-row form { margin: 0; }
.check-summary { margin: 0 0 1rem; color: var(--ink-2); }
.check-queue { display: block; margin-top: 0.2rem; }
.checks { display: grid; gap: 0.9rem; }
.check-card { border-left: 4px solid var(--hair); }
/* At a glance: a checked card is green all over, a card with a note amber. */
.check-card.is-checked { border-left-color: var(--moss); background: color-mix(in srgb, var(--moss) 9%, var(--card)); }
.check-card.is-needs, .check-card.is-stale { border-left-color: var(--hard); background: color-mix(in srgb, var(--hard) 9%, var(--card)); }
.check-head .check-state { font-weight: 600; }
.is-needs .check-state, .is-stale .check-state { color: var(--hard); }
.btn.is-chosen { background: var(--moss); border-color: var(--moss); color: var(--on-moss); }
.check-filter { display: flex; flex-wrap: wrap; gap: 0.5rem; margin: 0 0 1rem; }
.check-filter .chip { display: inline-flex; align-items: center; min-height: 44px; padding: 0 0.9rem; border: 1px solid var(--hair); border-radius: 999px; text-decoration: none; color: var(--ink); background: var(--card); }
.check-filter .chip.is-on { border-color: var(--moss); box-shadow: inset 0 0 0 1px var(--moss); }
.check-toon-row { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: 0.5rem; }
.check-toon-row form { margin: 0; }
.check-toon-row .btn { width: 100%; min-width: 0; padding-inline: 0.4rem; }
.check-note { display: grid; gap: 0.35rem; margin: 0.4rem 0 0.7rem; }
.check-note textarea { width: 100%; box-sizing: border-box; }
.check-note p { margin: 0; min-height: 1.2em; }
.check-head { display: flex; flex-wrap: wrap; justify-content: space-between; gap: 0.3rem 1rem; font-size: 14px; }
.check-num { font-weight: 600; }
.check-state { color: var(--ink-2); }
.is-checked .check-state { color: var(--moss); }
.check-said, .check-decided { margin: 0 0 0.5rem; color: var(--ink); }
.check-toon { display: grid; grid-template-columns: minmax(0, 1fr); gap: 0.45rem; margin: 0.6rem 0 0.9rem; padding: 0.8rem 0.9rem; border: 1px dashed var(--hair); border-radius: var(--radius); }
.check-toon p { margin: 0; }
.check-toon-pic { display: block; width: 100%; max-width: 40rem; height: auto; object-fit: contain; border-radius: 8px; }
.check-script-head { font-weight: 600; }
.check-script { display: grid; grid-template-columns: max-content 1fr; gap: 0.3rem 0.8rem; margin: 0; }
.check-script dt { font-size: 13px; font-weight: 600; color: var(--ink-2); }
.check-script dd { margin: 0; }
.check-toon-form, .check-needs form { display: grid; grid-template-columns: minmax(0, 1fr); gap: 0.4rem; }
.check-toon-form textarea, .check-needs textarea { width: 100%; box-sizing: border-box; }
.check-toon-form .btn-row { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); }
.check-toon-form .btn { min-width: 0; padding-inline: 0.4rem; }
.check-answer { display: flex; flex-wrap: wrap; align-items: flex-start; gap: 0.5rem; }
.check-right { margin: 0; }
.check-needs { flex: 0 0 auto; }
.check-needs[open] { flex-basis: 100%; }
.check-needs > summary { list-style: none; display: inline-flex; }
.check-needs > summary::-webkit-details-marker { display: none; }
.check-needs form { margin-top: 0.5rem; }
@media (max-width: 30rem) { .check-script { grid-template-columns: 1fr; } }

.app-nav .count { display: inline-grid; place-items: center; min-width: 1.35rem; height: 1.35rem; padding: 0 0.35rem; margin-left: 0.2rem;
  border-radius: 999px; background: var(--moss); color: var(--on-moss); font-size: 12px; font-weight: 600; }

/* Check Cards: what changed since Derick checked a card (CheckDiff), new words in green. */
.check-diff { margin: 0 0 12px; padding: 10px 12px; border-radius: 8px; background: color-mix(in srgb, var(--accent, #176b45) 8%, transparent); }
.check-diff p { margin: 4px 0; line-height: 1.5; }
.check-diff ins.diff-add, .check-diff .diff-add { background: color-mix(in srgb, #2e9e5e 28%, transparent); color: inherit; text-decoration: none; border-radius: 3px; padding: 0 2px; }
.check-diff del.diff-del { color: var(--muted, #777); text-decoration: line-through; opacity: .75; }

.check-recent { margin: 0 0 14px; }
.check-recent > summary { cursor: pointer; font-weight: 600; }

/* A deck whose every card is checked and has no note waiting. */
.check-decks .chip.is-done { background: color-mix(in srgb, #2e9e5e 30%, transparent); border-color: #2e9e5e; }
.check-decks .chip.is-done.is-on { background: color-mix(in srgb, #2e9e5e 45%, transparent); }

.check-card.is-closing { opacity: .4; pointer-events: none; }
.checks { overflow-anchor: none; }
.check-toast { position: fixed; left: 50%; bottom: 20px; transform: translateX(-50%); z-index: 50; display: flex; gap: 14px; align-items: center; padding: 10px 14px; border-radius: 10px; background: var(--ink, #1c1d20); color: var(--card, #fff); box-shadow: 0 6px 24px rgba(0,0,0,.35); }
.check-toast .btn { padding: 4px 12px; }

/* History: sessions read back out of the review log, and their replay. */
.hist-day { font: 600 15px/1.2 var(--sans); color: var(--ink-2); margin: 1.4rem 0 .5rem; }
.hist-list { list-style: none; margin: 0; padding: 0; display: grid; gap: .5rem; }
.hist-row { display: grid; grid-template-columns: 5.2rem 1fr auto; gap: .8rem; align-items: center; padding: .8rem 1rem; background: var(--card); border: 1px solid var(--hair); border-radius: 12px; text-decoration: none; color: var(--ink); }
.hist-row:hover { border-color: var(--moss); }
.hist-when { font-weight: 600; white-space: nowrap; }
.hist-main { display: grid; gap: .1rem; min-width: 0; }
.hist-tally { display: flex; gap: .35rem; flex-wrap: wrap; justify-content: flex-end; }
.rt { display: inline-flex; gap: .3rem; align-items: center; font: 600 12.5px/1 var(--sans); padding: .3rem .55rem; border-radius: 999px; border: 1px solid var(--hair); white-space: nowrap; }
.rt1 { color: var(--again); } .rt2 { color: var(--hard); } .rt3 { color: var(--good); } .rt4 { color: var(--easy); }
.rt.zero { opacity: .4; }
.replay { margin-bottom: 1rem; }
.replay-bar { display: flex; gap: .6rem; align-items: center; flex-wrap: wrap; }
.replay-stage { margin-top: 1rem; padding: 1.2rem; border-radius: 12px; background: var(--card); border: 1px solid var(--hair); min-height: 9rem; }
.replay-q { font: 600 1.35rem/1.35 var(--serif, Literata, serif); margin: .3rem 0; white-space: pre-line; }
.replay-a { color: var(--ink-2); white-space: pre-line; }
.replay-r { margin-top: .5rem; }
.hist-steps { list-style: none; margin: 0; padding: 0; display: grid; gap: .35rem; }
.hist-steps li { display: grid; grid-template-columns: 2rem 5.6rem 1fr auto auto; gap: .7rem; align-items: center; padding: .55rem .8rem; border: 1px solid var(--hair); border-radius: 10px; background: var(--card); }
.hist-steps li.now { border-color: var(--moss); box-shadow: inset 0 0 0 1px var(--moss); }
.hist-n { font-weight: 600; color: var(--ink-2); }
.hist-card { display: grid; gap: .1rem; min-width: 0; }
@media (max-width: 560px) {
  .hist-row { grid-template-columns: 1fr; }
  .hist-tally { justify-content: flex-start; }
  .hist-steps li { grid-template-columns: 1.6rem 1fr auto; }
  .hist-steps li .hist-t { display: none; }
}
.seg { display: inline-flex; gap: .35rem; }
.seg .chip { display: inline-flex; align-items: center; min-height: 40px; padding: 0 .8rem; border: 1px solid var(--hair); border-radius: 999px; background: var(--card); color: var(--ink); font: inherit; cursor: pointer; }
.seg .chip.is-on { border-color: var(--moss); box-shadow: inset 0 0 0 1px var(--moss); }

/* An earlier, closed note on a Check Cards card: kept in view so a done note never looks deleted. */
.check-earlier { margin: 0; padding: 0.3rem 0.5rem; border-left: 3px solid var(--rule, #ccc); }
