/* The whole visual system, derived from vocab-design-spec.md §2.
 *
 * The app looks like a dictionary, not a flashcard app, because that is what it
 * is: an entry has a headword, a gender and part of speech in the margin, and
 * numbered senses. The one loud element is the highlighter stroke under the
 * word being studied — every word here was met while reading, and the marker is
 * that fact drawn.
 *
 * Mobile is the primary target. Every screen is laid out at 390px first and
 * widened from there, because the common use is standing up with a phone.
 *
 * No gradients, no glass, no icons, no second accent colour. If something needs
 * emphasis it gets space, not decoration.
 */

/* ---------------------------------------------------------------- tokens -- */

:root {
  color-scheme: light;

  --paper: #f6f5f1;
  --ink: #15171d;
  --ink-soft: #5c5a54;
  /* Spec §2 gives #8E8B83. Measured against --paper that is 3.12:1, which fails
     the 4.5:1 body-text floor in §7 — and §7 says to darken the token rather
     than enlarge the text, so: 4.87:1 on paper, 5.32:1 on a card. It carries
     every label, hint and date on the site at 11px, so it had to move. */
  --ink-faint: #6e6b64;
  --rule: #dfdcd4;
  --card: #ffffff;
  --marker: #ffe34d;
  /* The stroke itself. Solid on paper; in the dark theme a translucent wash of
     the same yellow, because full-strength highlighter on a near-black card is
     a glare and dimming it with `opacity` would take the word down with it. */
  --marker-wash: #ffe34d;
  --bind: #1e3a5f;
  --on-bind: #ffffff;

  /* Gender. Only ever the colour of the article word itself — never a
     background, border or badge, so a colourblind reader loses nothing by
     reading "die". */
  --der: #2f6fb2;
  --die: #b8433c;
  --das: #3b7a56;

  --f-display: "Literata", Georgia, "Times New Roman", serif;
  --f-body: "IBM Plex Sans", system-ui, -apple-system, sans-serif;
  --f-mono: "IBM Plex Mono", ui-monospace, Menlo, monospace;

  --s1: 4px;  --s2: 8px;  --s3: 12px; --s4: 16px; --s5: 24px;
  --s6: 32px; --s7: 48px; --s8: 64px; --s9: 96px;

  --r-chip: 4px;
  --r-control: 7px;
  --r-card: 14px;

  --page: 60rem;
  --column: 34rem;

  /* How much room the fixed rating grid needs at the foot of the study screen.
     Zero until the card is turned, because until then the buttons do not
     exist. */
  --rate-space: 0px;
}

/* Set by the inline script in <head> before first paint, from a stored
   preference or the system setting — so there is no flash of the wrong theme
   and no need to write the palette twice. */
html[data-theme="dark"] {
  color-scheme: dark;

  --paper: #101219;
  --ink: #e9e7e1;
  --ink-soft: #a9a69e;
  /* Spec §2 gives #6F6C65: 3.57:1 on --paper and worse on --card. Same rule as
     above, in the direction that raises contrast in a dark theme — 5.23:1 on
     paper, 4.87:1 on a card. */
  --ink-faint: #8b8780;
  --rule: #242832;
  --card: #171a22;
  --marker: #ffe34d;
  --marker-wash: rgba(255, 227, 77, 0.22);
  --bind: #8fb6e0;
  --on-bind: #0c1017;

  --der: #7fb0e6;
  --die: #e58a83;
  --das: #7fc49c;
}

/* ------------------------------------------------------------------ base -- */

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

body {
  margin: 0;
  min-height: 100dvh;
  display: flex;
  flex-direction: column;
  background: var(--paper);
  color: var(--ink);
  font-family: var(--f-body);
  /* 1rem, and no input anywhere may go below it: iOS Safari zooms the viewport
     when a focused field is under 16px and never zooms back out. */
  font-size: 1rem;
  line-height: 1.55;
  -webkit-font-smoothing: antialiased;
  -webkit-text-size-adjust: 100%;
}

a { color: inherit; }

/* The `hidden` attribute has to win over a component's own display rule, or a
   section styled `display: flex` quietly ignores being hidden. */
[hidden] { display: none !important; }

/* Not a hover affordance — hover does not exist on the device this is mostly
   used on, so every interactive thing is legible standing still. */
:focus-visible {
  outline: 2px solid var(--bind);
  outline-offset: 1px;
  border-radius: 2px;
}

button, input, select, textarea { font: inherit; color: inherit; }
button { touch-action: manipulation; }

.muted { color: var(--ink-soft); }
.faint { color: var(--ink-faint); }

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

.topbar {
  display: flex;
  align-items: center;
  gap: var(--s3);
  padding: var(--s4) var(--s4) var(--s3);
  border-bottom: 1px solid var(--rule);
}

.wordmark {
  font-family: var(--f-display);
  font-weight: 600;
  font-size: 1.15rem;
  letter-spacing: -0.01em;
  text-decoration: none;
  margin-right: auto;
}
.wordmark span { color: var(--ink-faint); font-weight: 300; }

.topbar-actions { display: flex; align-items: center; gap: var(--s2); }

.themebtn, .signout {
  font-family: var(--f-mono);
  font-size: 0.688rem;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  background: none;
  border: 1px solid var(--rule);
  border-radius: var(--r-chip);
  color: var(--ink-faint);
  padding: 6px var(--s2);
  cursor: pointer;
  white-space: nowrap;
}

main {
  flex: 1;
  width: 100%;
  max-width: var(--page);
  margin: 0 auto;
  padding: var(--s5) var(--s4) var(--s7);
}

/* Bottom navigation, because the thumb is at the bottom of the phone. The +
   is the primary action and is the one thing here that carries weight. */
.mainnav {
  position: fixed;
  inset: auto 0 0 0;
  z-index: 20;
  display: grid;
  grid-template-columns: repeat(4, 1fr);
  background: var(--card);
  border-top: 1px solid var(--rule);
  padding-bottom: env(safe-area-inset-bottom, 0px);
}
.mainnav a {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 2px;
  min-height: 52px;
  padding: var(--s2) var(--s1);
  font-family: var(--f-mono);
  font-size: 0.688rem;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--ink-faint);
  text-decoration: none;
  touch-action: manipulation;
}
.mainnav a.is-on { color: var(--ink); }
/* The current page is marked by a rule as well as by colour. */
.mainnav a.is-on::before {
  content: "";
  position: absolute;
  top: -1px;
  width: 2.5rem;
  border-top: 2px solid var(--ink);
}
.mainnav a { position: relative; }

.mainnav .plus {
  font-family: var(--f-body);
  font-size: 1.5rem;
  letter-spacing: 0;
  line-height: 1;
  color: var(--ink);
}
.mainnav .plus span {
  font-family: var(--f-mono);
  font-size: 0.688rem;
  letter-spacing: 0.08em;
  color: var(--ink-faint);
}

/* Clear the fixed bar. Anything below this is unreachable. */
body:not(.no-nav) main { padding-bottom: calc(52px + var(--s7) + env(safe-area-inset-bottom, 0px)); }
/* Signed out, or in a session: no bar to clear. */
body.no-nav .mainnav { display: none; }
/* A session is a mode. The attribution is true and belongs on every other
   page; during eight cards on a tram it is a line of small print under the
   thing you are trying to remember. */
body.studying .colophon { display: none; }

.colophon {
  padding: var(--s5) var(--s4);
  font-family: var(--f-mono);
  font-size: 0.688rem;
  line-height: 1.6;
  color: var(--ink-faint);
}
body:not(.no-nav) .colophon { padding-bottom: calc(52px + var(--s5) + env(safe-area-inset-bottom, 0px)); }
.colophon a { color: inherit; }

/* ------------------------------------------------------------ shared type -- */

.eyebrow {
  font-family: var(--f-mono);
  font-size: 0.688rem;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--ink-faint);
}

.headword {
  font-family: var(--f-display);
  font-weight: 400;
  letter-spacing: -0.015em;
  line-height: 1.1;
  /* A German compound is one word and will not break at a space, because there
     isn't one. With lang="de" on the element the browser hyphenates where a
     German reader expects; `anywhere` is the last resort that keeps
     Geschwindigkeitsbegrenzung inside the card instead of through its edge. */
  hyphens: auto;
  overflow-wrap: anywhere;
}

.art { font-weight: 300; }
.art-der { color: var(--der); }
.art-die { color: var(--die); }
.art-das { color: var(--das); }

/* The signature: a marker stroke behind the word, crooked and overshooting.
   The only loud element in the design, and it appears on exactly one thing.

   Drawn as a background rather than as the mockup's absolutely positioned
   pseudo-element, because a compound long enough to wrap — and German produces
   those — gave the pseudo-element one rotated rectangle stretched across both
   lines, with the end of the first line sticking out above it. A background
   with `box-decoration-break: clone` gives each line its own stroke, which is
   also what a highlighter actually does to a phrase that wraps.

   The crookedness survives: the gradient axis is a fraction off vertical, so
   the band tilts across the width instead of sitting square. The overshoot is
   the padding, pulled back by an equal negative margin so the word keeps its
   place in the line. */
.mark {
  padding: 0 0.18em;
  margin: 0 -0.16em;
  /* The stops are measured down an inline box whose height is the font's
     ascent plus descent — 1.48em in Literata, with the baseline 1.05em down.
     So the band starts a little above the capitals and finishes below the
     baseline, the way a hand holding a marker overshoots in both directions.
     Ending it at 0.94em, as a naive reading of "x-height" suggests, leaves the
     bottom of every letter sticking out under the stroke. */
  background-image: linear-gradient(
    179.3deg,
    transparent 0 0.26em,
    var(--marker-wash) 0.26em 1.2em,
    transparent 1.2em
  );
  background-repeat: no-repeat;
  border-radius: 3px 10px 4px 12px / 12px 4px 10px 3px;
  -webkit-box-decoration-break: clone;
  box-decoration-break: clone;
}

.flag {
  display: inline-block;
  font-family: var(--f-mono);
  font-size: 0.625rem;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  background: var(--marker);
  color: #3a3306;
  border-radius: var(--r-chip);
  padding: 2px 6px;
  vertical-align: 0.15em;
  white-space: nowrap;
}
html[data-theme="dark"] .flag { background: #4a4008; color: var(--marker); }

/* -------------------------------------------------------------- controls -- */

.primary {
  display: inline-block;
  width: 100%;
  font-family: var(--f-body);
  font-size: 1rem;
  font-weight: 500;
  text-align: center;
  text-decoration: none;
  background: var(--bind);
  color: var(--on-bind);
  border: none;
  border-radius: var(--r-control);
  padding: var(--s4);
  min-height: 48px;
  cursor: pointer;
}

.quiet {
  font-family: var(--f-body);
  font-size: 0.938rem;
  background: var(--card);
  color: var(--ink);
  border: 1px solid var(--rule);
  border-radius: var(--r-control);
  padding: var(--s3) var(--s4);
  min-height: 44px;
  cursor: pointer;
  text-align: left;
}
.quiet:disabled { color: var(--ink-faint); cursor: default; }

.textlink {
  font-family: var(--f-body);
  font-size: 0.875rem;
  background: none;
  border: 0;
  border-bottom: 1px solid var(--rule);
  padding: 0;
  color: var(--ink-soft);
  cursor: pointer;
}

.label {
  display: block;
  font-size: 0.813rem;
  color: var(--ink-soft);
  margin-bottom: var(--s2);
}
.label .optional { color: var(--ink-faint); }

.input, .field {
  width: 100%;
  font-family: var(--f-body);
  /* Never below 16px — see the note on body. */
  font-size: 1rem;
  color: var(--ink);
  background: var(--card);
  border: 1px solid var(--rule);
  border-radius: var(--r-control);
  padding: var(--s3) var(--s4);
  min-height: 48px;
}
.input::placeholder, .field::placeholder { color: var(--ink-faint); }
.input.big { font-family: var(--f-display); font-size: 1.375rem; }
.stack { margin-bottom: var(--s5); }

.error {
  font-size: 0.938rem;
  color: var(--die);
  margin: 0 0 var(--s4);
}

/* An amber note: a job still to finish, never a failure. */
.callout {
  border-left: 3px solid var(--marker);
  background: var(--card);
  border-radius: 0 var(--r-control) var(--r-control) 0;
  padding: var(--s4);
  margin: var(--s4) 0;
  font-size: 0.938rem;
}
.callout h2 { font-family: var(--f-body); font-size: 1rem; margin: 0 0 var(--s2); }
.callout p { margin: 0 0 var(--s2); color: var(--ink-soft); }
.callout code {
  font-family: var(--f-mono);
  font-size: 0.813rem;
  background: var(--paper);
  border-radius: var(--r-chip);
  padding: 1px 4px;
}

.pagehead {
  display: flex;
  align-items: baseline;
  flex-wrap: wrap;
  gap: var(--s3);
  margin-bottom: var(--s5);
}
.pagehead h1 {
  font-family: var(--f-display);
  font-weight: 600;
  font-size: 1.5rem;
  line-height: 1.2;
  margin: 0;
}
.count { font-family: var(--f-mono); font-size: 0.75rem; color: var(--ink-faint); }

.section-h {
  font-family: var(--f-mono);
  font-size: 0.688rem;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--ink-faint);
  margin: var(--s6) 0 var(--s3);
}

.note { font-size: 0.813rem; color: var(--ink-faint); margin-top: var(--s3); }

/* Never a blank page. An empty state is an invitation. */
.invitation {
  text-align: center;
  padding: var(--s7) var(--s4);
}
.invitation h2 {
  font-family: var(--f-display);
  font-weight: 400;
  font-size: 1.375rem;
  margin: 0 0 var(--s3);
}
.invitation p { color: var(--ink-soft); margin: 0 auto var(--s5); max-width: 26rem; }
.invitation .primary { width: auto; padding-inline: var(--s6); }

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

.studyscreen {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  /* dvh, not vh: mobile browser chrome makes 100vh taller than what you can
     see, and what falls off the bottom is exactly the buttons that have to be
     reachable. */
  min-height: calc(100dvh - 12rem);
  padding-bottom: var(--rate-space);
}
/* Room for the fixed grid, only once the grid exists. */
.studyscreen.is-open { --rate-space: calc(9rem + env(safe-area-inset-bottom, 0px)); }

.session-head {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  width: min(var(--column), 100%);
  margin-bottom: var(--s5);
}
.progress {
  font-family: var(--f-mono);
  font-size: 0.75rem;
  letter-spacing: 0.1em;
  color: var(--ink-faint);
  font-variant-numeric: tabular-nums;
}
.progress b { color: var(--ink-soft); font-weight: 500; }
.stop {
  font-family: var(--f-mono);
  font-size: 0.688rem;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--ink-faint);
  text-decoration: none;
}

/* The card is a button, so turning it is one target the size of the card and
   the browser gives us Enter, Space and a role for free. */
.flip {
  width: min(var(--column), 100%);
  perspective: 1600px;
  background: none;
  border: 0;
  padding: 0;
  font: inherit;
  color: inherit;
  text-align: inherit;
  cursor: pointer;
  display: block;
  touch-action: manipulation;
}
.flip-inner {
  /* A span, because only phrasing content may live inside a button — and an
     inline box ignores min-height, which collapsed the card to nothing. */
  display: block;
  position: relative;
  min-height: 19rem;
  transform-style: preserve-3d;
  transition: transform 0.52s cubic-bezier(0.2, 0.75, 0.25, 1);
}
.flip[aria-expanded="true"] .flip-inner { transform: rotateY(180deg); }

.face {
  position: absolute;
  inset: 0;
  backface-visibility: hidden;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  text-align: center;
  padding: var(--s6) var(--s5) var(--s7);
  background: var(--card);
  border: 1px solid var(--rule);
  border-radius: var(--r-card);
  /* The single elevation in the design. */
  box-shadow: 0 1px 1px rgba(20, 22, 28, 0.04), 0 18px 40px -26px rgba(20, 22, 28, 0.45);
  overflow: hidden;
}
.face.back { transform: rotateY(180deg); }
.flip:focus-visible { outline: none; }
.flip:focus-visible .face { outline: 2px solid var(--bind); outline-offset: 3px; }

.study-word {
  font-size: clamp(2rem, 9vw, 3.5rem);
  margin: 0;
  max-width: 100%;
}
/* A long compound has to shrink before it wraps, and the marker stroke is one
   swipe over one line. The step comes from the server with the card, so the
   first card rendered in HTML and every card rendered by script agree. */
.study-word[data-length="long"] { font-size: clamp(1.625rem, 7vw, 2.75rem); }
.study-word[data-length="xlong"] { font-size: clamp(1.25rem, 5.5vw, 2.25rem); }

.gram {
  margin-top: var(--s5);
  font-family: var(--f-mono);
  font-size: 0.688rem;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--ink-faint);
}

.tapcue {
  position: absolute;
  left: 0;
  right: 0;
  bottom: var(--s5);
  font-family: var(--f-mono);
  font-size: 0.688rem;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--ink-faint);
}

.backword {
  font-family: var(--f-display);
  font-size: 1.0625rem;
  color: var(--ink-faint);
  margin: 0 0 var(--s5);
  hyphens: auto;
  overflow-wrap: anywhere;
}
.backword .art { color: inherit; }

/* The English gloss is the thing being recalled, so it is the largest thing on
   the back. */
.gloss {
  font-family: var(--f-display);
  font-size: clamp(1.375rem, 5vw, 2rem);
  font-weight: 400;
  line-height: 1.25;
  margin: 0;
}
.gloss:empty { display: none; }

/* The German definition is depth beneath it, and a poor thing to be graded on. */
.defn {
  margin: var(--s4) 0 0;
  color: var(--ink-soft);
  font-size: 0.938rem;
  line-height: 1.6;
  max-width: 26rem;
}
.defn:empty { display: none; }

.example { font-style: italic; }

.editlink {
  display: inline-block;
  margin-top: var(--s5);
  font-size: 0.813rem;
  color: var(--ink-faint);
  text-decoration: none;
  border-bottom: 1px solid var(--rule);
}

/* Rating buttons. Two by two, in the bottom corner of the screen, inside the
   safe area — reaching the top of a phone to grade a card is the difference
   between doing a session standing up and not.

   They are created when the card turns and removed when it advances. Not
   disabled: absent. There is no way to grade a card you have not tried to
   recall, and that is the whole point of the two faces. */
.rate {
  position: fixed;
  inset: auto 0 0 0;
  z-index: 20;
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: var(--s2);
  padding: var(--s3) var(--s4);
  padding-bottom: calc(var(--s3) + env(safe-area-inset-bottom, 0px));
  background: var(--paper);
  border-top: 1px solid var(--rule);
  animation: rise 0.3s cubic-bezier(0.2, 0.7, 0.3, 1) both;
}
@keyframes rise {
  from { opacity: 0; transform: translateY(8px); }
  to { opacity: 1; transform: none; }
}

.ratebtn {
  position: relative;
  font-family: var(--f-body);
  font-size: 0.938rem;
  background: var(--card);
  color: var(--ink);
  border: 1px solid var(--rule);
  border-radius: var(--r-control);
  /* Comfortably past the 48px floor: this is a moving tram. */
  min-height: 56px;
  padding: var(--s2) var(--s3);
  cursor: pointer;
  touch-action: manipulation;
}
.ratebtn:disabled { opacity: 0.55; }
.ratebtn .ivl {
  display: block;
  font-family: var(--f-mono);
  font-size: 0.688rem;
  color: var(--ink-faint);
  margin-top: 2px;
  letter-spacing: 0.04em;
  min-height: 1em;
}
/* The key hint sits in the corner, never inside the label, or it reads as part
   of the interval — "Again 1 day1" was the symptom. */
.ratebtn .key {
  position: absolute;
  top: 5px;
  right: 7px;
  font-family: var(--f-mono);
  font-size: 0.625rem;
  color: var(--ink-faint);
}
/* A phone has no 3 key and the hint is noise there. */
@media (pointer: coarse) { .ratebtn .key { display: none; } }

.ratebtn.again { border-color: color-mix(in srgb, var(--die) 34%, var(--rule)); }

/* Calm and final. No count, no streak, nothing that implies debt. */
.done { text-align: center; padding: var(--s8) var(--s4); }
.done h1 {
  font-family: var(--f-display);
  font-weight: 400;
  font-size: clamp(1.5rem, 6vw, 2rem);
  margin: 0 0 var(--s3);
}
.done p { color: var(--ink-soft); margin: 0 0 var(--s5); }
.done .links { font-size: 0.938rem; }

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

.filters { display: grid; gap: var(--s2); margin-bottom: var(--s5); }
.filters .row { display: grid; grid-template-columns: 1fr 1fr; gap: var(--s2); }
.filters .checks {
  display: flex;
  flex-wrap: wrap;
  gap: var(--s4);
  font-size: 0.875rem;
  color: var(--ink-soft);
  padding: var(--s2) 0;
}
.filters .checks label { display: flex; align-items: center; gap: var(--s2); }
.filters .checks input { width: 20px; height: 20px; accent-color: var(--bind); }
.filters .actions { display: flex; align-items: center; gap: var(--s4); }
.filters .actions .primary { width: auto; padding-inline: var(--s5); }

.entries { list-style: none; margin: 0; padding: 0; }

/* A lexicographic entry: grammar in the margin, headword, senses under it. The
   margin column only exists once there is room for it. */
.entry {
  display: grid;
  gap: var(--s2);
  padding: var(--s5) 0;
  border-top: 1px solid var(--rule);
}
.entry:last-child { border-bottom: 1px solid var(--rule); }
.entry a { text-decoration: none; }
.entry .headword { font-size: 1.25rem; display: inline; }
.entry .headline { margin: 0; display: flex; flex-wrap: wrap; align-items: baseline; gap: var(--s2); }
.entry .gloss-line {
  font-family: var(--f-display);
  font-size: 1rem;
  margin: var(--s1) 0 0;
}
.entry .defn { margin-top: var(--s1); font-size: 0.875rem; max-width: none; }
.entry .src {
  font-family: var(--f-mono);
  font-size: 0.688rem;
  color: var(--ink-faint);
  margin-top: var(--s3);
}

/* Numbered senses, mono numerals hanging in the margin. */
.senses { margin: var(--s3) 0 0; padding: 0; list-style: none; display: grid; gap: var(--s2); }
.senses li { display: grid; grid-template-columns: 1.25rem 1fr; gap: var(--s3); font-size: 0.938rem; }
.senses .n {
  font-family: var(--f-mono);
  font-size: 0.688rem;
  color: var(--ink-faint);
  padding-top: 0.35em;
}
.senses .on-card { color: var(--ink); }
.senses .other { color: var(--ink-soft); }

/* A card with nothing on its back is unfinished work, and the fix is offered
   where the problem is rather than three pages away. */
.unfinished {
  margin-top: var(--s3);
  padding: var(--s3) var(--s4);
  border-left: 3px solid var(--marker);
  background: var(--card);
  border-radius: 0 var(--r-control) var(--r-control) 0;
  font-size: 0.875rem;
}
.unfinished a { color: var(--ink); }

.paging { display: flex; gap: var(--s5); margin-top: var(--s5); font-size: 0.938rem; }

/* ------------------------------------------------------------------- add -- */

.addwrap { max-width: 32rem; }

/* What just happened, in the same shape whether it worked or not — except that
   an unresolved word is visibly a job still open, so two in a row cannot read
   as two successes. */
.receipt {
  border-left: 3px solid var(--bind);
  background: var(--card);
  padding: var(--s4) var(--s5);
  border-radius: 0 var(--r-control) var(--r-control) 0;
  margin-bottom: var(--s6);
}
.receipt.unresolved_saved_raw { border-left-color: var(--marker); }
.receipt.failed { border-left-color: var(--die); }
.receipt .eyebrow { display: block; margin-bottom: var(--s2); }
.receipt .headword { font-size: 1.375rem; }
.receipt p { margin: var(--s2) 0 0; font-size: 0.875rem; color: var(--ink-soft); }
.receipt a { color: var(--ink); }

/* ------------------------------------------------------------- dashboard -- */

.today { margin-bottom: var(--s6); }
.today h1 {
  font-family: var(--f-display);
  font-weight: 600;
  font-size: clamp(1.5rem, 6vw, 1.75rem);
  line-height: 1.2;
  margin: 0 0 var(--s4);
}
.today .primary { width: auto; padding-inline: var(--s6); }

/* Actionable, unlike an overdue count: each of these is fixed in one tap. */
.attention {
  border-left: 3px solid var(--marker);
  background: var(--card);
  border-radius: 0 var(--r-control) var(--r-control) 0;
  padding: var(--s4);
  margin-bottom: var(--s6);
}
.attention h2 { font-family: var(--f-body); font-size: 1rem; margin: 0 0 var(--s2); }
.attention ul { margin: 0; padding: 0; list-style: none; display: grid; gap: var(--s2); }
.attention li { font-size: 0.938rem; }
.attention a { color: var(--ink); }

.stats { display: grid; grid-template-columns: repeat(2, 1fr); gap: var(--s3); }
.stat {
  background: var(--card);
  border: 1px solid var(--rule);
  border-radius: var(--r-control);
  padding: var(--s4);
}
.stat .n {
  font-family: var(--f-display);
  font-size: 2rem;
  font-weight: 400;
  line-height: 1;
  font-variant-numeric: tabular-nums;
}
.stat .lbl {
  display: block;
  font-family: var(--f-mono);
  font-size: 0.688rem;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--ink-faint);
  margin-top: var(--s2);
}
.stat .sub { display: block; font-size: 0.75rem; color: var(--ink-faint); margin-top: var(--s1); }

/* Fourteen columns, always. A chart drawn only from the days with activity
   stretches one studied day across the whole width and says the opposite of
   what happened. */
.weeks { display: flex; gap: 3px; margin-top: var(--s3); }
.day { flex: 1; height: 34px; border-radius: 2px; background: var(--rule); }
.day.on { background: var(--marker); }

.languages { list-style: none; margin: 0; padding: 0; display: grid; gap: var(--s2); }
.languages li {
  display: grid;
  grid-template-columns: 6rem 1fr auto;
  align-items: center;
  gap: var(--s3);
  font-size: 0.938rem;
}
.languages a { text-decoration: none; }
.languages .bar { height: 6px; border-radius: 3px; background: var(--rule); }
.languages .bar::before {
  content: "";
  display: block;
  height: 100%;
  width: var(--share);
  border-radius: 3px;
  background: var(--ink-faint);
}
.languages .n { font-family: var(--f-mono); font-size: 0.75rem; color: var(--ink-faint); }

/* ---------------------------------------------------------------- detail -- */

.backlink {
  display: inline-block;
  font-family: var(--f-mono);
  font-size: 0.688rem;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--ink-faint);
  text-decoration: none;
  margin-bottom: var(--s4);
}

.detail-head { margin-bottom: var(--s5); }
.detail-head .headword { font-size: clamp(1.75rem, 8vw, 2.5rem); margin: 0; }
.detail-head .gram {
  margin-top: var(--s2);
  font-family: var(--f-mono);
  font-size: 0.688rem;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--ink-faint);
}

.chooser {
  background: var(--card);
  border: 1px solid var(--rule);
  border-radius: var(--r-control);
  padding: var(--s4);
  margin-bottom: var(--s6);
}
.chooser h2 { font-family: var(--f-body); font-size: 1rem; margin: 0 0 var(--s2); }
.chooser > p { margin: 0 0 var(--s4); font-size: 0.875rem; color: var(--ink-soft); }
.chooser .options { display: grid; gap: var(--s2); }
.chooser .options form { margin: 0; }
.chooser .quiet { width: 100%; }

/* The meaning currently on the card. Marked so you can see which one you have,
   still pressable so you can say it is the right one. */
.chooser .is-current { border-color: var(--ink-faint); }
.chooser .keep {
  display: block;
  font-family: var(--f-mono);
  font-size: 0.688rem;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--ink-faint);
  margin-top: var(--s1);
}

.senselist { list-style: none; margin: 0; padding: 0; }
.senselist li {
  display: grid;
  grid-template-columns: 1.25rem 1fr;
  gap: var(--s3);
  padding: var(--s3) 0;
  border-top: 1px solid var(--rule);
  font-size: 0.938rem;
}
.senselist li:last-child { border-bottom: 1px solid var(--rule); }
.senselist .n { font-family: var(--f-mono); font-size: 0.688rem; color: var(--ink-faint); padding-top: 0.35em; }
.senselist .meta {
  display: block;
  font-family: var(--f-mono);
  font-size: 0.688rem;
  color: var(--ink-faint);
  margin-top: var(--s1);
}
/* Its own line, always. Inline, it lands wherever the definition happens to
   stop — tucked under a short one, welded to the last word of a long one. */
.senselist form { display: block; margin: var(--s1) 0 0; }

.forms { display: flex; flex-wrap: wrap; gap: var(--s2); margin: 0; padding: 0; list-style: none; }
.forms li {
  font-family: var(--f-mono);
  font-size: 0.75rem;
  background: var(--card);
  border: 1px solid var(--rule);
  border-radius: var(--r-chip);
  padding: 2px 6px;
  color: var(--ink-soft);
}

.examples { list-style: none; margin: 0; padding: 0; display: grid; gap: var(--s3); }
.examples li { font-size: 0.938rem; }
.examples .translation { display: block; color: var(--ink-faint); font-size: 0.875rem; }

/* ------------------------------------------------------------------ auth -- */

.authwrap { max-width: 24rem; margin: var(--s7) auto 0; }
.authwrap h1 {
  font-family: var(--f-display);
  font-weight: 600;
  font-size: 1.5rem;
  margin: 0 0 var(--s6);
}
.authwrap .alt { margin-top: var(--s5); font-size: 0.938rem; text-align: center; }

/* --------------------------------------------------------------- desktop -- */

/* Everything above is the phone. From here up there is room for the margin
   column an entry wants, four rating buttons in a row, and the navigation
   back at the top where a pointer already is. */
@media (min-width: 40rem) {
  .topbar { padding: var(--s5) var(--s5) var(--s4); gap: var(--s5); }
  main { padding: var(--s6) var(--s5) var(--s9); }
  body:not(.no-nav) main { padding-bottom: var(--s9); }
  .colophon { padding: var(--s6) var(--s5); }
  body:not(.no-nav) .colophon { padding-bottom: var(--s6); }

  /* A study session hides the bottom bar so the rating grid can own the bottom
     of a phone screen. Up here the grid is back in the flow and the navigation
     costs a row of the top bar, so there is nothing to hide it from. */
  body.no-nav .mainnav { display: flex; }
  body.studying .colophon { display: block; }

  .mainnav {
    position: static;
    display: flex;
    gap: var(--s5);
    background: none;
    border: 0;
    padding: 0;
    margin-right: var(--s5);
  }
  .mainnav a {
    flex-direction: row;
    min-height: 0;
    padding: 0 0 2px;
    font-family: var(--f-body);
    font-size: 0.875rem;
    letter-spacing: 0;
    text-transform: none;
    color: var(--ink-soft);
    border-bottom: 1.5px solid transparent;
  }
  .mainnav a.is-on { color: var(--ink); border-bottom-color: var(--ink); }
  .mainnav a.is-on::before { content: none; }
  .mainnav .plus { font-size: 0.875rem; font-family: var(--f-body); }
  .mainnav .plus span { font-family: inherit; font-size: inherit; color: inherit; letter-spacing: 0; }

  .pagehead h1 { font-size: 1.75rem; }
  .entry { grid-template-columns: 5.5rem 1fr; gap: var(--s5); }
  .entry .margin { padding-top: 0.4rem; }
  .entry .headword { font-size: 1.375rem; }

  .stats { grid-template-columns: repeat(4, 1fr); }
  .filters { grid-template-columns: 2fr 1fr 1fr; align-items: center; }
  .filters .row { display: contents; }
  .filters .checks, .filters .actions { grid-column: 1 / -1; }

  .studyscreen { min-height: calc(100dvh - 16rem); padding-bottom: 0; }
  .flip-inner { min-height: 21rem; }

  /* Back in the flow beneath the card: on a desktop the bottom of the window
     is nowhere near the hand. */
  .rate {
    position: static;
    grid-template-columns: repeat(4, 1fr);
    width: min(var(--column), 100%);
    margin-top: var(--s6);
    padding: 0;
    background: none;
    border: 0;
  }

  .authwrap { margin-top: var(--s9); }
}

/* ------------------------------------------------------- reduced motion -- */

/* The flip becomes a swap. Rotating a card in three dimensions is exactly the
   kind of movement that causes trouble for people who ask for less of it. */
@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after {
    animation-duration: 0.001ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 0.001ms !important;
    scroll-behavior: auto !important;
  }
  .flip-inner { transform: none !important; min-height: 0; }
  .face { position: relative; inset: auto; backface-visibility: visible; }
  .face.back { transform: none; display: none; }
  .flip[aria-expanded="true"] .face.front { display: none; }
  .flip[aria-expanded="true"] .face.back { display: flex; }
  .tapcue { position: static; margin-top: var(--s6); }
}
