/* app.css - the look of Issho ni Nihongo: Indigo (藍) with Pop's play layer and Paper's study marks.
   docs/design-system.md lists every class a screen should use, with what it is for and a snippet.
   Rules it keeps:
   - Every colour, size, space and radius is a token on :root. Light ships on :root; dark ("indigo night") on
     html[data-theme=dark], and again under prefers-color-scheme when no theme is chosen. A screen never
     writes a colour; the old names (--panel, --sunk, --fg, --mute ...) stay as aliases of the new tokens so
     every existing screen and the other sheets follow.
   - One family, Noto Sans JP (public/fonts, sliced by unicode-range), with tabular numbers on every stat;
     Klee One only for the handwriting model (the 方眼 card, Write). 14 px is the floor for any word, inputs
     are 16 px (phones never zoom), every target is 44 px with 8 px between neighbours.
   - Vermilion (朱) is warm and rare: Tomo, the JLPT pass line, the brand's dot, the teacher's はなまる and the
     season's starburst. Done, known and right are one mark everywhere (.maru: a tick cut out of a filled
     tokiwa-green circle, --i-done), since English speakers read an open ring as an empty radio button. Wrong is
     enji (臙脂), a different hue, always with an icon and words.
   - Japanese: native <ruby>, rt at 14 px or more, centred over its word, never spread across a compound
     (ruby-align: center; the base is never letter-spaced; the rt itself never gets a margin). A reading wider
     than its word reaches over the kana or punctuation beside it by up to one reading character a side, never
     over a kanji (JIS X 4051): core.js fitRuby() measures each and pulls the neighbour in with a negative margin
     on the <ruby>, and one at the start of its block starts with its word (rb-head), so a list row keeps to
     the gutter; blocks that can carry ruby use a fixed line height of about
     2.1, which holds the ruby band, so lines never jump when furigana come and go, but a bubble keeps a
     normal line (its ruby lines take what they need); lines break between phrases (auto-phrase, strict).
   - Calm first screen, full width for data on a 12-column grid, no sideways scroll at 320 px, flat cards
     (a hairline, no shadow; only what floats over the page casts one), hover only where a pointer hovers,
     nothing in a phone's notch or home-indicator band (env(safe-area-inset-*)), and the bar, the tab bar and
     Tomo's corner are allowed for when the browser scrolls something into view.
   - Tomo (tutor.js sets html.has-tutor and --tutor-room, how far her corner reaches up from the bottom of the
     window; app.js sets html[data-tomo]): on an upright phone she stands at the right end of the bar
     (data-tomo=bar), is only a passing word under the bar in a session (quiet), and is not there at all over
     a writing pad, a test or a call (off), so she never covers a button; elsewhere the page is padded by her
     corner and sticky bottom bars are lifted by it. She hides completely while a field outside her chat has
     the keyboard (.tutor.typing), at every width. */

/* ===================================================================================================
   Tokens
   =================================================================================================== */
:root {
  color-scheme: light;
  /* type */
  --font-sans: "Noto Sans JP", "Hiragino Sans", "Hiragino Kaku Gothic ProN", "Yu Gothic UI", "Meiryo", system-ui, -apple-system, "Segoe UI", Roboto, sans-serif;
  --font-hand: "Klee One", "Noto Sans JP", "Hiragino Mincho ProN", serif;
  --mono: ui-monospace, "SF Mono", Menlo, Consolas, monospace;
  --fs-14: 14px; --fs-16: 16px; --fs-18: 18px; --fs-20: 20px; --fs-22: 22px; --fs-24: 24px; --fs-26: 26px;
  --fs-30: 30px; --fs-40: 40px; --fs-56: 56px; --fs-input: 16px;
  --lh-tight: 1.15; --lh-body: 1.6; --lh-ja: 1.85; --lh-ruby: 2.1; --lh-ruby-big: 1.9;
  --track-label: .08em;
  /* space (4-pt), radius, sizes */
  --s-1: 4px; --s-2: 8px; --s-3: 12px; --s-4: 16px; --s-5: 24px; --s-6: 32px; --s-7: 48px; --s-8: 64px; --s-9: 96px;
  --r-1: 4px; --r-2: 8px; --r-3: 12px; --r-pill: 999px;
  --tap: 44px; --btn-pad: 20px; --bar-h: 64px; --tab-h: 0px; --tutor-w: 64px;
  --gutter: clamp(16px, 2.8vw, 40px); --page-max: 1440px; --grid-gap: 24px; --measure: 66ch;
  --gut-l: max(var(--gutter), env(safe-area-inset-left)); --gut-r: max(var(--gutter), env(safe-area-inset-right));
  /* the signed-in bar's right padding: ⋯'s dots end on the gutter (app.css, the bar), Tomo's face stands beside it */
  --bar-r: max(calc(var(--gutter) - 13px), env(safe-area-inset-right));

  /* light: white cards on a cool washed page, kon ink */
  --bg: #F3F5F8;
  --surface: #FFFFFF;
  --surface-2: #EDF2F6;
  --line: #DCE2EA;
  --line-strong: #B4BFCE;
  --field: #77849C;            /* the edge of a field or switch: 3:1 or more on every surface */
  --ink: #0E1830;              /* 褐 kachi */
  --ink-2: #36435F;
  --ink-3: #5B6782;
  --rt: #4E5A75;               /* furigana */
  --accent: #1C356B;           /* 紺 kon: primary buttons, current tab, key tile */
  --accent-hover: #24477F;
  --accent-ink: #FFFFFF;
  --accent-soft: #E4EDF5;      /* 瓶覗 kamenozoki */
  --accent-text: #1F4F8A;      /* 藍 ai: links, section numbers, pitch lines */
  --band: #172B5C;             /* the one indigo band (Today) */
  --band-ink: #FFFFFF;
  --band-ink-2: #C2D0EA;
  --band-seg: rgba(255, 255, 255, .17);
  --band-sg: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='44' height='22' viewBox='0 0 44 22'%3E%3Ccircle cx='22' cy='-11' r='22' fill='%23172B5C'/%3E%3Ccircle cx='22' cy='-11' r='16.28' fill='none' stroke='rgba(255,255,255,.11)' stroke-width='1.2'/%3E%3Ccircle cx='22' cy='-11' r='10.56' fill='none' stroke='rgba(255,255,255,.11)' stroke-width='1.2'/%3E%3Ccircle cx='22' cy='-11' r='4.84' fill='none' stroke='rgba(255,255,255,.11)' stroke-width='1.2'/%3E%3Ccircle cx='0' cy='0' r='22' fill='%23172B5C'/%3E%3Ccircle cx='0' cy='0' r='16.28' fill='none' stroke='rgba(255,255,255,.11)' stroke-width='1.2'/%3E%3Ccircle cx='0' cy='0' r='10.56' fill='none' stroke='rgba(255,255,255,.11)' stroke-width='1.2'/%3E%3Ccircle cx='0' cy='0' r='4.84' fill='none' stroke='rgba(255,255,255,.11)' stroke-width='1.2'/%3E%3Ccircle cx='44' cy='0' r='22' fill='%23172B5C'/%3E%3Ccircle cx='44' cy='0' r='16.28' fill='none' stroke='rgba(255,255,255,.11)' stroke-width='1.2'/%3E%3Ccircle cx='44' cy='0' r='10.56' fill='none' stroke='rgba(255,255,255,.11)' stroke-width='1.2'/%3E%3Ccircle cx='44' cy='0' r='4.84' fill='none' stroke='rgba(255,255,255,.11)' stroke-width='1.2'/%3E%3Ccircle cx='22' cy='11' r='22' fill='%23172B5C'/%3E%3Ccircle cx='22' cy='11' r='16.28' fill='none' stroke='rgba(255,255,255,.11)' stroke-width='1.2'/%3E%3Ccircle cx='22' cy='11' r='10.56' fill='none' stroke='rgba(255,255,255,.11)' stroke-width='1.2'/%3E%3Ccircle cx='22' cy='11' r='4.84' fill='none' stroke='rgba(255,255,255,.11)' stroke-width='1.2'/%3E%3Ccircle cx='0' cy='22' r='22' fill='%23172B5C'/%3E%3Ccircle cx='0' cy='22' r='16.28' fill='none' stroke='rgba(255,255,255,.11)' stroke-width='1.2'/%3E%3Ccircle cx='0' cy='22' r='10.56' fill='none' stroke='rgba(255,255,255,.11)' stroke-width='1.2'/%3E%3Ccircle cx='0' cy='22' r='4.84' fill='none' stroke='rgba(255,255,255,.11)' stroke-width='1.2'/%3E%3Ccircle cx='44' cy='22' r='22' fill='%23172B5C'/%3E%3Ccircle cx='44' cy='22' r='16.28' fill='none' stroke='rgba(255,255,255,.11)' stroke-width='1.2'/%3E%3Ccircle cx='44' cy='22' r='10.56' fill='none' stroke='rgba(255,255,255,.11)' stroke-width='1.2'/%3E%3Ccircle cx='44' cy='22' r='4.84' fill='none' stroke='rgba(255,255,255,.11)' stroke-width='1.2'/%3E%3Ccircle cx='22' cy='33' r='22' fill='%23172B5C'/%3E%3Ccircle cx='22' cy='33' r='16.28' fill='none' stroke='rgba(255,255,255,.11)' stroke-width='1.2'/%3E%3Ccircle cx='22' cy='33' r='10.56' fill='none' stroke='rgba(255,255,255,.11)' stroke-width='1.2'/%3E%3Ccircle cx='22' cy='33' r='4.84' fill='none' stroke='rgba(255,255,255,.11)' stroke-width='1.2'/%3E%3C/svg%3E");
  --bubble: #FFFFFF;
  --bubble-line: #DCE2EA;
  --shu: #D8462A;              /* 朱 vermilion */
  --shu-text: #B63A20;
  --good: #1D7550; --good-soft: #E3F3EA; --good-text: #17643F;   /* 常磐 tokiwa green */
  --bad: #A3294F; --bad-soft: #F8E6EC; --bad-text: #8F2244;      /* 臙脂 enji, far from Tomo's vermilion */
  --warn: #8A5D00; --warn-soft: #FBF1D6; --warn-text: #6E4A00;   /* 黄土 ōdo: almost, and notes */
  --track: #E6ECF2;
  --dye-1: #CFE0EC; --dye-2: #93B8D8; --dye-3: #4F82B6; --dye-4: #23497F;   /* the dye ladder, light to deep */
  --focus: #2E6FD0;
  --scrim: rgba(14, 24, 48, .45);
  --mark-bg: #1C356B; --mark-ink: #FFFFFF; --mark-dot: #E0502F; --mark-rim: transparent;
  /* Pop's play layer and Paper's marks */
  --highlight: #FFE27A;        /* the highlighter behind a pattern: the whole glyph, the text in the page's ink */
  --highlight-line: #D9A520;   /* and the 2 px line under it */
  --tag-bg: #FCEFC4; --tag-ink: #0E1830;          /* price-tag minutes */
  --pop-bg: #FFE89A; --pop-ink: #0E1830; --pop-ink-2: #3A3524; --pop-edge: rgba(14, 24, 48, .08);
  --tape: rgba(79, 130, 182, .78); --tape-2: rgba(255, 255, 255, .45);   /* aizome washi tape */
  --burst-bg: #B63A20; --burst-ink: #FFFFFF; --burst-edge: #FFFFFF;     /* the season's starburst */
  --grid-line: rgba(31, 79, 138, .14); --grid-guide: rgba(31, 79, 138, .34);   /* 方眼 paper */
  --sticker-edge: #FFFFFF;
  /* drawn marks, used as masks so they take a token colour */
  --i-hanamaru: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='-17 -17 34 34' fill='none' stroke='%23000' stroke-width='1.6' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M0.1 -0.6L0.3 -0.6L0.5 -0.6L0.7 -0.5L0.9 -0.4L1.1 -0.1L1.2 0.2L1.2 0.5L1.1 0.8L0.9 1.2L0.6 1.5L0.2 1.7L-0.2 1.8L-0.7 1.8L-1.2 1.6L-1.6 1.3L-2 0.9L-2.3 0.3L-2.4 -0.3L-2.3 -0.9L-2.1 -1.6L-1.7 -2.1L-1.1 -2.6L-0.4 -2.9L0.4 -3L1.2 -2.9L1.9 -2.6L2.6 -2L3.1 -1.3L3.5 -0.5L3.6 0.4L3.4 1.4L3 2.3L2.4 3.1L1.6 3.7L0.6 4.1L-0.5 4.2L-1.6 4L-2.6 3.5L-3.5 2.8L-4.2 1.8L-4.6 0.7L-4.8 -0.6L-4.5 -1.8L-4 -3L-3.1 -4L-2 -4.8L-0.7 -5.2L0.7 -5.4L2.1 -5.1L3.4 -4.5L4.5 -3.5L5.3 -2.3L5.8 -0.8L6 0.7L5.7 2.3L5 3.7L3.9 5L2.5 5.9L0.9 6.4L-0.8 6.6'/%3E%3Cpath d='M-2.1 -7.9A4.6 4.6 0 1 1 4 -7.1L5.8 -5.8A4.6 4.6 0 1 1 8.2 -0.1L7.9 2.1A4.6 4.6 0 1 1 4.2 7L2.1 7.9A4.6 4.6 0 1 1 -4 7.2L-5.8 5.8A4.6 4.6 0 1 1 -8.2 0.1L-7.9 -2.1A4.6 4.6 0 1 1 -4.2 -7Z'/%3E%3C/svg%3E");
  --i-batsu: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23000' stroke-width='2.6' stroke-linecap='round'%3E%3Cpath d='M6 6l12 12M18 6L6 18'/%3E%3C/svg%3E");
  --i-sankaku: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23000' stroke-width='2.4' stroke-linejoin='round'%3E%3Cpath d='M12 4.5l8.5 15h-17z'/%3E%3C/svg%3E");
  --i-chev: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23000' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M6 9l6 6 6-6'/%3E%3C/svg%3E");
  --i-tick: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23000' stroke-width='3.2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M5 12.5l4.5 4.5L19 7'/%3E%3C/svg%3E");
  --i-next: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23000' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M9 6l6 6-6 6'/%3E%3C/svg%3E");
  /* done, known, right: one mark everywhere, a filled tokiwa-green circle with its tick cut out (English
     speakers read an open ring as an empty radio button, not as done); ◎ excellent adds a ring round it */
  --i-done: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'%3E%3Cmask id='m'%3E%3Crect width='24' height='24' fill='white'/%3E%3Cpath d='M7 12.4l3.3 3.3 6.9-6.9' fill='none' stroke='black' stroke-width='2.6' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/mask%3E%3Ccircle cx='12' cy='12' r='11.5' mask='url(%23m)'/%3E%3C/svg%3E");
  --i-done-2: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'%3E%3Cmask id='m'%3E%3Crect width='24' height='24' fill='white'/%3E%3Cpath d='M8.4 12.2l2.5 2.5 4.9-5' fill='none' stroke='black' stroke-width='2.2' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/mask%3E%3Ccircle cx='12' cy='12' r='11' fill='none' stroke='black' stroke-width='1.6'/%3E%3Ccircle cx='12' cy='12' r='8' mask='url(%23m)'/%3E%3C/svg%3E");
  /* depth: cards are flat; only what floats over the page has a shadow */
  --shadow: 0 0 0 0 transparent;
  --shadow-lg: 0 16px 40px -12px rgba(14, 24, 48, .28), 0 2px 8px rgba(14, 24, 48, .08);
  --shadow-pop: 0 1px 0 rgba(14, 24, 48, .06), 0 12px 24px -16px rgba(14, 24, 48, .45);
  /* categorical colours (kinds of correction, charts), one step apart in hue, all text-safe on the surface */
  --c1: #1F4F8A; --c2: #1D7550; --c3: #4B5FB0; --c4: #8A5D00; --c5: #7A3F8F; --c6: #1E6A85; --c7: #5A6B14; --c8: #A3294F; --c9: #5B6782; --c10: #9A4A12;
  /* Tomo (tutor.js draws her with these; the band and the dark theme change her rim, handle, shadow and glow) */
  --tomo-paper: #DF4B2C; --tomo-lit: #F0714C; --tomo-lit-bright: #F7895F; --tomo-face: #FFE6D2;
  --tomo-ink: #1E1A2C; --tomo-hi: #FFFFFF; --tomo-cap: #19203A; --tomo-handle: #2A3150; --tomo-rim: transparent;
  --tomo-rib: #A9341C; --tomo-cheek: #F59A7E; --tomo-mouth: #7C2516; --tomo-arm: #C79A5E;   /* paper-and-bamboo arms */
  --tomo-wood: #CFA676; --tomo-wood2: #94704A; --tomo-thong: #C7352A;
  --tomo-season: #E7B42C; --tomo-season-2: #B9871A; --tomo-spark: #E9A42A;
  --tomo-shadow: rgba(14, 24, 48, .16); --tomo-glow: rgba(255, 128, 80, .55); --tomo-glow-size: 9px;

  /* the names the screens and the other sheets already use, kept as aliases */
  --font: var(--font-sans); --serif: var(--font-sans);
  --panel: var(--surface); --sunk: var(--surface-2); --fg: var(--ink); --mute: var(--ink-3); --faint: var(--ink-3);
  --line2: var(--line-strong); --accent-2: var(--accent-text); --accent-2-soft: var(--accent-soft);
  --r: var(--r-2); --r-sm: 6px;
}
html[data-theme=dark] {
  color-scheme: dark;
  --bg: #0A1022; --surface: #111A33; --surface-2: #17213F; --line: #233059; --line-strong: #3A4A7C; --field: #6B7CAD;
  --ink: #E8EDF7; --ink-2: #B7C2DA; --ink-3: #8D9AB8; --rt: #A3AFCA;
  --accent: #9CB9F2; --accent-hover: #B5CBF6; --accent-ink: #0A1022; --accent-soft: #1A2A55; --accent-text: #A9C4F6;
  --band: #15265A; --band-ink: #F2F5FB; --band-ink-2: #B3C3E4; --band-seg: rgba(255, 255, 255, .15); --band-sg: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='44' height='22' viewBox='0 0 44 22'%3E%3Ccircle cx='22' cy='-11' r='22' fill='%2315265A'/%3E%3Ccircle cx='22' cy='-11' r='16.28' fill='none' stroke='rgba(255,255,255,.075)' stroke-width='1.2'/%3E%3Ccircle cx='22' cy='-11' r='10.56' fill='none' stroke='rgba(255,255,255,.075)' stroke-width='1.2'/%3E%3Ccircle cx='22' cy='-11' r='4.84' fill='none' stroke='rgba(255,255,255,.075)' stroke-width='1.2'/%3E%3Ccircle cx='0' cy='0' r='22' fill='%2315265A'/%3E%3Ccircle cx='0' cy='0' r='16.28' fill='none' stroke='rgba(255,255,255,.075)' stroke-width='1.2'/%3E%3Ccircle cx='0' cy='0' r='10.56' fill='none' stroke='rgba(255,255,255,.075)' stroke-width='1.2'/%3E%3Ccircle cx='0' cy='0' r='4.84' fill='none' stroke='rgba(255,255,255,.075)' stroke-width='1.2'/%3E%3Ccircle cx='44' cy='0' r='22' fill='%2315265A'/%3E%3Ccircle cx='44' cy='0' r='16.28' fill='none' stroke='rgba(255,255,255,.075)' stroke-width='1.2'/%3E%3Ccircle cx='44' cy='0' r='10.56' fill='none' stroke='rgba(255,255,255,.075)' stroke-width='1.2'/%3E%3Ccircle cx='44' cy='0' r='4.84' fill='none' stroke='rgba(255,255,255,.075)' stroke-width='1.2'/%3E%3Ccircle cx='22' cy='11' r='22' fill='%2315265A'/%3E%3Ccircle cx='22' cy='11' r='16.28' fill='none' stroke='rgba(255,255,255,.075)' stroke-width='1.2'/%3E%3Ccircle cx='22' cy='11' r='10.56' fill='none' stroke='rgba(255,255,255,.075)' stroke-width='1.2'/%3E%3Ccircle cx='22' cy='11' r='4.84' fill='none' stroke='rgba(255,255,255,.075)' stroke-width='1.2'/%3E%3Ccircle cx='0' cy='22' r='22' fill='%2315265A'/%3E%3Ccircle cx='0' cy='22' r='16.28' fill='none' stroke='rgba(255,255,255,.075)' stroke-width='1.2'/%3E%3Ccircle cx='0' cy='22' r='10.56' fill='none' stroke='rgba(255,255,255,.075)' stroke-width='1.2'/%3E%3Ccircle cx='0' cy='22' r='4.84' fill='none' stroke='rgba(255,255,255,.075)' stroke-width='1.2'/%3E%3Ccircle cx='44' cy='22' r='22' fill='%2315265A'/%3E%3Ccircle cx='44' cy='22' r='16.28' fill='none' stroke='rgba(255,255,255,.075)' stroke-width='1.2'/%3E%3Ccircle cx='44' cy='22' r='10.56' fill='none' stroke='rgba(255,255,255,.075)' stroke-width='1.2'/%3E%3Ccircle cx='44' cy='22' r='4.84' fill='none' stroke='rgba(255,255,255,.075)' stroke-width='1.2'/%3E%3Ccircle cx='22' cy='33' r='22' fill='%2315265A'/%3E%3Ccircle cx='22' cy='33' r='16.28' fill='none' stroke='rgba(255,255,255,.075)' stroke-width='1.2'/%3E%3Ccircle cx='22' cy='33' r='10.56' fill='none' stroke='rgba(255,255,255,.075)' stroke-width='1.2'/%3E%3Ccircle cx='22' cy='33' r='4.84' fill='none' stroke='rgba(255,255,255,.075)' stroke-width='1.2'/%3E%3C/svg%3E");
  --bubble: #1C2A57; --bubble-line: rgba(255, 255, 255, .14);
  --shu: #F2694A; --shu-text: #FF8F74;
  --good: #58C795; --good-soft: #0F2F2A; --good-text: #7FDAB0;
  --bad: #F07EA2; --bad-soft: #36152A; --bad-text: #F7A3BE;
  --warn: #F1C25B; --warn-soft: #2E2614; --warn-text: #F5D07E;
  --track: #1C2647; --dye-1: #1F3668; --dye-2: #2F5596; --dye-3: #4F80C8; --dye-4: #8FB4EF;
  --focus: #8FB4EF; --scrim: rgba(2, 5, 14, .66); --mark-rim: rgba(255, 255, 255, .28);
  --highlight: rgba(241, 194, 91, .28); --highlight-line: #D9A845; --tag-bg: rgba(255, 210, 77, .16); --tag-ink: #FFDB8A;
  --pop-bg: #2A2615; --pop-ink: #F6EED2; --pop-ink-2: #D9CFA8; --pop-edge: rgba(255, 214, 102, .35);
  --tape: rgba(79, 128, 200, .7); --tape-2: rgba(255, 255, 255, .2);
  --burst-bg: #F2694A; --burst-ink: #0A1022; --burst-edge: #111A33;
  --grid-line: rgba(169, 196, 246, .14); --grid-guide: rgba(169, 196, 246, .32); --sticker-edge: #111A33;
  --shadow-lg: 0 18px 48px -12px rgba(0, 0, 0, .7), 0 2px 8px rgba(0, 0, 0, .4);
  --shadow-pop: 0 0 0 1px rgba(255, 214, 102, .12), 0 12px 28px -14px rgba(0, 0, 0, .8);
  --c1: #A9C4F6; --c2: #7FDAB0; --c3: #AEB9FF; --c4: #F1C25B; --c5: #DDA6EA; --c6: #86D3EE; --c7: #C4D86A; --c8: #F7A3BE; --c9: #A3AFCA; --c10: #F4AE7A;
  --tomo-paper: #E5532F; --tomo-lit: #F37A52; --tomo-lit-bright: #F88C62; --tomo-arm: #D9B47E;
  --tomo-cap: #121830; --tomo-handle: #7483AE; --tomo-rim: rgba(255, 255, 255, .34);
  --tomo-shadow: rgba(0, 0, 0, .5); --tomo-glow: rgba(255, 128, 80, .34); --tomo-glow-size: 7px;
}
@media (prefers-color-scheme: dark) { html:not([data-theme=light]) {
  color-scheme: dark;
  --bg: #0A1022; --surface: #111A33; --surface-2: #17213F; --line: #233059; --line-strong: #3A4A7C; --field: #6B7CAD;
  --ink: #E8EDF7; --ink-2: #B7C2DA; --ink-3: #8D9AB8; --rt: #A3AFCA;
  --accent: #9CB9F2; --accent-hover: #B5CBF6; --accent-ink: #0A1022; --accent-soft: #1A2A55; --accent-text: #A9C4F6;
  --band: #15265A; --band-ink: #F2F5FB; --band-ink-2: #B3C3E4; --band-seg: rgba(255, 255, 255, .15); --band-sg: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='44' height='22' viewBox='0 0 44 22'%3E%3Ccircle cx='22' cy='-11' r='22' fill='%2315265A'/%3E%3Ccircle cx='22' cy='-11' r='16.28' fill='none' stroke='rgba(255,255,255,.075)' stroke-width='1.2'/%3E%3Ccircle cx='22' cy='-11' r='10.56' fill='none' stroke='rgba(255,255,255,.075)' stroke-width='1.2'/%3E%3Ccircle cx='22' cy='-11' r='4.84' fill='none' stroke='rgba(255,255,255,.075)' stroke-width='1.2'/%3E%3Ccircle cx='0' cy='0' r='22' fill='%2315265A'/%3E%3Ccircle cx='0' cy='0' r='16.28' fill='none' stroke='rgba(255,255,255,.075)' stroke-width='1.2'/%3E%3Ccircle cx='0' cy='0' r='10.56' fill='none' stroke='rgba(255,255,255,.075)' stroke-width='1.2'/%3E%3Ccircle cx='0' cy='0' r='4.84' fill='none' stroke='rgba(255,255,255,.075)' stroke-width='1.2'/%3E%3Ccircle cx='44' cy='0' r='22' fill='%2315265A'/%3E%3Ccircle cx='44' cy='0' r='16.28' fill='none' stroke='rgba(255,255,255,.075)' stroke-width='1.2'/%3E%3Ccircle cx='44' cy='0' r='10.56' fill='none' stroke='rgba(255,255,255,.075)' stroke-width='1.2'/%3E%3Ccircle cx='44' cy='0' r='4.84' fill='none' stroke='rgba(255,255,255,.075)' stroke-width='1.2'/%3E%3Ccircle cx='22' cy='11' r='22' fill='%2315265A'/%3E%3Ccircle cx='22' cy='11' r='16.28' fill='none' stroke='rgba(255,255,255,.075)' stroke-width='1.2'/%3E%3Ccircle cx='22' cy='11' r='10.56' fill='none' stroke='rgba(255,255,255,.075)' stroke-width='1.2'/%3E%3Ccircle cx='22' cy='11' r='4.84' fill='none' stroke='rgba(255,255,255,.075)' stroke-width='1.2'/%3E%3Ccircle cx='0' cy='22' r='22' fill='%2315265A'/%3E%3Ccircle cx='0' cy='22' r='16.28' fill='none' stroke='rgba(255,255,255,.075)' stroke-width='1.2'/%3E%3Ccircle cx='0' cy='22' r='10.56' fill='none' stroke='rgba(255,255,255,.075)' stroke-width='1.2'/%3E%3Ccircle cx='0' cy='22' r='4.84' fill='none' stroke='rgba(255,255,255,.075)' stroke-width='1.2'/%3E%3Ccircle cx='44' cy='22' r='22' fill='%2315265A'/%3E%3Ccircle cx='44' cy='22' r='16.28' fill='none' stroke='rgba(255,255,255,.075)' stroke-width='1.2'/%3E%3Ccircle cx='44' cy='22' r='10.56' fill='none' stroke='rgba(255,255,255,.075)' stroke-width='1.2'/%3E%3Ccircle cx='44' cy='22' r='4.84' fill='none' stroke='rgba(255,255,255,.075)' stroke-width='1.2'/%3E%3Ccircle cx='22' cy='33' r='22' fill='%2315265A'/%3E%3Ccircle cx='22' cy='33' r='16.28' fill='none' stroke='rgba(255,255,255,.075)' stroke-width='1.2'/%3E%3Ccircle cx='22' cy='33' r='10.56' fill='none' stroke='rgba(255,255,255,.075)' stroke-width='1.2'/%3E%3Ccircle cx='22' cy='33' r='4.84' fill='none' stroke='rgba(255,255,255,.075)' stroke-width='1.2'/%3E%3C/svg%3E");
  --bubble: #1C2A57; --bubble-line: rgba(255, 255, 255, .14);
  --shu: #F2694A; --shu-text: #FF8F74;
  --good: #58C795; --good-soft: #0F2F2A; --good-text: #7FDAB0;
  --bad: #F07EA2; --bad-soft: #36152A; --bad-text: #F7A3BE;
  --warn: #F1C25B; --warn-soft: #2E2614; --warn-text: #F5D07E;
  --track: #1C2647; --dye-1: #1F3668; --dye-2: #2F5596; --dye-3: #4F80C8; --dye-4: #8FB4EF;
  --focus: #8FB4EF; --scrim: rgba(2, 5, 14, .66); --mark-rim: rgba(255, 255, 255, .28);
  --highlight: rgba(241, 194, 91, .28); --highlight-line: #D9A845; --tag-bg: rgba(255, 210, 77, .16); --tag-ink: #FFDB8A;
  --pop-bg: #2A2615; --pop-ink: #F6EED2; --pop-ink-2: #D9CFA8; --pop-edge: rgba(255, 214, 102, .35);
  --tape: rgba(79, 128, 200, .7); --tape-2: rgba(255, 255, 255, .2);
  --burst-bg: #F2694A; --burst-ink: #0A1022; --burst-edge: #111A33;
  --grid-line: rgba(169, 196, 246, .14); --grid-guide: rgba(169, 196, 246, .32); --sticker-edge: #111A33;
  --shadow-lg: 0 18px 48px -12px rgba(0, 0, 0, .7), 0 2px 8px rgba(0, 0, 0, .4);
  --shadow-pop: 0 0 0 1px rgba(255, 214, 102, .12), 0 12px 28px -14px rgba(0, 0, 0, .8);
  --c1: #A9C4F6; --c2: #7FDAB0; --c3: #AEB9FF; --c4: #F1C25B; --c5: #DDA6EA; --c6: #86D3EE; --c7: #C4D86A; --c8: #F7A3BE; --c9: #A3AFCA; --c10: #F4AE7A;
  --tomo-paper: #E5532F; --tomo-lit: #F37A52; --tomo-lit-bright: #F88C62; --tomo-arm: #D9B47E;
  --tomo-cap: #121830; --tomo-handle: #7483AE; --tomo-rim: rgba(255, 255, 255, .34);
  --tomo-shadow: rgba(0, 0, 0, .5); --tomo-glow: rgba(255, 128, 80, .34); --tomo-glow-size: 7px;
} }

/* ===================================================================================================
   Base
   =================================================================================================== */
*, *::before, *::after { box-sizing: border-box; }
html { -webkit-text-size-adjust: 100%; -webkit-tap-highlight-color: transparent;
  /* what the browser scrolls into view (focus, find, scrollIntoView) lands clear of the bar, the tab bar and Tomo */
  scroll-padding-top: calc(var(--bar-h) + env(safe-area-inset-top) + var(--s-3));
  scroll-padding-bottom: calc(max(var(--tab-h) + env(safe-area-inset-bottom), var(--tutor-room, 0px)) + var(--s-4)); }
/* the tour places each step itself, allowing for the bar (tour.js) */
html:has(.tour-ring) { scroll-padding: 0; }
body { margin: 0; background: var(--bg); color: var(--ink); font: 400 var(--fs-16)/var(--lh-body) var(--font-sans); min-height: 100vh; overflow-x: hidden;
  font-kerning: normal; text-rendering: optimizeLegibility; }
a { color: var(--accent-text); text-underline-offset: 3px; }
h1, h2, h3 { line-height: 1.25; margin: 0 0 .4em; font-weight: 700; letter-spacing: -.005em; }
h1 { font-size: var(--fs-30); letter-spacing: -.01em; }
h2 { font-size: var(--fs-20); }
h3 { font-size: var(--fs-18); }
p { margin: 0 0 .8em; }
b, strong { font-weight: 700; }
/* the browser's own "smaller" would take a 16 px line under the 14 px floor */
small { font-size: max(var(--fs-14), .83em); }
/* an address written out as a link (a licence, a source) breaks where it must on a narrow phone */
:is(p, li, dd, td) > a { overflow-wrap: anywhere; }
.mute { color: var(--ink-3); }
.faint { color: var(--ink-3); }
.small { font-size: var(--fs-14); }
.tiny { font-size: var(--fs-14); }
.num, .stat, .big-num, time { font-variant-numeric: tabular-nums; }
.hidden, [hidden] { display: none !important; }
/* A tap is a tap: no double-tap zoom delay on anything pressed (the page itself stays zoomable). */
button, .btn, a, summary, label { touch-action: manipulation; }
.sr { position: absolute; width: 1px; height: 1px; overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; }
:focus-visible { outline: 3px solid var(--focus); outline-offset: 2px; border-radius: var(--r-1); }
/* a page's heading (or the page) takes the focus when the page changes (app.js focusPage): a place to start, not a
   control, so it never shows a ring */
#main h1[tabindex="-1"]:focus, #main:focus { outline: none; }
.skip:focus { position: fixed; z-index: 300; top: var(--s-2); left: var(--s-3); width: auto; height: auto; clip: auto; padding: var(--s-2) var(--s-4); border-radius: var(--r-2); background: var(--surface); color: var(--ink); font-weight: 700; box-shadow: var(--shadow-lg); }
.eyebrow { margin: 0; font-size: var(--fs-14); font-weight: 700; letter-spacing: var(--track-label); text-transform: uppercase; color: var(--ink-3); }
.eyebrow :lang(ja), .eyebrow[lang=ja] { letter-spacing: .1em; text-transform: none; }
.lead { font-size: var(--fs-18); color: var(--ink-2); }
.measure { max-width: var(--measure); }
/* No one-word last lines ('them.', 'わ'): paragraphs, list lines and captions wrap evenly, titles balance. The
   longhand, so a line kept on one line (nowrap, an ellipsis) stays as it is. */
p, li, dd, figcaption, small { text-wrap-style: pretty; }
h1, h2, h3 { text-wrap-style: balance; }
.ic, .icon svg, button svg, .btn svg { flex: none; }
svg.ic { width: 24px; height: 24px; fill: none; stroke: currentColor; stroke-width: 1.8; stroke-linecap: round; stroke-linejoin: round; }

/* ===================================================================================================
   Japanese text
   =================================================================================================== */
:lang(ja) { word-break: auto-phrase; line-break: strict; font-synthesis-style: none; }
/* Japanese whose phrases are marked (core.js markPhrases: a <wbr> where each starts) breaks only there; a phrase
   longer than its line still breaks rather than run past it. */
.phrased, .phrased :lang(ja) { word-break: keep-all; overflow-wrap: anywhere; }
ruby { ruby-position: over; ruby-align: center; }
/* at the start of its block a long reading starts with its word and reaches over the kana after it (fitRuby) */
ruby.rb-head { ruby-align: start; }
/* the reading sits centred over its word; no margins of its own (a negative one pulled it off its word and left
   a gap after the word, which reads as a space in Japanese): fitRuby() pulls the kana beside the <ruby> in
   instead. Its kana are set proportionally (palt: a small ゅ or っ takes less room), so a reading longer than its
   word pads the word as little as it can. */
rt { font-size: max(var(--fs-14), .5em); line-height: 1; font-weight: 400; font-style: normal; letter-spacing: 0; text-transform: none;
  color: var(--rt); font-variant-east-asian: ruby; font-feature-settings: "palt"; ruby-align: center; }
/* the furigana switch hides readings but keeps their band, so the lines stay where they were */
[data-furigana=off] rt, .furigana-off rt { visibility: hidden; }
/* a word with a reading and the kana that end it, kept on one line (tutor.js keepWords, welcome.js phrased) */
.ph { white-space: nowrap; }
/* Japanese that can carry ruby: a fixed line height that holds the ruby band */
.ja { font-size: var(--fs-18); line-height: var(--lh-ruby); }
.ja-lg { font-size: var(--fs-26); line-height: var(--lh-ruby); font-weight: 400; }
.ja-xl { font-size: var(--fs-40); line-height: var(--lh-ruby-big); font-weight: 700; }
.ja-table, td.ja, .ja-table td:lang(ja), .ja-table th:lang(ja) { font-size: var(--fs-20); line-height: var(--lh-ruby); }
p:lang(ja), li:lang(ja), dd:lang(ja), .ja p { line-height: var(--lh-ruby); }
/* Japanese that carries no reading at all (a pattern list, a register note) needs no ruby band: it is set at --lh-ja,
   so it never looks double-spaced. A block with readings keeps its band, furigana off or not, so nothing jumps. */
p:lang(ja):not(:has(ruby)), li:lang(ja):not(:has(ruby)), dd:lang(ja):not(:has(ruby)), .ja p:not(:has(ruby)) { line-height: var(--lh-ja); }
/* a word inside English: the same face, a little weight, never a jump in the line */
.jp-inline { font-weight: 700; color: var(--ink); }
/* old screens mark their Japanese with .es (the copied Spanish class): the same face, no serif */
.es { font-family: var(--font-sans); }
.list .es, td.es, .msg .es { font-weight: 700; font-size: var(--fs-16); }
.ja-romaji { display: block; font-size: var(--fs-14); color: var(--ink-3); letter-spacing: .02em; line-height: 1.4; }
/* the highlighter behind the part of a pattern being learned (Pop) */
/* (the whole glyph, broken lines each with their own ends, a 2 px line under it; the text in the page's ink, 4.5:1 or
   more in both looks: dark's is a warm tint, never a muddy block) */
.hl, mark.hl { background: var(--highlight); color: var(--ink); padding: .1em .06em; border-radius: 2px; box-shadow: inset 0 -2px 0 var(--highlight-line);
  -webkit-box-decoration-break: clone; box-decoration-break: clone; }
.hl-key { color: var(--accent-text); font-weight: 700; box-shadow: inset 0 -3px 0 var(--dye-1); }

/* ===================================================================================================
   Buttons and links
   =================================================================================================== */
button, .btn { font: 700 var(--fs-16)/1.1 var(--font-sans); border-radius: var(--r-2); min-height: var(--tap); padding: 0 var(--btn-pad); border: 1px solid var(--line-strong);
  background: var(--surface); color: var(--ink); cursor: pointer; display: inline-flex; align-items: center; justify-content: center; gap: var(--s-2);
  text-decoration: none; white-space: nowrap; transition: background .15s, border-color .15s, color .15s, transform .08s; }
@media (hover: hover) { button:hover, .btn:hover { border-color: var(--field); background: var(--surface-2); } }
button:active, .btn:active { transform: translateY(1px); }
button:disabled { opacity: .5; cursor: not-allowed; transform: none; }
.primary { background: var(--accent); border-color: var(--accent); color: var(--accent-ink); }
@media (hover: hover) { .primary:hover { background: var(--accent-hover); border-color: var(--accent-hover); } }
.secondary { background: var(--surface); border-color: var(--line-strong); color: var(--ink); }
.ghost { background: transparent; border-color: transparent; }
@media (hover: hover) { .ghost:hover { background: var(--surface-2); border-color: transparent; } }
.quiet { background: var(--surface-2); border-color: transparent; }
@media (hover: hover) { .quiet:hover { background: var(--accent-soft); border-color: transparent; } }
.danger { color: var(--bad-text); }
.big { min-height: 52px; padding: 0 var(--s-6); font-size: var(--fs-18); }
.icon { width: var(--tap); padding: 0; flex: none; }
.icon svg, button svg, .btn svg { width: 20px; height: 20px; }
.btn-play, button.play { width: var(--tap); height: var(--tap); padding: 0; border-radius: 50%; border: 0; background: var(--accent-soft); color: var(--accent-text); flex: none; }
@media (hover: hover) { .btn-play:hover, button.play:hover { background: var(--dye-1); } }
.link { background: none; border: 0; padding: 0; min-height: 0; color: var(--accent-text); font-weight: 700; text-decoration: underline; text-underline-offset: 3px; border-radius: var(--r-1); }
@media (hover: hover) { .link:hover { background: none; } }
a.go, .go-link { display: inline-flex; align-items: center; gap: var(--s-2); min-height: var(--tap); font-weight: 700; color: var(--accent-text); text-decoration: none; }
a.go svg, .go-link svg { width: 20px; height: 20px; }
@media (hover: hover) { a.go:hover, .go-link:hover { text-decoration: underline; } }
.row { display: flex; gap: var(--s-2) var(--s-3); align-items: center; flex-wrap: wrap; }
.row.end { justify-content: flex-end; }
.row.between { justify-content: space-between; }
.row.bottom { align-items: flex-end; }
.grow { flex: 1; min-width: 0; }
.stack { display: grid; grid-template-columns: minmax(0, 1fr); gap: var(--s-4); }

/* ===================================================================================================
   The bar (docs/mobile-plan.md 1.1, docs/polish-plan.md A1): the brand (or ‹ and the page above on a phone)
   at the left, the four places on a computer, the tools, and the menu's ⋯ last, at the top right, at every
   width. A page is in one of three modes (html[data-shell], app.js and js/shell.js): top, deep (‹ on a phone)
   or focus (a session: the count, a progress line on the bar's foot and one tool; no tab bar). Signed out
   the same ⋯ ends the bar, after Help (a computer's), Sign in and ☾ (a computer's).
   =================================================================================================== */
header.bar { position: sticky; top: 0; z-index: 30; display: flex; align-items: center; gap: var(--s-2); height: calc(var(--bar-h) + env(safe-area-inset-top));
  padding: env(safe-area-inset-top) var(--gut-r) 0 var(--gut-l); background: var(--surface); border-bottom: 1px solid var(--line); }
/* Signed in, ⋯ ends the bar: its dots end on the page's right gutter (the 44 px button holds a 24 px icon whose
   dots end 2.85 px short of its edge, so 12.85 px in from the button's), and on a phone held sideways the whole
   button stays clear of the notch (--bar-r, on :root, so Tomo's place beside it can be worked out). */
header.bar:has(> #menu:not([hidden])) { padding-right: var(--bar-r); }
header.bar .menu { display: flex; align-items: center; flex: none; }
#menu-open svg.ic { width: 24px; height: 24px; }
#menu-open svg.dots { fill: currentColor; stroke: none; }
#menu-open[aria-expanded=true] { background: var(--surface-2); }
.brand { display: flex; align-items: center; gap: var(--s-3); min-height: var(--tap); min-width: 0; color: var(--ink); text-decoration: none; margin-right: var(--s-6); border-radius: var(--r-2); }
.brand .mark, .brand > svg { width: 36px; height: 36px; flex: none; }
.mark-bg { fill: var(--mark-bg); stroke: var(--mark-rim); stroke-width: 1; }
.mark-ink { fill: var(--mark-ink); }
.mark-dot { fill: var(--mark-dot); }
.brand-name { display: grid; line-height: 1.2; min-width: 0; }
.brand-ja { font-size: var(--fs-18); font-weight: 700; letter-spacing: .06em; white-space: nowrap; }
.brand-en { font-size: var(--fs-14); font-weight: 400; color: var(--ink-3); letter-spacing: .02em; white-space: nowrap; }
.nav { display: flex; align-self: stretch; min-width: 0; }
.nav a { position: relative; display: inline-flex; align-items: center; gap: var(--s-2); height: 100%; min-height: var(--tap); padding: 0 var(--s-4);
  color: var(--ink-2); font: 400 var(--fs-16)/1 var(--font-sans); text-decoration: none; white-space: nowrap; border: 0; border-radius: 0; background: none; }
@media (hover: hover) { .nav a:hover { color: var(--ink); background: none; } }
.nav a[aria-current=page] { color: var(--ink); font-weight: 700; }
.nav a[aria-current=page]::after { content: ""; position: absolute; left: var(--s-4); right: var(--s-4); bottom: -1px; height: 3px; background: var(--accent); }
.nav a:focus-visible { outline-offset: -3px; }
header.bar .add-btn { width: var(--tap); padding: 0; gap: 0; flex: none; }
header.bar .add-btn .add-txt { position: absolute; width: 1px; height: 1px; overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; }
header.bar .add-btn.dup { visibility: hidden; }
header.bar .add-btn[aria-current=page] { background: var(--surface-2); border-color: transparent; }
/* The way back (‹ and the page above, a phone's), a session's count and its one tool (a phone's), the room
   Tomo stands in at the bar's end (an upright phone's) and the progress line on the bar's foot. */
.bar-back { display: none; align-items: center; gap: var(--s-1); flex: 0 1 auto; min-width: 0; max-width: calc(100vw - 168px); min-height: var(--tap); padding: 0 var(--s-2) 0 0;
  color: var(--ink); font-size: var(--fs-16); font-weight: 700; text-decoration: none; border-radius: var(--r-2); }
.bar-back svg.ic { width: 24px; height: 24px; flex: none; color: var(--ink-2); }
/* ‹ goes to the page above; in a session the way out is ✕ Stop (app.js backLabel sets .stop), since a ‹ on a
   phone means the page before, not the end of what you are doing. */
.bar-back svg.bb-stop, .bar-back.stop svg.bb-back { display: none; }
.bar-back.stop svg.bb-stop { display: block; }
.bar-back span { min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
@media (hover: hover) { .bar-back:hover { background: var(--surface-2); } }
.bar-count { display: none; flex: none; font-size: var(--fs-14); color: var(--ink-2); white-space: nowrap; }
.bar-tool { display: flex; align-items: center; gap: var(--s-2); flex: none; }
.bar-tool:empty { display: none; }
/* a tool lent by the screen (Undo, ⚙, Mark it) is a quiet 44 px button in the bar; one with nothing to do goes */
.bar-tool > :is(button, .btn), .bar-tool > * > :is(button, .btn) { min-height: var(--tap); padding: 0 var(--s-3); font-size: var(--fs-14); white-space: nowrap; }
.bar-tool :is(button, .btn).icon { width: var(--tap); padding: 0; }
.bar-tool .undo-btn.idle { display: none; }
.bar-tomo { display: none; flex: none; width: var(--tap); height: var(--tap); pointer-events: none; }
.bar-progress { position: absolute; left: 0; bottom: -1px; height: 3px; background: var(--accent); border-radius: 0 2px 2px 0; transition: width .25s ease-out; pointer-events: none; }
/* the line's track, the bar's whole width under it, so the line reads as how far along, not as a lit tab; a session's
   alone (app.js draws neither on a page that is not one) */
html[data-shell=focus] header.bar:has(> .bar-progress:not([hidden]))::after { content: ""; position: absolute; left: 0; right: 0; bottom: -1px; height: 3px; background: var(--line-strong); pointer-events: none; }
html:not([data-shell=focus]) .bar-progress { display: none; }
.bar-progress { z-index: 1; }
/* A page's own menu: a list under its button. A bare ⋯ is the site's menu alone (the bar's, top right), so a page's
   button says what it holds: .page-opts, 44 px, the sliders and a short word (Options, Show, More), with ▾. On a
   phone its rows go into the site's menu as the first group (app.js pageMenu) and the button steps aside (.pm-away). */
.menu { position: relative; }
.page-opts { display: inline-flex; align-items: center; gap: var(--s-1); min-height: var(--tap); padding: 0 var(--s-3); font-size: var(--fs-14); font-weight: 700; color: var(--ink-2); white-space: nowrap; }
.page-opts > svg { width: 20px; height: 20px; flex: none; }
.page-opts > svg.caret { width: 16px; height: 16px; margin-left: -2px; color: var(--ink-3); }
.page-opts[aria-expanded=true] > svg.caret { transform: rotate(180deg); }
.pm-away { display: none !important; }
.menu-list { position: absolute; right: 0; top: calc(100% + var(--s-2)); z-index: 40; display: flex; flex-direction: column; min-width: 260px; padding: var(--s-2); background: var(--surface);
  border: 1px solid var(--line); border-radius: var(--r-3); box-shadow: var(--shadow-lg); }
.menu-list > button, .menu-list > a { justify-content: flex-start; text-align: left; border: 0; background: none; padding: var(--s-2) var(--s-3); min-height: var(--tap); border-radius: var(--r-2);
  font: 400 var(--fs-16)/1.3 var(--font-sans); color: var(--ink); width: 100%; text-decoration: none; display: flex; align-items: center; gap: var(--s-3); cursor: pointer; white-space: normal; }
@media (hover: hover) { .menu-list > button:hover, .menu-list > a:hover { background: var(--surface-2); } }
.menu-list > a[aria-current=page] { background: var(--surface-2); font-weight: 700; }
/* an item's icon is the size of a button's, whatever the item is (a link too) */
.menu-list svg { width: 20px; height: 20px; flex: none; }
.menu-list .menu-h { margin: var(--s-2) var(--s-3) var(--s-1); }
/* A page's own More menu never runs past the screen either: it scrolls inside itself (lessons.js, decks.js). */
.page .menu-list { max-height: calc(100dvh - var(--bar-h) - var(--tab-h) - env(safe-area-inset-top) - env(safe-area-inset-bottom) - var(--s-5)); overflow-y: auto; overscroll-behavior: contain; }
.menu-list hr { border: 0; border-top: 1px solid var(--line); margin: var(--s-2) var(--s-1); width: auto; }

/* The menu (#menu-list): a panel from the right over a dimmed page, at every width, under the ⋯ that opened it.
   Opened, it slides in (flat with reduced motion); the page under it is out of reach (app.js makes it inert) and
   does not scroll. Its head is the learner's name over their level, ✕ beside them (where ⋯ was); first the page's own choices on a phone (under the page's short name), then rows
   are 48 px, a 20 px icon and a 16 px label, in groups, the page you are on marked. Sign out comes last. */
.drawer { position: fixed; z-index: 80; top: 0; right: 0; bottom: 0; width: min(320px, calc(100vw - 56px)); display: flex; flex-direction: column;
  padding: env(safe-area-inset-top) env(safe-area-inset-right) env(safe-area-inset-bottom) 0; background: var(--surface); color: var(--ink); border-left: 1px solid var(--line);
  box-shadow: var(--shadow-lg); overflow-y: auto; overscroll-behavior: contain; transform: translateX(100%); transition: transform .22s cubic-bezier(.2, .8, .2, 1); }
.drawer.in { transform: none; }
.drawer-scrim { position: fixed; inset: 0; z-index: 79; background: var(--scrim); opacity: 0; transition: opacity .2s; }
.drawer-scrim.in { opacity: 1; }
html.drawer-open { overflow: hidden; }
/* ✕ stands where ⋯ was: the head is the bar's height, with the bar's right padding (less the panel's own notch room).
   It is one quiet 44 px ✕ at every width: no fill under the pointer (it opens right under it), the ink darkens. */
.drawer-head { display: flex; align-items: center; gap: var(--s-1); padding: 0 max(calc(var(--gutter) - 13px), 0px) 0 var(--s-4); min-height: var(--bar-h); }
.drawer-who { display: grid; flex: 1; min-width: 0; line-height: 1.35; }
.drawer-who .who-name { font-size: var(--fs-16); font-weight: 700; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.drawer-who .who-level { font-size: var(--fs-14); color: var(--ink-3); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.drawer-who .who-level:empty { display: none; }
.drawer-rows { display: flex; flex-direction: column; padding: 0 var(--s-2) var(--s-3); }
.drawer-rows > a, .drawer-rows > button, .drawer-rows .smcp-inline { display: flex; align-items: center; justify-content: flex-start; gap: var(--s-3); width: 100%; min-height: 48px;
  padding: var(--s-1) var(--s-3); border: 0; border-radius: var(--r-2); background: none; color: var(--ink); font: 400 var(--fs-16)/1.3 var(--font-sans); text-align: left;
  text-decoration: none; white-space: normal; cursor: pointer; }
.drawer-rows svg { width: 20px; height: 20px; flex: none; color: var(--ink-3); }
/* Software MCP's "Problem or idea?" row comes without an icon: a bug, drawn like the other rows' icons. */
.drawer-rows .smcp-inline::before { content: ''; width: 20px; height: 20px; flex: none; background: var(--ink-3); -webkit-mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='black' stroke-width='1.8' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M9 7.5V7a3 3 0 0 1 6 0v.5'/%3E%3Crect x='7' y='8' width='10' height='12' rx='5'/%3E%3Cpath d='M12 12v8M3.5 13H7M17 13h3.5M4.5 19.5 7.4 17M19.5 19.5 16.6 17M4.5 6.5 7.4 9M19.5 6.5 16.6 9'/%3E%3C/svg%3E") center / contain no-repeat; mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='black' stroke-width='1.8' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M9 7.5V7a3 3 0 0 1 6 0v.5'/%3E%3Crect x='7' y='8' width='10' height='12' rx='5'/%3E%3Cpath d='M12 12v8M3.5 13H7M17 13h3.5M4.5 19.5 7.4 17M19.5 19.5 16.6 17M4.5 6.5 7.4 9M19.5 6.5 16.6 9'/%3E%3C/svg%3E") center / contain no-repeat; }
@media (hover: hover) { .drawer-rows > a:hover, .drawer-rows > button:hover, .drawer-rows .smcp-inline:hover { background: var(--surface-2); border-color: transparent; } }
.drawer-rows > a[aria-current=page] { background: var(--accent-soft); color: var(--accent-text); font-weight: 700; }
.drawer-rows > a[aria-current=page] svg { color: currentColor; }
.drawer-rows > :is(a, button):active { transform: none; }
#menu-report:empty { display: none; }
/* Signed out, the same ⋯ holds what is open to anyone: Help, Report a problem, Install the app, About, Privacy and
   Terms (and Dark look on a phone); signed in, the places of an account. */
body.signed-out .drawer-rows > [data-in], body:not(.signed-out) .drawer-rows > [data-out] { display: none !important; }
body.signed-out .drawer-rows > [data-menu=help] { margin-top: 0; }
/* No group labels and no rules: a little space before each group (Help, then What's new, then Admin). */
.drawer-rows > .drawer-gap { margin-top: var(--s-3); }
.drawer-rows > .menu-signout { margin-top: var(--s-4); color: var(--ink-2); }
/* Tour this page (a phone's) is a row under Help. */
.drawer-head > .icon { color: var(--ink-2); background: none; border-color: transparent; }
@media (hover: hover) { .drawer-head > .icon:hover { background: none; border-color: transparent; color: var(--ink); } }
/* Dark look (a phone's): a 48 px row under Settings, its switch at the row's end (role=switch, aria-checked). */
.drawer-rows > .drawer-switch .sw { position: relative; flex: none; width: 40px; height: 24px; margin-left: auto; border-radius: 12px; background: var(--line-strong); transition: background .15s; }
.drawer-rows > .drawer-switch .sw::after { content: ""; position: absolute; top: 3px; left: 3px; width: 18px; height: 18px; border-radius: 50%; background: var(--surface); transition: transform .15s; }
.drawer-rows > .drawer-switch[aria-checked=true] .sw { background: var(--accent); }
.drawer-rows > .drawer-switch[aria-checked=true] .sw::after { transform: translateX(16px); background: var(--accent-ink); }
/* The page's own choices (app.js pageMenu): the first group, under the page's short name, a hairline after it. */
.drawer-page { padding: 0 var(--s-2) var(--s-2); margin-bottom: var(--s-2); border-bottom: 1px solid var(--line); }
.drawer-page[hidden] { display: none; }
.drawer-page-h { margin: 0 var(--s-3) var(--s-1); font-size: var(--fs-14); font-weight: 700; letter-spacing: var(--track-label); text-transform: uppercase; color: var(--ink-3); }
.drawer-page-rows { display: flex; flex-direction: column; }
.drawer-page-rows > :is(a, button) { display: flex; align-items: center; justify-content: flex-start; gap: var(--s-3); width: 100%; min-height: 48px; padding: var(--s-1) var(--s-3);
  border: 0; border-radius: var(--r-2); background: none; color: var(--ink); font: 400 var(--fs-16)/1.3 var(--font-sans); text-align: left; text-decoration: none; white-space: normal; cursor: pointer; }
.drawer-page-rows > :is(a, button)[hidden] { display: none; }
.drawer-page-rows svg { width: 20px; height: 20px; flex: none; color: var(--ink-3); }
.drawer-page-rows > :is(p, .menu-h) { margin: var(--s-1) var(--s-3); font-size: var(--fs-14); color: var(--ink-3); }
.drawer-page-rows > hr { border: 0; border-top: 1px solid var(--line); margin: var(--s-1) var(--s-3); }
@media (hover: hover) { .drawer-page-rows > :is(a, button):hover { background: var(--surface-2); } }
.drawer-page-rows > :is(a, button):active { transform: none; }
/* A short phone: rows of 44 px and the groups closer, so the whole menu (Dark look's row too) fits 360 × 640. */
@media (max-height: 700px) {
  .drawer-rows > a, .drawer-rows > button, .drawer-rows .smcp-inline, .drawer-page-rows > :is(a, button) { min-height: var(--tap); }
  .drawer-head { min-height: 52px; }
  .drawer-rows { padding-bottom: var(--s-1); }
  /* the groups sit close here, so a hairline in the gap keeps them groups (Decks to Dark look, Help to Report, What's
     new to About, Admin), at no height */
  .drawer-rows > .drawer-gap { margin-top: 2px; position: relative; }
  .drawer-rows > .drawer-gap::before { content: ""; position: absolute; left: var(--s-3); right: var(--s-3); top: -1.5px; border-top: 1px solid var(--line); pointer-events: none; }
  body.signed-out .drawer-rows > [data-menu=help]::before { content: none; }
  .drawer-rows > .menu-signout { margin-top: var(--s-1); }
}
/* Only on a phone (the menu's Dark look and Tour this page; a screen's own phone-only piece). */
@media (min-width: 721px) { .only-sm { display: none !important; } }
.pubnav { display: flex; gap: var(--s-2); align-items: center; margin-right: var(--s-1); }
.pubnav[hidden] { display: none; }
/* The Software MCP report form reads its colours from these (no shadow DOM): the site's own tokens, light and
   dark, so it opens as part of this site. !important beats the widget's own sheet, which loads after this one. */
.smcp-ui { --smcp-bg: var(--surface) !important; --smcp-fg: var(--ink) !important; --smcp-mute: var(--ink-3) !important; --smcp-line: var(--line) !important;
  --smcp-soft: var(--surface-2) !important; --smcp-accent: var(--accent) !important; --smcp-on: var(--accent-ink) !important; --smcp-bad: var(--bad-text) !important; }
/* Its form keeps this site's rules too: text 14 px or more, the box 16 px with the fields' border, Problem and
   Idea 8 px apart with the chosen one in the accent, and Cancel and Send kept at the foot while the rest
   scrolls; under 500 px tall it takes the whole screen, as the site's own dialog does. */
.smcp-ui.smcp-back { padding: var(--s-3); }
.smcp-ui .smcp-dlg { max-height: calc(100dvh - 24px); padding-bottom: 0; overscroll-behavior: contain; border-radius: var(--r-3); }
.smcp-ui .smcp-sub, .smcp-ui .smcp-dlg label, .smcp-ui .smcp-shot, .smcp-ui .smcp-err, .smcp-ui .smcp-kind button, .smcp-ui .smcp-btn, .smcp-ui .smcp-mine, .smcp-ui .smcp-new { font-size: var(--fs-14); }
.smcp-ui .smcp-dlg textarea { font-size: var(--fs-16); border-color: var(--field); }
.smcp-ui .smcp-kind { gap: var(--s-2); }
.smcp-ui .smcp-kind button { min-height: var(--tap); padding: 0 var(--s-4); }
.smcp-ui .smcp-kind button[aria-checked="true"] { background: var(--accent); color: var(--accent-ink); box-shadow: none; }
.smcp-ui .smcp-btn { min-height: var(--tap); }
.smcp-ui .smcp-acts { position: sticky; bottom: 0; z-index: 1; margin: var(--s-4) -20px 0; padding: 10px 20px calc(10px + env(safe-area-inset-bottom)); background: var(--smcp-bg); border-top: 1px solid var(--smcp-line); }
.smcp-ui .smcp-mine { padding-bottom: var(--s-4); }
@media (max-height: 500px) {
  .smcp-ui.smcp-back { padding: 0; }
  .smcp-ui .smcp-dlg { width: 100%; height: 100dvh; max-height: 100dvh; border: 0; border-radius: 0; padding: 14px max(20px, env(safe-area-inset-right)) 0 max(20px, env(safe-area-inset-left)); }
  .smcp-ui .smcp-acts { margin-left: calc(-1 * max(20px, env(safe-area-inset-left))); margin-right: calc(-1 * max(20px, env(safe-area-inset-right)));
    padding-left: max(20px, env(safe-area-inset-left)); padding-right: max(20px, env(safe-area-inset-right)); }
}
/* Its note after sending ("Sent as #57. Thank you."): 14 px, its × a full 44 px, and on a phone or a short
   screen under the bar like the site's own toasts. It goes by itself after a few seconds (app.js). */
.smcp-ui .smcp-note { font-size: var(--fs-14); align-items: center; }
.smcp-ui .smcp-x { min-width: var(--tap); min-height: var(--tap); margin: -8px -10px -8px 0; }
@media (max-width: 720px), (max-height: 500px) {
  .smcp-ui.smcp-notes { top: calc(var(--bar-h) + env(safe-area-inset-top) + var(--s-2)); bottom: auto; left: max(12px, env(safe-area-inset-left)); right: max(12px, env(safe-area-inset-right)); max-width: none; }
}

/* ===================================================================================================
   Page, grid, tab bar
   =================================================================================================== */
.page { max-width: var(--page-max); margin: 0 auto;
  padding: var(--s-6) var(--gut-r) calc(max(var(--tab-h) + env(safe-area-inset-bottom), var(--tutor-room, 0px)) + var(--s-7)) var(--gut-l); }
.page.wide { max-width: var(--page-max); }
/* Tomo's corner is a lane of its own wherever the width allows: on a tablet and a phone on its side the page
   keeps clear of it on the right, on a wide screen on both sides (so the page stays centred). On an upright
   phone the width is the page's: she stands in the bar, not over the page (--tutor-room is 0 there). */
@media (min-width: 721px), (max-height: 500px) {
  html.has-tutor .page:not(.public) { --gut-r: calc(var(--tutor-w) + var(--s-4) + env(safe-area-inset-right)); }
}
@media (min-width: 1100px) {
  html.has-tutor .page:not(.public) { --gut-l: max(var(--gutter), calc(var(--tutor-w) + var(--s-6)), env(safe-area-inset-left)); --gut-r: max(var(--gutter), calc(var(--tutor-w) + var(--s-6)), env(safe-area-inset-right)); }
}
.tabbar { display: none; }
/* the 12-column grid: spans collapse to half on a tablet and to one column on a phone */
.grid12 { display: grid; grid-template-columns: repeat(12, minmax(0, 1fr)); gap: var(--grid-gap); align-items: start; }
.grid12 > * { grid-column: 1 / -1; min-width: 0; }
.grid12 > .span-3 { grid-column: span 3; } .grid12 > .span-4 { grid-column: span 4; } .grid12 > .span-5 { grid-column: span 5; }
.grid12 > .span-6 { grid-column: span 6; } .grid12 > .span-7 { grid-column: span 7; } .grid12 > .span-8 { grid-column: span 8; }
.grid12 > .span-9 { grid-column: span 9; } .grid12 > .span-12 { grid-column: 1 / -1; }
.grid12.stretch { align-items: stretch; }
@media (max-width: 1099px) {
  .grid12 > .span-3, .grid12 > .span-4, .grid12 > .span-5, .grid12 > .span-6 { grid-column: span 6; }
  .grid12 > .span-7, .grid12 > .span-8, .grid12 > .span-9 { grid-column: 1 / -1; }
}
@media (max-width: 720px) { .grid12 > [class*=span-] { grid-column: 1 / -1; } }
.col-stack { display: grid; grid-template-columns: minmax(0, 1fr); gap: var(--grid-gap); align-content: start; min-width: 0; }

/* ===================================================================================================
   Cards, panels and page heads
   =================================================================================================== */
.panel { background: var(--surface); border: 1px solid var(--line); border-radius: var(--r-2); padding: var(--s-5); min-width: 0; }
.panel.flat { box-shadow: none; }
.panel h2 { display: flex; align-items: center; gap: var(--s-2); flex-wrap: wrap; }
.card { background: var(--surface); border: 1px solid var(--line); border-radius: var(--r-2); min-width: 0; }
.card-head { display: flex; align-items: baseline; flex-wrap: wrap; gap: var(--s-1) var(--s-3); padding: var(--s-5) var(--s-5) var(--s-4); }
.card-head h2 { font-size: var(--fs-20); margin: 0; }
.card-head .ja-sub, .ja-sub { font-size: var(--fs-14); color: var(--ink-3); font-weight: 400; letter-spacing: .04em; }
/* a title's Japanese moves to the next line as a whole, never broken in the middle of its phrase */
.ja-sub { display: inline-block; max-width: 100%; }
.card-head .end { margin-left: auto; font-size: var(--fs-14); color: var(--ink-3); }
.card-body { padding: 0 var(--s-5) var(--s-5); }
.card-foot { display: flex; flex-wrap: wrap; align-items: center; justify-content: space-between; gap: var(--s-3); padding: var(--s-3) var(--s-5); border-top: 1px solid var(--line); }
.well { background: var(--surface-2); border-radius: var(--r-2); padding: var(--s-3) var(--s-4); }
.grid { display: grid; gap: var(--grid-gap); align-items: start; }
.grid.two { grid-template-columns: minmax(0, 1.35fr) minmax(0, 1fr); }
.grid.three { grid-template-columns: repeat(3, minmax(0, 1fr)); align-items: stretch; }
.grid.even { grid-template-columns: repeat(2, minmax(0, 1fr)); align-items: stretch; }
.grid.stretch { align-items: stretch; }
.chip { white-space: nowrap; display: inline-flex; align-items: center; gap: var(--s-1); min-height: 28px; padding: 2px 10px; border-radius: var(--r-pill); background: var(--surface-2);
  color: var(--ink-2); font-size: var(--fs-14); font-weight: 700; line-height: 1.3; max-width: 100%; }
.chip.accent { background: var(--accent-soft); color: var(--accent-text); }
.chip.level, .chip.solid { background: var(--accent); color: var(--accent-ink); }
.chip.outline { background: transparent; box-shadow: inset 0 0 0 1px var(--line-strong); font-weight: 400; }
.chip.green { background: var(--accent-soft); color: var(--accent-text); }
.chip.good { background: var(--good-soft); color: var(--good-text); }
.chip.bad { background: var(--bad-soft); color: var(--bad-text); }
.chip.warn { background: var(--warn-soft); color: var(--warn-text); }
.chips { display: flex; gap: var(--s-2); flex-wrap: wrap; }
/* a sticker (Pop): a die-cut pill for one playful word ("Casual", "New", "秋") */
.sticker { display: inline-flex; align-items: center; gap: var(--s-1); min-height: 28px; padding: 2px 12px; border-radius: var(--r-pill); background: var(--accent); color: var(--accent-ink);
  font-size: var(--fs-14); font-weight: 700; white-space: nowrap; box-shadow: 0 0 0 2px var(--sticker-edge), var(--shadow-pop); transform: rotate(-2deg); }
.sticker.soft { background: var(--accent-soft); color: var(--accent-text); }
.back { display: inline-flex; align-items: center; gap: var(--s-1); color: var(--ink-2); font-weight: 700; text-decoration: none; margin-bottom: var(--s-3); min-height: var(--tap); padding: 0 var(--s-3) 0 var(--s-1); border-radius: var(--r-2); }
@media (hover: hover) { .back:hover { background: var(--surface-2); color: var(--ink); } }
.back svg { width: 20px; height: 20px; }
.crumbs { display: flex; flex-wrap: wrap; align-items: center; gap: 0 var(--s-1); margin: 0 0 var(--s-2); padding: 0; list-style: none; font-size: var(--fs-14); color: var(--ink-3); }
.crumbs li { display: inline-flex; align-items: center; }
.crumbs li + li::before { content: "/"; margin: 0 var(--s-2); color: var(--line-strong); }
.crumbs a { display: inline-flex; align-items: center; min-height: var(--tap); min-width: var(--tap); color: var(--ink-2); text-decoration: none; }
@media (hover: hover) { .crumbs a:hover { color: var(--ink); text-decoration: underline; } }
.pagehead { display: flex; gap: var(--s-4); align-items: flex-start; justify-content: space-between; flex-wrap: wrap; margin-bottom: var(--s-5); }
.pagehead h1 { margin: 0; }
.pagehead h1 .ja-sub { display: block; margin-top: var(--s-1); }
/* the title keeps a readable width; the buttons beside it go under it instead of squeezing it */
.pagehead > .grow { flex: 1 1 320px; }
.pagehead p { margin: var(--s-2) 0 0; }
.title-ja { font-size: var(--fs-56); line-height: var(--lh-ruby-big); font-weight: 700; letter-spacing: .01em; margin: 0; }
.subtitle { font-size: var(--fs-24); color: var(--ink-2); margin: var(--s-1) 0 0; }

/* ===================================================================================================
   Today: the indigo band (.band; .hero is the same band on the Today screen), with one faint seigaiha
   =================================================================================================== */
.band, .hero { position: relative; isolation: isolate; overflow: hidden; display: grid; grid-template-columns: minmax(0, 1fr) auto; gap: var(--s-5) var(--s-7); align-items: end;
  padding: var(--s-6) var(--s-7); border-radius: var(--r-3); background: var(--band); color: var(--band-ink); border: 0;
  --tomo-shadow: rgba(0, 0, 0, .3); --tomo-rim: rgba(255, 255, 255, .3); --tomo-handle: #8A98C2; }
.band::before, .hero::before { content: ""; position: absolute; z-index: -1; inset: 0 0 0 auto; width: 64%; background: var(--band-sg) right top / 44px 22px repeat;
  -webkit-mask-image: linear-gradient(90deg, transparent, #000 55%); mask-image: linear-gradient(90deg, transparent, #000 55%); pointer-events: none; }
.band h1, .hero h1 { font-size: var(--fs-40); line-height: var(--lh-tight); margin: var(--s-3) 0 var(--s-4); letter-spacing: -.015em; color: var(--band-ink); }
.band .lead, .hero .lead, .band p, .hero p { color: var(--band-ink-2); }
.band .lead, .hero .lead { font-size: var(--fs-18); margin-top: var(--s-3); max-width: 46ch; }
.band .eyebrow, .hero .eyebrow, .hero .greet { color: var(--band-ink-2); }
.hero .greet { font-size: var(--fs-16); font-weight: 700; letter-spacing: .04em; margin-bottom: var(--s-2); }
.band a:not(.btn), .hero a:not(.btn) { color: var(--band-ink); }
.band :focus-visible, .hero :focus-visible { outline-color: var(--band-ink); }
.band .primary, .hero .primary { background: var(--band-ink); border-color: var(--band-ink); color: var(--band); }
@media (hover: hover) { .band .primary:hover, .hero .primary:hover { background: var(--band-ink-2); border-color: var(--band-ink-2); } }
.band .btn:not(.primary), .hero .btn:not(.primary), .band button:not(.primary), .hero button:not(.primary) { background: transparent; color: var(--band-ink); border-color: var(--band-seg); }
@media (hover: hover) { .band .btn:not(.primary):hover, .hero .btn:not(.primary):hover, .band button:not(.primary):hover, .hero button:not(.primary):hover { background: var(--band-seg); } }
.band .ghost, .hero .ghost { border-color: transparent; }
.band .seg, .hero .seg { background: transparent; border-color: var(--band-seg); }
.band .seg button, .hero .seg button { color: var(--band-ink); border-color: var(--band-seg); }
.band .seg button[aria-pressed=true], .hero .seg button[aria-pressed=true], .band .seg button[aria-checked=true], .hero .seg button[aria-checked=true] { background: var(--band-ink); color: var(--band); }
.band .chip, .hero .chip { background: var(--band-seg); color: var(--band-ink); }
.band rt, .hero rt { color: var(--band-ink-2); }
.hero .actions { display: flex; flex-direction: column; gap: var(--s-3); align-items: stretch; min-width: 240px; }
.band-text { align-self: start; min-width: 0; }
.band > .tutor-hello { grid-column: 2; grid-row: 1 / span 2; align-self: end; }
.band-go { display: flex; flex-wrap: wrap; align-items: center; gap: var(--s-4) var(--s-5); }
/* the day's steps as segments sized by their minutes (style="--m:4"), the next one lit */
.segbar { display: flex; gap: 3px; flex: 1 1 340px; max-width: 560px; margin: 0; padding: 0; list-style: none; }
.segbar > * { flex: var(--m, 1) 1 0; min-width: 0; height: 36px; display: flex; align-items: center; padding-left: var(--s-2); border-radius: var(--r-1); background: var(--band-seg);
  color: var(--band-ink); font-size: var(--fs-14); font-variant-numeric: tabular-nums; overflow: hidden; }
.segbar > .next { background: var(--band-ink); color: var(--band); font-weight: 700; }
.segbar > .done { background: transparent; box-shadow: inset 0 0 0 1px var(--band-seg); color: var(--band-ink-2); }
.btn.start, .start-btn { min-height: 52px; padding: 0 var(--s-6); font-size: var(--fs-18); }
.btn.start .num, .start-btn .num { font-weight: 400; opacity: .85; }

/* Tomo's hello at the top of Today: tutor.js greetInline(el) adds .tutor-hello to el and draws her small self
   (a button that opens her chat) beside a bubble whose tail points at her. It is part of the page, so it never
   covers anything. <b> in the bubble is her name tag, in vermilion. */
.tutor-hello { display: flex; flex-direction: row-reverse; justify-content: flex-end; align-items: flex-end; gap: var(--s-3); min-width: 0; }
.tutor-hello-me { flex: none; display: block; width: 88px; height: auto; aspect-ratio: 120 / 150; min-height: 0; padding: 0; border: 0; background: none; border-radius: var(--r-3); cursor: pointer; }
@media (hover: hover) { .tutor-hello-me:hover, .band .tutor-hello-me:hover, .hero .tutor-hello-me:hover { background: none; border: 0; } }
.band .tutor-hello-me, .hero .tutor-hello-me { background: none; border: 0; }
.tutor-hello-me svg { display: block; width: 100%; height: 100%; overflow: visible; }
.tutor-hello-say { position: relative; flex: 0 1 400px; min-width: 0; margin: 0 0 var(--s-5); padding: var(--s-3) var(--s-4); border-radius: var(--r-3); background: var(--bubble);
  color: var(--ink); border: 1px solid var(--bubble-line); font-size: var(--fs-18); line-height: 1.75; }
.band .tutor-hello-say, .hero .tutor-hello-say, .band .tutor-hello-say p, .hero .tutor-hello-say p { color: var(--ink); }
.tutor-hello-say::after { content: ""; position: absolute; right: -9px; bottom: 22px; width: 16px; height: 16px; background: var(--bubble); border: solid var(--bubble-line); border-width: 1px 1px 0 0;
  transform: rotate(45deg) skew(8deg, 8deg); }
.tutor-hello-say rt, .band .tutor-hello-say rt, .hero .tutor-hello-say rt { color: var(--rt); }
.tutor-hello-say > b:first-child, .bubble .who { display: block; font-size: var(--fs-14); font-weight: 700; color: var(--shu-text); letter-spacing: .02em; line-height: 1.5; }
.tutor-hello-say .en, .tutor-hello-say small { display: block; font-size: var(--fs-14); color: var(--ink-3); line-height: 1.5; margin-top: var(--s-1); }
/* Today's hello on a phone (home.js .td-hello-line): one unboxed line under Keep going, her 40 px self beside her
   Japanese and its English, in the band's own ink (no bubble: the band is the headline and the button). */
@media (max-width: 720px) {
  .td-band > .tutor-hello.td-hello-line { flex-direction: row; justify-content: flex-start; align-items: center; gap: var(--s-3); margin-top: calc(-1 * var(--s-1)); }
  .td-band:has(> .td-hello-line) { padding: var(--s-4); gap: var(--s-3); }
  /* drawn at 36 px (45 tall), pressed over 44 */
  .td-band .td-hello-line .tutor-hello-me { position: relative; width: 36px; }
  .td-band .td-hello-line .tutor-hello-me::after { content: ""; position: absolute; inset: 0 -4px; }
  .td-band .td-hello-line p.tutor-hello-say { flex: 1 1 auto; margin: 0; padding: 0; background: none; border: 0; border-radius: 0; box-shadow: none; color: var(--band-ink);
    font-size: var(--fs-16); line-height: 1.5; }
  .td-band .td-hello-line p.tutor-hello-say::after { display: none; }
  .td-band .td-hello-line .td-hi-ja { display: inline; }
  .td-band .td-hello-line .td-hi-ja:has(rt) { line-height: var(--lh-ja); }
  .td-band .td-hello-line .tutor-hello-say .en { display: inline; margin: 0 0 0 var(--s-2); color: var(--band-ink-2); }
  .td-band .td-hello-line .tutor-hello-say rt { color: var(--band-ink-2); }
}

/* ===================================================================================================
   Today: plan rows with icon wells, kanji tiles and price-tag minutes
   =================================================================================================== */
.plan-list { list-style: none; margin: 0; padding: 0; border-top: 1px solid var(--line); }
.plan-list > li { border-bottom: 1px solid var(--line); }
.plan-row { display: grid; grid-template-columns: 28px 44px minmax(0, 1fr) auto auto; grid-template-areas: "idx icon main extra time"; align-items: center; gap: var(--s-4);
  min-height: 80px; padding: var(--s-3) var(--s-5); color: inherit; text-decoration: none; }
a.plan-row { cursor: pointer; }
@media (hover: hover) { a.plan-row:hover { background: var(--surface-2); } }
.plan-row .idx { grid-area: idx; font-size: var(--fs-14); color: var(--ink-3); font-variant-numeric: tabular-nums; }
.plan-row .pi, .icon-well { grid-area: icon; width: 44px; height: 44px; border-radius: var(--r-2); display: grid; place-items: center; background: var(--accent-soft); color: var(--accent-text); flex: none; }
.plan-row .pi svg, .icon-well svg { width: 24px; height: 24px; }
.plan-row.next .pi { background: var(--accent); color: var(--accent-ink); }
.plan-row.done .pi { background: var(--surface-2); color: var(--shu); }
.plan-row.done .pt { color: var(--ink-3); }
.plan-row .pr-main { grid-area: main; min-width: 0; }
.plan-row .pt { display: block; font-size: var(--fs-18); font-weight: 700; line-height: 1.35; }
.plan-row .pd { display: block; font-size: var(--fs-14); color: var(--ink-3); margin-top: 2px; line-height: 1.45; }
.plan-row .extra { grid-area: extra; display: flex; gap: var(--s-2); }
.plan-row .extra:empty { display: none; }
.plan-row .price, .plan-row .time { grid-area: time; justify-self: end; }
.plan-foot { display: flex; flex-wrap: wrap; align-items: center; justify-content: space-between; gap: var(--s-3); padding: var(--s-3) var(--s-5); }
.plan-foot .total { font-size: var(--fs-14); color: var(--ink-3); }
.plan-foot .total b { color: var(--ink); font-size: var(--fs-16); }
/* a price tag for minutes (Pop): the punched hole on the left, the minutes in tabular figures */
.price { position: relative; display: inline-flex; align-items: center; gap: 3px; height: 32px; padding: 0 var(--s-3) 0 22px; background: var(--tag-bg); color: var(--tag-ink);
  font-size: var(--fs-14); font-weight: 700; white-space: nowrap; font-variant-numeric: tabular-nums;
  clip-path: polygon(10px 0, 100% 0, 100% 100%, 10px 100%, 0 50%); border-radius: 0 var(--r-1) var(--r-1) 0; }
.price::before { content: ""; position: absolute; left: 9px; top: 50%; width: 6px; height: 6px; margin-top: -3px; border-radius: 50%; background: var(--surface); }
.price b { font-size: var(--fs-18); }
/* kanji tiles: one character in a square; .write shows it in the handwriting model on a 田 guide */
.kt { width: 48px; height: 48px; display: inline-grid; place-items: center; flex: none; border: 1px solid var(--line-strong); border-radius: var(--r-1); background: var(--surface);
  font-size: 28px; line-height: 1; color: var(--ink); text-decoration: none; }
a.kt { cursor: pointer; }
@media (hover: hover) { a.kt:hover { border-color: var(--accent-text); background: var(--accent-soft); } }
.kt.lg { width: 72px; height: 72px; font-size: 44px; }
.kt.kon { background: var(--accent); border-color: var(--accent); color: var(--accent-ink); }
.kt.write { position: relative; font-family: var(--font-hand); border: 1.5px solid var(--accent-text); }
.kt.write::before, .kt.write::after { content: ""; position: absolute; border: 0 dashed var(--line-strong); }
.kt.write::before { left: 50%; top: 0; bottom: 0; border-left-width: 1px; }
.kt.write::after { top: 50%; left: 0; right: 0; border-top-width: 1px; }
.kt > span { position: relative; }
.kt-row { display: flex; flex-wrap: wrap; gap: var(--s-2); }

/* ===================================================================================================
   Numbered sections (a lesson sheet): 01 かたち · 02 いみ · 03 れいぶん · 04 れんしゅう
   =================================================================================================== */
.sheet { background: var(--surface); border: 1px solid var(--line); border-radius: var(--r-3); padding: 0 var(--s-7); }
.sec { display: grid; grid-template-columns: repeat(12, minmax(0, 1fr)); gap: var(--s-4) var(--grid-gap); padding-block: var(--s-7); border-top: 1px solid var(--line); }
.sec:first-child { border-top: 0; }
.sec-head { grid-column: 1 / span 3; min-width: 0; }
.sec-num { display: block; font-size: var(--fs-14); font-weight: 700; letter-spacing: var(--track-label); color: var(--accent-text); font-variant-numeric: tabular-nums; }
.sec-head h2 { font-size: var(--fs-24); line-height: 1.3; margin: var(--s-2) 0 0; }
.sec-ja { display: block; font-size: var(--fs-14); color: var(--ink-3); margin-top: var(--s-1); letter-spacing: .08em; }
.sec-body { grid-column: 4 / -1; min-width: 0; }
.prose { max-width: var(--measure); color: var(--ink-2); font-size: var(--fs-18); }
.prose b, .prose strong { color: var(--ink); }
.prose p + p, .sec-body > p + p { margin-top: var(--s-3); }
.split { display: grid; grid-template-columns: minmax(0, 1.1fr) minmax(0, 1fr); gap: var(--s-6); align-items: start; }
.trap { border: 1px solid var(--line); border-left: 4px solid var(--accent); border-radius: var(--r-2); padding: var(--s-4) var(--s-5); background: var(--surface-2); }
.trap h3 { font-size: var(--fs-20); }
.trap p { color: var(--ink-2); margin-top: var(--s-2); }
/* the pattern as tiles: the key tile in kon, the rest outlined, + between them */
.formula { display: flex; flex-wrap: wrap; align-items: stretch; gap: var(--s-2) var(--s-3); }
.ftile { display: grid; align-content: start; gap: var(--s-1); padding: var(--s-3) var(--s-5) var(--s-4); border: 1.5px solid var(--line-strong); border-radius: var(--r-2); min-width: 112px; }
.ftile .role { font-size: var(--fs-14); color: var(--ink-3); }
.ftile .ja { font-size: var(--fs-40); font-weight: 700; line-height: 1.2; }
.ftile.kon { background: var(--accent); border-color: var(--accent); color: var(--accent-ink); }
.ftile.kon .role { color: var(--accent-ink); }
.fop { align-self: center; font-size: var(--fs-30); color: var(--ink-3); }
/* example sentences: play, the Japanese (26 px, 22 on a phone) with ruby, the English under it */
.examples { list-style: none; margin: 0; padding: 0; }
.ex { display: grid; grid-template-columns: var(--tap) minmax(0, 1fr); gap: var(--s-3) var(--s-4); align-items: start; padding: var(--s-5) 0; border-top: 1px solid var(--line); }
.ex:first-child { border-top: 0; padding-top: 0; }
.ex .ja-lg { margin: 0; }
.ex .en { color: var(--ink-2); margin: 0; }

/* ===================================================================================================
   JLPT: how close am I (a meter with the likely range, the estimate, the pass line and section minimums)
   =================================================================================================== */
.estimate-total { display: flex; align-items: baseline; flex-wrap: wrap; gap: var(--s-1) var(--s-3); }
.big-num { font-size: var(--fs-56); font-weight: 400; line-height: 1; letter-spacing: -.02em; font-variant-numeric: tabular-nums; }
.of { font-size: var(--fs-18); color: var(--ink-3); font-variant-numeric: tabular-nums; }
.range-note { font-size: var(--fs-14); color: var(--ink-2); }
.gauge { position: relative; height: 10px; margin: var(--s-4) 0 34px; background: var(--track); border-radius: 5px; }
.gauge i, .gauge-legend i { font-style: normal; }
.gauge .g-range { position: absolute; top: 0; bottom: 0; left: calc(var(--lo) * 1%); width: calc((var(--hi) - var(--lo)) * 1%); background: var(--dye-2); border-radius: 5px; }
.gauge .g-est { position: absolute; top: -6px; width: 4px; height: 22px; left: calc(var(--v) * 1% - 2px); background: var(--accent-text); border-radius: 2px; box-shadow: 0 0 0 2px var(--surface); }
.gauge .g-pass, .gauge .g-min { position: absolute; top: -8px; height: 26px; width: 2px; left: calc(var(--at) * 1% - 1px); background: var(--shu); }
.gauge .g-min { background: var(--ink-2); }
.gauge .g-pass > span, .gauge .g-min > span { position: absolute; top: 28px; left: 50%; transform: translateX(-50%); font-size: var(--fs-14); font-weight: 700; color: var(--shu-text); white-space: nowrap; font-variant-numeric: tabular-nums; }
.gauge .g-min > span { color: var(--ink-2); }
.gauge-row { display: grid; gap: var(--s-1); }
.gauge-head { display: flex; align-items: baseline; flex-wrap: wrap; gap: 0 var(--s-2); }
.gauge-head .name { font-weight: 700; }
.gauge-head .val { margin-left: auto; font-variant-numeric: tabular-nums; font-weight: 700; }
.gauge-head .val small { font-weight: 400; color: var(--ink-3); font-size: var(--fs-14); }
.gauge-legend { display: flex; flex-wrap: wrap; gap: var(--s-2) var(--s-4); font-size: var(--fs-14); color: var(--ink-2); }
.gauge-legend i { display: inline-block; width: 18px; height: 10px; border-radius: 5px; margin-right: var(--s-2); vertical-align: 0; }
.gauge-legend .lg-range { background: var(--dye-2); } .gauge-legend .lg-est { width: 4px; height: 16px; background: var(--accent-text); border-radius: 2px; vertical-align: -3px; }
.gauge-legend .lg-pass, .gauge-legend .lg-min { width: 2px; height: 16px; border-radius: 0; vertical-align: -3px; background: var(--shu); } .gauge-legend .lg-min { background: var(--ink-2); }
.fineprint { font-size: var(--fs-14); color: var(--ink-3); margin-top: var(--s-3); }

/* ===================================================================================================
   Streak: the stamp card (Pop) with はなまる for study days (Paper) and a banked rest day; the dye heat map
   =================================================================================================== */
.stampcard { display: grid; grid-template-columns: repeat(7, minmax(0, 1fr)); gap: var(--s-2); margin: var(--s-4) 0 0; padding: var(--s-4) var(--s-3) var(--s-3); list-style: none;
  border-radius: var(--r-2); background: var(--surface-2); border: 1.5px dashed var(--line-strong); }
.stampcard > li { display: flex; flex-direction: column; align-items: center; gap: 6px; min-width: 0; font-size: var(--fs-14); color: var(--ink-2); }
.stampcard .st { position: relative; width: 44px; height: 44px; max-width: 100%; aspect-ratio: 1; height: auto; border-radius: 50%; display: grid; place-items: center; border: 2px dashed var(--line-strong); }
.stampcard .done .st, .stampcard .missed .st { border: 0; }
.stampcard .done .st::before { content: ""; position: absolute; inset: -2px; background: var(--shu); -webkit-mask: var(--i-hanamaru) center / contain no-repeat; mask: var(--i-hanamaru) center / contain no-repeat; transform: rotate(-12deg); }
.stampcard .done:nth-child(2n) .st::before { transform: rotate(18deg) scale(.96); }
.stampcard .done:nth-child(3n) .st::before { transform: rotate(-30deg) scale(1.02); }
.stampcard .today .st { border: 2.5px solid var(--accent-text); }
.stampcard .today { color: var(--accent-text); font-weight: 700; }
.stampcard .rest .st { border: 0; background: var(--accent-soft); color: var(--accent-text); font-weight: 700; font-size: var(--fs-18); transform: rotate(-6deg); }
.stampcard .missed .st { background: transparent; }
.hanamaru { display: inline-block; width: 1.6em; height: 1.6em; vertical-align: middle; background: var(--shu); -webkit-mask: var(--i-hanamaru) center / contain no-repeat; mask: var(--i-hanamaru) center / contain no-repeat; flex: none; }
.rest-bank { display: flex; align-items: center; gap: var(--s-2); margin: var(--s-3) 0 0; font-size: var(--fs-14); color: var(--ink-2); }
.rest-bank .rest-chip { display: inline-grid; place-items: center; width: 28px; height: 28px; border-radius: 50%; background: var(--accent-soft); color: var(--accent-text); font-weight: 700; }
.streak-top { display: flex; align-items: baseline; flex-wrap: wrap; gap: var(--s-2) var(--s-3); }
.streak-top .words { font-size: var(--fs-18); color: var(--ink-2); }
.heat { display: grid; grid-template-columns: repeat(7, minmax(0, 32px)); gap: 5px; }
.heat .dow { font-size: var(--fs-14); color: var(--ink-3); text-align: center; line-height: 20px; }
.heat .cell, .heat-legend .cell { aspect-ratio: 1; border-radius: 3px; background: var(--track); }
.cell.l1 { background: var(--dye-1); } .cell.l2 { background: var(--dye-2); } .cell.l3 { background: var(--dye-3); } .cell.l4 { background: var(--dye-4); }
.cell.now { background: transparent; box-shadow: inset 0 0 0 2px var(--accent-text); }
.cell.future { background: transparent; box-shadow: inset 0 0 0 1px var(--line); }
.heat-legend { display: flex; align-items: center; gap: 5px; font-size: var(--fs-14); color: var(--ink-3); margin-top: var(--s-3); }
.heat-legend .cell { width: 14px; }

/* ===================================================================================================
   POP card (culture and pop-culture notes): washi tape on top, the season's starburst in the corner
   =================================================================================================== */
.pop-card { position: relative; margin-top: 14px; padding: var(--s-6) var(--s-5) var(--s-5); border-radius: var(--r-2); background: var(--pop-bg); color: var(--pop-ink);
  box-shadow: 0 0 0 1px var(--pop-edge), var(--shadow-pop); min-width: 0; }
.pop-card .eyebrow { color: var(--pop-ink-2); }
.pop-card h2, .pop-card h3 { margin-top: var(--s-2); color: var(--pop-ink); }
.pop-card p { color: var(--pop-ink-2); }
.pop-card rt { color: var(--pop-ink-2); }
.pop-card a:not(.btn) { color: var(--pop-ink); text-decoration-color: var(--shu); text-decoration-thickness: 3px; text-underline-offset: 5px; font-weight: 700; }
.pop-card .well, .pop-list > li { background: color-mix(in srgb, var(--surface) 55%, transparent); border-radius: var(--r-1); }
.pop-list { display: grid; gap: var(--s-2); margin: var(--s-3) 0 0; padding: 0; list-style: none; }
.pop-list > li { display: grid; grid-template-columns: auto minmax(0, 1fr); gap: var(--s-1) var(--s-3); align-items: baseline; padding: var(--s-2) var(--s-3); }
.pop-list [lang=ja] { font-weight: 700; font-size: var(--fs-18); line-height: var(--lh-ruby); }
.tape { position: absolute; top: -12px; left: 28px; width: 110px; height: 26px; transform: rotate(-4deg); pointer-events: none;
  background: repeating-linear-gradient(45deg, var(--tape) 0 8px, var(--tape-2) 8px 12px), var(--tape);
  clip-path: polygon(0 8%, 4% 0, 8% 10%, 12% 0, 100% 0, 97% 50%, 100% 100%, 6% 100%, 2% 90%, 0 100%); }
.tape.right { left: auto; right: 28px; transform: rotate(5deg); }
.burst { position: absolute; top: -20px; right: 18px; width: 68px; height: 68px; display: grid; place-items: center; transform: rotate(10deg); pointer-events: none;
  filter: drop-shadow(0 0 0 var(--burst-edge)) drop-shadow(0 2px 0 var(--burst-edge)); }
.burst::before { content: ""; position: absolute; inset: 0; background: var(--burst-bg); clip-path: polygon(50.0% 0.0%, 57.4% 12.7%, 69.1% 3.8%, 71.1% 18.4%, 85.4% 14.6%, 81.6% 28.9%, 96.2% 30.9%, 87.3% 42.6%, 100.0% 50.0%, 87.3% 57.4%, 96.2% 69.1%, 81.6% 71.1%, 85.4% 85.4%, 71.1% 81.6%, 69.1% 96.2%, 57.4% 87.3%, 50.0% 100.0%, 42.6% 87.3%, 30.9% 96.2%, 28.9% 81.6%, 14.6% 85.4%, 18.4% 71.1%, 3.8% 69.1%, 12.7% 57.4%, 0.0% 50.0%, 12.7% 42.6%, 3.8% 30.9%, 18.4% 28.9%, 14.6% 14.6%, 28.9% 18.4%, 30.9% 3.8%, 42.6% 12.7%); }
.burst > span { position: relative; color: var(--burst-ink); font-size: var(--fs-26); font-weight: 700; line-height: 1; }

/* ===================================================================================================
   Today's kanji on 方眼 grid paper, in the handwriting model (Klee One)
   =================================================================================================== */
.kanji-card { padding: var(--s-5); }
.kanji-card-body { display: grid; grid-template-columns: 132px minmax(0, 1fr); gap: var(--s-3) var(--s-5); align-items: start; margin-top: var(--s-3); }
.hogan { position: relative; width: 132px; aspect-ratio: 1; border-radius: var(--r-2); display: grid; place-items: center; border: 2px solid var(--line-strong);
  background: repeating-linear-gradient(0deg, var(--grid-line) 0 1px, transparent 1px 22px), repeating-linear-gradient(90deg, var(--grid-line) 0 1px, transparent 1px 22px), var(--surface-2); }
.hogan::before, .hogan::after { content: ""; position: absolute; border: 0 dashed var(--grid-guide); pointer-events: none; }
.hogan::before { left: 50%; top: 6px; bottom: 6px; border-left-width: 1.5px; }
.hogan::after { top: 50%; left: 6px; right: 6px; border-top-width: 1.5px; }
.hogan .model, .model { font-family: var(--font-hand); font-weight: 400; }
.hogan .model { position: relative; z-index: 1; font-size: 92px; line-height: 1; color: var(--ink); }
.kmeaning { font-size: var(--fs-24); font-weight: 700; line-height: 1.25; margin: 0; }
.kread { display: grid; gap: 2px; margin: var(--s-2) 0 0; }
.kread > div { display: flex; gap: var(--s-3); align-items: baseline; }
.kread dt { font-size: var(--fs-14); font-weight: 700; color: var(--accent-text); min-width: 32px; }
.kread dd { margin: 0; display: flex; flex-wrap: wrap; gap: 0 var(--s-4); font-size: var(--fs-18); font-weight: 700; }
.kparts { display: flex; flex-wrap: wrap; align-items: center; gap: var(--s-2); margin-top: var(--s-3); }
.pchip { display: inline-flex; align-items: center; gap: 6px; min-height: 36px; padding: 2px var(--s-3) 2px 6px; border-radius: var(--r-pill); background: var(--surface-2); border: 1px solid var(--line); font-size: var(--fs-14); }
.pchip b { display: inline-grid; place-items: center; width: 28px; height: 28px; border-radius: 50%; background: var(--surface); font-size: var(--fs-18); box-shadow: 0 0 0 1px var(--line); }
.kwords { display: flex; flex-wrap: wrap; gap: var(--s-2); margin-top: var(--s-4); padding-top: var(--s-4); border-top: 1px solid var(--line); }
.kword { display: inline-flex; align-items: baseline; gap: var(--s-2); padding: 6px var(--s-3); border-radius: var(--r-1); background: var(--surface-2); font-size: var(--fs-14); color: var(--ink-2); text-decoration: none; }
.kword [lang=ja] { font-size: var(--fs-18); font-weight: 700; color: var(--ink); }

/* ===================================================================================================
   Marks: done, known and right are one mark (.maru, the --i-done tick on a filled green circle; .maru-2 adds a
   ring for excellent), × wrong in enji, △ almost (partial credit). The teacher's red stays for はなまる.
   =================================================================================================== */
.maru { display: inline-block; flex: none; width: 1.15em; height: 1.15em; vertical-align: -.2em; background: var(--good);
  -webkit-mask: var(--i-done) center / contain no-repeat; mask: var(--i-done) center / contain no-repeat; }
.maru.maru-2 { -webkit-mask-image: var(--i-done-2); mask-image: var(--i-done-2); }
.batsu, .sankaku { display: inline-block; flex: none; width: 1.15em; height: 1.15em; vertical-align: -.2em; background: var(--bad); -webkit-mask: var(--i-batsu) center / contain no-repeat; mask: var(--i-batsu) center / contain no-repeat; }
.sankaku { background: var(--warn); -webkit-mask: var(--i-sankaku) center / contain no-repeat; mask: var(--i-sankaku) center / contain no-repeat; }
.verdict { position: relative; margin-top: var(--s-3); padding: var(--s-3) var(--s-4) var(--s-3) 48px; border-radius: var(--r-2); font-weight: 700; text-align: left; background: var(--surface-2); color: var(--ink); min-height: var(--tap); }
.verdict::before { content: ""; position: absolute; left: var(--s-4); top: calc(var(--s-3) + 2px); width: 20px; height: 20px; }
.verdict:has(ruby) { line-height: var(--lh-ruby); padding-block: var(--s-2); }
.verdict:has(ruby)::before { top: calc(var(--s-2) + 7px); }
.verdict.ok { background: var(--good-soft); color: var(--good-text); }
.verdict.ok::before { background: var(--good); -webkit-mask: var(--i-done) center / contain no-repeat; mask: var(--i-done) center / contain no-repeat; }
.verdict.ok.excellent::before { -webkit-mask-image: var(--i-done-2); mask-image: var(--i-done-2); }
.verdict.no { background: var(--bad-soft); color: var(--bad-text); }
.verdict.no::before { background: var(--bad); -webkit-mask: var(--i-batsu) center / contain no-repeat; mask: var(--i-batsu) center / contain no-repeat; }
.verdict.near { background: var(--warn-soft); color: var(--warn-text); }
.verdict.near::before { background: var(--warn); -webkit-mask: var(--i-sankaku) center / contain no-repeat; mask: var(--i-sankaku) center / contain no-repeat; }
/* the answer inside a verdict is emphasis in the verdict's own face */
.verdict .es { font-weight: 700; }
.verdict rt { color: inherit; opacity: .85; }

/* ===================================================================================================
   The furigana switch and the casual-register box ("Heard it in anime?")
   =================================================================================================== */
.furigana-switch { display: inline-flex; align-items: center; gap: var(--s-3); flex-wrap: wrap; }
.furigana-switch > .label, .seg-label { font-size: var(--fs-14); color: var(--ink-3); }
.casual { position: relative; padding: var(--s-5); border-radius: var(--r-2); border: 1.5px dashed var(--line-strong); background: var(--surface); min-width: 0; }
.casual-head { display: flex; align-items: center; flex-wrap: wrap; gap: var(--s-3); margin: 0; font-size: var(--fs-14); font-weight: 700; color: var(--ink-2); }
.casual h3 { margin: var(--s-3) 0 var(--s-2); }
.casual-lines { display: grid; gap: var(--s-2); margin: var(--s-4) 0; }
.casual-line { justify-self: start; max-width: 90%; margin: 0; padding: var(--s-2) var(--s-4); border-radius: var(--r-3) var(--r-3) var(--r-3) var(--r-1); background: var(--surface-2);
  font-size: var(--fs-20); line-height: var(--lh-ruby); }
.casual-line.reply { justify-self: end; border-radius: var(--r-3) var(--r-3) var(--r-1) var(--r-3); background: var(--accent-soft); }
.casual-line small { display: block; font-size: var(--fs-14); color: var(--ink-3); line-height: 1.5; }
.casual-note { font-size: var(--fs-14); color: var(--ink-3); margin: 0; }

/* ===================================================================================================
   Existing pieces in the new look: segmented controls, stats, meters, lists, plans, decks
   =================================================================================================== */
.seg { display: inline-flex; flex-wrap: wrap; padding: 0; gap: 0; border: 1px solid var(--line-strong); border-radius: var(--r-2); background: var(--surface); overflow: hidden; max-width: 100%; }
.seg button { min-height: var(--tap); padding: 0 var(--s-4); border: 0; border-left: 1px solid var(--line-strong); border-radius: 0; background: transparent; color: var(--ink-2); font-size: var(--fs-14); font-weight: 400; }
.seg button:first-child { border-left: 0; }
@media (hover: hover) { .seg button:hover { background: var(--surface-2); } }
/* the chosen one is unmistakable in light and dark: the accent, not a slightly lighter pill */
.seg button[aria-pressed=true], .seg button[aria-checked=true] { background: var(--accent); color: var(--accent-ink); font-weight: 700; box-shadow: none; }
.seg .short { display: none; }
.stat-row { display: grid; grid-template-columns: repeat(4, minmax(0, 1fr)); gap: var(--s-3); }
.stat { padding: var(--s-4); border-radius: var(--r-2); background: var(--surface-2); min-width: 0; }
.stat b { display: block; font-size: var(--fs-30); font-weight: 400; line-height: 1.15; font-variant-numeric: tabular-nums; letter-spacing: -.01em; }
.stat span { color: var(--ink-3); font-size: var(--fs-14); }
.meter { display: flex; height: 10px; border-radius: var(--r-1); overflow: hidden; background: var(--track); }
.meter i { display: block; height: 100%; }
.meter .m-new { background: transparent; } .meter .m-master { background: var(--dye-4); }
.meter .m-learn { background: repeating-linear-gradient(135deg, var(--dye-2) 0 4px, var(--dye-1) 4px 8px); }
.legend .sw-learn { background: repeating-linear-gradient(135deg, var(--dye-2) 0 3px, var(--dye-1) 3px 6px); }
.legend { display: flex; gap: var(--s-2) var(--s-4); flex-wrap: wrap; font-size: var(--fs-14); color: var(--ink-3); margin-top: var(--s-2); }
.legend i { display: inline-block; width: 10px; height: 10px; border-radius: 3px; margin-right: 6px; vertical-align: -1px; }
.legend i.m-master { background: var(--dye-4) !important; }
.list { list-style: none; margin: 0; padding: 0; }
.list > li { display: flex; gap: var(--s-3); align-items: center; padding: var(--s-3) 2px; border-top: 1px solid var(--line); min-width: 0; flex-shrink: 0; }
.list > li:first-child { border-top: 0; }
.list .main { flex: 1; min-width: 0; overflow-wrap: anywhere; }
.list .main small { display: block; color: var(--ink-3); font-size: var(--fs-14); }
.badge { font: 700 var(--fs-14)/1 var(--font-sans); padding: 5px var(--s-2); border-radius: var(--r-pill); background: var(--bad-soft); color: var(--bad-text); font-variant-numeric: tabular-nums; white-space: nowrap; }
.plan { list-style: none; margin: 0; padding: 0; display: grid; gap: var(--s-1); }
.plan li { display: flex; gap: var(--s-3); align-items: flex-start; padding: var(--s-2) var(--s-1); }
.plan label { display: flex; gap: var(--s-3); align-items: flex-start; cursor: pointer; flex: 1; min-height: var(--tap); }
.plan input { width: 22px; height: 22px; margin: 1px 0 0; accent-color: var(--accent); flex: none; }
.plan .day { font-weight: 700; color: var(--accent-text); font-size: var(--fs-14); min-width: 44px; padding-top: 2px; }
.plan .done { color: var(--ink-3); text-decoration: line-through; }
.empty { text-align: center; padding: var(--s-6) var(--s-5); color: var(--ink-3); }
.empty svg { width: 48px; height: 48px; color: var(--ink-3); margin-bottom: var(--s-2); }
.deckgrid { display: grid; grid-template-columns: repeat(auto-fill, minmax(min(100%, 280px), 1fr)); gap: var(--s-4); }
.deck { display: flex; flex-direction: column; gap: var(--s-2); text-decoration: none; color: inherit; padding: var(--s-5); border-radius: var(--r-2); background: var(--surface); border: 1px solid var(--line);
  min-width: 0; transition: border-color .15s; }
@media (hover: hover) { .deck:hover { border-color: var(--accent-text); } }
.deck h3 { margin: 0; overflow-wrap: anywhere; }
.deck p { margin: 0; color: var(--ink-3); font-size: var(--fs-14); }
.deck .foot { display: flex; justify-content: space-between; gap: var(--s-1) var(--s-3); flex-wrap: wrap; color: var(--ink-3); font-size: var(--fs-14); font-weight: 700; margin-top: auto; }

/* ===================================================================================================
   Shared pieces (docs/mobile-plan.md 1.4 and S9): the level chip, rows that are links, folds, a page's
   own count, and the dialog as a sheet on a phone
   =================================================================================================== */
/* The one level control: N5 ▾ beside the title (core.js levelChip); it opens a sheet of the five levels. */
.lvl-chip { display: inline-flex; align-items: center; gap: var(--s-1); flex: none; min-height: var(--tap); padding: 0 var(--s-3) 0 var(--s-4); border: 1.5px solid var(--line-strong);
  border-radius: var(--r-pill); background: var(--surface); color: var(--ink); font: 700 var(--fs-16)/1 var(--font-sans); white-space: nowrap; vertical-align: middle; }
.lvl-chip svg { width: 18px; height: 18px; color: var(--ink-3); }
@media (hover: hover) { .lvl-chip:hover { border-color: var(--field); background: var(--surface-2); } }
h1 .lvl-chip, h2 .lvl-chip { margin-left: var(--s-2); }
/* its sheet: five 48 px rows, the one shown marked, the learner's own tagged */
.lvl-rows { list-style: none; margin: 0; padding: 0; display: grid; gap: var(--s-1); }
.lvl-rows button { display: flex; align-items: center; justify-content: flex-start; gap: var(--s-3); width: 100%; min-height: 48px; padding: 0 var(--s-4); border-color: transparent;
  background: none; font-weight: 400; text-align: left; white-space: normal; }
.lvl-rows button b { min-width: 2.2em; }
.lvl-rows button[aria-current=true] { background: var(--accent-soft); color: var(--accent-text); }
.lvl-rows button[aria-current=true] b { color: inherit; }
.lvl-rows .lvl-mine { margin-left: auto; font-size: var(--fs-14); font-weight: 700; color: var(--ink-3); white-space: nowrap; }
/* Rows that are links (a calm list on a phone): the whole row one target, its title over one quiet line, a
   quiet figure on the right and ›; at most a 44 px tile or a 64 px picture at the left. */
ul.list.links > li { display: block; padding: 0; }
ul.list.links > li > :is(a, button) { display: flex; align-items: center; justify-content: flex-start; gap: var(--s-3); width: 100%; min-height: 56px; padding: var(--s-2) 2px; color: var(--ink); text-decoration: none; border-radius: var(--r-1);
  border: 0; background: none; font: 400 var(--fs-16)/var(--lh-body) var(--font-sans); text-align: left; white-space: normal; }
ul.list.links > li > button:active { transform: none; }
/* the › (a button row that draws its own icon keeps just that one) */
ul.list.links > li > :is(a, button:not(:has(> svg)))::after { content: ""; flex: none; width: 20px; height: 20px; background: var(--ink-3); -webkit-mask: var(--i-next) center / 20px no-repeat; mask: var(--i-next) center / 20px no-repeat; }
@media (hover: hover) { ul.list.links > li > :is(a, button):hover { background: var(--surface-2); border-color: transparent; } }
ul.list.links > li > :is(a, button):focus-visible { outline-offset: -2px; }
ul.list.links .main { flex: 1; min-width: 0; overflow-wrap: normal; }
ul.list.links .main > :is(b, strong) { display: block; font-size: var(--fs-16); font-weight: 700; line-height: 1.35; overflow-wrap: anywhere; }
/* its quiet line may take two lines on a narrow phone, never a word cut off with … after one */
ul.list.links .main > small { display: -webkit-box; -webkit-box-orient: vertical; -webkit-line-clamp: 2; line-clamp: 2; font-size: var(--fs-14); color: var(--ink-3); line-height: 1.45; overflow: hidden; overflow-wrap: anywhere; }
ul.list.links .meta { flex: none; font-size: var(--fs-14); color: var(--ink-3); font-variant-numeric: tabular-nums; white-space: nowrap; }
ul.list.links .lead { flex: none; display: grid; place-items: center; width: 44px; height: 44px; border-radius: var(--r-2); background: var(--surface-2); color: var(--accent-text); }
ul.list.links .lead svg { width: 22px; height: 22px; }
ul.list.links :is(.thumb, img) { flex: none; width: 64px; height: 64px; border-radius: var(--r-2); object-fit: cover; }
/* A fold for what can wait ("How this works" at a page's foot, "About the voices"): a 44 px row with a chevron. */
details.how { margin-top: var(--s-5); border-top: 1px solid var(--line); }
details.how > summary { display: flex; align-items: center; gap: var(--s-2); min-height: var(--tap); color: var(--ink-2); font-weight: 700; cursor: pointer; list-style: none; border-radius: var(--r-1); }
details.how > summary::-webkit-details-marker { display: none; }
details.how > summary::after { content: ""; flex: none; width: 18px; height: 18px; margin-left: auto; background: var(--ink-3); -webkit-mask: var(--i-chev) center / 18px no-repeat;
  mask: var(--i-chev) center / 18px no-repeat; transition: transform .15s; }
details.how[open] > summary::after { transform: rotate(180deg); }
details.how > :not(summary) { color: var(--ink-2); }
details.how > :last-child { margin-bottom: var(--s-2); }
/* The site's dialog as a sheet (core.js dialog({ sheet: true })): docked at the foot of a phone. */
@media (max-width: 720px) and (min-height: 501px) {
  dialog.sheet { width: 100% !important; max-width: 100%; max-height: 85dvh; margin: auto 0 0; border-bottom: 0; border-radius: var(--r-3) var(--r-3) 0 0;
    padding-bottom: env(safe-area-inset-bottom); animation: sheet-up .22s cubic-bezier(.2, .8, .2, 1); }
  dialog.sheet .dfoot:empty { display: none; }
  dialog.sheet .dbody { padding-bottom: var(--s-4); }
}
dialog .dfoot:empty { display: none; }
@keyframes sheet-up { from { transform: translateY(24px); opacity: 0; } }

/* ===================================================================================================
   Study, quiz and drills
   =================================================================================================== */
.study { max-width: 760px; margin: 0 auto; }
.study-top { display: flex; align-items: center; gap: var(--s-3); flex-wrap: wrap; margin-bottom: var(--s-3); }
.progressline { height: 6px; background: var(--track); border-radius: var(--r-pill); overflow: hidden; margin-bottom: var(--s-4); }
.progressline i { display: block; height: 100%; background: var(--accent); border-radius: var(--r-pill); transition: width .3s; }
.flash { position: relative; min-height: 320px; border-radius: var(--r-3); background: var(--surface); border: 1px solid var(--line); padding: var(--s-6) var(--s-6) var(--s-5);
  display: flex; flex-direction: column; align-items: center; justify-content: center; text-align: center; cursor: pointer; user-select: text; }
.flash .kind { position: absolute; top: var(--s-4); left: var(--s-4); }
.flash .say { position: absolute; top: var(--s-3); right: var(--s-3); }
.flash .front { font-size: clamp(26px, 8.2vw, 44px); line-height: 1.35; overflow-wrap: anywhere; max-width: 100%; }
/* a question that carries furigana holds their band */
.flash .front:has(ruby) { line-height: var(--lh-ruby-big); }
.flash .front.en { font-weight: 700; font-size: clamp(22px, 4.2vw, 30px); line-height: 1.3; }
.flash .front .chip { font-family: var(--font-sans); }
.flash .hint { margin-top: var(--s-3); color: var(--ink-3); font-size: var(--fs-14); }
.flash .answer { margin-top: var(--s-5); padding-top: var(--s-5); border-top: 1px dashed var(--line-strong); width: 100%; }
.flash .answer .big-ans { font-size: clamp(22px, 3.6vw, 26px); font-weight: 700; overflow-wrap: anywhere; line-height: var(--lh-ruby); }
.flash .answer .big-ans.es { font-weight: 400; }
.flash .note { margin-top: var(--s-2); color: var(--ink-3); }
.flash .ex { margin-top: var(--s-2); color: var(--ink-2); font-size: var(--fs-18); line-height: var(--lh-ruby); }
.blank { display: inline-block; min-width: 3.2em; border-bottom: 3px solid var(--accent); margin: 0 .1em; color: var(--accent-text); font-weight: 700; }
.grades { display: grid; grid-template-columns: 1fr 1fr; gap: var(--s-3); margin-top: var(--s-4); }
.grades button { min-height: 56px; font-size: var(--fs-18); border-radius: var(--r-2); }
.grades .again { background: var(--bad-soft); border-color: transparent; color: var(--bad-text); }
.grades .got { background: var(--good-soft); border-color: transparent; color: var(--good-text); }
@media (hover: hover) { .grades .again:hover { border-color: var(--bad); background: var(--bad-soft); } .grades .got:hover { border-color: var(--good); background: var(--good-soft); } }
.grades kbd, .key { font: 700 var(--fs-14) var(--mono); padding: 2px 6px; border-radius: var(--r-1); background: color-mix(in srgb, currentColor 12%, transparent); }
/* inside the accent button the key's chip darkens the button instead of lightening it, so it stays readable */
.primary kbd, .primary .key { background: color-mix(in srgb, var(--accent-ink) 22%, transparent); color: var(--accent-ink); }
.typeans { display: flex; gap: var(--s-2); margin-top: var(--s-4); }
.typeans input { flex: 1; min-width: 0; }
.kana-hint { font-size: var(--fs-14); color: var(--ink-3); }
.accents { display: flex; gap: var(--s-2); flex-wrap: wrap; margin-top: var(--s-2); }
.accents button { min-height: var(--tap); min-width: var(--tap); padding: 0 10px; border-radius: var(--r-2); font-size: var(--fs-18); }
.summary { text-align: center; padding: var(--s-6) var(--s-5); }
.summary .score { font-size: var(--fs-56); font-weight: 400; line-height: 1.1; letter-spacing: -.02em; font-variant-numeric: tabular-nums; }
/* a summary's heading sits centred under its score, on one line or several (.panel h2 is a flex row) */
.summary h2 { justify-content: center; }
.choices { display: grid; gap: var(--s-2); margin-top: var(--s-4); width: 100%; text-align: left; }
.choice { display: flex; gap: var(--s-3); align-items: center; justify-content: flex-start; width: 100%; min-height: 56px; padding: var(--s-2) var(--s-4); border-radius: var(--r-2);
  border: 1.5px solid var(--line-strong); background: var(--surface); cursor: pointer; font: 400 var(--fs-18)/1.4 var(--font-sans); color: var(--ink); text-align: left; white-space: normal; }
@media (hover: hover) { .choice:hover { border-color: var(--accent-text); background: var(--surface); } }
.choice .dot { width: 22px; height: 22px; border-radius: 50%; border: 2px solid var(--field); flex: none; display: grid; place-items: center; }
.choice[aria-checked=true] .dot::after { content: ""; width: 10px; height: 10px; border-radius: 50%; background: var(--accent); }
.choice.right { border-color: var(--good); background: var(--good-soft); }
/* the right one gets the site's done mark (an open ring would read as an empty radio button) */
.choice.right .dot { border: 0; background: var(--good); -webkit-mask: var(--i-done) center / contain no-repeat; mask: var(--i-done) center / contain no-repeat; }
.choice.right .dot::after { display: none; }
.choice.wrong { border-color: var(--bad); background: var(--bad-soft); }
.choice.wrong .dot { border: 0; }
.choice.wrong .dot::after { content: ""; width: 20px; height: 20px; border-radius: 0; background: var(--bad); -webkit-mask: var(--i-batsu) center / contain no-repeat; mask: var(--i-batsu) center / contain no-repeat; }
.choice:disabled { opacity: 1; cursor: default; }
/* the quiz's verdict and Next sit inside the centred card: full width there too, as in Study and Drills */
.flash > #after { align-self: stretch; text-align: left; }
.flash > #after .verdict { margin-top: var(--s-3); }
.flash.drill-card { min-height: 240px; cursor: default; }

/* ===================================================================================================
   Forms
   =================================================================================================== */
/* A field's own width comes from its font's average character, which for a Japanese face is a full em: so a
   label and a stack never take a field's natural width, they give it theirs (no field past its card). */
label.field { display: grid; grid-template-columns: minmax(0, 1fr); gap: 6px; font-weight: 700; font-size: var(--fs-14); min-width: 0; }
label.field > span.mute { font-weight: 400; }
input[type=text], input[type=email], input[type=number], input[type=search], input[type=password], input[type=url], input[type=tel], input[type=date], input[type=time], input:not([type]), select, textarea {
  font: 400 var(--fs-input)/1.4 var(--font-sans); color: var(--ink); background: var(--surface); border: 1.5px solid var(--field); border-radius: var(--r-2); padding: 10px var(--s-3);
  min-height: var(--tap); width: 100%; min-width: 0; }
input::placeholder, textarea::placeholder { color: var(--ink-3); opacity: 1; }
/* the romaji-to-kana box (ja.js): Japanese comes out of it, so it is set as Japanese, a size larger */
input[data-kana], textarea[data-kana] { font-size: var(--fs-18); line-height: 1.5; }
textarea { resize: vertical; min-height: 120px; }
select { appearance: none; -webkit-appearance: none; padding-right: 40px; background: var(--surface) var(--i-chev) right 12px center / 18px no-repeat; }
html[data-theme=dark] select { color-scheme: dark; }
input:focus-visible, select:focus-visible, textarea:focus-visible { outline: 3px solid var(--focus); outline-offset: 0; border-color: var(--accent-text); }
input.right, input[data-state=right] { border-color: var(--good); background: var(--good-soft); }
input.wrong, input[data-state=wrong] { border-color: var(--bad); background: var(--bad-soft); }
.field-row { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: var(--s-3); }
.err { color: var(--bad-text); font-weight: 700; margin: 6px 0; line-height: 1.5; }   /* whole pixels: scrolled into view, it lands whole */
.err:empty { display: none; }
.ok-text { color: var(--good-text); font-weight: 700; overflow-wrap: anywhere; }
.ok-text svg { width: 18px; height: 18px; vertical-align: -3px; }
.switch { display: inline-flex; align-items: center; gap: 10px; min-height: var(--tap); cursor: pointer; font-weight: 700; }
.switch input { appearance: none; -webkit-appearance: none; width: 44px; height: 26px; border-radius: var(--r-pill); background: var(--surface); border: 1.5px solid var(--field); position: relative; cursor: pointer; transition: background .15s; margin: 0; flex: none; }
.switch input::after { content: ""; position: absolute; top: 3px; left: 3px; width: 17px; height: 17px; border-radius: 50%; background: var(--field); transition: transform .15s, background .15s; }
.switch input:checked { background: var(--accent); border-color: var(--accent); }
.switch input:checked::after { transform: translateX(18px); background: var(--accent-ink); }
input[type=checkbox], input[type=radio] { accent-color: var(--accent); }
/* A checkbox in the site's tokens, light and dark (the browser's own is a filled grey square in dark, which reads as
   switched off for good): an open box with a 2 px field edge, filled with the accent and its tick when checked. A
   switch (.switch) draws its own. */
input[type=checkbox]:not(.switch > input) { appearance: none; -webkit-appearance: none; flex: none; display: inline-grid; place-items: center; width: 22px; height: 22px;
  margin: 0; vertical-align: middle; border: 2px solid var(--field); border-radius: 5px; background: transparent; color: var(--accent-ink); cursor: pointer;
  transition: background .12s, border-color .12s; }
input[type=checkbox]:not(.switch > input)::before { content: ""; width: 78%; height: 78%; background: currentColor; -webkit-mask: var(--i-tick) center / contain no-repeat;
  mask: var(--i-tick) center / contain no-repeat; transform: scale(0); transition: transform .12s; }
input[type=checkbox]:not(.switch > input):checked { background: var(--accent); border-color: var(--accent); }
input[type=checkbox]:not(.switch > input):checked::before { transform: none; }
input[type=checkbox]:not(.switch > input):disabled { opacity: .55; cursor: default; }
input[type=checkbox]:not(.switch > input):focus-visible { outline: 2px solid var(--focus); outline-offset: 2px; }
@media (prefers-reduced-motion: reduce) { input[type=checkbox]:not(.switch > input), input[type=checkbox]:not(.switch > input)::before { transition: none; } }
/* A field a form refused (a card's empty side, a lesson with no title) is marked, beside the words saying why. */
input[aria-invalid=true], textarea[aria-invalid=true] { border-color: var(--bad); }

/* ===================================================================================================
   Tables
   =================================================================================================== */
.tablewrap { overflow-x: auto; border: 1px solid var(--line); border-radius: var(--r-2); background: var(--surface); }
table { border-collapse: collapse; width: 100%; font-size: var(--fs-16); font-variant-numeric: tabular-nums; }
th, td { text-align: left; padding: 10px var(--s-3); border-top: 1px solid var(--line); vertical-align: middle; overflow-wrap: break-word; word-break: normal; hyphens: auto; }
th { border-top: 0; font-size: var(--fs-14); font-weight: 700; color: var(--ink-3); background: var(--surface-2); position: sticky; top: 0; }
td:lang(ja) { font-size: var(--fs-20); line-height: var(--lh-ruby); }
tr.clickable { cursor: pointer; }
/* A clickable row's first words are its button (or link) for the keyboard: they look like the cell's text. */
td .cell-btn, td .cell-link { display: inline; min-height: 0; padding: 0; border: 0; border-radius: var(--r-1); background: none; color: inherit; font: inherit; letter-spacing: inherit;
  text-align: left; text-decoration: none; white-space: normal; overflow-wrap: anywhere; cursor: pointer; }
@media (hover: hover) { tr.clickable:hover .cell-btn, tr.clickable:hover .cell-link { text-decoration: underline; text-underline-offset: 3px; } }
td .cell-btn:active { transform: none; }
@media (hover: hover) { tr.clickable:hover td { background: var(--surface-2); } td .cell-btn:hover { background: none; } }
th.n, td.n { text-align: right; font-variant-numeric: tabular-nums; white-space: nowrap; }
td.act { white-space: nowrap; text-align: right; width: 1%; }
@media (min-width: 721px) { td.act > button + button { margin-left: var(--s-2); } }
/* a lesson table (conjugations, readings): a rule under the head, Japanese at 20 px, group rows in ai */
.ja-table th { background: none; border-bottom: 1.5px solid var(--ink); position: static; padding: 0 var(--s-4) var(--s-3) 0; }
.ja-table td { padding: var(--s-2) var(--s-4) var(--s-2) 0; border-top: 0; border-bottom: 1px solid var(--line); }
.ja-table .grp td { padding-top: var(--s-5); font-size: var(--fs-14); font-weight: 700; color: var(--accent-text); border-bottom: 0; line-height: 1.4; }
.ja-table tr.watch td { background: var(--accent-soft); }
.ja-table tr.watch td:first-child { box-shadow: inset 3px 0 0 var(--accent); padding-left: var(--s-3); }
.ja-table .rule { font-size: var(--fs-14); color: var(--ink-2); line-height: 1.5; }

/* ===================================================================================================
   Drop zone, working, spinner
   =================================================================================================== */
.drop { border: 2px dashed var(--line-strong); border-radius: var(--r-3); padding: var(--s-6) var(--s-5); text-align: center; background: var(--surface); transition: border-color .15s, background .15s; }
.drop.over { border-color: var(--accent-text); background: var(--accent-soft); }
.drop > svg { width: 48px; height: 48px; color: var(--accent-text); }
.dropveil { position: fixed; inset: 0; z-index: 90; background: color-mix(in srgb, var(--accent) 22%, transparent); display: grid; place-items: center; pointer-events: none; }
.dropveil div { background: var(--surface); padding: var(--s-5) var(--s-6); border-radius: var(--r-3); font-weight: 700; font-size: var(--fs-20); box-shadow: var(--shadow-lg); border: 2px dashed var(--accent-text); }
.spinner { width: 22px; height: 22px; border-radius: 50%; border: 3px solid var(--line-strong); border-top-color: var(--accent-text); animation: spin .9s linear infinite; flex: none; }
@keyframes spin { to { transform: rotate(360deg); } }
.working { display: flex; gap: var(--s-3); align-items: center; padding: var(--s-4); border-radius: var(--r-2); background: var(--surface-2); }

/* ===================================================================================================
   Dialogs, toasts, the error card
   =================================================================================================== */
/* A dialog is one column: its heading and buttons always show, and only its body scrolls (dvh: the height
   with the browser's own bars, and the keyboard where the browser resizes for it). */
dialog { border: 1px solid var(--line); border-radius: var(--r-3); background: var(--surface); color: var(--ink); padding: 0; width: min(560px, calc(100vw - 24px)); max-height: calc(100dvh - 24px); box-shadow: var(--shadow-lg); }
dialog[open]:has(> .dbody) { display: flex; flex-direction: column; }   /* the site's own (core.js), not a widget's */
dialog::backdrop { background: var(--scrim); }
dialog .dhead { display: flex; align-items: center; gap: var(--s-2); padding: var(--s-5) var(--s-5) var(--s-2); flex: none; }
dialog .dhead h2 { margin: 0; flex: 1; }
dialog .dbody { padding: var(--s-2) var(--s-5); overflow: auto; flex: 1 1 auto; min-height: 0; overscroll-behavior: contain; }
dialog .dfoot { display: flex; gap: var(--s-2); justify-content: flex-end; padding: var(--s-3) var(--s-5) var(--s-5); flex-wrap: wrap; flex: none; }
/* A short screen (a phone on its side, or any phone with the keyboard up): the dialog takes the whole screen and
   scrolls as one, its heading going up with the fields and its buttons kept at the foot. */
@media (max-height: 500px) {
  dialog:has(> .dbody) { width: 100vw !important; max-width: 100vw; height: 100dvh; max-height: 100dvh; border: 0; border-radius: 0; }
  dialog[open]:has(> .dbody) { overflow: auto; overscroll-behavior: contain; scroll-padding: 8px 0 76px; }
  dialog .dbody { flex: none; overflow: visible; padding: 6px max(14px, env(safe-area-inset-right)) 8px max(14px, env(safe-area-inset-left)); }
  dialog .dhead { padding: 8px max(10px, env(safe-area-inset-right)) 0 max(14px, env(safe-area-inset-left)); }
  dialog .dfoot { position: sticky; bottom: 0; margin-top: auto; background: var(--surface); border-top: 1px solid var(--line);
    padding: 8px max(14px, env(safe-area-inset-right)) calc(8px + env(safe-area-inset-bottom)) max(14px, env(safe-area-inset-left)); }
}
.toasts { position: fixed; left: 50%; bottom: calc(var(--s-5) + env(safe-area-inset-bottom)); transform: translateX(-50%); z-index: 100; display: grid; gap: var(--s-2); width: min(520px, calc(100vw - 24px)); pointer-events: none; }
.toast { pointer-events: auto; display: flex; align-items: center; gap: var(--s-3); padding: 6px 6px 6px var(--s-4); border-radius: var(--r-2); background: var(--ink); color: var(--bg); box-shadow: var(--shadow-lg); font-weight: 700; animation: rise .2s ease-out; }
/* a toast takes no tap until it has settled (core.js), so a tap aimed at what was there lands there */
.toast.new { pointer-events: none; }
.toast span { flex: 1; min-width: 0; }
.toast button { min-height: var(--tap); background: transparent; color: var(--bg); border-color: color-mix(in srgb, var(--bg) 40%, transparent); }
@media (hover: hover) { .toast button:hover { background: color-mix(in srgb, var(--bg) 12%, transparent); } }
.toast.bad { background: var(--bad-text); color: var(--bg); }
@keyframes rise { from { transform: translateY(8px); opacity: 0; } }
.guard { max-width: 520px; margin: 12vh auto; padding: var(--s-5); border-radius: var(--r-3); background: var(--surface); border: 1px solid var(--line); }
/* what went wrong, folded under the buttons for a problem report */
.guard-why code { white-space: normal; overflow-wrap: anywhere; }
.guard-why > button { margin-bottom: var(--s-2); }

/* ===================================================================================================
   Tomo's corner (tutor.js): a 52-64 px button above the tab bar (in the bar on an upright phone), her bubble, her chat
   =================================================================================================== */
.tutor { position: fixed; z-index: 60; right: max(var(--gut-r), 8px); bottom: calc(var(--tab-h, 0px) + var(--s-2) + env(safe-area-inset-bottom));
  display: flex; flex-direction: column; align-items: flex-end; gap: var(--s-2); pointer-events: none; }
.tutor > * { pointer-events: auto; }
/* where the page leaves her a lane (see .page) she keeps to it, at the window's edge */
@media (min-width: 721px), (max-height: 500px) { .tutor { right: calc(max(8px, env(safe-area-inset-right))); } }
@media (min-width: 1100px) { .tutor { right: calc(max(8px, env(safe-area-inset-right)) + var(--s-2)); } }
/* Back after a field let go of the keyboard, she waits a moment before taking taps, so the tap that let go of
   it (on Check, on Next) lands on what it was aimed at, never on her as she reappears (tutor.js adds .back for
   that moment only, so she takes her own tap at once when a page opens). Not a motion: it stays with reduced
   motion too. */
.tutor.back .tu-char { animation: tutor-back .45s step-end both; }
@keyframes tutor-back { from { visibility: hidden; } to { visibility: visible; } }
/* a field outside her chat has the keyboard: she steps away completely */
.tutor.typing:not(.open) { display: none !important; }
/* The tutor's hello is on the page (Today, a lesson): the corner steps aside until it scrolls away or her chat opens. */
html.tutor-inline .tutor:not(.open) { visibility: hidden; opacity: 0; pointer-events: none; }
.tu-char, .tutor .lola { position: relative; display: block; width: var(--tutor-w); height: calc(var(--tutor-w) * 1.25); min-height: 0; padding: 0; border: 0; background: none; border-radius: var(--r-3); cursor: pointer; flex: none; }
@media (hover: hover) { .tu-char:hover, .tutor .lola:hover { background: none; border: 0; } }
.tu-char svg, .tutor .lola svg { display: block; width: 100%; height: 100%; overflow: visible; }
.tu-char:focus-visible { outline-offset: 0; }
.tutor.collapsed .bubble { display: none; }
.tutor.locked .tu-char { cursor: default; }
.bubble { position: relative; max-width: 300px; padding: var(--s-3) 44px var(--s-3) var(--s-4); border-radius: var(--r-3); background: var(--bubble); color: var(--ink); border: 1px solid var(--bubble-line);
  box-shadow: var(--shadow-lg); font-size: var(--fs-16); line-height: 1.75; }
.bubble::after { content: ""; position: absolute; right: 22px; bottom: -8px; width: 14px; height: 14px; background: var(--bubble); border: solid var(--bubble-line); border-width: 0 1px 1px 0; transform: rotate(45deg); }
.bubble.lock { font-size: var(--fs-14); line-height: 1.5; max-width: 260px; }
/* the close × is a full 44 px to press, in the bubble's corner */
.bubble button.x { position: absolute; top: 0; right: 0; min-height: var(--tap); width: var(--tap); padding: 0; border: 0; background: none; color: var(--ink-3); }
@media (hover: hover) { .bubble button.x:hover { color: var(--ink); background: none; } }
/* a tap goes through a bubble to what is under it: only its × takes one, and Tomo herself opens the chat */
.tutor .bubble { pointer-events: none; }
.tutor .bubble button.x { pointer-events: auto; }
/* overflow clip: nothing (not even focusing the message box) can scroll the head out of the panel */
.chat { width: min(400px, calc(100vw - 24px)); height: min(560px, calc(100dvh - 180px)); background: var(--surface); border: 1px solid var(--line); border-radius: var(--r-3); box-shadow: var(--shadow-lg);
  display: flex; flex-direction: column; overflow: hidden; overflow: clip; }
/* opened by a tap, the chat itself has the focus (tutor.js), so the keyboard waits for a tap in the box */
.chat:focus { outline: none; }
/* Her chat's head is one line at every width: a 32 px her, her name, Hands-free, her speaker and minimise. Hands-free
   says its state in a word too ("on" beside its name; the slashed microphone while muted), never colour alone; on
   the narrowest phones (340 px and less) it is its microphone alone while off, and "On" while on. (.tutor before
   these beats voice.css, which loads later.) Talk live is a row of its own under the head. */
.chat-head { display: flex; align-items: center; gap: var(--s-1); padding: 6px 6px 6px var(--s-3); min-height: 56px; border-bottom: 1px solid var(--line); flex-shrink: 0; }
.chat-head .mini { width: 32px; height: 40px; flex: none; margin: 0 var(--s-1) 0 0; }
.chat-head .mini svg { width: 100%; height: 100%; }
.chat-head .chat-name { flex: 1; min-width: 0; font-size: var(--fs-18); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.tutor .chat-head button.talk-btn { width: auto; min-height: var(--tap); padding: 0 12px; gap: 6px; font-size: var(--fs-14); white-space: nowrap; }
.tutor .chat-head .talk-btn > span { position: static; width: auto; height: auto; overflow: visible; clip: auto; display: inline-flex; }
.tutor .chat-head .talk-btn svg { width: 18px; height: 18px; }
.tutor .chat-head .talk-btn .tb-on { display: none; }
.tutor .chat-head .talk-btn.on .tb-on { display: inline-flex; }
@media (max-width: 340px) {
  .tutor .chat-head .talk-btn > .tb-word { position: absolute; width: 1px; height: 1px; overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; }
  .tutor .chat-head button.talk-btn:not(.on) { width: var(--tap); padding: 0; }
  .tutor .chat-head .talk-btn .tb-on { text-transform: capitalize; }
}
.chat-live { display: flex; align-items: center; gap: var(--s-2); min-height: var(--tap); padding: 0 var(--s-3) 0 var(--s-4); border-bottom: 1px solid var(--line); flex-shrink: 0;
  color: var(--ink-2); font-size: var(--fs-14); text-decoration: none; }
.chat-live[hidden] { display: none; }
.chat-live > span { flex: 1; min-width: 0; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.chat-live b { color: var(--accent-text); }
.chat-live svg { width: 18px; height: 18px; flex: none; color: var(--accent-text); }
.chat-live svg:last-child { color: var(--ink-3); }
@media (hover: hover) { .chat-live:hover { background: var(--surface-2); } }
.chat-log { flex: 1; overflow-y: auto; padding: var(--s-4); display: flex; flex-direction: column; gap: 10px; min-height: min(96px, 18dvh); }
.msg { position: relative; max-width: 86%; padding: 10px 13px; border-radius: var(--r-3); white-space: pre-wrap; overflow-wrap: anywhere; font-size: var(--fs-16); line-height: var(--lh-body); }
.msg:lang(ja), .msg [lang=ja] { line-height: var(--lh-ja); }
/* her readings take no line height of their own, so only a line that carries one grows (by what it needs) */
.msg rt { line-height: 1; }
/* her Japanese at 18 px, so its 14 px furigana sit over words, not over gaps */
.msg.bot { align-self: flex-start; background: var(--surface-2); border-bottom-left-radius: var(--r-1); padding-right: 34px; font-size: var(--fs-18); }
.msg.bot .fix, .msg.note { font-size: var(--fs-16); }
.msg.me { align-self: flex-end; background: var(--accent); color: var(--accent-ink); border-bottom-right-radius: var(--r-1); }
.msg.me rt { color: var(--accent-ink); }
.msg .fix { display: block; margin-top: var(--s-2); padding-top: var(--s-2); border-top: 1px solid var(--line-strong); color: var(--accent-text); font-weight: 700; }
.msg .again { position: absolute; right: 2px; bottom: 2px; width: 30px; min-height: 30px; color: var(--ink-3); }
/* drawn at 30 px in the bubble's corner, pressed over 44 */
.msg .again::after { content: ""; position: absolute; inset: -7px; }
.msg .again svg { width: 16px; height: 16px; }
.msg.note { align-self: center; max-width: 92%; background: none; color: var(--ink-3); font-size: var(--fs-14); text-align: center; padding: 4px 8px; }
.msg.note.state { background: var(--warn-soft); color: var(--ink); border-radius: var(--r-2); padding: 10px var(--s-3); }
.msg.typing { display: flex; gap: 4px; padding: 14px var(--s-4); }
.msg.typing i { width: 7px; height: 7px; border-radius: 50%; background: var(--ink-3); animation: dot 1s ease-in-out infinite; }
.msg.typing i:nth-child(2) { animation-delay: .15s; } .msg.typing i:nth-child(3) { animation-delay: .3s; }
@keyframes dot { 50% { transform: translateY(-4px); opacity: .5; } }
/* the quick replies are one row that scrolls sideways, so they never push the conversation off a short screen */
.chat-quick { display: flex; gap: var(--s-2); padding: 0 var(--s-3) var(--s-2); flex-wrap: nowrap; flex-shrink: 0; overflow-x: auto; scrollbar-width: none; overscroll-behavior-x: contain; }
.chat-quick::-webkit-scrollbar { display: none; }
.chat-quick button { min-height: var(--tap); font-size: var(--fs-14); padding: 0 14px; flex-shrink: 0; }
.chat-in { display: flex; gap: var(--s-2); padding: 10px; border-top: 1px solid var(--line); align-items: flex-end; flex-shrink: 0; }
.chat-in textarea { flex: 1; width: auto; min-height: var(--tap); max-height: 120px; resize: none; border-radius: var(--r-2); }
.chat-in > button { flex: none; }
.chat-in .icon { width: var(--tap); min-height: var(--tap); }
.chat-in .lang { width: var(--tap); min-width: var(--tap); padding: 0; font-size: var(--fs-14); color: var(--ink-3); }
/* the dictation language sits on the microphone it is for: one control, JA (or EN) and the mic */
.chat-in .lang + .mic { margin-left: calc(-1 * var(--s-2)); border-radius: 0 var(--r-2) var(--r-2) 0; }
.chat-in .lang:has(+ .mic) { border: 1px solid var(--line-strong); border-right: 0; border-radius: var(--r-2) 0 0 var(--r-2); background: var(--surface-2); color: var(--ink-2); }
/* the hint stays on one line, whatever the box's width */
.chat-in textarea::placeholder { white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.mic { position: relative; }
.mic.on { background: var(--bad); border-color: var(--bad); color: var(--surface); animation: pulse 1.2s ease-in-out infinite; }
@keyframes pulse { 50% { box-shadow: 0 0 0 6px color-mix(in srgb, var(--bad) 25%, transparent); } }
.mic .lvl { display: none; position: absolute; inset: 0; align-items: center; justify-content: center; gap: 3px; }
.mic.on svg { opacity: 0; }
.mic.on .lvl { display: flex; }
.mic .lvl i { width: 4px; height: 20px; border-radius: 2px; background: var(--surface); transform: scaleY(.15); transition: transform .06s linear; }
/* Hands-free talk's Mute (tutor.js, docs/polish-plan.md A2): the same toggle as every microphone's (voice.css
   .mic-mute, Talk live's): always the slashed microphone and its word, 48 px, a quiet fill; pressed, it fills with
   ink and says Muted, the level lies flat and the words say so. Its word stays at every width. */
.talk-mute { display: inline-flex; align-items: center; gap: var(--s-1); flex: none; min-height: 48px; padding: 0 var(--s-3); border-radius: var(--r-pill);
  font-size: var(--fs-14); font-weight: 700; background: var(--surface-2); color: var(--ink); border: 1.5px solid var(--line-strong); }
.talk-mute > span { display: inline-flex; }
.talk-mute svg { width: 20px; height: 20px; }
.talk-mute[aria-pressed=true] { background: var(--ink); color: var(--surface); border-color: var(--ink); }
@media (hover: hover) { .talk-mute:hover { background: var(--accent-soft); border-color: var(--field); } .talk-mute[aria-pressed=true]:hover { background: var(--ink); border-color: var(--ink); filter: brightness(1.15); } }
.talkbar.muted .talk-lvl { opacity: .35; }
.talkbar #tu-talkstop { min-height: 48px; }
/* one row at every width: the level, a word or two for what she is doing (the whole sentence is for screen readers),
   Mute and Stop at the right */
.tutor .talkbar { flex-wrap: nowrap; gap: var(--s-2); padding: var(--s-2) var(--s-2) var(--s-2) var(--s-3); }
.tutor .talkbar .talk-state { flex: 1 1 auto; min-width: 0; font-size: var(--fs-14); line-height: 1.25; }
/* Today's kanji card: Write it, a 44 px button of its own under the readings (home.js). */
.td-kwrite { margin: var(--s-3) 0 0; }
.td-kwrite .btn svg { width: 20px; height: 20px; }
/* Today with no connection (app.js renderOffline): what works offline, then Try again. */
.offline-home > .row { margin-top: var(--s-5); }
.chat-scrim { display: none; }
.tutor.open .tu-char, .tutor.open .lola { width: 56px; height: 70px; }
/* Her chat is a sheet from the bottom on a phone, on any short screen (a phone on its side) and on a touch
   tablet, over a dimmed page, so the page under it never looks usable. It sits on the keyboard and fits what
   is visible above it: app.js keeps --vvh (the visible height) and --kb (how much of the window the keyboard
   covers), since iOS does not shrink the page for it. */
@media (max-width: 720px), (max-height: 520px), (pointer: coarse) and (max-width: 1100px) {
  .tutor.open { left: 0; right: 0; bottom: var(--kb, 0px); align-items: stretch; }
  .tutor.open .tu-char, .tutor.open .lola { display: none; }
  .tutor.open .chat { width: 100%; height: min(calc(var(--vvh, 100dvh) - 8px), calc(100dvh - 8px), 78dvh, 640px); border-radius: var(--r-3) var(--r-3) 0 0; border-bottom: 0;
    padding: 0 env(safe-area-inset-right) env(safe-area-inset-bottom) env(safe-area-inset-left); }
  .tutor.open .chat-scrim { display: block; position: fixed; inset: 0; background: var(--scrim); z-index: -1; }
  .chat-head { padding-top: var(--s-2); padding-bottom: var(--s-2); }
}
/* A short screen gives the sheet nearly all of it, and the quick replies step aside while you type; with a
   phone on its side and the keyboard up, the head keeps to one slim line, so the message box always shows. */
@media (max-height: 600px) {
  .tutor.open .chat { height: min(calc(var(--vvh, 100dvh) - 8px), calc(100dvh - 8px), 640px); }
  .chat:has(#tu-text:focus) .chat-quick { display: none; }
}
@media (max-height: 320px) {
  .chat-live { display: none; }
  .chat-head { padding-top: 4px; padding-bottom: 4px; min-height: 48px; }
  .chat-log { padding-top: 6px; padding-bottom: 6px; }
  .chat-in { padding-top: 6px; padding-bottom: 6px; }
}
/* On a wider short screen (a phone on its side) or a touch tablet the sheet keeps to the right, under her corner. */
@media (min-width: 721px) and (max-height: 520px), (min-width: 721px) and (max-width: 1100px) and (pointer: coarse) {
  .tutor.open .chat { align-self: flex-end; width: min(100%, 640px); margin-right: env(safe-area-inset-right); padding-right: 0; }
}
.tour-ring { position: fixed; z-index: 110; border-radius: var(--r-3); box-shadow: 0 0 0 4px var(--focus), 0 0 0 9999px var(--scrim); pointer-events: none; transition: all .45s cubic-bezier(.3, .7, .3, 1); }

/* ===================================================================================================
   Tour, guide
   =================================================================================================== */
.tourpop { position: fixed; z-index: 120; width: min(360px, calc(100vw - 16px)); background: var(--surface); color: var(--ink); border: 1px solid var(--line); border-radius: var(--r-3); box-shadow: var(--shadow-lg); padding: var(--s-4) var(--s-4) var(--s-3); }
.tourpop p { color: var(--ink-2); margin: 0 0 var(--s-3); font-size: var(--fs-16); }
.tourpop .tour-sec { font-size: var(--fs-14); font-weight: 700; letter-spacing: var(--track-label); text-transform: uppercase; color: var(--accent-text); margin-bottom: var(--s-1); }
.tourpop .tour-t { margin: 0 0 6px; font-size: var(--fs-18); }
.tourpop .tour-bar { height: 4px; border-radius: var(--r-pill); background: var(--track); overflow: hidden; margin-bottom: 10px; }
.tourpop .tour-bar i { display: block; height: 100%; background: var(--accent); }
.tourpop button { min-height: var(--tap); padding: 0 var(--s-4); font-size: var(--fs-14); }
/* tour.js writes its button row's gap inline (6 px); 8 px between them */
.tourpop .row[style] { gap: 8px !important; }
/* On a short screen the tour's card is as small as it can be, so it finds room beside what it rings. */
@media (max-height: 500px) {
  .tourpop { padding: var(--s-3) var(--s-3) var(--s-2); }
  .tourpop p { font-size: var(--fs-14); margin-bottom: var(--s-2); line-height: 1.45; }
  .tourpop .tour-t { font-size: var(--fs-16); margin-bottom: var(--s-1); }
}
/* A target too tall to sit beside the card on a phone (tour.js): it goes to the top and the card docks at the
   foot of the screen as a short sheet, the full width, its title, its words at 14 px and Next; the ring stops
   above it. */
.tourpop.dock { width: 100%; border-width: 1px 0 0; border-radius: var(--r-3) var(--r-3) 0 0;
  padding: var(--s-3) max(var(--s-4), env(safe-area-inset-right)) calc(var(--s-2) + env(safe-area-inset-bottom)) max(var(--s-4), env(safe-area-inset-left)); }
.tourpop.dock :is(.tour-sec, .tour-bar) { display: none; }
.tourpop.dock .tour-t { font-size: var(--fs-16); margin-bottom: var(--s-1); }
.tourpop.dock p { font-size: var(--fs-14); line-height: 1.45; margin-bottom: var(--s-2); }
/* Over the page while the tour's next step gets ready (tour.js): a tap then lands here, on nothing. */
.tour-shield { position: fixed; inset: 0; z-index: 115; }
.guide .lead { font-size: var(--fs-18); }
.guide h3 { margin-top: var(--s-4); }
.guide p { color: var(--ink-2); }

/* ===================================================================================================
   Splash: the mark on the night band, once a session
   =================================================================================================== */
.splash { position: fixed; inset: 0; z-index: 200; display: grid; place-items: center; background: #0A1022; color: #F2F5FB; transition: opacity .45s; }
.splash::before { content: ""; position: absolute; inset: 0; background: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='44' height='22' viewBox='0 0 44 22'%3E%3Ccircle cx='22' cy='-11' r='22' fill='%230A1022'/%3E%3Ccircle cx='22' cy='-11' r='16.28' fill='none' stroke='rgba(255,255,255,.07)' stroke-width='1.2'/%3E%3Ccircle cx='22' cy='-11' r='10.56' fill='none' stroke='rgba(255,255,255,.07)' stroke-width='1.2'/%3E%3Ccircle cx='22' cy='-11' r='4.84' fill='none' stroke='rgba(255,255,255,.07)' stroke-width='1.2'/%3E%3Ccircle cx='0' cy='0' r='22' fill='%230A1022'/%3E%3Ccircle cx='0' cy='0' r='16.28' fill='none' stroke='rgba(255,255,255,.07)' stroke-width='1.2'/%3E%3Ccircle cx='0' cy='0' r='10.56' fill='none' stroke='rgba(255,255,255,.07)' stroke-width='1.2'/%3E%3Ccircle cx='0' cy='0' r='4.84' fill='none' stroke='rgba(255,255,255,.07)' stroke-width='1.2'/%3E%3Ccircle cx='44' cy='0' r='22' fill='%230A1022'/%3E%3Ccircle cx='44' cy='0' r='16.28' fill='none' stroke='rgba(255,255,255,.07)' stroke-width='1.2'/%3E%3Ccircle cx='44' cy='0' r='10.56' fill='none' stroke='rgba(255,255,255,.07)' stroke-width='1.2'/%3E%3Ccircle cx='44' cy='0' r='4.84' fill='none' stroke='rgba(255,255,255,.07)' stroke-width='1.2'/%3E%3Ccircle cx='22' cy='11' r='22' fill='%230A1022'/%3E%3Ccircle cx='22' cy='11' r='16.28' fill='none' stroke='rgba(255,255,255,.07)' stroke-width='1.2'/%3E%3Ccircle cx='22' cy='11' r='10.56' fill='none' stroke='rgba(255,255,255,.07)' stroke-width='1.2'/%3E%3Ccircle cx='22' cy='11' r='4.84' fill='none' stroke='rgba(255,255,255,.07)' stroke-width='1.2'/%3E%3Ccircle cx='0' cy='22' r='22' fill='%230A1022'/%3E%3Ccircle cx='0' cy='22' r='16.28' fill='none' stroke='rgba(255,255,255,.07)' stroke-width='1.2'/%3E%3Ccircle cx='0' cy='22' r='10.56' fill='none' stroke='rgba(255,255,255,.07)' stroke-width='1.2'/%3E%3Ccircle cx='0' cy='22' r='4.84' fill='none' stroke='rgba(255,255,255,.07)' stroke-width='1.2'/%3E%3Ccircle cx='44' cy='22' r='22' fill='%230A1022'/%3E%3Ccircle cx='44' cy='22' r='16.28' fill='none' stroke='rgba(255,255,255,.07)' stroke-width='1.2'/%3E%3Ccircle cx='44' cy='22' r='10.56' fill='none' stroke='rgba(255,255,255,.07)' stroke-width='1.2'/%3E%3Ccircle cx='44' cy='22' r='4.84' fill='none' stroke='rgba(255,255,255,.07)' stroke-width='1.2'/%3E%3Ccircle cx='22' cy='33' r='22' fill='%230A1022'/%3E%3Ccircle cx='22' cy='33' r='16.28' fill='none' stroke='rgba(255,255,255,.07)' stroke-width='1.2'/%3E%3Ccircle cx='22' cy='33' r='10.56' fill='none' stroke='rgba(255,255,255,.07)' stroke-width='1.2'/%3E%3Ccircle cx='22' cy='33' r='4.84' fill='none' stroke='rgba(255,255,255,.07)' stroke-width='1.2'/%3E%3C/svg%3E") center / 44px 22px repeat;
  -webkit-mask-image: radial-gradient(circle at 50% 50%, transparent 20%, #000 75%); mask-image: radial-gradient(circle at 50% 50%, transparent 20%, #000 75%); opacity: .9; }
.splash .mark { position: relative; text-align: center; }
.splash svg { width: 88px; height: 88px; animation: splash-in .9s cubic-bezier(.3, .7, .3, 1) both; }
.splash .splash-dot { transform-box: fill-box; transform-origin: center; animation: splash-dot 1.2s .7s ease-out both; }
.splash .word { margin-top: var(--s-4); font: 700 var(--fs-22) var(--font-sans); letter-spacing: .08em; opacity: 0; animation: fade .6s 1s forwards; }
.splash .word small { display: block; font-size: var(--fs-14); font-weight: 400; letter-spacing: .04em; color: #B3C3E4; }
@keyframes splash-in { from { transform: scale(.85); opacity: 0; } }
@keyframes splash-dot { 0% { transform: scale(0); } 60% { transform: scale(1.3); } 100% { transform: scale(1); } }
@keyframes fade { to { opacity: 1; } }
/* A screen that takes a moment says so, but only after the moment, so a quick one never flickers. */
.page-wait { opacity: 0; animation: fade .2s .6s forwards; }
.splash.out { opacity: 0; pointer-events: none; }

/* ===================================================================================================
   Signed out (the sign-in card in app.js's own pages; the landing page is public.css)
   =================================================================================================== */
.signin { max-width: 980px; margin: 0 auto; display: grid; grid-template-columns: minmax(0, 1.1fr) minmax(0, 1fr); gap: var(--s-6); align-items: center; padding: 6vh 0; }
.signin h1 { font-size: var(--fs-40); letter-spacing: -.02em; }
.signin .lead { font-size: var(--fs-18); color: var(--ink-2); }
.signin .panel { padding: var(--s-5); }
.signin .hello { display: flex; gap: var(--s-1); align-items: flex-start; margin-top: var(--s-5); }
.signin .hello .char { width: 112px; height: 140px; flex: none; }
.signin .hello .char svg { width: 100%; height: 100%; }
.signin .hello .bubble { position: static; margin-top: var(--s-4); }
.codebox { font: 700 var(--fs-26)/1 var(--mono); letter-spacing: 8px; text-align: center; }

/* ===================================================================================================
   Lessons, Add, Settings, Admin (the copied screens)
   =================================================================================================== */
.keep-hit { display: inline-grid; place-items: center; cursor: pointer; min-width: var(--tap); min-height: var(--tap); }
.titlerow { flex-wrap: nowrap; align-items: flex-start; }
.titlerow h1 { min-width: 0; overflow-wrap: break-word; hyphens: auto; }
.titlerow .icon { margin-top: -6px; }
.ways .panel { display: flex; flex-direction: column; align-items: flex-start; gap: var(--s-2); padding: var(--s-4); }
.ways .panel p { margin: 0; }
.ways .panel .btn { margin-top: auto; }
.ways .copyline { align-self: stretch; margin-top: auto; }
/* A lesson card opens from anywhere on it (its title's link reaches over the whole card), like a deck's;
   its Study and Drills buttons sit above that and open on their own. */
.lesson-card { position: relative; }
.lesson-card h3 a { color: inherit; text-decoration: none; }
.lesson-card a.stretch::after { content: ""; position: absolute; inset: 0; border-radius: inherit; }
@media (hover: hover) { .lesson-card h3 a:hover { text-decoration: underline; text-underline-offset: 3px; } }
.lesson-card .lift { margin-top: 2px; position: relative; z-index: 1; gap: var(--s-2); }
.lesson-card .lift .btn { min-height: var(--tap); font-size: var(--fs-14); padding: 0 var(--s-4); }
.nowrap { white-space: nowrap; }
tr.off td { opacity: .45; }
tr.off td.act { opacity: 1; }
.preview { margin: 14px 0 0; padding: var(--s-3) 14px; border-radius: var(--r-2); background: var(--surface-2); color: var(--ink-2); font-size: var(--fs-16); max-height: 9.5em; overflow: hidden; white-space: pre-wrap; overflow-wrap: anywhere; }
.cost { display: flex; gap: var(--s-3); align-items: flex-start; margin-top: 14px; padding: 14px var(--s-4); border-radius: var(--r-2); background: var(--accent-soft); color: var(--ink); }
.cost > svg { width: 22px; height: 22px; color: var(--accent-text); flex: none; margin-top: 1px; }
.cost.stop { background: var(--warn-soft); }
.cost.stop > svg { color: var(--warn); }
.tips .panel h3 { margin-bottom: 6px; }
.tips .panel p { margin: 0; }
.settings { display: grid; grid-template-columns: 220px minmax(0, 1fr); gap: var(--s-5); align-items: start; }
.settings .side { position: sticky; top: calc(var(--bar-h) + var(--s-5)); display: grid; gap: 2px; }
.settings .side a { color: var(--ink-2); text-decoration: none; padding: 10px var(--s-3); border-radius: var(--r-2); }
.settings .side a[aria-current] { background: var(--surface); color: var(--ink); font-weight: 700; box-shadow: inset 3px 0 0 var(--accent), 0 0 0 1px var(--line); }
@media (hover: hover) { .settings .side a:hover { background: var(--surface); color: var(--ink); } }
.settings section { scroll-margin-top: 4px; }   /* the page's scroll-padding already clears the bar */
.settings select { max-width: 280px; }
/* Settings on a phone (settings.js): #/settings is the list of sections; a section's address shows it alone,
   flat on the page under its own heading (the page's "Settings" stays for screen readers; ‹ Settings is in the
   bar). In Your Japanese each choice is one line, its about line under the chosen one only. */
@media (max-width: 720px) {
  .settings[data-sub=""] { display: none; }
  .settings > .side { display: none; }
  .settings:not([data-sub=""]) > .stack > section:not(.on) { display: none; }
  .settings .stack > .panel { padding: 0; border: 0; background: none; }
  .settings .stack > .panel > h2:first-child { font-size: var(--fs-24); }
  .pagehead.set-sub { position: absolute; width: 1px; height: 1px; margin: 0; overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; }
  #s-japanese .vcard:not(:has(input:checked)) .vtext small { display: none; }
}
.flash-in { animation: flashin 1.6s ease-out; }
@keyframes flashin { 0% { box-shadow: 0 0 0 3px var(--focus); } 100% { box-shadow: 0 0 0 3px transparent; } }
.steps { margin: 6px 0 var(--s-3); padding-left: 22px; display: grid; gap: var(--s-3); }
.steps li::marker { font-weight: 700; color: var(--accent-text); }
.copyline { display: flex; gap: var(--s-2); align-items: center; margin-top: var(--s-2); flex-wrap: wrap; }
.copyline code, code { font: var(--fs-14)/1.4 var(--mono); background: var(--surface-2); border-radius: var(--r-1); padding: 2px 6px; overflow-wrap: anywhere; }
.copyline code { padding: 10px var(--s-3); flex: 1; min-width: 0; white-space: nowrap; overflow-x: auto; overflow-wrap: normal; }
/* An address to read and check (Connect ChatGPT's, an invite link) wraps where it must: never a box to scroll. */
.ways .copyline code, .notice .copyline code { white-space: normal; overflow-wrap: anywhere; overflow-x: visible; }
.tile .label { color: var(--ink-3); font-weight: 700; font-size: var(--fs-14); }
.tile .big-num { font-size: var(--fs-30); font-weight: 400; letter-spacing: -.01em; margin: var(--s-1) 0 10px; line-height: 1.15; }
.tile .big-num .mute { font-size: var(--fs-16); font-weight: 400; }
.tile p { margin: 10px 0 0; }
.meter.thick { height: 12px; }
.bars { display: grid; gap: 6px; }
.bar-row { display: grid; grid-template-columns: 70px minmax(0, 1fr) 64px; gap: 10px; align-items: center; }
.bar-row .bar { height: 10px; border-radius: var(--r-1); background: var(--track); overflow: hidden; }
.bar-row .bar i { display: block; height: 100%; background: var(--dye-3); border-radius: var(--r-1); }
.bar-row .num { text-align: right; }
.notice { margin-top: var(--s-3); padding: var(--s-3) 14px; border-radius: var(--r-2); background: var(--warn-soft); }
.notice p { margin: 0 0 4px; }
.picked { display: flex; gap: var(--s-3); align-items: center; min-width: 0; }
.picked > svg { width: 34px; height: 34px; color: var(--accent-text); flex: none; }
.picked b { overflow-wrap: anywhere; }
.more-line { margin: var(--s-2) 2px 0; }
/* A heading beside a text button sits on the same line as it; borderless buttons line up with the content edge. */
.row.between > h2, .row.between > h3 { margin: 0; }
.ghost.flush-l { margin-left: calc(-1 * var(--btn-pad)); }
.ghost.flush-r { margin-right: calc(-1 * var(--btn-pad)); }
.title-link { color: inherit; text-decoration: none; }
@media (hover: hover) { .title-link:hover { text-decoration: underline; text-underline-offset: 3px; } }
.filter { max-width: 300px; }
.empty.compact { padding: var(--s-4) 10px 6px; }
.empty.compact svg { width: 40px; height: 40px; }
.empty.compact p { margin: 0 0 6px; }
.empty.compact .btn { margin-top: var(--s-2); }
.flash:has(> .kind) { padding-top: 56px; }
.grades button:focus-visible { outline-color: currentColor; }
.coarse-only { display: none; }
/* a card's progress stays on one line where there is room for it; on a tablet it may wrap, so the words keep theirs */
@media (min-width: 1001px) { td.side { white-space: nowrap; } }
/* Admin › This month (owner.js): three meters in a row on a computer, one compact list on a phone; each a name and
   "under $0.01 of $5.00" over its meter and a line that matters now; one "How these are counted" fold at the
   card's foot, a short line for each. */
.own-meters { list-style: none; margin: 0; padding: 0; display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: var(--s-4) var(--s-6); }
.own-m-head { display: flex; align-items: baseline; justify-content: space-between; flex-wrap: wrap; gap: 0 var(--s-3); margin-bottom: var(--s-2); }
.own-m-head .label { color: var(--ink); font-size: var(--fs-16); }
.own-m-val { font-weight: 700; }
/* on a phone the value is a step smaller, so name and value share one row on all three (it wraps under the name only
   where they cannot) */
@media (max-width: 720px) { .own-m-val { font-size: var(--fs-14); } .own-m-head { gap: 0 var(--s-2); } }
.own-meters .own-m-now { margin: var(--s-2) 0 0; color: var(--ink-2); }
.own-month details.own-how { margin-top: var(--s-3); }
.own-how dl { margin: 0; }
.own-how dt { font-weight: 700; color: var(--ink); margin-top: var(--s-2); }
.own-how dt:first-child { margin-top: 0; }
.own-how dd { margin: 0; color: var(--ink-2); }
@media (max-width: 900px) {
  .own-meters { grid-template-columns: minmax(0, 1fr); gap: 0; }
  .own-meters > li { padding: var(--s-3) 0; border-top: 1px solid var(--line); }
  .own-meters > li:first-child { border-top: 0; padding-top: 0; }
}
/* Admin on a phone (owner.js): #/owner is this month's spending, then a list of the parts, each with what it says
   now; #/owner/<part> shows that part alone, flat on the page (the page's "Admin" stays for screen readers; ‹ Admin
   is in the bar). A computer keeps every part on one page. */
@media (max-width: 720px) {
  .own-page[data-sub=""] [data-own]:not([data-own=month]), .own-page[data-sub=""] .own-pair { display: none; }
  .own-page:not([data-sub=""]) :is(.own-month, .own-index) { display: none; }
  .own-page:not([data-sub=""]) [data-own]:not(.on) { display: none; }
  .own-page:not([data-sub=""]) .own-pair { display: block; margin-top: 0 !important; }
  .own-page:not([data-sub=""]) :is(.panel[data-own].on, [data-own].on > .panel) { padding: 0; border: 0; background: none; margin-top: 0 !important; }
  .own-page:not([data-sub=""]) :is(.panel[data-own].on, [data-own].on > .panel) + .panel.on { margin-top: var(--s-6) !important; }
  .own-page .own-index { margin-top: var(--s-4); border-top: 1px solid var(--line); }
  .pagehead.own-sub { position: absolute; width: 1px; height: 1px; margin: 0; overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; }
}
/* Admin › Voices: each column's meters and forms (owner.js) */
.own-voices .stack { gap: var(--s-5); }
.own-voices h3 { display: flex; align-items: center; gap: var(--s-2); flex-wrap: wrap; }
.own-meter .big-num { font-size: var(--fs-22); font-weight: 700; letter-spacing: -.01em; margin: 10px 0 var(--s-2); font-variant-numeric: tabular-nums; line-height: 1.2; }
.own-meter .big-num .mute { font-size: var(--fs-14); font-weight: 400; }
.own-form { align-items: flex-end; margin-top: 10px; }
.own-form .field { max-width: 220px; flex: 1 1 160px; }
.own-chosen .chip { flex: none; }
.own-voices .switch { min-height: var(--tap); }
/* A button whose words may take two lines on a narrow phone (Settings: Download the ChatGPT plugin). */
.btn.wrap { white-space: normal; text-align: center; line-height: 1.25; padding-top: var(--s-2); padding-bottom: var(--s-2); }
/* Text with no spaces to break at (an email address) still fits a narrow phone. */
.any-wrap { overflow-wrap: anywhere; min-width: 0; }
/* No connection (js/install.js): a calm note at the foot of the window, never a pop-up. On a computer a
   small bar in the middle, clear of Tomo in the corner; on a phone a full-width bar just above the tab bar
   (or the foot of the screen, signed out), in one short line. Toasts sit above it. */
.offline-note { position: fixed; z-index: 35; left: 50%; bottom: calc(var(--s-4) + env(safe-area-inset-bottom)); transform: translateX(-50%); width: max-content; max-width: min(520px, calc(100vw - 240px));
  padding: 10px 18px; border-radius: var(--r-pill); background: var(--warn-soft); color: var(--ink); border: 1px solid var(--line-strong); box-shadow: var(--shadow-lg);
  font-size: var(--fs-14); font-weight: 700; line-height: 1.35; text-align: center; pointer-events: none; }
.offline-note .short { display: none; }
body:has(.offline-note) .toasts { bottom: calc(76px + env(safe-area-inset-bottom)); }
body:has(.offline-note) .pub-foot { padding-bottom: calc(32px + 64px + env(safe-area-inset-bottom)); }
.offline-start h1 { font-size: var(--fs-24); }
/* On Talk live the call's controls are pinned to the foot of the screen, so above 720 px the note sits at the
   top of the page instead, under the bar (--above, live.css and talk.js). */
@media (min-width: 721px) {
  body.live-on .offline-note { position: absolute; z-index: 20; bottom: auto; top: calc(var(--bar-h) + env(safe-area-inset-top) + var(--s-2)); }
  body.live-on:has(.offline-note) #toasts { top: calc(var(--bar-h) + env(safe-area-inset-top) + 64px); }
}
@media (min-width: 721px) and (max-height: 500px) {
  body.live-on .offline-note .long { position: absolute; width: 1px; height: 1px; overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; }
  body.live-on .offline-note .short { display: inline; }
}
.add-alt.foot { display: none; }

/* ===================================================================================================
   Tablet (721 to 1099 px)
   =================================================================================================== */
@media (max-width: 1099px) {
  .band, .hero { padding: var(--s-5) var(--s-6); gap: var(--s-4) var(--s-5); }
}
@media (min-width: 721px) and (max-width: 1100px) {
  .nav a { padding: 0 var(--s-3); }
  .nav a[aria-current=page]::after { left: var(--s-3); right: var(--s-3); }
  .brand { margin-right: var(--s-3); }
}
@media (min-width: 721px) and (max-width: 900px) {
  /* the wordmark keeps its Japanese line; the English waits for a wider bar (it stays for screen readers) */
  header.bar:has(#nav:not([hidden])) .brand-en { position: absolute; width: 1px; height: 1px; overflow: hidden; clip: rect(0 0 0 0); }
  header.bar:has(#nav:not([hidden])) .brand-ja { font-size: var(--fs-16); }
}
@media (min-width: 721px) and (max-width: 800px) {
  header.bar:has(#nav:not([hidden])) .brand-name { position: absolute; width: 1px; height: 1px; overflow: hidden; clip: rect(0 0 0 0); }
}
@media (max-width: 900px) {
  .grid.two, .grid.three, .grid.even { grid-template-columns: minmax(0, 1fr); }
  .signin { grid-template-columns: minmax(0, 1fr); padding: 2vh 0; }
  .stat-row { grid-template-columns: repeat(2, minmax(0, 1fr)); }
  .settings { grid-template-columns: minmax(0, 1fr); }
  .settings .side { position: static; display: flex; flex-wrap: wrap; gap: var(--s-2); }
  .settings .side a { display: inline-flex; align-items: center; min-height: var(--tap); padding: 0 14px; background: var(--surface); border: 1px solid var(--line); font-size: var(--fs-14); }
  .settings .side a[aria-current] { box-shadow: inset 0 -3px 0 var(--accent); }
  .sec { grid-template-columns: minmax(0, 1fr); padding-block: var(--s-6); }
  .sec-head, .sec-body { grid-column: 1 / -1; }
  .split { grid-template-columns: minmax(0, 1fr); gap: var(--s-5); }
}

/* ===================================================================================================
   Phone (720 px and under): the tab bar, a 16 px gutter, one column
   =================================================================================================== */
@media (max-width: 720px) {
  :root { --bar-h: 56px; --gutter: 16px; --grid-gap: 16px; --tutor-w: 52px; --btn-pad: 16px; }
  /* a button's words may take two lines on the narrowest phones rather than run past the edge */
  .page :is(.btn, button):not(.icon) { white-space: normal; max-width: 100%; flex-wrap: wrap; row-gap: 2px; }
  /* the tab bar's height, signed in (a signed-out page has none, and a session hides it) */
  :root:has(#tabbar:not([hidden])) { --tab-h: 56px; }
  header.bar { padding: env(safe-area-inset-top) max(8px, env(safe-area-inset-right)) 0 var(--gut-l); }
  header.bar .nav, header.bar .add-btn { display: none; }
  .pubnav .ghost { display: none; }
  .brand { margin-right: auto; }
  .brand .mark, .brand > svg { width: 32px; height: 32px; }
  .brand-ja { font-size: var(--fs-16); }
  /* Signed in: the 28 px mark and 一緒に日本語 on one line (the romaji stays for screen readers); ? and ☾ live in
     the menu (Tour this page, Dark look), making room for the name or ‹, Tomo and ⋯. */
  header.bar:has(> #menu:not([hidden])) .brand { gap: var(--s-2); margin-right: 0; }
  header.bar:has(> #menu:not([hidden])) .brand .mark { width: 28px; height: 28px; }
  header.bar:has(> #menu:not([hidden])) .brand-ja { font-size: var(--fs-16); font-weight: 700; letter-spacing: .04em; }
  header.bar:has(> #menu:not([hidden])) .brand-en { position: absolute; width: 1px; height: 1px; overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; }
  header.bar:has(> #menu:not([hidden])) :is(#help-btn, #theme) { display: none; }
  /* The four tabs: an icon in a pill over its name; the current one in the accent, its pill lit. */
  .tabbar { display: grid; grid-template-columns: repeat(4, minmax(0, 1fr)); position: fixed; left: 0; right: 0; bottom: 0; z-index: 29; background: var(--surface); border-top: 1px solid var(--line);
    padding: 0 env(safe-area-inset-right) env(safe-area-inset-bottom) env(safe-area-inset-left); }
  .tabbar a { position: relative; display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 2px; min-height: var(--tab-h); height: var(--tab-h); padding: 0;
    font-size: var(--fs-14); line-height: 1.2; font-weight: 400; color: var(--ink-3); text-decoration: none; white-space: nowrap; min-width: 0; }
  .tabbar .tab-ic { display: grid; place-items: center; width: 56px; height: 28px; border-radius: var(--r-pill); transition: background .15s; }
  .tabbar a svg { width: 24px; height: 24px; flex: none; }
  .tabbar a[aria-current=page] { color: var(--accent-text); font-weight: 700; }
  .tabbar a[aria-current=page] .tab-ic { background: var(--accent-soft); }
  .tabbar a:focus-visible { outline-offset: -4px; }
  .page { padding-top: var(--s-4); }
  .band, .hero { grid-template-columns: minmax(0, 1fr); padding: var(--s-5); gap: var(--s-4); }
  .band::before, .hero::before { width: 100%; }
  .band > .tutor-hello { grid-column: 1; grid-row: auto; }
  .band h1, .hero h1 { font-size: var(--fs-26); margin-bottom: var(--s-2); }
  .band .lead, .hero .lead { font-size: var(--fs-16); }
  .hero .actions { min-width: 0; }
  .segbar { flex-basis: 100%; max-width: none; }
  .btn.start, .start-btn { width: 100%; padding: var(--s-2) var(--s-4); }
  .tutor-hello-me { width: 64px; }
  .tutor-hello-say { font-size: var(--fs-16); margin-bottom: var(--s-3); }
  h1 { font-size: var(--fs-26); }
  .title-ja { font-size: var(--fs-40); }
  .subtitle { font-size: var(--fs-20); }
  .panel { padding: var(--s-4); }
  .card-head { padding: var(--s-4) var(--s-4) var(--s-3); }
  .card-body { padding: 0 var(--s-4) var(--s-4); }
  .card-foot, .plan-foot { padding: var(--s-3) var(--s-4); }
  .sheet { padding: 0 var(--s-4); }
  .sec-head h2 { font-size: var(--fs-20); }
  .prose { font-size: var(--fs-16); }
  .plan-row { grid-template-columns: 44px minmax(0, 1fr) auto; grid-template-areas: "icon main time" "icon extra time"; gap: var(--s-2) var(--s-3); padding: var(--s-3) var(--s-4); min-height: 72px; }
  .plan-row .idx { display: none; }
  .plan-row .pi { align-self: start; }
  .plan-row .price, .plan-row .time { align-self: start; }
  .kt { width: 44px; height: 44px; font-size: var(--fs-24); }
  .ja-lg { font-size: var(--fs-22); }
  .ja-xl { font-size: var(--fs-30); }
  .ex { gap: var(--s-3); }
  .formula { display: grid; grid-template-columns: minmax(0, 1fr) minmax(0, 1fr); gap: var(--s-2); }
  .ftile { padding: var(--s-2) var(--s-3) var(--s-3); min-width: 0; }
  .ftile .ja { font-size: var(--fs-30); }
  .fop { display: none; }
  .kanji-card { padding: var(--s-4); }
  .kanji-card-body { grid-template-columns: 112px minmax(0, 1fr); gap: var(--s-3) var(--s-4); }
  .hogan { width: 112px; }
  .hogan .model { font-size: 78px; }
  .stampcard { gap: 2px; padding: var(--s-3) var(--s-2) var(--s-2); }
  .pop-card { padding: var(--s-6) var(--s-4) var(--s-4); }
  .casual { padding: var(--s-4); }
  .heat { grid-template-columns: repeat(7, minmax(0, 36px)); }
  .seg .long { display: none; }
  .seg .short { display: inline; }
  th, td { padding: 9px 10px; }
  .chat { height: min(520px, calc(100dvh - 230px)); }
  .flash { min-height: 260px; padding: var(--s-6) var(--s-4) var(--s-4); }
  .field-row { grid-template-columns: minmax(0, 1fr); }
  .signin h1 { font-size: var(--fs-30); }
  .hide-sm { display: none !important; }
  /* Tables become stacked rows on a phone: the Japanese, its meaning under it, the state on the right. */
  .stack-sm thead { display: none; }
  .stack-sm tbody tr { display: grid; grid-template-columns: minmax(0, 1fr) auto; column-gap: var(--s-3); row-gap: 2px; padding: 11px 14px; border-top: 1px solid var(--line); align-items: center; }
  .stack-sm tbody tr:first-child { border-top: 0; }
  .stack-sm td { display: block; border: 0; padding: 0; grid-column: 1; }
  .stack-sm td.hide-sm { display: none !important; }
  .stack-sm td.mean { color: var(--ink-3); font-size: var(--fs-14); }
  .stack-sm td.side { grid-column: 2; grid-row: 1 / span 2; justify-self: end; }
  .stack-sm td.act { grid-column: 2; grid-row: 1 / span 3; justify-self: end; width: auto; display: flex; gap: var(--s-3); align-items: center; }
  .stack-sm, .stack-sm tbody { display: block; width: 100%; }
  .stack-sm.with-side td.side { grid-column: 1; grid-row: auto; justify-self: start; margin-top: 4px; }
  /* a row with two right-hand cells (Admin's Talk live calls: the length, then the credits) puts them one over the other */
  .stack-sm tbody tr:has(td.side + td.side) { grid-auto-flow: row dense; }
  .stack-sm tbody tr:has(td.side + td.side) td.side { grid-row: auto; }
  .stack-sm.lead tbody tr { grid-template-columns: auto minmax(0, 1fr); }
  .stack-sm.lead td { grid-column: 2; }
  .stack-sm.lead td.act { grid-column: 1; grid-row: 1 / span 3; justify-self: start; }
  /* keep or take out: the box is big enough to see and its whole 44px cell takes the tap */
  .stack-sm td.act input[type=checkbox] { width: 26px; height: 26px; margin: 9px 0; }
  .stack-sm.lead td.act { min-width: var(--tap); min-height: var(--tap); display: grid; place-items: center; }
  .stack-sm.below td.act { grid-column: 1 / -1; grid-row: auto; justify-self: stretch; display: flex; gap: var(--s-2); margin-top: var(--s-2); }
  .stack-sm.below td.act .btn, .stack-sm.below td.act button.quiet { flex: 1; }
  /* the accent keys in rows, each a full 44 px key with 8 px between */
  .accents { display: grid; grid-template-columns: repeat(5, minmax(0, 1fr)); gap: var(--s-2); }
  .accents button { min-width: 0; min-height: var(--tap); padding: 0; }
  .row > .accents { flex: 1 1 100%; }
  .pagehead > .row { width: 100%; }
  .pagehead > .row > .btn, .pagehead > .row > button { flex: 1 1 0; }
  .pagehead > .row > .primary { flex-basis: 100%; }
  .pagehead > .row > .menu { flex: none; }
  .pagehead > .row > .ghost.flush-l { margin-left: 0; }
  .drop .row, .settings .panel .row:not(.copyline) { flex-direction: column; align-items: stretch; }
  .row.end > .primary.big { flex-basis: 100%; order: -1; }
  .filter { max-width: none; flex-basis: 100%; }
  /* the phone's side gutter is 16px, so a flush button never reaches past the screen edge */
  .ghost.flush-l { margin-left: -12px; }
  .ghost.flush-r { margin-right: -12px; }
  /* !important beats the align-items written on Admin's voice rows (owner.js); .row.bottom is the class for it */
  .voice-row { flex-direction: column; align-items: stretch !important; }
  /* the whole address shows on a phone, with Copy under it */
  .ways .copyline code { flex-basis: 100%; }
  .study-top h1 { order: 3; flex-basis: 100%; font-size: var(--fs-18) !important; display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; overflow: hidden; }
  .study-top #count { margin-left: auto; }
  .add-alt.top { display: none; }
  .add-alt.foot { display: flex; margin-top: var(--s-4); }
  .offline-note { left: 0; right: 0; bottom: 0; transform: none; width: auto; max-width: none; border-radius: 0; border-width: 1px 0 0; box-shadow: none;
    padding: 9px max(16px, env(safe-area-inset-right)) calc(9px + env(safe-area-inset-bottom)) max(16px, env(safe-area-inset-left)); text-align: left; }
  .offline-note .long { position: absolute; width: 1px; height: 1px; overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; }
  .offline-note .short { display: inline; }
  #tabbar:not([hidden]) ~ .offline-note { bottom: calc(var(--tab-h) + env(safe-area-inset-bottom)); padding-bottom: 9px; }
  /* Her bubble on a phone sits up under the bar, never over what the thumb works at the foot (the main button,
     the microphone, an input, Check, Again and Got it). */
  .bubble { max-width: min(260px, calc(100vw - 96px)); }
  .tutor.saying .bubble { position: fixed; top: calc(var(--bar-h) + env(safe-area-inset-top) + var(--s-2)); right: max(10px, env(safe-area-inset-right)); max-width: min(300px, calc(100vw - 20px)); }
  .tutor.saying .bubble::after { display: none; }
  /* in a session it is a passing word of praise that takes no tap at all (its × would sit on Undo) */
  :is(html[data-tomo=quiet], body:has(#main .study)) .tutor.saying .bubble { padding-right: 14px; }
  :is(html[data-tomo=quiet], body:has(#main .study)) .tutor.saying .bubble button.x { display: none; }
}
/* One way back, one count and one progress line on an upright phone (docs/mobile-plan.md 1.2, S6): below a tab's
   own page and in a session the bar (which never scrolls away there) has ‹ and the page above in place of the
   name, so the page's own crumbs and top-of-page Back go (a .back marked .keep stays); in a session the bar's
   count and progress line stand for the page's own (.own-count, .progressline), and the page's own head row is
   kept for screen readers only. A phone on its side, whose bar scrolls away, keeps the page's own. */
@media (max-width: 720px) and (min-height: 501px) {
  /* ‹ (or ✕) at the left edge: its glyph on the page's 16 px gutter, the 44 px target reaching 8 px further out */
  html:is([data-shell=deep], [data-shell=focus]) .bar-back:not([hidden]) { display: inline-flex; margin-left: calc(-1 * var(--s-2)); padding-left: 0; }
  html:is([data-shell=deep], [data-shell=focus]) header.bar:has(> .bar-back:not([hidden])) .brand { display: none; }
  html[data-shell=focus] .bar-count:not([hidden]) { display: inline; }
  html:is([data-shell=deep], [data-shell=focus]) header.bar:has(> .bar-back:not([hidden])) ~ #main :is(.crumbs, nav:has(> .crumbs), .back:not(.keep)) { display: none; }
  html[data-bar-count] #main .own-count { display: none; }
  html[data-bar-progress] #main .progressline { display: none; }
  html[data-shell=focus] #main .study-top { position: absolute; width: 1px; height: 1px; margin: 0; overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; }
}
/* Short page heads on a phone (S10): the Japanese on the title's line (or, whole, on the next), a utility card's
   Japanese gone; in a fold's row it is a small line of its own under the row's words. */
@media (max-width: 720px) {
  .pagehead { margin-bottom: var(--s-4); }
  .pagehead h1 .ja-sub { display: inline-block; margin: 0 0 0 var(--s-2); }
  .card-head .ja-sub:not(.keep) { display: none; }
  details > summary:has(> .ja-sub) { flex-wrap: wrap; }
  details > summary > .ja-sub { order: 2; flex-basis: 100%; margin: -2px 0 var(--s-1); line-height: 1.4; }
  details > summary:has(> .ja-sub)::after { order: 1; }
}
/* About's title (about.js): このサイトについて on its own line under it on the narrowest phones, never broken mid-way */
@media (max-width: 390px) {
  .lr-aboutpage .pagehead h1 .ja-sub { display: block; margin: var(--s-1) 0 0; }
}
/* A small phone on its side keeps her corner: with no connection she stands above the note. */
@media (max-width: 720px) and (max-height: 500px) {
  body:has(.offline-note) .tutor:not(.open) { bottom: calc(var(--tab-h) + 52px + env(safe-area-inset-bottom)); }
}
/* Tomo on an upright phone (docs/mobile-plan.md 1.3): on a tab's page and below it, a 44 × 52 face at the right
   end of the bar, just left of ⋯ (#bar-tomo keeps her place), her chat the sheet it always is, her bubbles
   dropping under the bar with a notch pointing up at her; in a session no face, only a passing word that takes
   no tap. */
@media (max-width: 720px) and (min-height: 501px) {
  html.has-tutor[data-tomo=bar] .bar-tomo { display: block; }
  /* her hello on the page (Today) is her, while it shows: no empty place in the bar; nor while a word's sheet or
     the keyboard has sent her away, so the page's tool (Aa) keeps to the right edge */
  html.has-tutor.tutor-inline[data-tomo] .bar-tomo, html.has-tutor.lk-open[data-tomo] .bar-tomo, body:has(> #tutor.typing) .bar-tomo { display: none; }
  html[data-tomo=bar] .tutor:not(.open) { top: calc(env(safe-area-inset-top) + (var(--bar-h) - 52px) / 2); bottom: auto; right: calc(var(--bar-r) + var(--tap) + var(--s-2)); }
  html[data-tomo=bar] .tutor:not(.open) .tu-char { width: 44px; height: 52px; }
  /* the notch points up at her face, which stands left of ⋯ (her middle is 22 px in from her right edge) */
  html[data-tomo=bar] .tutor.saying .bubble::after { display: block; top: -8px; bottom: auto; right: calc(var(--bar-r) + var(--tap) + var(--s-2) + 14px - max(10px, env(safe-area-inset-right))); border-width: 1px 0 0 1px; }
  html[data-tomo=quiet] .tutor:not(.open) .tu-char { display: none; }
  .tutor.open { top: auto; }
}
/* Not there at all, at every width: over a writing pad, a running test or a call (data-tomo=off), and while a
   word's sheet is open (reading). */
html[data-tomo=off] .tutor:not(.open), body:has(#main :is(.wr-pad, .gk)) .tutor:not(.open), html.lk-open .tutor:not(.open) { display: none; }
/* The smallest phones (320 to 380 px wide, or short): Start and Tomo's hello still share the first screen. The
   band is tighter, the lead waits (the plan under the band says the same), Tomo is 48 px. */
@media (max-width: 420px) { .btn.start, .start-btn { font-size: var(--fs-16); } }
@media (max-width: 380px), (max-width: 720px) and (max-height: 700px) {
  .band, .hero { padding: var(--s-4); gap: var(--s-3); }
  .band .lead { display: none; }
  .band .eyebrow { letter-spacing: .02em; }
  .band h1, .hero h1 { margin-top: var(--s-1); }
  .page:has(> .band:first-child, > .grid12:first-child > .band:first-child) { padding-top: var(--s-3); }
  .band .tutor-hello-me { width: 48px; }
  .btn.start, .start-btn { font-size: var(--fs-16); }
  .band .tutor-hello-say { margin-bottom: var(--s-2); padding: var(--s-2) var(--s-3); line-height: var(--lh-ruby-big); }
  .segbar > * { height: 32px; }
  .btn.start, .start-btn { min-height: 48px; }
}
@media (max-width: 560px) {
  .kanji-card-body { grid-template-columns: minmax(0, 1fr); }
  .stampcard .st { width: 36px; }
}
/* The narrowest phones: the name stays on one line beside Sign in and the theme button. */
@media (max-width: 380px) {
  .brand { gap: var(--s-2); margin-right: var(--s-2); }
  .brand .mark, .brand > svg { width: 30px; height: 30px; }
  .brand-ja { font-size: var(--fs-14); letter-spacing: .04em; }
  .pubnav { gap: var(--s-1); margin-right: 0; }
  .pubnav #signin { padding: 0 14px; }
}
@media (max-width: 340px) {
  .brand-en { display: none; }
  .pubnav #signin { padding: 0 12px; }
}
/* Reduced motion: animations end at once, and nothing transitions at all (a 0 s transition never starts, so a style
   change shows in the same frame: a .01 ms one would leave every changed property at its old value until the next
   frame, under whatever measures the page meanwhile, as fitRuby() does). */
@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after { animation-duration: .01ms !important; animation-iteration-count: 1 !important; transition-duration: 0s !important; transition-delay: 0s !important; }
  .tutor.back .tu-char { animation-duration: .45s !important; }
}
/* On a touch screen there are no keys to press on the buttons and nothing to drop. The keys still show where
   they are the subject (Keyboard shortcuts, the help pages). */
@media (hover: none) and (pointer: coarse) {
  .grades kbd, button kbd, button .key, .choice > .faint { display: none; }
  .fine-only { display: none; }
  .coarse-only { display: inline; }
}
/* The same once a finger has touched the page (app.js marks html[data-touch]), whatever the browser says about
   hovering, until a real keyboard is used (html[data-keys]; a phone's own keyboard never sets it). */
html[data-touch]:not([data-keys]) :is(.grades kbd, button kbd, button .key, .choice > .faint, .fine-only) { display: none; }
html[data-touch]:not([data-keys]) .coarse-only { display: inline; }
/* Data tables: a column marked hide-md (a deck's Note) waits for a wider screen, so the actions never go past the edge. */
@media (max-width: 1000px) { .hide-md { display: none !important; } }

/* ===================================================================================================
   Toasts, study answer buttons and the bar on a phone or a short screen
   =================================================================================================== */
/* Toasts on a phone or a short screen sit at the top, under the bar: never over the buttons at the foot of
   the screen (Again and Got it, Next, the tab bar), where the thumb is. */
@media (max-width: 720px), (max-height: 500px) {
  .toasts, body:has(.offline-note) .toasts { top: calc(var(--bar-h) + env(safe-area-inset-top) + var(--s-2)); bottom: auto; }
}
/* Under 500 px tall the bar scrolls away and a card fills the screen, so they keep to the top right corner. */
@media (max-height: 500px) {
  .toasts, body:has(.offline-note) .toasts { top: calc(env(safe-area-inset-top) + var(--s-2)); left: auto; right: max(10px, env(safe-area-inset-right)); transform: none; width: min(300px, 44vw); }
}
/* Study, the quiz and drills on a phone or a short screen: the buttons that answer (Turn it over, Again and
   Got it, Next) stay in sight just above the tab bar (or the home indicator, in a session) while the card
   scrolls under them, the full width (Tomo is in the bar upright, and in her own lane on her side). */
@media (max-width: 720px), (max-height: 500px) {
  html:has(#main .study .grades, #main .study .study-next) { scroll-padding-bottom: calc(var(--tab-h) + env(safe-area-inset-bottom) + 96px); }
  .study .grades, .study #stage > .study-next, .flash > #after:has(.study-next) { position: sticky; bottom: calc(var(--tab-h) + env(safe-area-inset-bottom)); z-index: 5; }
  .study .grades, .study #stage > .study-next { background: var(--bg); margin-top: var(--s-2); padding: var(--s-2) 0; }
  .flash > #after:has(.study-next) { background: var(--surface); padding-bottom: var(--s-2); }
}
/* A short screen: a compact card, so its answer and the buttons fit together. */
@media (max-height: 700px) {
  .flash { min-height: 200px; padding: var(--s-5) var(--s-4) 18px; }
  .flash:has(> .kind) { padding-top: 52px; }
  .flash.drill-card { min-height: 0; }
}
/* A phone on its side (or any window under 500 px tall): the bar scrolls away with the page, the tab bar is one
   slim row, and the study screens drop what can wait (the title stays for screen readers). */
@media (max-height: 500px) {
  :root { --bar-h: 52px; }
  html { scroll-padding-top: calc(env(safe-area-inset-top) + var(--s-3)); }
  header.bar { position: relative; }
  .page { padding-top: var(--s-3); }
  h1 { font-size: var(--fs-24); }
  .pagehead { margin-bottom: var(--s-3); }
  .study { display: flex; flex-wrap: wrap; align-items: center; gap: 6px var(--s-4); }
  .study > * { flex: 1 1 100%; min-width: 0; }
  .study > .study-top, .study > .row.between { flex: 1 1 auto; margin: 0; }
  .progressline { margin-bottom: 10px; }
  .flash { min-height: 0; padding: var(--s-4); }
  .flash:has(> .kind) { padding-top: 52px; }
  .flash .front { font-size: 28px; }
  .grades button { min-height: 48px; }
  .drop .row { flex-direction: row; align-items: center; }
  .drop .row > .big { flex: 1 1 auto; min-height: var(--tap); padding: 0 var(--s-4); font-size: var(--fs-16); }
}
/* Sideways, and on the shortest upright phones, the study screens' title waits for a bigger screen (it stays for
   screen readers), so the way back, the count and Undo fit above the question after every answer (study.js). */
@media (max-height: 500px), (max-width: 720px) and (max-height: 600px) {
  .study-top h1 { position: absolute; width: 1px; height: 1px; overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; }
  .study-top { margin-bottom: var(--s-2); }
  .progressline { margin-bottom: var(--s-3); }
}
/* The very shortest (a small phone on its side) leaves out the progress line too: the count says the same. */
@media (max-height: 340px) { .study .progressline { display: none; } }
@media (max-width: 720px) and (max-height: 500px) {
  :root:has(#tabbar:not([hidden])) { --tab-h: 48px; }
  .tabbar a { flex-direction: row; gap: 6px; }
  .tabbar .tab-ic { width: 44px; }
  .tabbar a svg { width: 22px; height: 22px; }
}
@media (max-width: 720px), (max-height: 500px) {
  .drop { display: flex; flex-direction: column; padding: 18px var(--s-4); }
  .drop > svg { display: none; }
  .drop > p.mute { order: 2; margin: var(--s-3) 0 0; font-size: var(--fs-14); }
}
/* While a text field has the keyboard on a phone or a short screen, the tab bar and Tomo step away, so nothing
   fixed sits over what is being typed (the bar, with ⋯, stays put). */
@media (max-width: 720px), (max-height: 500px) {
  body:has(:is(input:not([type=checkbox]):not([type=radio]):not([type=range]):not([type=file]):not([type=button]):not([type=submit]), textarea):focus) :is(.tabbar, .tutor:not(.open)) { display: none; }
}
/* Study's Undo (study.js): a 44 px button beside the count, from an answer until the next one, so nothing
   floats over the card or the controls above it (an answer's Undo is never a toast here). */
.study-top .undo-btn { min-height: var(--tap); padding: 0 14px; font-size: var(--fs-14); flex: none; }
.study-top .undo-btn svg { width: 18px; height: 18px; }
.study-top .undo-btn.idle { visibility: hidden; }   /* nothing to undo just now: its place is kept */
