/* Electroharmonix (Ray Larabie), the faux-katakana display face used for the
   wordmark and nothing else. The file is vendored rather than linked: this page
   is opened from file:// as often as from a server, and a remote font would make
   the title depend on being online. */
@font-face {
  font-family: Electroharmonix;
  font-style: normal;
  font-weight: 400;
  /* Shown in the fallback face until the file arrives rather than hidden, so the
     name is never a blank space on a slow read from disk. */
  font-display: swap;
  src: url("assets/fonts/electroharmonix.woff") format("woff");
}

:root {
  /* A lit cabinet rather than a printed page: everything sits on one deep
     ground, every panel is a surface catching light, and saturated colour is
     spent only on the things that change — progress, the answer, the strokes
     being asked about. The old paper palette made the app read as an article
     about kanji instead of a thing you play. */
  --bg: #0a0f1a;
  --panel: rgba(255, 255, 255, 0.048);
  --panel-lit: rgba(255, 255, 255, 0.085);
  --well: rgba(5, 9, 17, 0.55);
  --ink: #eaf0fb;
  --muted: #94a3bd;
  --line: rgba(255, 255, 255, 0.105);
  --line-lit: rgba(140, 196, 255, 0.45);
  --paper: #141c2e;
  --blue: #55a4ff;
  --blue-deep: #9cc9ff;
  --orange: #ffa14d;
  --orange-pale: rgba(255, 161, 77, 0.14);
  --green: #4fe0a6;
  --red: #ff8272;
  --shadow: 0 26px 64px rgba(0, 0, 0, 0.55);
  --lift: 0 14px 34px rgba(0, 0, 0, 0.45);
  /* Japanese glyph stacks. Never fall back to the generic `serif`/`sans-serif`
     keywords for CJK: on a machine with no Japanese font the browser resolves
     those to a Chinese face (SimSun, Microsoft YaHei), which draws Chinese
     variants of 糸, 言, 金, 牛 and hops fonts per character, so components
     printed beside a kanji no longer match the kanji. Both stacks therefore end
     on faces that ship with Windows/macOS and cover the radical blocks. */
  --jp-serif: "Yu Mincho", "YuMincho", "Hiragino Mincho ProN", "Noto Serif JP",
    "Noto Serif CJK JP", "BIZ UDMincho", "MS Mincho", "Yu Gothic", "Hiragino Sans",
    "Noto Sans JP", "MS Gothic", sans-serif;
  --jp-sans: "Yu Gothic UI", "Yu Gothic", "Hiragino Sans", "Noto Sans JP",
    "Meiryo", "MS Gothic", sans-serif;
  --ui: Inter, ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;
}

* { box-sizing: border-box; }

body {
  margin: 0;
  min-width: 320px;
  min-height: 100dvh;
  color: var(--ink);
  background-color: var(--bg);
  /* Three lights aimed at the ground so the page has a middle rather than an
     even wash. Fixed, so scrolling moves the content across the light instead
     of dragging the light with it. */
  background-image:
    radial-gradient(1100px 680px at 10% -12%, rgba(63, 122, 255, 0.26), transparent 68%),
    radial-gradient(900px 620px at 94% 4%, rgba(255, 138, 61, 0.17), transparent 66%),
    radial-gradient(820px 560px at 52% 116%, rgba(79, 224, 166, 0.12), transparent 68%);
  background-attachment: fixed;
  font-family: var(--ui);
}

/* A faint board grid, faded out below the fold. It is the cheapest way to say
   "play surface" without putting a texture behind the kanji, which have to stay
   the only detailed thing on screen. */
body::before {
  content: "";
  position: fixed;
  z-index: 0;
  inset: 0;
  pointer-events: none;
  background-image:
    linear-gradient(rgba(255, 255, 255, 0.026) 1px, transparent 1px),
    linear-gradient(90deg, rgba(255, 255, 255, 0.026) 1px, transparent 1px);
  background-size: 48px 48px;
  -webkit-mask-image: radial-gradient(circle at 50% 0%, #000, transparent 78%);
  mask-image: radial-gradient(circle at 50% 0%, #000, transparent 78%);
}

/* Radicals drifting up behind everything. It sits between the board grid and the
   shell, and stays far enough down in contrast that the kanji on a card is still
   the only detailed thing on screen — the moment it competes for the eye it has
   stopped being a background. Transform-only animation, so it never lays out. */
.kanji-drift { position: fixed; z-index: 0; inset: 0; overflow: hidden; pointer-events: none; }
.kanji-drift span {
  position: absolute;
  bottom: -22vh;
  color: var(--blue-deep);
  opacity: 0.04;
  font-family: var(--jp-serif);
  line-height: 1;
  user-select: none;
  will-change: transform;
  animation: kanji-drift var(--drift-time) var(--drift-delay) linear infinite;
}

@keyframes kanji-drift {
  from { transform: translate3d(0, 0, 0) rotate(0deg); }
  to { transform: translate3d(var(--drift-sway), -128vh, 0) rotate(var(--drift-turn)); }
}

/* Square corners throughout. This is a cabinet of panels and keys rather than a
   deck of soft cards, and the character on screen is the only thing allowed a
   curve. Set once rather than as a value on each surface, so a panel added later
   cannot arrive rounded on its own. */
*, *::before, *::after { border-radius: 0; }

button { font: inherit; }
button:focus-visible { outline: 2px solid var(--blue); outline-offset: 3px; }

body.profile-locked { overflow: hidden; }
body.profile-locked .shell,
body.profile-locked .kanji-drift { visibility: hidden; }

.profile-gate {
  position: fixed;
  z-index: 50;
  inset: 0;
  display: grid;
  place-items: center;
  overflow-y: auto;
  padding: 24px;
  background:
    radial-gradient(680px 420px at 20% 0%, rgba(85, 164, 255, 0.2), transparent 70%),
    radial-gradient(620px 420px at 90% 100%, rgba(255, 161, 77, 0.15), transparent 70%),
    rgba(5, 9, 17, 0.96);
}
.profile-gate[hidden] { display: none; }
.profile-panel { width: calc(100vw - 48px); max-width: 610px; min-width: 0; overflow: hidden; padding: 30px; text-align: center; background: var(--paper); border: 1px solid var(--line-lit); box-shadow: var(--shadow); }
.profile-panel h1 { margin: 5px 0 8px; font-family: var(--ui); font-size: clamp(1.75rem, 6vw, 2.7rem); letter-spacing: -0.035em; text-transform: none; }
.profile-intro { margin: 0 0 24px; color: var(--muted); font-size: 0.9rem; }
.profile-picker-view[hidden],
.profile-choices[hidden] { display: none; }
.profile-choices { display: grid; min-width: 0; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: 16px; }
.profile-choice { display: grid; min-width: 0; gap: 12px; justify-items: center; padding: 16px; cursor: pointer; color: var(--ink); background: rgba(255, 255, 255, 0.05); border: 1px solid var(--line); transition: transform 150ms ease, border-color 150ms ease, background 150ms ease, box-shadow 150ms ease; }
.profile-choice:hover { transform: translateY(-3px); background: rgba(85, 164, 255, 0.11); border-color: var(--line-lit); box-shadow: var(--lift); }
.profile-choice img { display: block; width: 100%; max-width: 190px; aspect-ratio: 1; object-fit: cover; border: 3px solid rgba(255, 255, 255, 0.16); border-radius: 50%; }
.profile-choice strong { font-size: 1.05rem; letter-spacing: 0.08em; text-transform: uppercase; }
.profile-choice small { margin-top: -8px; color: var(--muted); font-size: 0.62rem; }
.profile-password { display: grid; width: min(100%, 360px); justify-items: stretch; margin: 0 auto; text-align: left; }
.profile-password[hidden] { display: none; }
.profile-password img { display: block; width: 126px; aspect-ratio: 1; margin: 4px auto 12px; object-fit: cover; border: 3px solid var(--line-lit); border-radius: 50%; }
.profile-password p { margin: 0; text-align: center; color: var(--muted); font-size: 0.74rem; }
.profile-password h2 { margin: 2px 0 18px; text-align: center; }
.profile-password label { margin-bottom: 7px; color: var(--muted); font-size: 0.7rem; font-weight: 800; letter-spacing: 0.09em; text-transform: uppercase; }
.profile-password input { width: 100%; height: 52px; padding: 0 14px; color: var(--ink); background: var(--well); border: 1px solid var(--line-lit); font: 1rem var(--ui); }
.profile-password input:focus { outline: 2px solid var(--blue); outline-offset: 2px; }
.profile-submit { margin-top: 12px; padding: 14px 18px; cursor: pointer; color: #08101d; background: var(--blue); border: 1px solid var(--blue); font-weight: 850; letter-spacing: 0.07em; text-transform: uppercase; }
.profile-back { justify-self: start; margin: 0 0 14px; padding: 0; cursor: pointer; color: var(--blue-deep); background: transparent; border: 0; font-size: 0.72rem; font-weight: 800; }
.profile-password .profile-feedback { min-height: 1.3em; margin-top: 10px; color: var(--red); font-weight: 750; }

.shell { position: relative; z-index: 1; width: min(100% - 34px, 1400px); margin: 0 auto; padding: 26px 0 40px; }

/* Playing is a screen, not a section of a page: the frame is exactly the window,
   the HUD sits at the top of it and the controls at the bottom, and nothing
   scrolls out from under the card. This is claimed at every size. What used to
   gate it on a window being big enough was the worry that a short one cannot
   hold a kanji and five answer boxes at once — so the way to always fit is to
   let the contents shrink, below, rather than to let the page grow. The stage
   keeps overflow-y and safe centring as the last resort: on a window too small
   for any layout the card scrolls inside its own box, so nothing is ever
   unreachable, but the frame around it still holds. */
body.is-playing { overflow: hidden; }
body.is-playing .shell { display: flex; height: 100dvh; flex-direction: column; width: min(100% - 24px, 1560px); padding: 14px 0 18px; }
body.is-playing .game { display: flex; flex: 1 1 auto; min-height: 0; flex-direction: column; }
body.is-playing .flashcard { display: flex; flex: 1 1 auto; min-height: 0; }
/* min-height:0 lets the stage shrink below the card's own minimum on a short
   window, and safe centring keeps the top of a card that outgrows the stage
   reachable instead of centring it out of reach. */
body.is-playing .card-face { flex: 1 1 auto; min-height: 0; overflow-y: auto; justify-content: safe center; scrollbar-width: thin; scrollbar-color: rgba(255, 255, 255, 0.22) transparent; }
body.is-playing .card-face::-webkit-scrollbar { width: 8px; }
body.is-playing .card-face::-webkit-scrollbar-thumb { background: rgba(255, 255, 255, 0.18); }
body.is-playing .card-face::-webkit-scrollbar-track { background: transparent; }

/* Fitting a short window is a matter of giving back everything that is padding
   rather than content. The character is sized in vh already, so it comes down on
   its own; these are the fixed costs around it — the tall uppercase labels
   above each box, and the padding on every nested panel. */
@media (max-height: 760px) {
  body.is-playing .shell { padding: 8px 0 10px; }
  body.is-playing .game { padding: 10px; }
  body.is-playing .hud { margin-top: 7px; padding: 6px 10px; }
  body.is-playing .card-face { min-height: 0; padding: 12px 14px; }
  body.is-playing .answer-form { margin-top: 8px; padding: 10px 12px 8px; }
  body.is-playing .answer-fields { gap: 9px; }
  body.is-playing .answer-field label { min-height: 0; margin-bottom: 4px; }
  body.is-playing .answer-field input { height: 40px; }
  body.is-playing .streak-status { padding: 5px 11px; }
  /* The status line and the deck's kicker are the first things the bar can lose
     when the height is wanted for the character. */
  body.is-playing .hud-status, body.is-playing #deck-kicker { display: none; }
  /* Only the clamp *floor* comes down here. A 5rem minimum is what overflows a
     short window, so the character has to be allowed to shrink — but the vh term
     stays generous, because this block applies to any window under 760px tall
     and that includes plenty of ordinary laptops with room to spare. Clamping
     the vh term here as well was what kept the character small on a 695px
     window: this rule won, and every ceiling raised elsewhere never applied. */
  body.is-playing .glyph { margin: 0; font-size: clamp(2.6rem, min(58vw, 40vh), 16rem); }
  body.is-playing .flashcard.is-partial-card .glyph { margin: 0; font-size: clamp(2.6rem, min(54vw, 40vh), 16rem); }
  body.is-playing .flashcard.is-form-card .glyph { margin: 0; font-size: clamp(2.6rem, min(30vw, 40vh), 16rem); }
}

/* Shorter still: the prompt chip and the progress readout are the last things
   worth the space they take, so they go before the character does. */
@media (max-height: 560px) {
  body.is-playing .prompt { font-size: 0.82rem; }
  body.is-playing .answer-form { margin-top: 5px; }
  body.is-playing .answer-field input { height: 36px; font-size: 0.95rem; }
  body.is-playing .card-face { padding: 8px 10px; }
  body.is-playing .glyph { margin: 0; font-size: clamp(1.9rem, min(54vw, 36vh), 11rem); }
  body.is-playing .flashcard.is-partial-card .glyph { margin: 0; font-size: clamp(1.9rem, min(50vw, 36vh), 11rem); }
  body.is-playing .flashcard.is-form-card .glyph { margin: 0; font-size: clamp(1.9rem, min(28vw, 36vh), 11rem); }
}

/* Past this width the console stands beside the stage rather than under it. The
   character keeps the middle of the screen at full size, the boxes sit off to
   one side where the hands are, and the card stops being a form on a page. */
@media (min-width: 1240px) and (min-height: 700px) {
  body.is-playing .card-face { flex-direction: row; align-items: center; gap: 26px; padding: 22px; }
  body.is-playing .card-stage { flex: 1 1 auto; min-width: 0; }
  body.is-playing .answer-form,
  body.is-playing .flashcard.is-form-card .answer-form,
  body.is-playing .flashcard.is-partial-card .answer-form { flex: 0 0 clamp(400px, 31%, 540px); width: auto; align-self: center; margin-top: 0; }
  /* The side console is a narrow column, so the boxes pair up two to a row. */
  body.is-playing .answer-field { flex-basis: 44%; }
  /* With the boxes off to the side the stage has its whole height to spend, and
     it should spend it on the character: this is the thing being learned, and at
     this size the strokes are readable rather than merely identifiable. */
  /* The console stands beside the stage here, so the character has roughly half
     the window's width rather than all of it — hence the tighter vw terms. */
  body.is-playing .glyph { font-size: clamp(7rem, min(34vw, 48vh), 27rem); }
  body.is-playing .flashcard.is-form-card .glyph { font-size: clamp(5.5rem, min(25vw, 41vh), 21rem); }
  body.is-playing .flashcard.is-partial-card .glyph { font-size: clamp(6rem, min(32vw, 44vh), 23rem); }
}

/* The masthead is for the menu. Once a card is on screen it is just text
   between you and the kanji. */
body.is-playing .hero { display: none; }

/* A title bar holding the name and nothing else. The strapline and the category
   badge that used to sit beside it were copy about the app rather than part of
   it, and a game does not explain itself above its own menu. With the name
   standing alone it is centred: left-aligned type reads as the start of a
   paragraph that never arrives. */
.hero { position: relative; max-width: none; margin: 0 0 16px; padding: 15px 18px; text-align: center; background: var(--panel); border: 1px solid var(--line); }
.eyebrow { margin: 0; color: var(--orange); font-size: 0.66rem; font-weight: 800; letter-spacing: 0.15em; text-transform: uppercase; }
.hero h1 { margin: 0; }
.active-profile { position: absolute; top: 50%; right: 12px; display: flex; align-items: center; gap: 8px; padding: 5px 10px 5px 5px; cursor: pointer; color: var(--ink); background: rgba(255, 255, 255, 0.055); border: 1px solid var(--line); transform: translateY(-50%); }
.active-profile:hover { border-color: var(--line-lit); background: rgba(85, 164, 255, 0.12); }
.active-profile img { width: 31px; height: 31px; object-fit: cover; border-radius: 50%; }
.active-profile-copy { display: grid; justify-items: start; gap: 1px; }
.active-profile-copy span { font-size: 0.68rem; font-weight: 850; letter-spacing: 0.07em; text-transform: uppercase; }
.active-profile-copy small { color: var(--muted); font-size: 0.55rem; font-weight: 700; letter-spacing: 0.025em; }

h1, h2, p { margin-top: 0; }
h1 {
  margin-bottom: 0;
  /* Electroharmonix draws each Latin letter as a katakana lookalike, which is the
     whole wordmark — so it is named alone with no fallback stack. If the file
     fails to load the browser's default face shows the name in plain letters,
     which is the right failure: legible, just not styled. */
  font-family: Electroharmonix;
  font-size: clamp(1.9rem, 4.4vw, 3rem);
  font-weight: 400;
  line-height: 1.15;
  /* The glyphs are dense and read as kana, so they need air between them; the
     indent gives back the trailing letter-space so the centring is optical. */
  letter-spacing: 0.16em;
  text-indent: 0.16em;
  color: var(--orange);
  /* A flat 2D drop shadow: offset, no blur. The near step is the page's own
     ground so the letters read as cut out of it, with a cool accent behind. */
  text-shadow: 3px 3px 0 var(--bg), 6px 6px 0 rgba(85, 164, 255, 0.3);
}
h2 { margin-bottom: 0; font-size: clamp(1.2rem, 2.6vw, 1.6rem); font-weight: 850; letter-spacing: -0.015em; }

/* Level select. At full width one deck per row would be a mile of list for
   fifty-eight decks; a grid of tiles is something to scan and pick from, and it
   fills the screen the way a game's stage-select does. The tile itself carries
   only what tells decks apart: a glyph, a name, one line, and how far in you
   are. Anything more turned the grid into a wall at about deck fifteen. */
/* One row per line, the full width of the panel. Two and three columns packed
   more sets onto the screen but made the list a grid to hunt around; a single
   column is a list you run down, which is what up and down are steering. */
.deck-picker { display: grid; grid-template-columns: 1fr; gap: 10px; }
.menu-head { display: flex; align-items: center; justify-content: space-between; gap: 12px; margin-bottom: 15px; padding-bottom: 13px; border-bottom: 1px solid var(--line); }
.menu-head h2 { margin: 0; }
/* Which study mode the list is in, said in the smallest way that still reads:
   the set names are long enough that a second clause in the heading wraps. */
.menu-mode { display: inline-block; margin-left: 9px; padding: 4px 10px; vertical-align: 0.18em; color: var(--blue-deep); background: rgba(85, 164, 255, 0.14); border: 1px solid rgba(85, 164, 255, 0.34); font-family: var(--ui); font-size: 0.64rem; font-weight: 800; letter-spacing: 0.08em; text-transform: uppercase; }
.menu-head .text-button { flex: 0 0 auto; }
.menu-head .text-button[hidden] { display: none; }

.deck-progress { display: flex; align-items: center; gap: 8px; margin-top: 9px; color: var(--muted); font-size: 0.66rem; font-weight: 800; font-variant-numeric: tabular-nums; letter-spacing: 0.04em; }
.deck-progress i { display: block; flex: 1 1 auto; height: 6px; overflow: hidden; background: rgba(5, 9, 17, 0.6); border: 1px solid var(--line); }
.deck-progress b { display: block; height: 100%; background: linear-gradient(90deg, #3f8fe0, var(--blue)); box-shadow: 0 0 12px rgba(85, 164, 255, 0.55); transition: width 220ms ease; }
.deck-progress span { flex: 0 0 auto; }
.deck-progress.is-complete b { background: linear-gradient(90deg, #2fb987, var(--green)); box-shadow: 0 0 12px rgba(79, 224, 166, 0.55); }
.deck-progress.is-complete span { color: var(--green); }

.start-menu { width: 100%; margin: 0 0 18px; padding: 20px; background: var(--panel); border: 1px solid var(--line); box-shadow: var(--shadow); }
.start-menu-copy { margin: 16px 0 0; color: var(--muted); font-size: 0.78rem; line-height: 1.5; }

.deck-option { position: relative; display: flex; gap: 13px; align-items: center; width: 100%; padding: 13px 15px; overflow: hidden; cursor: pointer; text-align: left; color: var(--ink); background: linear-gradient(158deg, rgba(255, 255, 255, 0.075), rgba(255, 255, 255, 0.028)); border: 1px solid var(--line); transition: transform 150ms ease, border-color 150ms ease, box-shadow 150ms ease, background 150ms ease; }
.deck-option-text { flex: 1 1 auto; min-width: 0; }
.deck-option:hover { transform: translateY(-2px); background: linear-gradient(158deg, rgba(255, 255, 255, 0.11), rgba(255, 255, 255, 0.04)); border-color: var(--line-lit); box-shadow: var(--lift), 0 0 26px rgba(85, 164, 255, 0.14); }
.deck-option.active { border-color: rgba(255, 161, 77, 0.55); box-shadow: 0 0 0 1px rgba(255, 161, 77, 0.25), 0 0 26px rgba(255, 161, 77, 0.16); }
.deck-icon { display: grid; flex: 0 0 54px; height: 54px; place-items: center; color: var(--orange); background: radial-gradient(circle at 32% 24%, rgba(255, 161, 77, 0.3), rgba(255, 161, 77, 0.07)); border: 1px solid rgba(255, 161, 77, 0.32); box-shadow: inset 0 0 20px rgba(255, 161, 77, 0.12); font-family: var(--jp-serif); font-size: 2rem; text-shadow: 0 0 18px rgba(255, 161, 77, 0.35); transition: transform 150ms ease; }
.deck-option:hover .deck-icon { transform: scale(1.06); }
.deck-option strong, .deck-option small { display: block; }
.deck-option strong { margin-bottom: 3px; font-size: 0.92rem; font-weight: 800; letter-spacing: -0.005em; }
.deck-option small { color: var(--muted); font-size: 0.73rem; line-height: 1.35; }
/* The sets list explains itself in a line under the name. Capped in measure
   rather than left to run the full width of a wide window, where a single
   sentence spanning 1400px is a line you lose your place in. */
.deck-about { max-width: 74ch; margin-bottom: 6px; color: var(--ink); }
/* The keyboard is the primary way through this list, so the focused row is
   styled as the selection rather than as a stray outline on a button. */
.deck-option:focus-visible { outline: none; border-color: var(--line-lit); background: linear-gradient(158deg, rgba(255, 255, 255, 0.13), rgba(255, 255, 255, 0.05)); box-shadow: 0 0 0 1px var(--line-lit), var(--lift), 0 0 30px rgba(85, 164, 255, 0.2); }
.deck-option:focus-visible .deck-go { transform: translateX(3px); color: var(--blue); }
.deck-go { flex: 0 0 auto; color: rgba(255, 255, 255, 0.28); font-size: 1.15rem; transition: transform 150ms ease, color 150ms ease; }
.deck-option:hover .deck-go { transform: translateX(3px); color: var(--blue); }

.game { position: relative; padding: 16px; background: linear-gradient(180deg, rgba(255, 255, 255, 0.062), rgba(255, 255, 255, 0.024)); border: 1px solid var(--line); box-shadow: var(--shadow); }

/* The HUD: which deck, and how far through it. Boxed and pinned to the top of
   the panel so it reads as instrumentation rather than as a heading. */
/* One bar at the foot of the frame: totals in the left corner, the deck in the
   middle, the card's state and the session controls on the right. The two sides
   are given equal flex so the middle sits on the centre of the frame rather than
   wherever the two corners happen to leave it. */
.hud { display: flex; align-items: center; gap: 14px; margin-top: 10px; padding: 8px 12px; background: var(--well); border: 1px solid var(--line); }
.hud-side { display: flex; flex: 1 1 0; min-width: 0; align-items: center; gap: 8px; }
.hud-right { justify-content: flex-end; }
.hud-mid { flex: 0 1 auto; min-width: 0; text-align: center; }
.hud-mid h2 { margin: 2px 0 0; font-size: 0.95rem; letter-spacing: -0.01em; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.hud-status { margin: 0; min-width: 0; overflow: hidden; color: var(--muted); font-size: 0.72rem; line-height: 1.35; text-overflow: ellipsis; }
.progress { margin-top: 5px; color: var(--muted); font-size: 0.66rem; font-weight: 800; letter-spacing: 0.06em; text-transform: uppercase; font-variant-numeric: tabular-nums; }
.progress-track { width: min(240px, 28vw); margin-inline: auto; }

/* The session controls are one button until asked for. They are the rarest thing
   on this bar — chosen once a session, if at all — and at full width they were
   three shouting labels beside the thing you actually watch. */
.hud-menu { position: relative; flex: 0 0 auto; }
.hud-menu-button { display: grid; width: 34px; height: 34px; place-items: center; cursor: pointer; background: rgba(255, 255, 255, 0.07); border: 1px solid var(--line); transition: background 140ms ease, border-color 140ms ease; }
.hud-menu-button:hover, .hud-menu-button[aria-expanded="true"] { background: rgba(85, 164, 255, 0.18); border-color: var(--line-lit); }
/* The outer bars sit ±7px from the middle one, so the stack is 16px tall against
   16px wide. A square icon is what makes an equal inset read as an equal gap —
   at ±5 the stack was 12px tall, and the same inset left more air above it than
   beside it. */
.hamburger, .hamburger::before, .hamburger::after { display: block; width: 16px; height: 2px; background: var(--ink); }
.hamburger { position: relative; }
.hamburger::before, .hamburger::after { position: absolute; left: 0; content: ""; }
.hamburger::before { top: -7px; }
.hamburger::after { top: 7px; }
/* Opens upward: the bar is at the foot of the window, so downward has nowhere to
   go. Right-anchored so a long label grows into the frame, not off the edge. */
/* The pause screen. It takes the window rather than hanging off the button, and
   the ground behind it is only dimmed — the card stays visible underneath, so
   stopping to shuffle never loses your place on it. */
.hud-menu-panel { position: fixed; z-index: 30; display: grid; inset: 0; place-items: center; padding: 24px; background: rgba(5, 9, 17, 0.78); backdrop-filter: blur(7px); }
/* A class setting `display` outranks the user-agent's `[hidden] { display: none }`,
   so the attribute stops hiding anything and the panel sits open over the card.
   Every element styled with a display here has to say so itself. */
.hud-menu-panel[hidden] { display: none; }
/* No panel behind the choices — the dimmed window is the surface. A box here
   would be a second frame inside a frame the dimming already draws. */
.hud-menu-sheet { display: flex; width: min(100%, 300px); flex-direction: column; gap: 9px; text-align: center; }
.hud-menu-sheet .eyebrow { margin-bottom: 6px; }
.input-setting { display: grid; grid-template-columns: 1fr 1fr; gap: 7px; margin: 3px 0 5px; padding: 0; border: 0; text-align: left; }
.input-setting legend { grid-column: 1 / -1; margin-bottom: 1px; color: var(--muted); font-size: 0.64rem; font-weight: 800; letter-spacing: 0.08em; text-transform: uppercase; }
.input-setting label { display: flex; align-items: flex-start; gap: 7px; padding: 9px; cursor: pointer; background: rgba(255, 255, 255, 0.05); border: 1px solid var(--line); }
.input-setting label:has(input:checked) { background: rgba(85, 164, 255, 0.15); border-color: var(--blue); }
.input-setting input { flex: 0 0 auto; margin: 2px 0 0; accent-color: var(--blue); }
.input-setting span, .input-setting small { display: block; }
.input-setting span { color: var(--ink); font-size: 0.7rem; font-weight: 750; line-height: 1.25; }
.input-setting small { margin-top: 3px; color: var(--muted); font-size: 0.61rem; font-weight: 600; }
.hud-menu-panel .text-button { width: 100%; padding: 14px 18px; white-space: nowrap; font-size: 0.76rem; text-align: center; }
.hud-menu-dismiss { margin: 4px 0 0; color: var(--muted); font-size: 0.68rem; }
/* Resume is the way out, not a change to the session, so it reads as the lit
   one and the three that end the card sit behind it. */
.hud-menu-panel .text-button.is-primary { color: #08101d; background: var(--blue); border-color: var(--blue); }
.hud-menu-panel .text-button.is-primary:hover { background: var(--blue-deep); border-color: var(--blue-deep); }

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

/* Standing explanations, folded away. They are read once and then reread by
   nobody, but they cost their height on every card and they are the prose
   competing with the character for the eye. The toggle remembers itself, so
   opening it once keeps it open. */
/* Pinned to the corner of its panel rather than given a line of its own: it is a
   way in to something, not a step in reading the panel, and on its own line it
   cost the height the fold was meant to save. */
/* The corner button is out of flow, so each panel keeps a strip clear for it
   rather than letting a long label or a wide legend run underneath. */
.radical-pick, .radical-detail, .form-context, .answer-form { position: relative; }
.radical-detail, .form-context { padding-right: 32px; }
.answer-form { padding-top: 26px; }
.info-toggle { position: absolute; top: 6px; right: 6px; display: grid; width: 20px; height: 20px; place-items: center; cursor: pointer; color: var(--muted); background: rgba(255, 255, 255, 0.06); border: 1px solid var(--line); font-family: var(--ui); font-size: 0.68rem; font-weight: 900; font-style: italic; line-height: 1; transition: color 140ms ease, background 140ms ease, border-color 140ms ease; }
.info-toggle:hover, .info-toggle[aria-expanded="true"] { color: var(--ink); background: rgba(85, 164, 255, 0.18); border-color: var(--line-lit); }
/* Set apart as an aside rather than as more of the panel: italic, a rule down the
   side, and quieter than the thing it is explaining. It is a note about the
   card, not part of the question. */
.info-body { margin-top: 10px; padding-left: 9px; border-left: 2px solid var(--line-lit); font-style: italic; text-align: left; }
.info-body > p { margin: 0; }
.info-body > p + p { margin-top: 6px; }
.progress-track { height: 10px; margin-top: 8px; overflow: hidden; background: rgba(5, 9, 17, 0.75); border: 1px solid var(--line); box-shadow: inset 0 1px 3px rgba(0, 0, 0, 0.6); }
.progress-track i { display: block; width: 2%; height: 100%; background: linear-gradient(90deg, var(--green), var(--blue)); box-shadow: 0 0 14px rgba(85, 164, 255, 0.6); transition: width 220ms ease; }

.flashcard { width: 100%; }
/* The stage. Lit from the top so the character sits in a pool of light with the
   panel falling away below it. */
/* The stage packs from the top rather than centring its column. Centring stacked
   the prompt, the character and the console into a block in the middle of the
   frame with equal dead space above and below it; starting at the top gives the
   prompt its own place, lets the character take the room that opens up, and the
   console sits under it instead of being squeezed against it. */
.card-face { position: relative; display: flex; min-height: 340px; flex-direction: column; align-items: center; justify-content: space-between; gap: clamp(12px, 2.5vh, 36px); padding: 20px 22px 26px; background: radial-gradient(120% 86% at 50% -6%, rgba(85, 164, 255, 0.13), transparent 62%), var(--well); border: 1px solid var(--line); box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.05), inset 0 -60px 90px rgba(0, 0, 0, 0.35); }
/* Real space between the question, the character and the boxes. These used to be
   three blocks pressed together with only their own margins between them, which
   read as one crowded lump in the middle of an otherwise empty frame. The gap
   scales with the window, so a tall screen spends its extra height on air rather
   than on a bigger void underneath. */
.card-stage { display: flex; width: 100%; min-width: 0; flex: 1 1 auto; flex-direction: column; align-items: center; justify-content: space-evenly; gap: clamp(14px, 4vh, 52px); }
/* Set larger than the words around it, in the serif stack the kanji use, and
   with the prompt's wide tracking cancelled — that tracking is for uppercase
   Latin and puts a gap after a character that has no gap in it. */
/* The same glow the character on the stage carries, so the one named in the
   question reads as the same object rather than as a letter in the sentence. */
.prompt-kanji { color: var(--ink); font-family: var(--jp-serif); font-size: 1.75em; font-weight: 700; letter-spacing: 0; line-height: 1; vertical-align: -0.16em; text-shadow: 0 0 18px rgba(148, 198, 255, 0.4), 0 2px 4px rgba(5, 9, 17, 0.85); }

/* Centred, because on a narrow window the question wraps to two lines and a
   ragged left edge inside a centred box reads as a mistake. */
.prompt { padding: 0; color: var(--muted); background: none; border: 0; font-size: clamp(0.95rem, 1.5vw, 1.25rem); font-weight: 800; letter-spacing: 0.09em; line-height: 1.45; text-align: center; text-transform: uppercase; }
/* Sized against the window's height as well as its width. A card has to hold a
   character, a legend and five boxes at once, so on a short window the glyph is
   the thing that gives way — it is still the biggest thing on screen. */
/* The character is the thing being learned and the only detailed thing on the
   screen, so it takes what the frame can give it. The vh terms rose with the
   header gone and the console hidden while picking — there is simply more stage
   than there was. The clamp floors are what a short window shrinks to, set in
   the short-viewport blocks above. */
/* Height is what actually limits the character; the vw term is only here to stop
   it running off the sides. A lone kanji is one em wide, so that limit can be
   generous — it used to be 30vw, which shrank the character on a narrow window
   that had all the vertical room in the world. Word cards keep a tighter limit
   below, because their glyph carries okurigana either side and is several em
   wide rather than one. */
.glyph { display: inline-flex; align-items: baseline; margin: 0; color: #fff; font-family: var(--jp-serif); font-size: clamp(5rem, min(58vw, 44vh), 22rem); line-height: 1; text-shadow: 0 0 46px rgba(148, 198, 255, 0.28); }
.kanji-glyph { position: relative; display: inline-block; line-height: 1; }
.word-affix { align-self: flex-end; padding-bottom: 0.05em; color: rgba(234, 240, 251, 0.72); font-size: 0.52em; line-height: 1; }
.word-prefix { margin-right: 0.03em; }
.word-suffix { margin-left: 0.03em; }
.flashcard.is-form-card .glyph { margin: 0; font-size: clamp(4.4rem, min(30vw, 36vh), 18rem); }
/* Larger than a word card's glyph, not smaller: the arrow round asks you to
   read the parts of one character, and the boxes have to be told apart. */
.flashcard.is-partial-card .glyph { margin: 0; font-size: clamp(4.8rem, min(54vw, 39vh), 19rem); }
/* Colouring a component, not boxing it.
   The kanji is drawn from its own stroke paths rather than set as text, so a
   component can be coloured exactly. Clipping a text glyph to a rectangle cut
   strokes in half wherever a component reached across the dividing line, which
   it does constantly — 包's strokes inside 泡 run left past the midpoint. */
/* Exactly one of the two renderings is visible, chosen by the parent's class.
   Neither child carries a `hidden` attribute, because that attribute behaves
   differently on an HTML span and an SVG root. */
.kanji-glyph.shows-strokes > #kanji-text { display: none; }
.kanji-glyph:not(.shows-strokes) > .kanji-svg { display: none; }
.kanji-svg {
  display: block;
  width: 1em;
  height: 1em;
  overflow: visible;
  filter: drop-shadow(0 0 22px rgba(0, 0, 0, 0.5));
}
.kanji-svg path {
  fill: none;
  stroke-width: 4.6;
  stroke-linecap: round;
  stroke-linejoin: round;
  transition: stroke 180ms ease;
}
/* While the choice is open both parts are tinted, differently, so the split is
   visible without either being marked as the answer. Every stroke colour here
   is a light on a dark ground now, so all of them are the bright end of their
   hue rather than the ink end. */
.kanji-svg path.s-one { stroke: #7fb2ff; }
.kanji-svg path.s-two { stroke: #e0b880; }
.kanji-svg path.s-target { stroke: var(--orange); }
.kanji-svg path.s-rest { stroke: #5b687f; }

/* Parts are numbered rather than pointed at, so a kanji made of five pieces
   asks its question the same way one made of two does. The legend draws each
   part as just its own strokes, in the same box the whole character occupies,
   so both its shape and where it sits are visible at a glance. */
/* No trailing margin: it used to hold the legend off the help text, and with the
   help text folded away it was 11px hanging under the chips, sitting them above
   the middle of their own box. Whatever follows brings its own top margin. */
/* Six parts is the most any card here splits into, and they stay on one row:
   min-width lets each chip give way as the row fills rather than wrapping, which
   is what keeps the numbers in a line you can read across. */
.part-legend { display: flex; flex-wrap: wrap; gap: 10px; justify-content: center; margin: 0; }
.part-legend .part-key { flex: 0 1 auto; min-width: 0; }
/* The chips are the answer, so they are sized to be aimed at rather than read:
   a bigger drawing of the part and a bigger target around it. The part sits
   above its number rather than beside it — the drawing is what you are choosing
   between, and the number is only how you say which one. */
/* No padding on the chip itself: the coloured number is a band across the foot
   of it, so any padding here would inset that band and leave a frame of chip
   showing around three of its sides. The drawing carries its own margin
   instead. */
.part-key { position: relative; display: inline-flex; align-items: center; gap: 0; flex-direction: column; padding: 0; overflow: hidden; cursor: pointer; color: inherit; background: rgba(255, 255, 255, 0.06); border: 1px solid var(--line); border-bottom: 3px solid var(--part); transition: transform 120ms ease, background 120ms ease, border-color 120ms ease; }
.part-key:hover { transform: translateY(-2px); background: rgba(255, 255, 255, 0.11); border-color: var(--line-lit); }
.part-key:active { transform: translateY(0) scale(0.97); }
/* A band across the whole foot of the chip rather than a badge floating inside
   it — full width, flush to all three edges, so it reads as the chip's label and
   stays legible when a crowded row squashes the drawing above it. Kept slim: it
   names the key to press, so it has to be readable and nothing more. The part
   above is the thing being chosen and should carry the weight. */
/* Tucked into the top-right corner, out of the drawing's way. It is a key hint
   rather than a caption, so it sits where an index number sits: findable without
   being looked at, and never competing with the part below it. Absolute, so it
   costs the chip no height and the drawing keeps the whole face. */
.part-key b { position: absolute; top: 4px; right: 5px; color: var(--part); font-family: var(--ui); font-size: 0.62rem; font-weight: 900; line-height: 1; letter-spacing: 0.04em; }
/* The drawing is what gives when the row runs out of room: min-width lets it
   shrink below its ideal size, and the aspect ratio keeps it square while it
   does, so six parts squash rather than wrap into an unreadable stack. The
   margin is the chip's padding, carried here so the number band below can still
   reach the edges. */
/* With the number lifted into the corner the drawing has the chip's whole face,
   so it takes it: the part is what is actually being chosen between, and at the
   old size two similar components were told apart by squinting. */
.part-key svg { display: block; width: clamp(52px, 7vh, 96px); min-width: 0; height: auto; margin: 7px 9px 6px; aspect-ratio: 1; }
.part-key svg path { fill: none; stroke-width: 5.5; stroke-linecap: round; stroke-linejoin: round; }
/* The rest of the character behind the part: thinner as well as fainter, so it
   reads as the outline of where the part sits and never as a candidate itself. */
.part-key svg path.s-ghost { stroke: var(--muted); stroke-width: 4; opacity: 0.22; }

/* One colour per part, shared by the legend chip and the strokes it names. */
/* Each part's colour named once, then spent in three small places rather than as
   a filled block: the strokes, the digit, and a rule across the foot of the
   chip. A solid band of it under every part was more colour than the job needs —
   the tie between chip and strokes only has to be recognised, not announced. */
.s-p1 { --part: #6ea9ff; }
.s-p2 { --part: #4fd7ad; }
.s-p3 { --part: #b892ff; }
.s-p4 { --part: #f0b45c; }
.s-p5 { --part: #86d46a; }
.s-p6 { --part: #ff8fb0; }
.part-key svg path, .kanji-svg path.s-p1, .kanji-svg path.s-p2, .kanji-svg path.s-p3,
.kanji-svg path.s-p4, .kanji-svg path.s-p5, .kanji-svg path.s-p6 { stroke: var(--part); }

.radical-pick { width: min(100%, 780px); margin: 0; padding: 20px 22px; text-align: center; background: rgba(85, 164, 255, 0.07); border: 1px solid rgba(85, 164, 255, 0.26); }
.radical-pick-help { margin: 0; color: var(--muted); font-size: 0.8rem; line-height: 1.6; }
.radical-pick-help em { display: block; margin-top: 3px; color: #7f8ca2; font-size: 0.92em; font-style: normal; }
.radical-pick kbd { display: inline-block; min-width: 1.4em; color: var(--blue-deep); font-family: inherit; font-weight: 700; text-align: center; }
.radical-pick-note { margin: 8px 0 0; color: #d8b880; font-size: 0.75rem; line-height: 1.45; }
.radical-pick-note[hidden] { display: none; }
/* The reserved line kept the box from jumping when a verdict arrived, but it is
   empty on every pick that goes right — a blank row under the chips on the
   common case to spare a nudge on the rare one. It collapses when there is
   nothing in it, and the stage re-centres the box if it grows. */
.radical-pick-feedback { min-height: 1.2em; margin: 10px 0 0; font-size: 0.79rem; font-weight: 800; }
.radical-pick-feedback:empty { min-height: 0; margin: 0; }
.radical-pick-feedback.incorrect { color: var(--red); }

.radical-detail { width: min(100%, 620px); margin: 8px 0 10px; padding: 13px 15px; text-align: left; background: rgba(255, 161, 77, 0.06); border: 1px solid rgba(255, 161, 77, 0.24); }
.radical-forms { display: flex; flex-wrap: wrap; gap: 7px; margin-top: 8px; }
.radical-forms .form-example.is-this-form { border-color: var(--orange); box-shadow: 0 0 0 1px rgba(255, 161, 77, 0.35), 0 0 18px rgba(255, 161, 77, 0.2); }
.form-context { width: min(100%, 560px); margin: 8px 0 10px; padding: 13px 15px; text-align: left; background: rgba(255, 161, 77, 0.06); border: 1px solid rgba(255, 161, 77, 0.24); }
.form-context-title { margin: 0; color: var(--orange); font-size: 0.65rem; font-weight: 850; letter-spacing: 0.1em; text-transform: uppercase; }
.form-parent { margin: 6px 0 9px; color: var(--muted); font-family: var(--jp-serif); font-size: 0.83rem; }
.form-examples { display: flex; flex-wrap: wrap; gap: 7px; }
.form-example { display: inline-flex; align-items: center; gap: 6px; padding: 6px 9px; color: var(--ink); background: rgba(255, 255, 255, 0.055); border: 1px solid var(--line); font-family: var(--jp-serif); }
.form-fragment { display: inline-grid; min-width: 1.5em; place-items: center; color: var(--orange); background: var(--orange-pale); font-size: 1.07rem; font-weight: 800; }
.form-in { color: var(--muted); font-family: var(--ui); font-size: 0.72rem; }
.form-kanji { font-size: 1.25rem; }
.form-context-note { margin: 9px 0 0; color: var(--muted); font-size: 0.71rem; line-height: 1.4; }

/* The console the answers are typed into: a darker well under the lit stage, so
   the character is what the eye lands on and the boxes are where the hands go. */
.answer-form { width: min(100%, 780px); margin-top: 0; padding: 18px 20px 16px; background: rgba(5, 9, 17, 0.5); border: 1px solid var(--line); }
.flashcard.is-form-card .answer-form { width: min(100%, 1040px); }
.flashcard.is-partial-card .answer-form { width: min(100%, 1040px); }
/* Wrapping and centred rather than a fixed three columns. A card asks for two
   fields, three or five depending on its deck, and hidden fields are removed
   from layout — so a fixed grid left the two-field cards sitting in columns one
   and two with a dead column beside them, and the last two of five hanging off
   the left. Centring makes every count sit under the middle of the card. */
.answer-fields { display: flex; flex-wrap: wrap; justify-content: center; gap: 13px; }
.answer-field { display: flex; min-width: 0; flex: 1 1 clamp(160px, 29%, 300px); flex-direction: column; text-align: left; }
.answer-field[hidden], .field-answer[hidden] { display: none !important; }
.answer-field label { display: flex; min-height: 3.2em; align-items: flex-end; margin-bottom: 8px; color: var(--muted); font-size: 0.67rem; font-weight: 800; letter-spacing: 0.075em; text-transform: uppercase; }
.answer-field input { width: 100%; min-width: 0; height: 56px; padding: 10px 14px; color: var(--ink); background: rgba(255, 255, 255, 0.06); border: 1px solid rgba(255, 255, 255, 0.16); font-family: var(--jp-sans); font-size: 1.25rem; transition: border-color 140ms ease, box-shadow 140ms ease, background 140ms ease; }
.answer-field input:focus { outline: none; background: rgba(255, 255, 255, 0.1); border-color: var(--blue); box-shadow: 0 0 0 3px rgba(85, 164, 255, 0.2), 0 0 22px rgba(85, 164, 255, 0.22); }
.answer-field input:disabled { opacity: 0.5; }
/* The box itself answers back. The verdict already prints under the field, but
   a line of text below the cursor is not where the eye is — the border is. */
.answer-field:has(.field-feedback.correct) input { border-color: rgba(79, 224, 166, 0.75); box-shadow: 0 0 0 3px rgba(79, 224, 166, 0.15), 0 0 20px rgba(79, 224, 166, 0.2); }
.answer-field:has(.field-feedback.incorrect) input { border-color: rgba(255, 130, 114, 0.75); box-shadow: 0 0 0 3px rgba(255, 130, 114, 0.15); animation: shake 240ms ease; }

@keyframes shake {
  20% { transform: translateX(-5px); }
  50% { transform: translateX(4px); }
  80% { transform: translateX(-2px); }
}
/* The "empty if none" placeholder shows on every word card, never only on the
   rootless ones — a hint that appeared just for those would give the answer
   away before it was recalled. It lives in the input rather than the label
   because the label is a flex row, where a second child stacks beside the
   text instead of running on from it. */
.answer-field input::placeholder { color: #6c7a90; font-size: 0.78rem; }

.field-feedback { min-height: 1.2em; margin: 8px 0 0; font-size: 0.76rem; line-height: 1.35; }
/* Folded behind the ⓘ, so it needs no reserved line of its own — the info body
   already spaces it, and a min-height here would pad the open panel. */
.kana-help { margin: 0; color: var(--muted); font-size: 0.76rem; line-height: 1.35; }
.field-feedback { font-weight: 800; }
.field-feedback.correct { color: var(--green); }
.field-feedback.incorrect { color: var(--red); }
.field-answer { margin-top: 8px; padding: 9px 11px; background: rgba(255, 161, 77, 0.09); border: 1px solid rgba(255, 161, 77, 0.28); }
.field-answer span, .field-answer strong { display: block; }
.field-answer span { color: var(--muted); font-size: 0.63rem; font-weight: 800; letter-spacing: 0.08em; text-transform: uppercase; }
.field-answer strong { margin-top: 3px; color: #ffd0a0; font-family: var(--jp-sans); font-size: 1rem; }

/* Three lamps, not a sentence: how many clean passes this card has banked is
   the one number worth reading mid-card, and dots are read without stopping. */
.streak-status { display: inline-flex; align-items: center; gap: 10px; margin: 0; padding: 7px 14px; color: var(--green); background: rgba(79, 224, 166, 0.09); border: 1px solid rgba(79, 224, 166, 0.28); font-size: 0.68rem; font-weight: 850; letter-spacing: 0.1em; text-transform: uppercase; }
.streak-dots { display: inline-flex; gap: 5px; }
.streak-dots i { display: block; width: 9px; height: 9px; background: rgba(255, 255, 255, 0.13); box-shadow: inset 0 0 0 1px rgba(255, 255, 255, 0.12); transition: background 180ms ease, box-shadow 180ms ease; }
.streak-dots i.is-lit { background: var(--green); box-shadow: 0 0 11px rgba(79, 224, 166, 0.75); }

.stat { display: inline-flex; flex: 0 0 auto; align-items: baseline; gap: 6px; padding: 5px 10px; background: rgba(255, 255, 255, 0.05); border: 1px solid var(--line); font-size: 0.66rem; font-weight: 800; letter-spacing: 0.07em; text-transform: uppercase; white-space: nowrap; }
.stat b { font-size: 0.95rem; font-variant-numeric: tabular-nums; }
#got-it-count { color: var(--green); }
#again-count { color: var(--red); }

/* Controls are pressable things, not links in a paragraph. The back button in
   the menu head is the same object, so leaving a level and leaving a deck look
   and land the same. */
.text-button { padding: 0; cursor: pointer; color: var(--blue-deep); background: transparent; border: 0; font-size: inherit; font-weight: 800; }
.hud-menu-panel .text-button, .menu-head .text-button { padding: 8px 13px; color: var(--ink); background: rgba(255, 255, 255, 0.07); border: 1px solid var(--line); font-size: 0.7rem; font-weight: 800; letter-spacing: 0.08em; text-transform: uppercase; transition: background 140ms ease, border-color 140ms ease, transform 140ms ease; }
.hud-menu-panel .text-button:hover, .menu-head .text-button:hover { transform: translateY(-1px); background: rgba(85, 164, 255, 0.18); border-color: var(--line-lit); }

.completion-overlay { position: fixed; z-index: 20; inset: 0; display: grid; place-items: center; padding: 24px; background: rgba(3, 7, 14, 0.74); -webkit-backdrop-filter: blur(4px); backdrop-filter: blur(4px); }
.completion-overlay[hidden] { display: none; }
/* The reward beat. It arrives with a snap rather than a fade, because it is the
   answer to the thing you just did. */
.completion-popup { width: min(100%, 560px); max-height: calc(100vh - 48px); overflow-y: auto; padding: 30px; text-align: center; background: linear-gradient(180deg, #1a2438, #101728); border: 1px solid rgba(140, 196, 255, 0.3); box-shadow: 0 30px 90px rgba(0, 0, 0, 0.7), 0 0 70px rgba(85, 164, 255, 0.14); animation: pop-in 220ms cubic-bezier(0.2, 1.3, 0.5, 1) both; }
.completion-popup:focus { outline: none; }
.completion-popup h2 { color: var(--green); font-size: clamp(1.6rem, 3.4vw, 2.2rem); font-weight: 900; letter-spacing: -0.01em; text-shadow: 0 0 26px rgba(79, 224, 166, 0.35); }
.completion-glyph { min-height: 1em; margin: 8px 0 12px; color: #fff; font-family: var(--jp-serif); font-size: clamp(4.2rem, 17vw, 6.1rem); line-height: 1; text-shadow: 0 0 44px rgba(148, 198, 255, 0.3); }
.completion-answer { margin: 0; color: var(--ink); font-size: 1.05rem; font-weight: 800; }
.completion-note { margin: 14px auto 0; color: var(--muted); font-size: 0.9rem; line-height: 1.5; }
.completion-source { display: inline-block; margin-top: 12px; color: var(--blue); font-size: 0.76rem; font-weight: 800; }
.completion-source[hidden] { display: none; }
.listen-replay { margin-top: -4px; padding: 10px 16px; color: var(--ink); background: rgba(85, 164, 255, 0.12); border: 1px solid rgba(85, 164, 255, 0.35); font: inherit; font-size: 0.82rem; font-weight: 800; cursor: pointer; }
.listen-replay:hover, .listen-replay:focus-visible { background: rgba(85, 164, 255, 0.22); border-color: var(--blue); outline: none; }
.listen-replay[hidden] { display: none; }
.completion-radical, .completion-root { margin: 12px 0 0; padding: 10px 12px; font-size: 0.88rem; line-height: 1.45; }
.completion-radical { color: #ffc79a; background: var(--orange-pale); border: 1px solid rgba(255, 161, 77, 0.3); }
.completion-root { color: var(--blue-deep); background: rgba(85, 164, 255, 0.1); border: 1px solid rgba(85, 164, 255, 0.3); }
.completion-help { margin: 19px 0 0; color: var(--muted); font-size: 0.78rem; line-height: 1.45; }
.completion-help strong { color: var(--blue-deep); }

@keyframes pop-in {
  from { opacity: 0; transform: scale(0.9) translateY(10px); }
  to { opacity: 1; transform: none; }
}

@media (max-width: 900px) and (min-width: 621px) {
  .answer-field { flex-basis: 44%; }
}

@media (max-width: 620px) {
  .shell { width: min(100% - 20px, 1400px); padding-top: 18px; }
  .hero { padding: 12px 14px; }
  .active-profile-copy { display: none; }
  .active-profile { right: 8px; padding: 4px; }
  .profile-gate { padding: 12px; }
  .profile-panel { width: calc(100vw - 24px); padding: 22px 16px; }
  .profile-choices { gap: 10px; }
  .profile-choice { padding: 10px; }
  .profile-choice img { width: 100%; max-width: 140px; }
  .start-menu, .game { padding: 14px; }
  .card-face { min-height: 320px; padding: 18px 14px; }
  .answer-form { padding: 13px; }
  .answer-fields { gap: 9px; }
  .answer-field { flex-basis: 100%; }
  .answer-field label { min-height: 0; }
  /* Too narrow for three columns. The running totals and the status line are the
     parts that can be read again at any time, so the bar keeps what changes:
     the deck, the streak and the menu.

     The progress track becomes a hairline along the bottom edge of the whole
     bar. In the flow it was a stub of a rule under the deck name with nothing
     to align to, which read as a stray mark; pinned to the edge it is a
     progress bar for the bar itself and costs no height at all. The three lamps
     drop their label, because "CLEAN PASSES" beside them was most of the width
     spent on a caption for three dots that are already lit or not. */
  .hud { position: relative; gap: 10px; padding: 9px 12px 11px; }
  .hud-left { display: none; }
  .hud-mid { flex: 1 1 auto; min-width: 0; text-align: left; }
  .hud-mid h2 { margin: 0; font-size: 0.88rem; }
  .progress { margin-top: 2px; font-size: 0.6rem; }
  .progress-track { position: absolute; right: 0; bottom: 0; left: 0; width: auto; height: 3px; margin: 0; border: 0; box-shadow: none; }
  .hud-right { flex: 0 0 auto; gap: 8px; }
  .streak-label { display: none; }
  .streak-status { padding: 7px 10px; }
}

/* Any window too thin to hold the bar's three columns comfortably — a phone, or
   a narrow desktop window — lifts the menu out of the bar and floats it at the
   top-right of the screen, where a thumb reaches it and where it stops competing
   with the deck name for the width. It lives inside the game section, so it
   appears only while a card is up. */
@media (max-width: 860px) {
  /* The masthead is hidden while playing, so the shell's top padding and the
     game panel's own were 30px of nothing above the card on the screen with the
     least height to spare. Trimmed to the width of the frame's edge. */
  body.is-playing .shell { padding-top: 6px; }
  body.is-playing .game { padding: 8px; }
  /* Into the corner of the window. Just the icon — no border, no fill, nothing
     for it to sit inside. The bars carry a shadow so they hold up over a stroke
     of the kanji, which is the only thing that ever passes behind them. */
  /* A little further in from the right than from the top: the window's right
     edge has the frame's border running down it, and sitting level with that
     border reads as touching it. */
  .hud-menu { position: fixed; top: 5px; right: 11px; z-index: 20; }
  .hud-menu-button {
    width: 46px;
    height: 46px;
    /* Faint by default. It is reached for perhaps once a session, so it only has
       to be findable, not read — and full-strength ink in the corner of a screen
       whose whole point is the character in the middle is one bright thing too
       many. It comes up to full on hover, focus, and while open. */
    color: var(--ink);
    opacity: 0.34;
    background: none;
    border: 0;
    box-shadow: none;
    filter: drop-shadow(0 1px 3px rgba(5, 9, 17, 0.9));
    transition: opacity 160ms ease;
  }
  .hud-menu-button:hover,
  .hud-menu-button:focus-visible,
  .hud-menu-button[aria-expanded="true"] { background: none; opacity: 1; }
}

@media (max-width: 410px) {
  .streak-status { padding: 5px 9px; font-size: 0.62rem; }
  .hud-mid h2 { font-size: 0.85rem; }
}

@media (prefers-reduced-motion: reduce) {
  /* A field of things sliding across the whole viewport is the first thing to go
     when less motion has been asked for. Stopping the animation alone would park
     every glyph at its start position, off the bottom of the screen, so they are
     laid out down the page instead and simply hold still. */
  .kanji-drift span { top: var(--drift-top); bottom: auto; animation: none; }
  .deck-progress b, .progress-track i, .kanji-svg path, .streak-dots i { transition: none; }
  .deck-option, .deck-go, .deck-icon, .answer-field input, .text-button, .part-key { transition: none; }
  .part-key:hover, .part-key:active { transform: none; }
  .answer-field:has(.field-feedback.incorrect) input { animation: none; }
  .deck-option:hover { transform: none; }
  .deck-option:hover .deck-go, .deck-option:hover .deck-icon { transform: none; }
  .hud-menu-panel .text-button:hover, .menu-head .text-button:hover { transform: none; }
  .completion-popup { animation: none; }
}
