/* =====================================================================
   GeekLanguage — Words · product stylesheet
   Tokens, primitives and copy are lifted from design/design.html (approved
   2026-09-02). Light theme = warm paper, dark = warm charcoal, one moss-green
   accent; theme follows the system only.
   ===================================================================== */

:root{
  --bg:#f5f1e8;          /* warm paper */
  --bg-elev:#fffdf8;     /* cards */
  --bg-sunk:#ebe5d8;     /* tracks, wells */
  --ink:#1f1c17;
  --ink-2:#5a5449;
  --ink-3:#706a5c;       /* muted text: 4.77:1 on --bg, 5.29:1 on cards (WCAG AA) */
  --line:#dbd3c4;
  --line-2:#e9e3d6;

  --accent:#2e6d4d;      /* moss green — primary + Know */
  --accent-strong:#22553b;
  --accent-ink:#ffffff;
  --accent-soft:#e2eee5;
  --accent-line:#b9d5c4;

  --amber:#8a5312;       /* Don't Know — warm, quiet */
  --amber-soft:#f5e7cd;
  --amber-line:#e0c48e;
  --amber-hover:#f0dcb6;

  --danger:#a33a2a;
  --danger-soft:#f7e3df;
  --danger-line:#e5b9b1;

  --scroll:#c8bfad;
  --pic-dim:1;           /* the reveal picture keeps its own light tile; dark dims it a little */
  --shadow:0 1px 2px rgba(30,25,15,.05),0 14px 36px -16px rgba(30,25,15,.22);
  --shadow-soft:0 1px 0 rgba(30,25,15,.04),0 8px 24px -14px rgba(30,25,15,.16);
  --grain-opacity:.045;

  --font-ui:"Schibsted Grotesk","Helvetica Neue",Helvetica,Arial,sans-serif;
  --font-display:"Schibsted Grotesk","Helvetica Neue",Helvetica,Arial,sans-serif;   /* display = the UI face at 600-800 (the serif display face was dropped 2026-09-02) */
  --font-cjk:"PingFang SC","Hiragino Sans GB","Microsoft YaHei","Noto Sans CJK SC",sans-serif;
  --font-ipa:"Charis SIL","Doulos SIL","Gentium Plus","Noto Sans","Segoe UI","DejaVu Sans",sans-serif;
  /* one content column on wide screens: the mockup's 1180px screen. Full-bleed bands (nav,
     session header, landing sections, footer) keep their rules across the whole viewport and
     cap their content at --col by growing their side padding by --gutter past 1180px. */
  --col:1180px;
  --gutter:max(0px,calc((100% - var(--col)) / 2));
  color-scheme:light;
}
@media (prefers-color-scheme:dark){
  :root{
    --bg:#15130f;
    --bg-elev:#1d1a15;
    --bg-sunk:#0e0d0a;
    --ink:#ece6d9;
    --ink-2:#b1a999;
    --ink-3:#908979;     /* 5.34:1 on --bg, 4.99:1 on cards */
    --line:#2f2b24;
    --line-2:#262219;
    --accent:#5fb384;
    --accent-strong:#7cc79c;
    --accent-ink:#0d1a12;
    --accent-soft:#1b2c22;
    --accent-line:#2f4d3c;
    --amber:#e6ac60;
    --amber-soft:#2a2116;
    --amber-line:#5a4526;
    --amber-hover:#342917;
    --danger:#e08a7a;
    --danger-soft:#2c1c19;
    --danger-line:#5a3029;
    --scroll:#3a352c;
    --pic-dim:.8;
    --shadow:0 1px 2px rgba(0,0,0,.4),0 18px 40px -16px rgba(0,0,0,.6);
    --shadow-soft:0 1px 0 rgba(0,0,0,.3),0 8px 24px -14px rgba(0,0,0,.5);
    --grain-opacity:.06;
    color-scheme:dark;
  }
}

/* ---------- base ---------- */
*,*::before,*::after{box-sizing:border-box}
html{-webkit-text-size-adjust:100%;height:100%}
body{
  margin:0;background:var(--bg);color:var(--ink);
  font-family:var(--font-ui);font-size:16px;line-height:1.5;
  -webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale;
  overflow-x:hidden;min-height:100%;position:relative;
}
/* paper grain — fixed, non-interactive */
body::before{
  content:"";position:fixed;inset:0;z-index:9999;pointer-events:none;
  opacity:var(--grain-opacity);
  background-image:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='180' height='180'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='.9' numOctaves='2' stitchTiles='stitch'/%3E%3CfeColorMatrix type='saturate' values='0'/%3E%3C/filter%3E%3Crect width='100%25' height='100%25' filter='url(%23n)'/%3E%3C/svg%3E");
}
/* global thin scrollbars */
*{scrollbar-width:thin;scrollbar-color:var(--scroll) transparent}
*::-webkit-scrollbar{width:8px;height:8px}
*::-webkit-scrollbar-track{background:transparent}
*::-webkit-scrollbar-thumb{background:var(--scroll);border-radius:8px;border:2px solid transparent;background-clip:padding-box}
*::-webkit-scrollbar-thumb:hover{background:var(--ink-3);background-clip:padding-box;border:2px solid transparent}

a{color:inherit}
h1,h2,h3,p,dl,dd,figure{margin:0}
button{font-family:inherit;color:inherit}
/* Android Chrome paints its own blue flash on every tap: off, our :active / .pressed / busy states are the feedback (2026-09-04 live QA) */
button,a,[role="button"],input[type="range"]{-webkit-tap-highlight-color:transparent}
svg.i{width:1.25em;height:1.25em;flex:none;fill:none;stroke:currentColor;stroke-width:1.9;stroke-linecap:round;stroke-linejoin:round}
svg.i.fill{fill:currentColor;stroke:none}
.sr{position:absolute;width:1px;height:1px;overflow:hidden;clip:rect(0 0 0 0);white-space:nowrap}
[hidden]{display:none!important}
kbd.k{
  display:inline-flex;align-items:center;justify-content:center;min-width:24px;height:22px;padding:0 6px;
  font:600 12px/1 var(--font-ui);color:var(--ink-2);background:var(--bg-elev);
  border:1px solid var(--line);border-bottom-width:2px;border-radius:6px;
}
:focus-visible{outline:2px solid var(--accent);outline-offset:2px}
/* green-filled controls get an ink ring, so focus is visible against the fill */
.btn-primary:focus-visible,.ans-yes:focus-visible{outline-color:var(--ink)}
/* busy state: an inline SVG spinner replaces the button's icon while a request is in flight */
svg.i.spin{animation:en-spin .8s linear infinite}
@keyframes en-spin{to{transform:rotate(360deg)}}
@media (prefers-reduced-motion:reduce){svg.i.spin{animation:none}}
.sprite{position:absolute}

/* ---------- product primitives ---------- */
.app-nav{display:flex;align-items:center;justify-content:space-between;gap:24px;padding:14px calc(var(--gutter) + 40px);border-bottom:1px solid var(--line-2)}
/* wordmark "GeekLanguage": one word, two colours, same weight, no italic */
.wordmark{display:inline-flex;align-items:baseline;gap:0;text-decoration:none;white-space:nowrap}
.wordmark .a{font:700 19px/1 var(--font-ui);letter-spacing:-.02em;color:var(--ink)}
.wordmark .b{font:700 19px/1 var(--font-ui);letter-spacing:-.02em;color:var(--accent)}
.wordmark.small .a,.wordmark.small .b{font-size:15px}
.nav{display:flex;gap:4px;align-items:center}
.nav a{
  display:inline-flex;align-items:center;min-height:44px;padding:0 14px;border-radius:10px;
  text-decoration:none;color:var(--ink-2);font-weight:600;font-size:15px;
}
.nav a.on{color:var(--ink);background:var(--bg-sunk)}
.nav a:hover{color:var(--ink)}
.nav a.btn-primary,.nav a.btn-primary:hover{color:var(--accent-ink)}
.btn{
  display:inline-flex;align-items:center;justify-content:center;gap:10px;min-height:48px;padding:0 22px;
  border-radius:12px;font:600 16px var(--font-ui);border:1px solid var(--line);background:var(--bg-elev);
  color:var(--ink);cursor:pointer;text-decoration:none;white-space:nowrap;
  transition:transform .12s ease,background .15s ease,border-color .15s ease,box-shadow .15s ease;
}
.btn:hover{transform:translateY(-1px);border-color:var(--ink-3)}
.btn-primary{background:var(--accent);color:var(--accent-ink);border-color:var(--accent);box-shadow:0 8px 20px -10px var(--accent)}
.btn-primary:hover{background:var(--accent-strong);border-color:var(--accent-strong)}
.btn-ghost{background:transparent}
.btn-block{width:100%}
.btn-lg{min-height:56px;padding:0 28px;font-size:17px;border-radius:14px}
.btn[disabled]{opacity:.65;cursor:default;transform:none;pointer-events:none}
.btn.busy{opacity:1;cursor:progress;transform:none;pointer-events:none}
.ans.busy{cursor:progress;pointer-events:none}
.link{color:var(--accent);font-weight:600;text-decoration:none;border-bottom:1px solid transparent;background:none;border-top:0;border-left:0;border-right:0;padding:0;cursor:pointer;font-family:inherit;font-size:inherit}
.link:hover{border-bottom-color:currentColor}
/* standalone text links get a 44px tap area through padding + negative margins (layout unchanged) */
.swap .link,.state .link{display:inline-block;padding:12px 6px;margin:-12px -6px}
.eyebrow{font-size:12.5px;font-weight:700;letter-spacing:.14em;text-transform:uppercase;color:var(--accent)}
.muted{color:var(--ink-3)}

/* inputs — 16px minimum (iOS zoom rule), including hidden radios and the range */
input,select,textarea{font-size:16px}
.field{display:block;margin:0 0 18px}
.field .lbl{display:block;font-size:14px;font-weight:700;margin:0 0 8px;color:var(--ink-2)}
.field .box{position:relative;display:flex;align-items:center}
.field .box svg.i{position:absolute;left:14px;color:var(--ink-3);pointer-events:none}
.input{
  width:100%;min-height:50px;padding:0 14px;font:400 16px var(--font-ui);color:var(--ink);
  background:var(--bg-elev);border:1px solid var(--line);border-radius:12px;outline:none;
}
.box svg.i + .input{padding-left:44px}
.input:focus{border-color:var(--accent);box-shadow:0 0 0 3px var(--accent-soft)}
.input::placeholder{color:var(--ink-3)}
.input.pw{padding-right:52px}
.eye{
  position:absolute;right:4px;width:44px;height:44px;display:grid;place-items:center;border:0;background:transparent;
  color:var(--ink-3);border-radius:10px;cursor:pointer;
}
.eye:hover{color:var(--ink)}
.help{font-size:13px;color:var(--ink-3);margin:8px 0 0}
.form-error{
  display:flex;gap:10px;align-items:flex-start;margin:0 0 18px;padding:12px 14px;border-radius:12px;
  background:var(--danger-soft);border:1px solid var(--danger-line);color:var(--danger);font-size:14.5px;line-height:1.4;
}
.form-error svg.i{margin-top:2px}

/* tags */
.tag{
  display:inline-flex;align-items:center;gap:6px;height:26px;padding:0 10px;border-radius:999px;
  font-size:11.5px;font-weight:700;letter-spacing:.12em;text-transform:uppercase;
  color:var(--ink-2);background:var(--bg);border:1px solid var(--line);white-space:nowrap;
}
.tag-new{color:var(--accent);background:var(--accent-soft);border-color:var(--accent-line)}
.tag-confirm{color:var(--amber);background:var(--amber-soft);border-color:var(--amber-line)}
.tag .dots{display:inline-flex;gap:3px}
.tag .dots i{width:6px;height:6px;border-radius:50%;background:currentColor;opacity:.3;display:block}
.tag .dots i.on{opacity:1}
.tags{display:flex;gap:8px;flex-wrap:wrap;min-width:0}

/* session header: five counters + bar */
.session{padding:14px calc(var(--gutter) + 40px) 18px}
.counters{display:grid;grid-template-columns:repeat(5,minmax(0,1fr));gap:10px}
.counter{padding:8px 12px 6px;border-radius:12px;background:var(--bg-elev);border:1px solid var(--line-2);min-width:0}
.counter b{display:block;font:700 24px/1.1 var(--font-display);letter-spacing:-.02em}
.counter span{display:block;margin-top:3px;font-size:11px;font-weight:700;letter-spacing:.1em;text-transform:uppercase;color:var(--ink-3);white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
.counter span .short{display:none}
.counter.done b{color:var(--accent)}
.bar{margin-top:12px;height:6px;border-radius:3px;background:var(--bg-sunk);overflow:hidden}
.bar i{display:block;height:100%;width:0;background:var(--accent);border-radius:3px;transition:width .25s ease}
.bar-meta{display:flex;justify-content:space-between;margin-top:8px;font-size:12.5px;color:var(--ink-3)}

/* the card */
.card{background:var(--bg-elev);border:1px solid var(--line);border-radius:24px;box-shadow:var(--shadow);position:relative}
.card-head{display:flex;align-items:center;justify-content:space-between;gap:10px;padding:20px 22px 0}
.card-head .lvl-tag{font-size:13px;white-space:nowrap}
.word{
  margin:0;text-align:center;font-family:var(--font-display);font-weight:700;
  letter-spacing:-.035em;line-height:1;
  overflow-wrap:anywhere;
}
.card-A .word{font-size:96px;padding:84px 40px 96px}
.card-A .word.long{font-size:68px}
.card-A .word.xlong{font-size:48px}
.answers{display:grid;grid-template-columns:1fr 1fr;gap:14px}
.ans{
  min-height:72px;border-radius:16px;display:inline-flex;align-items:center;justify-content:center;gap:12px;
  font:700 19px var(--font-ui);border:1.5px solid;cursor:pointer;text-decoration:none;
  transition:transform .12s ease,background .15s ease,opacity .15s ease;-webkit-appearance:none;appearance:none;
}
.ans:hover{transform:translateY(-1px)}
.ans:active,.ans.pressed{transform:translateY(1px)}
.ans[disabled]:not(.pressed){cursor:default;transform:none;opacity:.7}   /* the pressed one keeps its 1px shift + spinner while the answer is in flight */
.ans-no{background:var(--amber-soft);color:var(--amber);border-color:var(--amber-line)}
.ans-no:hover{background:var(--amber-hover)}
.ans-yes{background:var(--accent);color:var(--accent-ink);border-color:var(--accent);box-shadow:0 10px 24px -12px var(--accent)}
.ans-yes:hover{background:var(--accent-strong);border-color:var(--accent-strong)}
/* the third answer, "Know - show details" (2026-09-04): the Know outcome, but the panel opens first.
   Secondary to the big pair: a ghost button under them - compact and centred from tablet up, full
   width on phones (the pinned footer's own rules further down); 44px tall at least */
.more{display:flex;justify-content:center;margin-top:10px}
.ans-more{
  display:inline-flex;align-items:center;justify-content:center;gap:9px;min-height:44px;padding:0 18px;border-radius:12px;
  border:1.5px solid var(--line);background:transparent;color:var(--ink-2);font:600 15px var(--font-ui);cursor:pointer;
  -webkit-appearance:none;appearance:none;transition:transform .12s ease,background .15s ease,border-color .15s ease,opacity .15s ease;
}
.ans-more svg.i{width:18px;height:18px;color:var(--ink-3)}
.ans-more:hover{border-color:var(--ink-3);color:var(--ink);background:var(--bg-sunk)}
.ans-more:active,.ans-more.pressed{transform:translateY(1px)}
.ans-more[disabled]:not(.pressed){cursor:default;transform:none;opacity:.7}
.ans-more.busy{opacity:1;cursor:progress;pointer-events:none}
.ans-more:focus-visible{outline:2px solid var(--ink);outline-offset:2px}
.hints{display:none;justify-content:center;flex-wrap:wrap;gap:8px 20px;margin-top:20px;font-size:13px;color:var(--ink-3);flex:none}
@media (hover:hover) and (min-width:1024px){.hints{display:flex}}   /* keyboard hints: desktop only */
.hints span{display:inline-flex;align-items:center;gap:6px}
.undo{
  display:inline-flex;align-items:center;gap:8px;font-size:13.5px;color:var(--ink-3);text-decoration:none;
  min-height:44px;padding:0;background:none;border:0;cursor:pointer;font-family:inherit;text-align:left;
}
.undo b{color:var(--ink-2);font-weight:600}
.undo .link{font-size:13.5px}

/* learn layout: the page is exactly one viewport tall and the card is the only part that
   may shrink — long reveal content scrolls INSIDE it (.card-scroll), so Know / Don't Know /
   Continue never leave the screen. `min-height:0` all the way down the flex chain is what
   lets the card shrink; `height` (not min-height) on .learn is what bounds it. Desktop
   keeps the design's fixed-height word card and centres it; below 1024px the card fills
   the viewport and the buttons sit under the thumb (pinned layout, further down). */
.learn{display:flex;flex-direction:column;height:100vh;height:100dvh}
.learn-main{flex:1 1 auto;min-height:0;display:flex;flex-direction:column;align-items:center;justify-content:center;padding:36px 40px 44px}
.learn-col{width:100%;max-width:700px;display:flex;flex-direction:column;min-height:0}
.learn-col .card{display:flex;flex-direction:column;flex:0 1 auto;min-height:0}
.learn-col .card-A{height:332px}              /* design height; shrinks only on a short viewport */
.learn-col .card-A .word{flex:1 1 auto;min-height:0;display:flex;align-items:center;justify-content:center;padding:0 40px 12px}
.card-scroll{flex:1 1 auto;min-height:0;overflow:auto;overscroll-behavior:contain}
/* reveal scroll cue: while .card-scroll can scroll further, a fade + down arrow sits over its
   bottom edge (sticky inside the scroller, so the desktop card and the pinned phone layout get
   the same cue); learn.js toggles .can-scroll on the card from scrollTop + clientHeight <
   scrollHeight. Why (QA 2026-09-03): at 1280 x 900 a 515px reveal in a 421px scroller hid
   EXAMPLE and the "again" line below the fold with nothing to say so. */
.scroll-cue{
  position:sticky;bottom:0;height:64px;margin-top:-64px;pointer-events:none;opacity:0;transition:opacity .15s ease;
  background:linear-gradient(to bottom,transparent,var(--bg-elev) 72%);
  display:flex;align-items:flex-end;justify-content:center;padding-bottom:8px;color:var(--ink-3);
}
.scroll-cue svg.i{width:20px;height:20px;transform:rotate(90deg)}   /* i-arrow-r pointing down */
.card-B.can-scroll .scroll-cue{opacity:1}
.learn-col .pfoot{margin-top:20px;flex:none}
/* desktop reveal: the footer is empty (Continue sits inside the card) and a tall viewport can
   spare some page padding, so the revealed card gets up to ~640px before it has to scroll
   (QA 2026-09-03: 568px at 1280 x 900 hid the example of a three-sense word) */
@media (min-width:1024px){
  .learn-col[data-state="reveal"] .pfoot{margin-top:0}
  .learn-col[data-state="reveal"] .hints{margin-top:12px}
}
@media (min-width:1024px) and (min-height:900px){
  .learn-main{padding:24px 40px 28px}
}
.learn-col .undo{margin-bottom:4px;flex:none}
.pfoot .cont{display:none}
.learn-col[data-state="reveal"] .answers,.learn-col[data-state="reveal"] .more{display:none}
.closed{flex:1;display:flex;flex-direction:column;align-items:center;justify-content:center;gap:20px;padding:32px 24px;text-align:center;color:var(--ink-2);font-size:16.5px}
.closed p{max-width:30ch}
/* learn-page state switches */
.complete{flex:1 1 auto;min-height:0;overflow:auto;display:flex;flex-direction:column}
body:not([data-mode="complete"]) .complete{display:none}
body[data-mode="complete"] .session,body[data-mode="complete"] .learn-main{display:none}
body[data-mode="loading"] .pfoot,body[data-mode="error"] .pfoot,body[data-mode="closed"] .pfoot,
body[data-mode="loading"] .hints,body[data-mode="error"] .hints,body[data-mode="closed"] .hints{visibility:hidden}
.counter em{font-style:normal}

/* revealed card (state B). The reveal grid `.rv` holds word, sound row, picture, Chinese and
   definition in ONE grid, from one DOM order (word, pron, pic, zh, def): on wide cards the picture
   sits BESIDE the word in a right column (38 % of the inner width, square, its top on the word's
   cap line) while word / sound / Chinese keep the left column and Definition + Example run the
   full width under the rule; on phones (below) the grid is block flow and the picture sits
   between the sound row and the Chinese. Without a picture the figure is not in the DOM and the
   grid is one column = the card as it was before pictures, no gap. Mockup: design/reveal-image.html */
.card-B{padding:0 0 28px}
.rv{
  display:grid;grid-template-columns:minmax(0,1fr) 38%;column-gap:28px;align-items:start;padding:0 32px;
  grid-template-areas:"word pic" "pron pic" "zh pic" "def def";
}
.rv:not(.has-pic){grid-template-columns:minmax(0,1fr);grid-template-areas:"word" "pron" "zh" "def"}
.card-B .word{grid-area:word;font-size:64px;padding:40px 0 14px;text-align:left;min-width:0}   /* the 32px sides are the grid's padding */
.card-B .word.long{font-size:50px}
.card-B .word.xlong{font-size:40px}
.rv > .pron{grid-area:pron;min-width:0}
.rv > .zh{grid-area:zh;min-width:0}
.rv > .def{grid-area:def;min-width:0}
.pron{display:flex;align-items:center;gap:14px;flex-wrap:wrap}
.ipa{font-family:var(--font-ipa);font-size:22px;color:var(--ink-2);letter-spacing:.01em}
.play{
  width:44px;height:44px;border-radius:50%;display:grid;place-items:center;border:1.5px solid var(--accent-line);
  background:var(--accent-soft);color:var(--accent);cursor:pointer;flex:none;
}
.play svg.i{width:20px;height:20px;margin-left:2px}
.play:hover{background:var(--accent);color:var(--accent-ink);border-color:var(--accent)}
.play[disabled]{opacity:.4;cursor:default;pointer-events:none}
.pron .cap{font-size:13px}
.zh{font-family:var(--font-cjk);font-size:21px;line-height:1.55;margin-top:18px;color:var(--ink)}
/* pos labels ("n." / "vt." / "adj."): the same face, size and colour in the Chinese gloss and
   in the English definition, so the two blocks read as one dictionary entry */
.rv .pos{font-family:var(--font-ui);font-style:italic;font-weight:500;color:var(--ink-3);font-size:16px;margin-right:4px}
/* several senses: one row per sense, label column + text column - the same grid in both blocks,
   so "n. 中心…" and "n. the middle point…" line up; a single sense stays inline (above) */
.senses{display:grid;grid-template-columns:max-content 1fr;column-gap:10px;row-gap:4px;align-items:baseline}
.senses .pos{margin:0;white-space:nowrap}
.def{margin-top:22px;padding-top:20px;border-top:1px solid var(--line-2);font-size:17px;line-height:1.55;color:var(--ink-2)}
.def dt{font-size:11.5px;font-weight:700;letter-spacing:.12em;text-transform:uppercase;color:var(--ink-3);margin:0 0 6px}
.def dd{margin:0}
.def dd + dt{margin-top:16px}
.example{font-family:var(--font-ui);font-style:italic;font-size:17.5px;color:var(--ink)}
.example mark{background:transparent;color:var(--accent);font-style:normal;text-decoration:underline;text-decoration-color:var(--accent-line);text-underline-offset:4px}
/* the sentence's Chinese (words.sentence_zh, 2026-09-04): a muted line right under the example, no
   label, the gloss's CJK stack at the definition block's size (a subordinate line, not a second
   heading); absent when the word has no translation yet */
.example-zh{font-family:var(--font-cjk);font-size:17px;line-height:1.55;color:var(--ink-2);margin-top:6px}
/* the example sentence's clip (reveal payload `sentence_audio`, learn.js): a small round button of the
   play button's family right after the EXAMPLE label - 28px, a muted ring on the card colour, the accent
   speaker icon; hover tints it like the big button's rest state, `playing` fills it while the clip runs.
   The label row becomes a flex row centred on the button (only when a clip exists, so the card
   without one measures as before); ::before widens the tap area to 44px without moving anything. */
.def dt.dt-clip{display:flex;align-items:center;gap:10px}
.play-sm{width:28px;height:28px;border-color:var(--line);background:var(--bg-elev);color:var(--accent);position:relative}
.play-sm::before{content:"";position:absolute;inset:-8px;border-radius:50%}
.play-sm svg.i{width:15px;height:15px;margin:0}
.play-sm:hover{background:var(--accent-soft);border-color:var(--accent-line);color:var(--accent)}
.play-sm.playing,.play-sm.playing:hover{background:var(--accent);border-color:var(--accent);color:var(--accent-ink)}
/* the picture (reveal payload `image` = /img/<word>.webp): a 1px-bordered tile on the theme's sunk
   colour, and that tile IS the loading state - the box has its final size from the first paint
   (aspect-ratio), the img is transparent and the report link hidden until the file is in
   (.pic-loading, learn.js), so the text never jumps; a load error removes the figure. In dark the
   picture's own light tile is dimmed 20 % (--pic-dim) so it reads as a print, not a lamp. */
.pic-wrap{grid-area:pic;margin-top:46px;min-width:0}   /* 46px: the tile's top edge meets the word's cap line (the word box starts at 40px) */
.pic{position:relative;aspect-ratio:1/1;border-radius:16px;border:1px solid var(--line);background:var(--bg-sunk);overflow:hidden}
.pic > img{display:block;width:100%;height:100%;object-fit:cover;filter:brightness(var(--pic-dim))}
.pic-foot{display:flex;justify-content:flex-end;margin:-8px -6px -12px 0}   /* a 44px tap row that takes 24px of flow */
/* "Report image": a small muted text button under the tile, right-aligned - findable when a picture
   is wrong, invisible when it is not; "Reported" / the daily-limit line take its place (disabled) */
.pic-report{
  display:inline-flex;align-items:center;gap:5px;min-height:44px;padding:0 6px;margin:0;border:0;background:none;
  border-radius:8px;font:500 12px/1.5 var(--font-ui);letter-spacing:.01em;color:var(--ink-3);text-align:right;cursor:pointer;
}
.pic-report svg.i{width:13px;height:13px;stroke-width:2}
.pic-report:hover{color:var(--ink-2);text-decoration:underline;text-underline-offset:3px}
.pic-report[disabled]{cursor:default;color:var(--ink-3);text-decoration:none}
.pic-report.busy{cursor:progress}
.pic-loading .pic > img{opacity:0}
.pic-loading .pic-foot{visibility:hidden}
/* the note strip: a fixed footer of the revealed card between the scrolling body and Continue
   (never inside the scroller - live QA 2026-09-04: it hid below the fold at 1366 x 768 / 1024 x 768) */
.again{display:flex;align-items:center;flex-wrap:nowrap;white-space:nowrap;min-width:0;gap:10px;margin:14px 32px 0;font-size:14px;color:var(--ink-3);flex:none}
.again svg.i{color:var(--amber)}
.again.known svg.i{color:var(--accent)}       /* the "Marked as known" notes after "Know - show details" */
.again .short,.again .dot{display:none}
/* "Actually, I didn't know it" (revert, only right after "Know - show details"): a muted text button
   at the right end of the note line, 44px tall through negative margins so the row keeps its height */
.revert{
  display:inline-flex;align-items:center;gap:6px;min-height:44px;margin:-11px 0 -11px auto;padding:0 8px;border:0;background:none;
  border-radius:8px;font:500 13px/1.4 var(--font-ui);color:var(--ink-3);cursor:pointer;text-align:left;white-space:nowrap;
}
.revert svg.i{width:14px;height:14px;stroke-width:2;color:var(--ink-3)}
.revert:hover{color:var(--ink-2);text-decoration:underline;text-underline-offset:3px}
.revert.busy{cursor:progress;pointer-events:none}
.revert[disabled]{cursor:default;color:var(--ink-3);text-decoration:none}
.cont{margin:12px 32px 0}
.card-scroll > .rv{padding-bottom:18px}          /* air under the last line, so the sticky scroll cue's fade has something to fade */
.cont .btn{width:100%;min-height:60px;font-size:18px;border-radius:16px}

/* inline network-error banner (never a blank screen) */
.banner{
  display:flex;align-items:center;justify-content:space-between;gap:14px;margin:0 0 14px;flex:none;
  padding:12px 16px;border-radius:14px;background:var(--danger-soft);border:1px solid var(--danger-line);color:var(--danger);font-size:14.5px;
}
.banner span{flex:1 1 auto;min-width:0}          /* the message wraps, Retry stays on the right */
.banner .btn{min-height:40px;padding:0 16px;font-size:14px;border-radius:10px;flex:none}
.loading{display:flex;align-items:center;justify-content:center;min-height:240px;color:var(--ink-3);font-size:14px}

/* "A new version is available" bar (EN.showUpdateBar in app.js): a small card pinned top
   centre above everything, on any page whose scripts are older than the server's (/learn
   uses its in-flow #banner instead). Centred with left/right 0 + auto margins, NOT left:50%:
   a fixed box only gets the space right of its `left`, which wrapped the text at 375px. */
.update-bar{
  position:fixed;top:12px;left:0;right:0;width:fit-content;margin:0 auto;z-index:60;
  display:flex;align-items:center;gap:14px;max-width:calc(100% - 24px);
  padding:8px 8px 8px 16px;border-radius:14px;background:var(--bg-elev);border:1px solid var(--line);
  box-shadow:var(--shadow);color:var(--ink);font-size:14.5px;
}
.update-bar span{min-width:0}
.update-bar .btn{min-height:40px;padding:0 16px;font-size:14px;border-radius:10px;flex:none}

/* complete */
.complete .body{flex:1;display:flex;flex-direction:column;align-items:center;justify-content:center;text-align:center;padding:48px 40px 56px}
.complete .ring{width:76px;height:76px;border-radius:50%;background:var(--accent-soft);color:var(--accent);display:grid;place-items:center;border:1.5px solid var(--accent-line)}
.complete .ring svg.i{width:34px;height:34px}
.complete h1{margin-top:22px;font-family:var(--font-display);font-weight:800;font-size:64px;line-height:1;letter-spacing:-.04em}
.complete h1 small{display:block;margin-top:10px;font:400 22px var(--font-ui);letter-spacing:0;color:var(--ink-2)}
.stats{display:grid;grid-template-columns:repeat(4,minmax(150px,1fr));gap:12px;margin-top:36px;width:100%;max-width:760px}
.stat{padding:18px 18px 16px;border:1px solid var(--line);border-radius:16px;background:var(--bg-elev);text-align:left}
.stat b{display:flex;align-items:center;gap:8px;font:700 30px/1 var(--font-display);letter-spacing:-.025em}
.stat b svg.i{color:var(--amber);width:26px;height:26px}
.stat span{display:block;margin-top:8px;font-size:12.5px;font-weight:700;letter-spacing:.08em;text-transform:uppercase;color:var(--ink-3)}
.stat.accent{background:var(--accent-soft);border-color:var(--accent-line)}
.stat.accent b{color:var(--accent)}
.tomorrow{margin-top:36px;font-family:var(--font-ui);font-style:italic;font-weight:500;font-size:21px;color:var(--ink-2)}
.complete .cta{display:flex;gap:14px;margin-top:22px;align-items:center;flex-wrap:wrap;justify-content:center}
.complete .cta .link{padding:0 8px;min-height:44px;display:inline-flex;align-items:center}

/* progress + settings pages: the mockup's 1000px block, centred on wide screens
   (auto margins; width:100% so the flex column does not shrink-wrap it) */
.page{width:100%;max-width:1000px;margin-inline:auto;padding:40px 64px 64px}
.page h1{font-family:var(--font-display);font-weight:700;font-size:40px;letter-spacing:-.03em}
.page .sub{margin-top:6px;color:var(--ink-2)}
.block{margin-top:36px}
.block-head{display:flex;align-items:baseline;justify-content:space-between;gap:16px;margin-bottom:14px;flex-wrap:wrap}
.block-head h2{font-size:13px;font-weight:700;letter-spacing:.14em;text-transform:uppercase;color:var(--ink-3)}
.block-head .state{font-size:14px;color:var(--ink-2)}
.tiles{display:grid;grid-template-columns:repeat(4,1fr);gap:14px}   /* Known · In review · Tomorrow · Streak */
.tile{padding:22px 22px 20px;border:1px solid var(--line);border-radius:18px;background:var(--bg-elev)}
.tile b{display:flex;align-items:center;gap:10px;font:700 40px/1 var(--font-display);letter-spacing:-.03em;white-space:nowrap}
.tile b svg.i{width:30px;height:30px;color:var(--amber)}
.tile span{display:block;margin-top:10px;font-size:13px;font-weight:700;letter-spacing:.08em;text-transform:uppercase;color:var(--ink-3)}
.tile small{display:block;margin-top:6px;font-size:13.5px;color:var(--ink-2)}
/* last-14-days chart: one .day column per date = count label, bar (max 64px), weekday tick (desktop) */
.days{display:flex;gap:6px;align-items:flex-end;height:84px;margin-top:6px}
.day{flex:1 1 0;min-width:0;height:100%;display:flex;flex-direction:column;justify-content:flex-end;align-items:center;border-radius:6px;outline-offset:3px}
.day i{display:block;width:100%;border-radius:4px 4px 2px 2px;background:var(--accent);opacity:.9;min-height:4px}
.day i.off{background:var(--line);opacity:1;height:6px}
.day .n{display:block;margin-bottom:3px;font-size:11px;font-weight:600;line-height:1;color:var(--ink-2);letter-spacing:-.02em;white-space:nowrap}   /* proportional figures: fixed-width digits spaced "1.2k" out to "1 . 2k" at 9.5px */
.day.today .n{color:var(--accent);font-weight:700}
.day .wd{display:none}
@media (min-width:1024px){
  .days{height:100px}
  .day .wd{display:block;margin-top:5px;font-size:10px;line-height:1;color:var(--ink-3);letter-spacing:.02em}
}
.days-x{display:flex;justify-content:space-between;margin-top:8px;font-size:12px;color:var(--ink-3)}
/* settings: one Save for staged changes + the result line */
.save-row{display:flex;align-items:center;gap:16px;flex-wrap:wrap}
.save-msg{font-size:14.5px;line-height:1.45;color:var(--ink-2)}
.save-msg.error{color:var(--danger)}
.save-msg .link{white-space:nowrap}

/* settings */
.set{display:grid;grid-template-columns:220px 1fr;gap:20px 48px;padding:28px 0;border-top:1px solid var(--line)}
.set:first-of-type{border-top:0;padding-top:8px}
.set h2{font-size:17px;font-weight:800;letter-spacing:-.01em}
.set .why{margin-top:6px;font-size:14px;color:var(--ink-3);max-width:26ch}
.target{display:grid;grid-template-columns:120px 1fr;gap:18px;align-items:center}
.target .input{text-align:center;font-size:20px;font-weight:600;font-family:var(--font-display);min-height:54px}
input[type=range].range{width:100%;height:44px;margin:0;accent-color:var(--accent);background:transparent;cursor:pointer}
input[type=range].range:focus-visible{outline-offset:-3px}   /* ring inside the 44px box, clear of the 50 / 1000 labels */
.field-msg{display:block;margin-top:10px;font-size:13.5px;font-weight:600;color:var(--danger)}
.range-x{display:flex;justify-content:space-between;font-size:12px;color:var(--ink-3);margin-top:-6px}
.split{
  display:inline-flex;align-items:center;gap:10px;margin-top:14px;padding:10px 14px;border-radius:12px;
  background:var(--bg-sunk);font-size:14.5px;color:var(--ink-2);flex-wrap:wrap;
}
.split b{color:var(--ink)}
.split .arrow{color:var(--ink-3)}
/* new-word share: the read-out sits in the target's 120px column so the two sliders line up;
   it is a value, not a field, so it is bare display text instead of a bordered box */
.share .pct{display:block;text-align:center;font:700 28px/1 var(--font-display);letter-spacing:-.03em;color:var(--ink);font-variant-numeric:tabular-nums}
.hint{margin-top:12px;font-size:13.5px;line-height:1.45;color:var(--ink-3)}
.levels{display:grid;gap:8px}
.lvl{display:flex;align-items:center;gap:14px;min-height:56px;padding:8px 14px 8px 10px;border:1px solid var(--line);border-radius:14px;background:var(--bg-elev);cursor:pointer;position:relative}
.lvl:hover{border-color:var(--ink-3)}
.lvl input{position:absolute;opacity:0;width:0;height:0}
.lvl .n{
  width:38px;height:38px;border-radius:10px;display:grid;place-items:center;flex:none;
  font:700 18px var(--font-display);background:var(--bg-sunk);color:var(--ink-2);
}
.lvl .t{flex:1;min-width:0;line-height:1.35}
.lvl .t b{display:block;font-weight:700}
.lvl .t span{display:block;font-size:14px;color:var(--ink-2)}
.lvl .r{width:20px;height:20px;border-radius:50%;border:1.5px solid var(--line);flex:none;display:grid;place-items:center}
.lvl:has(input:checked),.lvl.on{border-color:var(--accent);box-shadow:0 0 0 3px var(--accent-soft)}
.lvl:has(input:checked) .n,.lvl.on .n{background:var(--accent);color:var(--accent-ink)}
.lvl:has(input:checked) .r,.lvl.on .r{border-color:var(--accent);background:var(--accent)}
.lvl:has(input:checked) .r::after,.lvl.on .r::after{content:"";width:7px;height:7px;border-radius:50%;background:var(--accent-ink)}
.acct{display:flex;align-items:center;justify-content:space-between;gap:16px;padding:16px 18px;border:1px solid var(--line);border-radius:14px;background:var(--bg-elev);flex-wrap:wrap}
.acct .em{display:flex;align-items:center;gap:12px;font-size:16px;min-width:0;flex:1 1 auto}
.acct .em > span{min-width:0;overflow-wrap:anywhere}
.acct .em .part{display:inline-block;max-width:100%}   /* a long email breaks before the @ first, then anywhere */
.acct .em svg.i{color:var(--ink-3)}
.acct .btn{gap:8px}
.saved{color:var(--accent)}

/* auth */
.auth-body{flex:1;display:grid;place-items:center;padding:48px 24px}
.auth{width:100%;max-width:420px}
.auth h1{font-family:var(--font-display);font-weight:700;font-size:36px;letter-spacing:-.03em}
.auth .sub{margin:8px 0 28px;color:var(--ink-2)}
.auth .swap{margin-top:22px;text-align:center;font-size:14.5px;color:var(--ink-2)}
.shell{display:flex;flex-direction:column;min-height:100vh;min-height:100dvh}

/* landing */
.hero{display:grid;grid-template-columns:1.1fr .9fr;gap:48px;align-items:center;padding:72px calc(var(--gutter) + 64px) 56px}
.hero h1{
  margin-top:18px;font-family:var(--font-display);font-weight:800;
  font-size:60px;line-height:1.04;letter-spacing:-.04em;max-width:13ch;
}
.hero h1 em{font-style:italic;color:var(--accent)}
.hero .lede{margin-top:22px;font-size:19px;line-height:1.55;color:var(--ink-2);max-width:46ch}
.hero .cta{display:flex;gap:12px;margin-top:32px;flex-wrap:wrap}
.hero .fine{margin-top:18px;font-size:13.5px;color:var(--ink-3);display:flex;gap:14px;flex-wrap:wrap}
.hero .fine span{white-space:nowrap}
.hero .fine span::before{content:"·";margin-right:14px;color:var(--line)}
.hero .fine span:first-child::before{content:none;margin:0}
.preview{position:relative;justify-self:end;width:100%;max-width:440px;padding:24px 0 34px 24px}
.preview .ghost{position:absolute;inset:0 24px 34px 0;border-radius:24px;border:1px solid var(--line);background:var(--bg-sunk);transform:rotate(-3.5deg);opacity:.7}
.preview .card{position:relative;padding:0}
.preview .word{font-size:52px;padding:44px 24px 52px}
.preview .answers{margin-top:14px;position:relative}
.preview .ans{min-height:56px;font-size:16px}
.preview .cap{position:relative;margin-top:14px;text-align:center;font-size:13px;color:var(--ink-3)}
.steps{padding:16px calc(var(--gutter) + 64px) 72px;border-top:1px solid var(--line-2)}
.steps .eyebrow{display:block;margin-top:40px}
.steps h2{margin-top:10px;font-family:var(--font-display);font-weight:700;font-size:36px;letter-spacing:-.03em}
.steps ol{list-style:none;padding:0;margin:40px 0 0;display:grid;grid-template-columns:repeat(3,1fr);gap:0}
.steps li{padding:26px 28px 0 0;border-top:1px solid var(--line)}
.steps li + li{padding-left:28px;border-left:1px solid var(--line-2)}
.steps .n{font:800 46px/1 var(--font-display);color:var(--accent);letter-spacing:-.04em}
.steps h3{margin-top:16px;font-size:19px;font-weight:800;letter-spacing:-.01em}
.steps p{margin-top:8px;color:var(--ink-2);font-size:15.5px;max-width:34ch}
.foot{display:flex;align-items:center;justify-content:space-between;gap:24px;padding:24px calc(var(--gutter) + 64px);border-top:1px solid var(--line);font-size:13.5px;color:var(--ink-3);flex-wrap:wrap;margin-top:auto}
.foot .links{display:flex;gap:18px;flex-wrap:wrap}
.foot a{text-decoration:none;color:var(--ink-2)}
.foot a:hover{color:var(--ink)}

/* ---------- tablet ---------- */
@media (max-width:900px){
  .hero{grid-template-columns:1fr;padding:48px 28px}
  .preview{justify-self:start}
  .steps{padding:16px 28px 56px}
  .steps ol{grid-template-columns:1fr}
  .steps li + li{padding-left:0;border-left:0}
  .foot{padding:24px 28px}
  .set{grid-template-columns:1fr}
  .save-set > div:first-child{display:none}
  .tiles,.stats{grid-template-columns:1fr 1fr}             /* four tiles: 2 x 2 */
  .page{padding:32px 28px 56px}
  .app-nav{padding:12px 24px}
  .session{padding:12px 24px 16px}
  .card-A .word{font-size:80px;padding:64px 32px 76px}
  .card-A .word.long{font-size:58px}
  .card-A .word.xlong{font-size:44px}
  .complete h1{font-size:54px}
}

/* ---------- pinned learn layout (phone + tablet, < 1024px): the card fills what is left of
   the viewport, the answers sit at the bottom under the thumb, reveal content scrolls
   inside the card while Continue stays in the pinned footer ---------- */
@media (max-width:1023px){
  .learn-main{align-items:stretch;justify-content:flex-start;padding:16px 24px 0}
  .learn-col{flex:1 1 auto;max-width:none}
  .learn-col .card{flex:1 1 auto}
  .learn-col .card-A{height:auto}
  .pfoot{margin:0 -24px;padding:16px 24px calc(20px + env(safe-area-inset-bottom));flex:none}
  .learn-col .pfoot{margin-top:0}
  .card-B{padding-bottom:12px}                      /* the note strip closes the card; Continue is in the pinned footer */
  .again{margin:10px 24px 0}
  .card-B .cont{display:none}
  .learn-col[data-state="reveal"] .pfoot .cont{display:block;margin:0}
}

/* ---------- phone (matches the 375 x 812 frames of the design) ---------- */
@media (max-width:767px){
  .app-nav{padding:calc(8px + env(safe-area-inset-top)) 16px 8px;gap:12px}
  .app-nav .wordmark .a,.app-nav .wordmark .b{font-size:17px}
  .nav{gap:0}
  .nav a{padding:0 10px;font-size:14px;min-height:44px}
  .nav a.on{display:none}                 /* the current page needs no link at 375px */
  .nav a.phone-hide{display:none}
  .nav a.btn-primary{padding:0 14px;font-size:14px;min-height:44px}
  body[data-page="login"] .nav,body[data-page="register"] .nav{display:none}   /* auth: wordmark only (mockup) */
  .learn .app-nav{border-bottom:0;padding-bottom:0}                             /* learn: no rule, 10px of air */
  .session{padding:0 16px 12px}
  .learn .session{padding-top:10px}
  .counters{gap:6px}
  .counter{padding:6px 6px 5px;text-align:center;border-radius:10px}
  .counter b{font-size:18px}
  .counter span{font-size:9.5px;letter-spacing:.06em}
  .counter span .full{display:none}
  .counter span .short{display:inline}
  .bar{margin-top:10px}

  .learn-main{padding:6px 16px 0}
  .card-head{padding:14px 16px 0}
  .card-head .lvl-tag{font-size:12px}
  /* 64px is the phone maximum: learn.js (fitWord) measures the word and scales a long one down
     to the card width instead of the fixed long / xlong steps used on tablet and desktop */
  .learn-col .card-A .word,.learn-col .card-A .word.long,.learn-col .card-A .word.xlong{font-size:64px;padding:12px 20px 48px}
  .pfoot{margin:0 -16px;padding:14px 16px calc(16px + env(safe-area-inset-bottom))}
  .answers{gap:10px}
  .ans{min-height:64px;font-size:17px;border-radius:14px}
  .card-B .word{font-size:42px;padding:18px 0 8px}
  .card-B .word.long{font-size:34px}
  .card-B .word.xlong{font-size:26px}
  .rv{display:block;padding:0 20px}        /* stacked: word, sound, picture, Chinese, definition */
  .pron .cap{display:none}
  .ipa{font-size:18px}
  .pic-wrap{margin-top:14px}
  .pic{aspect-ratio:4/3;border-radius:14px} /* full card width at 4:3, so the Chinese still starts above the fold at 812px */
  .zh{font-size:18px;margin-top:12px}
  .def{margin-top:14px;padding-top:14px;font-size:15px}
  .example{font-size:16px}
  .again{margin:8px 16px 0;font-size:12.5px;gap:7px}
  .again .full{display:none}
  .again .short{display:inline}
  .again.has-revert .dot{display:inline}          /* ONE line: "Marked as known · Actually, I didn't know it" */
  .again .revert{margin:-11px 0;padding:0 2px;font-size:12.5px;gap:4px}
  .again .revert svg.i{width:13px;height:13px}
  .card-B{padding-bottom:10px}
  .example-zh{font-size:15px}
  .more{margin-top:10px}
  .ans-more{width:100%;min-height:48px;border-radius:14px}
  .cont .btn{min-height:60px;border-radius:14px}

  .complete .body{padding:32px 20px 40px}
  .complete h1{font-size:46px}
  .complete h1 small{font-size:18px}
  .stats{margin-top:28px}
  .stat b{font-size:26px}
  .tomorrow{font-size:18px}

  .page{padding:24px 16px 48px}
  .page h1{font-size:32px}
  .days{gap:2px}                                  /* 14 columns of ~22.6px: "1.2k" at 9.5px still fits */
  .day .n{font-size:9.5px;letter-spacing:-.04em}
  .tile{padding:18px 16px 16px}                   /* 2 x 2 at 375: 132px inside for "120 to review · 480 new" */
  .tile b{font-size:32px}
  .target{grid-template-columns:100px 1fr;gap:14px}
  .auth-body{padding:36px 20px 24px;align-items:start}
  .auth h1{font-size:30px}
  .hero{padding:36px 20px 40px}
  .hero h1{font-size:40px}
  .hero .lede{font-size:17px}
  .hero .fine{gap:6px 16px}
  .hero .fine span::before{content:none;margin:0}   /* no separator dots on phones: nothing to orphan on a wrap */
  .preview{max-width:100%;padding:16px 0 26px 16px}
  .preview .ghost{inset:0 16px 26px 0}
  .preview .word{font-size:44px;padding:36px 20px 44px}
  .steps{padding:8px 20px 44px}
  .steps h2{font-size:30px}
  .foot{padding:20px;flex-direction:column;align-items:flex-start;gap:12px}
}
