/* Shared shell: colours, header, buttons, panels, chat.
   Page-specific rules live in a <style> block on the page itself.

   Navigation is one ☰ at every width — see the NAV module in util.js, which
   owns the destination list and injects the button and the menu.

   Two breakpoints matter here:
     820px — the header tightens; a page title drops to its own line.
     640px — phone. The menu becomes a drawer, dialogs become sheets,
             long labels swap to their .short twins, controls grow to
             thumb size. */

:root{
  --bg:#0b0e1a; --panel:#121629; --panel-2:#161b31; --panel-3:#1a2039;
  --line:#242a45; --line-2:#33395c;
  --text:#e8eaf6; --dim:#c8cdea; --muted:#959bc0;
  --accent:#6366f1; --accent-soft:#6366f133; --accent-hi:#585bef;
  --green:#22c55e; --green-soft:#22c55e26;
  --amber:#f59e0b; --red:#f87171; --cyan:#06b6d4; --pink:#ec4899; --violet:#a855f7;
  --s1:#3b82f6; --s2:#22c55e; --s3:#f59e0b; --s4:#ec4899; --s5:#06b6d4; --s6:#a855f7;

  /* der / die / das. Three hues that stay apart at every size and never land
     on the green of a translation or the red of a mistake. */
  --art-m:#60a5fa; --art-f:#f472b6; --art-n:#fbbf24;

  /* One radius scale, so nothing lands between two sizes by accident. */
  --radius:12px;
  --r-xs:7px; --r-sm:9px; --r-md:12px; --r-lg:16px; --r-xl:20px;

  /* One type scale. This app had grown about twenty sizes — 11.5, 12.5, 13,
     13.5, 14, 14.5, 15.5, 16.5, 18.5, 23 — none of them wrong on its own and
     none of them in step with the others. Nine steps, each a clear jump from
     the last, so two things that differ look deliberately different. */
  --f-3xs:10px; --f-2xs:11px; --f-xs:12px; --f-sm:13px; --f-md:14px;
  --f-lg:16px;  --f-xl:18px;  --f-2xl:21px; --f-3xl:24px;

  /* And one spacing scale, for the same reason. */
  --sp-1:4px; --sp-2:8px; --sp-3:12px; --sp-4:16px; --sp-5:24px; --sp-6:32px;

  /* Elevation. Everything on this background needs a dark, wide shadow to read
     as lifted — a light one just disappears. */
  --sh-1:0 1px 2px #05070f66;
  --sh-2:0 6px 20px -6px #05070fb3;
  --sh-3:0 24px 60px -16px #05070fe6;

  /* Text and icons on an --accent fill: primary buttons, your own chat
     messages, the transcript line being read. White, unless the block just
     below this one can work out something better. */
  --on-accent:#fff;

  /* Focus. Uses --accent-soft so each page's own hue carries through. */
  --ring:0 0 0 3px var(--accent-soft);

  /* Smallest comfortable touch target; only applied on coarse pointers. */
  --tap:42px;

  /* Notch and home-indicator padding, zero on anything that isn't a phone. */
  --sa-t:env(safe-area-inset-top,0px);
  --sa-r:env(safe-area-inset-right,0px);
  --sa-b:env(safe-area-inset-bottom,0px);
  --sa-l:env(safe-area-inset-left,0px);
}

/* Each page picks its own --accent, and white is only readable on the dark
   ones. On the amber of Statistik, the cyan of Text, the green of Karten or
   the sky blue of the Übersetzer, white text comes out at barely 2:1, so the
   ink is worked out from the accent itself: white on a dark accent, and on a
   light one a very dark shade of the same hue, the way .btn.green already
   does it. The cut-off sits between the default indigo (OKLCH lightness
   0.585, white stays) and the rose of Aufräumen (0.645, the first to turn
   dark), and every accent in the app lands at 4.5:1 or better on either side
   of it — the default indigo aside, at 4.47:1 with white, which is still the
   better of the two. A page can set --on-accent itself to overrule this.
   Browsers that cannot compute a colour from another keep the white above. */
@supports (color: oklch(from red clamp(0, (0.62 - l) * 1000, 1) 0 0)) {
  :root{
    --on-accent:oklch(from var(--accent)
      clamp(0.2, (0.62 - l) * 1000, 1) clamp(0, (l - 0.62) * 1000, 0.05) h);
  }
}

*{box-sizing:border-box}
/* Class-level display rules outrank the UA rule for [hidden] — restore it. */
[hidden]{display:none !important}
html,body{height:100%}
body{
  margin:0; background:var(--bg); color:var(--text);
  font:15px/1.55 -apple-system,BlinkMacSystemFont,"Segoe UI",Inter,system-ui,sans-serif;
  -webkit-font-smoothing:antialiased;
  /* Nothing in this app is meant to scroll sideways. `clip` rather than
     `hidden` so it does not turn body into a scroll container and break
     position:sticky inside the pages. */
  overflow-x:clip;
  -webkit-text-size-adjust:100%;
}
a{color:inherit;text-decoration:none}
button,select,input,textarea{font:inherit;color:inherit}
button{cursor:pointer;border:none;background:none}
h1,h2,h3,h4{margin:0;font-weight:640;letter-spacing:-.3px}
::selection{background:var(--accent-soft);color:#fff}

/* One visible focus style for the whole app. Mouse clicks never trigger it;
   keyboard tabbing always does. */
:focus{outline:none}
:focus-visible{outline:none;box-shadow:var(--ring);border-radius:var(--r-sm)}
.btn:focus-visible,input:focus-visible,select:focus-visible,textarea:focus-visible{
  border-color:var(--accent)}

::-webkit-scrollbar{width:10px;height:10px}
::-webkit-scrollbar-thumb{background:#252b48;border-radius:99px;border:3px solid var(--bg)}
::-webkit-scrollbar-thumb:hover{background:#333a5f}

/* ---------- header ----------
   One row at every width: ☰, brand, then whatever the page puts on the right.
   It only ever gets smaller — the only thing that wraps to a second line is a
   page title, which is content rather than chrome. */
header.top{
  display:flex;align-items:center;gap:var(--sp-3);flex:none;flex-wrap:wrap;
  padding:var(--sp-3) var(--sp-4);
  padding-top:calc(11px + var(--sa-t));
  padding-left:calc(20px + var(--sa-l));
  padding-right:calc(20px + var(--sa-r));
  border-bottom:1px solid var(--line);background:var(--panel);
  box-shadow:var(--sh-1);position:relative;z-index:20;
}
.brand{display:flex;align-items:center;gap:var(--sp-2);font-weight:650;letter-spacing:-.2px;flex:none}
.brand .dot{width:9px;height:9px;border-radius:50%;background:var(--accent);
  box-shadow:0 0 0 4px var(--accent-soft);flex:none}
.spacer{flex:1}
.title{flex:1;min-width:0;display:flex;align-items:baseline;gap:var(--sp-2)}
.title b{font-weight:600;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.title span{color:var(--muted);font-size:var(--f-sm);flex:none}

/* Labels that are too long for a phone header carry a short twin. */
.short{display:none}

/* ---------- controls ---------- */
.btn{
  display:inline-flex;align-items:center;justify-content:center;gap:var(--sp-2);
  padding:var(--sp-2) var(--sp-3);border-radius:var(--r-sm);background:var(--panel-2);
  border:1px solid var(--line);font-size:var(--f-sm);font-weight:550;
  transition:background .15s,border-color .15s,color .15s,transform .1s;
  white-space:nowrap;box-shadow:var(--sh-1);
}
.btn:hover{background:#1e2440;border-color:var(--line-2)}
.btn:active{transform:translateY(1px)}
.btn:disabled{opacity:.45;cursor:not-allowed;box-shadow:none}
.btn:disabled:active{transform:none}
.btn.primary{background:var(--accent);border-color:var(--accent);color:var(--on-accent)}
.btn.primary:hover{background:var(--accent-hi);border-color:var(--accent-hi)}
/* For what cannot be undone. Quiet until it is the button you are about to
   press: red text rather than a red block, so it never outshouts the page. */
.btn.danger{color:var(--red);border-color:#f8717155;background:#f871710f}
.btn.danger:hover{background:#f8717126;border-color:var(--red)}
.btn.green{background:var(--green);border-color:var(--green);color:#052e14}
.btn.green:hover{background:#34d76c;border-color:#34d76c}
.btn.on{border-color:var(--accent);background:var(--accent-soft)}
.btn.small{padding:var(--sp-1) var(--sp-2);font-size:var(--f-xs);border-radius:var(--r-xs)}
.btn.ghost{background:none;box-shadow:none}

.tabs{display:flex;gap:4px;background:#0d1124;border:1px solid var(--line);
  border-radius:var(--r-sm);padding:3px}
.tabs button{padding:var(--sp-1) var(--sp-3);border-radius:var(--r-xs);font-size:var(--f-sm);color:var(--muted);
  transition:color .15s,background .15s;white-space:nowrap}
.tabs button:hover{color:var(--dim)}
.tabs button.on{background:var(--panel-2);color:var(--text);box-shadow:var(--sh-1)}

input,select,textarea{
  padding:var(--sp-2) var(--sp-3);border-radius:var(--r-sm);background:var(--panel);
  border:1px solid var(--line);outline:none;font-size:var(--f-sm);
  transition:border-color .15s,box-shadow .15s;
}
input:hover,select:hover,textarea:hover{border-color:var(--line-2)}
input:focus,select:focus,textarea:focus{border-color:var(--accent);box-shadow:var(--ring)}
::placeholder{color:#6f76a0}
select{cursor:pointer;
  /* Safari on iOS ignores most select styling unless appearance is reset. */
  -webkit-appearance:none;appearance:none;padding-right:30px;
  background-image:linear-gradient(45deg,transparent 50%,var(--muted) 50%),
                   linear-gradient(135deg,var(--muted) 50%,transparent 50%);
  background-position:calc(100% - 15px) 50%,calc(100% - 10px) 50%;
  background-size:5px 5px,5px 5px;background-repeat:no-repeat}
label.field{display:flex;flex-direction:column;gap:var(--sp-1)}
label.field>span{font-size:var(--f-2xs);text-transform:uppercase;letter-spacing:.6px;color:var(--muted)}

/* The article on a noun, wherever a term is printed. See setTerm in util.js.
   The class names carry the whole word on purpose: single letters would collide
   with the one-letter classes the card grid already uses. */
.art-der{color:var(--art-m)}
.art-die{color:var(--art-f)}
.art-das{color:var(--art-n)}
/* On a filled background — the active transcript line, a sent message — the hue
   would fight the fill, so it steps back to the inherited colour. */
.seg.active [class^="art-"],.msg.user [class^="art-"]{color:inherit}

.chip{font-size:var(--f-2xs);padding:3px var(--sp-2);border-radius:99px;border:1px solid var(--line);
  color:var(--muted);background:#0d1124;white-space:nowrap}
.chip.level{color:var(--green);border-color:#22c55e40}
.chip.src{color:var(--amber);border-color:#f59e0b40;background:#f59e0b10}
.chip.pos{color:var(--violet);border-color:#a855f740;background:#a855f710}

.badge{font-size:var(--f-2xs);font-weight:600;padding:4px var(--sp-2);border-radius:99px;flex:none;
  border:1px solid;letter-spacing:.2px;white-space:nowrap}
.badge.done{color:var(--green);border-color:#22c55e40;background:#22c55e14}
.badge.run{color:var(--amber);border-color:#f59e0b40;background:#f59e0b14}
.badge.err{color:var(--red);border-color:#f8717140;background:#f8717114}
.badge.queued{color:var(--muted);border-color:var(--line);background:#0d1124}

.panel{background:var(--panel);border:1px solid var(--line);border-radius:var(--r-md);
  box-shadow:var(--sh-1)}
.muted{color:var(--muted)}
.nums{font-variant-numeric:tabular-nums}
.empty{color:var(--muted);text-align:center;padding:60px 0;font-size:var(--f-md)}
.none{color:var(--muted);font-size:var(--f-sm);padding:var(--sp-4);text-align:center;
  border:1px dashed var(--line);border-radius:var(--r-md)}

.bar{height:6px;border-radius:99px;background:#20263f;overflow:hidden}
/* filled via --p (0..1) so the animation runs on the compositor, not layout */
.bar i{display:block;height:100%;width:100%;background:linear-gradient(90deg,#6366f1,#8b5cf6);
  border-radius:99px;transform:scaleX(var(--p,0));transform-origin:left center;
  transition:transform .35s ease}

kbd{background:#0d1124;border:1px solid var(--line);border-bottom-width:2px;
  border-radius:var(--r-xs);padding:1px var(--sp-1);font:inherit;font-size:var(--f-2xs);color:var(--dim)}

/* ---------- overlay + modal ---------- */
.overlay{position:fixed;inset:0;background:#05070fc4;backdrop-filter:blur(4px);
  display:grid;place-items:center;padding:var(--sp-5);z-index:60}
.overlay.hidden{display:none}
.modal{width:100%;max-width:520px;background:var(--panel);border:1px solid var(--line);
  border-radius:var(--r-lg);padding:var(--sp-5);max-height:88vh;overflow-y:auto;
  box-shadow:var(--sh-3)}
.modal h3{margin:0 0 16px;font-size:var(--f-lg)}
.modal .actions{display:flex;gap:var(--sp-2);justify-content:flex-end;margin-top:18px}
.modal .field{display:flex;flex-direction:column;gap:var(--sp-1);margin-bottom:12px}
.modal .field label{font-size:var(--f-2xs);text-transform:uppercase;letter-spacing:.6px;color:var(--muted)}
.modal .field input,.modal .field textarea,.modal .field select{
  background:var(--panel-2);width:100%;resize:vertical}
.modal .row{display:flex;gap:var(--sp-3)}
.modal .row .field{flex:1;min-width:0}
/* askConfirm() in util.js. Above everything, the teacher sheet and the menu
   included, because it answers a question the page is waiting on. */
.confirm-overlay{z-index:90}
.modal.confirm{max-width:420px}
.confirm-text{margin:0;font-size:var(--f-md);line-height:1.6;color:var(--dim)}
.err{color:var(--red);font-size:var(--f-sm);margin-top:10px;min-height:18px}

/* ---------- teacher chat ---------- */
.teacher{display:flex;flex-direction:column;min-height:0;background:var(--panel)}
.teacher.side{
  width:400px;flex:none;border-left:1px solid var(--line);
}
.teacher.hidden{display:none}
.teacher-head{padding:var(--sp-3) var(--sp-4);border-bottom:1px solid var(--line);flex:none;
  display:flex;align-items:flex-start;gap:var(--sp-2)}
.teacher-head h3{margin:0 0 2px;font-size:var(--f-md)}
.teacher-head p{margin:0;color:var(--muted);font-size:var(--f-xs)}
.teacher-head .close{color:var(--muted);font-size:var(--f-lg);padding:2px var(--sp-2);border-radius:var(--r-xs);
  flex:none}
.teacher-head .close:hover{background:#232a4a;color:var(--text)}
.teacher-log{flex:1;overflow-y:auto;padding:var(--sp-4);display:flex;flex-direction:column;gap:var(--sp-3);
  overscroll-behavior:contain}
.msg{font-size:var(--f-md);line-height:1.6;white-space:pre-wrap;word-wrap:break-word}
.msg.user{align-self:flex-end;max-width:88%;background:var(--accent);color:var(--on-accent);
  padding:var(--sp-2) var(--sp-3);border-radius:var(--r-md) 13px 3px 13px;white-space:pre-wrap;
  box-shadow:var(--sh-1)}
.msg.bot{align-self:flex-start;max-width:97%}
.msg.bot strong{color:#fff;font-weight:640}
.msg.bot em{color:var(--dim);font-style:italic}
.msg.bot code{background:#0d1124;border:1px solid var(--line);border-radius:var(--r-xs);
  padding:1px var(--sp-1);font-size:var(--f-xs)}
.msg.bot ul{margin:6px 0;padding-left:20px}
.tool{align-self:flex-start;font-size:var(--f-xs);color:var(--muted);background:#0d1124;
  border:1px solid var(--line);border-radius:var(--r-sm);padding:var(--sp-2) var(--sp-3);max-width:97%}
.tool.ok{color:var(--green);border-color:#22c55e33;background:#22c55e0f}
.tool.bad{color:var(--red);border-color:#f8717133;background:#f871710f}
.chat-err{color:#f4b8b8;font-size:var(--f-sm);background:#f871710f;border:1px solid #f8717133;
  border-radius:var(--r-sm);padding:var(--sp-3) var(--sp-3);line-height:1.6}
.chat-err b{color:var(--red)}
.chat-err code{background:#0d1124;padding:1px var(--sp-1);border-radius:var(--r-xs);font-size:var(--f-xs)}
.teacher-empty{color:var(--muted);font-size:var(--f-sm);text-align:center;padding:var(--sp-5) var(--sp-1)}
.teacher-empty b{color:var(--text);display:block;margin-bottom:8px;font-size:var(--f-md)}
.suggest{display:flex;flex-direction:column;gap:var(--sp-1);margin-top:14px}
.suggest button{text-align:left;font-size:var(--f-xs);padding:var(--sp-2) var(--sp-3);border-radius:var(--r-sm);
  border:1px solid var(--line);background:var(--panel-2);color:var(--dim);
  transition:border-color .15s,color .15s}
.suggest button:hover{border-color:var(--accent);color:var(--text)}
.teacher-input{padding:var(--sp-3);border-top:1px solid var(--line);flex:none;display:flex;gap:var(--sp-2);
  padding-bottom:calc(12px + var(--sa-b))}
.teacher-input textarea{flex:1;resize:none;max-height:130px;font-size:var(--f-md);
  background:var(--panel-2)}
.teacher-input .send{width:40px;flex:none;border-radius:var(--r-sm);background:var(--accent);
  color:var(--on-accent);display:grid;place-items:center;font-size:var(--f-md);transition:background .15s}
.teacher-input .send:hover{background:var(--accent-hi)}
.teacher-input .send:disabled{opacity:.45;cursor:not-allowed}
.dots::after{content:'';animation:dots 1.2s steps(4,end) infinite}
@keyframes dots{0%{content:''}25%{content:'.'}50%{content:'..'}75%{content:'...'}}

/* ---------- transcript ---------- */
.seg{padding:var(--sp-3) 4px var(--sp-3);border-radius:var(--r-md)}
.seg-head{display:flex;align-items:center;gap:var(--sp-2);margin-bottom:7px}
.seg-head .who{font-weight:600;font-size:var(--f-sm)}
.seg-head .at{color:var(--muted);font-size:var(--f-xs);font-variant-numeric:tabular-nums}
.seg-head .dot{width:23px;height:23px;border-radius:50%;display:grid;place-items:center;
  font-size:var(--f-xs);font-weight:700;color:#08101f;flex:none}
.seg .body-text{display:inline-block;padding:var(--sp-2) var(--sp-3);border-radius:var(--r-md);cursor:pointer;
  background:transparent;transition:background .15s}
.seg .body-text:hover{background:var(--panel-3)}
.seg.active .body-text{background:var(--accent);color:var(--on-accent);font-weight:500}
.seg.hit .body-text{box-shadow:inset 0 0 0 1px var(--amber)}
.seg mark{background:#f59e0b55;color:inherit;border-radius:var(--r-xs);padding:0 2px}
/* Words the vocabulary scan pulled out of this very text. */
.nw{color:#ff6b6b;font-weight:600;cursor:pointer;border-bottom:1px dotted #ff6b6b80}
/* On the filled line the red would fight the fill, and the pale pink this used
   to be vanishes once the ink is dark. The word keeps its weight and underline. */
.seg.active .nw{color:inherit;border-bottom-color:currentColor}
.nw:hover{background:#f8717122;border-radius:var(--r-xs)}

/* ---------- Satzweise (static/js/shadow.js) ----------
   The sentence being heard gets an accent bar: down the side of the lines it
   covers in a transcript, under the sentence itself in a pasted text. */
.seg.unit-on{box-shadow:inset 3px 0 0 var(--accent)}
.sent{border-radius:var(--r-xs);transition:background .15s}
.sent.unit-on{background:var(--accent-soft);box-shadow:0 2px 0 var(--accent)}
.seg.active .sent.unit-on{background:transparent}
/* Text verbergen: the words are there but unreadable until heard. Colour
   rather than a blur filter, so it also works on the inline sentence spans,
   and every coloured piece inside (articles, new words) goes with it. */
.seg.veiled .body-text,.seg.veiled .body-text *,.sent.veiled,.sent.veiled *{
  color:transparent !important;text-shadow:0 0 .6em var(--dim);
  border-bottom-color:transparent !important;user-select:none;-webkit-user-select:none}
.seg.veiled .body-text,.sent.veiled{cursor:pointer}
.seg.active.veiled .body-text,.seg.active.veiled .body-text *{text-shadow:0 0 .6em var(--on-accent)}
.seg.veiled mark{background:transparent}

.sent-switch{display:inline-flex;align-items:stretch}
.sent-switch .sent-toggle{border-radius:var(--r-sm) 0 0 var(--r-sm)}
.sent-switch .sent-more{border-radius:0 var(--r-sm) var(--r-sm) 0;border-left-width:0;
  padding-left:var(--sp-2);padding-right:var(--sp-2)}
/* Placed under the ▾ by shadow.js (--pop-x/--pop-y); a sheet on a phone. */
.sent-pop{position:fixed;left:var(--pop-x,8px);top:var(--pop-y,60px);z-index:50;
  width:min(320px,calc(100vw - 16px));padding:var(--sp-3);display:flex;flex-direction:column;
  gap:var(--sp-3);background:var(--panel);border:1px solid var(--line);border-radius:var(--r-lg);
  box-shadow:var(--sh-3)}
.sent-choice{display:flex;flex-direction:column;gap:var(--sp-1)}
.sent-label{font-size:var(--f-2xs);text-transform:uppercase;letter-spacing:.6px;color:var(--muted)}
.sent-options{display:flex;gap:4px;background:#0d1124;border:1px solid var(--line);
  border-radius:var(--r-sm);padding:3px}
.sent-option{flex:1 1 auto;padding:var(--sp-1) var(--sp-2);border-radius:var(--r-xs);font-size:var(--f-xs);
  color:var(--muted);transition:color .15s,background .15s;min-height:30px}
.sent-option:hover{color:var(--dim)}
.sent-option.on{background:var(--panel-2);color:var(--text);box-shadow:var(--sh-1)}
.sent-hide{display:flex;align-items:center;justify-content:space-between;gap:var(--sp-2);
  padding:var(--sp-2) var(--sp-3);border-radius:var(--r-sm);border:1px solid var(--line);
  background:var(--panel-2);font-size:var(--f-sm);color:var(--dim);text-align:left}
.sent-hide.on{border-color:var(--accent);background:var(--accent-soft);color:var(--text)}
.sent-keys{margin:0;font-size:var(--f-2xs);color:var(--muted);line-height:1.9}

/* The sentence buttons for fingers, with the counter 'Satz 12 / 340'. */
.sent-row{display:flex;align-items:center;gap:var(--sp-2);flex-wrap:wrap}
.sent-row .btn{flex:0 1 auto}
.sent-count{margin-left:auto;font-size:var(--f-xs);color:var(--muted);
  font-variant-numeric:tabular-nums;white-space:nowrap}

/* The line a link to a moment (/youtube#job=<job>&t=<seconds>) pointed at,
   or the sentence of a text one (/text#doc=<id>&satz=<n>) did, marked for a
   few seconds after the page opens there. */
.seg.moment,.sent.moment{box-shadow:inset 0 0 0 2px var(--amber);transition:box-shadow .4s}

/* ---------- Nachschlagen (static/js/lookup.js) ----------
   A marked word looked up in the deck. Placed by the selection on a wide
   screen (--lk-x/--lk-y), a sheet along the bottom on a phone, where the
   thumb is and the selection above stays visible. */
.lookup{position:fixed;left:var(--lk-x,8px);top:var(--lk-y,80px);z-index:60;
  width:min(320px,calc(100vw - 16px));max-height:min(70vh,520px);overflow-y:auto;
  overscroll-behavior:contain;display:flex;flex-direction:column;gap:var(--sp-2);
  padding:var(--sp-3);background:var(--panel);border:1px solid var(--line);
  border-radius:var(--r-lg);box-shadow:var(--sh-3);font-size:var(--f-sm)}
.lk-head{display:flex;align-items:center;gap:var(--sp-2)}
.lk-q{flex:1;min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;
  font-size:var(--f-md);color:var(--dim)}
.lk-x{flex:none;padding:2px var(--sp-2);border-radius:var(--r-xs);color:var(--muted)}
.lk-x:hover{color:var(--text);background:var(--panel-2)}
.lk-body{display:flex;flex-direction:column;gap:var(--sp-2)}
.lk-hit,.lk-compound,.lk-new{display:flex;flex-direction:column;gap:6px;padding:var(--sp-2) var(--sp-3);
  border:1px solid var(--line);border-radius:var(--r-md);background:var(--panel-2)}
/* A guess from a shared stem, not a sure hit: it steps back. */
.lk-hit.guess{border-style:dashed;background:transparent}
.lk-term{display:flex;align-items:center;gap:var(--sp-2)}
.lk-term b{font-size:var(--f-lg);font-weight:600;overflow-wrap:anywhere}
.lk-say{margin-left:auto;flex:none;padding:2px 6px;border-radius:var(--r-xs)}
.lk-say:hover,.lk-say.on{background:var(--accent-soft)}
.lk-back{color:var(--dim);overflow-wrap:anywhere}
.lk-chips{display:flex;flex-wrap:wrap;gap:var(--sp-1)}
.chip.lk-guess{color:var(--amber);border-color:#f59e0b40;border-style:dashed}
.chip.lk-state{color:var(--cyan);border-color:#06b6d440}
.lk-forms{color:var(--muted);font-size:var(--f-xs)}
.lk-acts{display:flex;flex-wrap:wrap;gap:var(--sp-2)}
.lk-label{font-size:var(--f-2xs);text-transform:uppercase;letter-spacing:.6px;color:var(--muted)}
.lk-parts{font-size:var(--f-lg);font-weight:600}
.lk-part{color:var(--accent-hi);text-decoration:underline dotted}
.lk-part.plain{color:var(--dim);text-decoration:none}
.lk-none,.lk-gloss,.lk-note{margin:0;color:var(--muted)}
.lk-note.bad{color:var(--red)}
.lk-gloss{color:var(--dim)}
.lk-form{display:flex;flex-direction:column;gap:var(--sp-2)}
.lk-form .field span{font-size:var(--f-2xs);color:var(--muted)}
.lk-form input,.lk-form textarea{width:100%}
.lk-form .btn{align-self:flex-start}

@media (max-width:640px){
  .lookup{left:0;right:0;top:auto;bottom:0;width:auto;max-height:70dvh;
    border-radius:var(--r-xl) var(--r-xl) 0 0;border-bottom:0;
    padding:var(--sp-4) var(--sp-4) calc(var(--sp-4) + var(--sa-b));z-index:66}
  .lk-acts .btn{flex:1 1 auto;text-align:center}
}
@media (hover:none) and (pointer:coarse){
  .lk-x,.lk-say{min-width:var(--tap);min-height:var(--tap)}
  .lk-acts .btn.small{min-height:var(--tap)}
}

/* ---------- new-word panel ---------- */
.vocab{border:1px solid var(--line);border-radius:var(--r-md);background:var(--panel);
  padding:var(--sp-3) var(--sp-4);margin-bottom:18px;box-shadow:var(--sh-1)}
/* The heading keeps the line to itself on a phone, and the two buttons
   ('Diese Wörter lernen', the scan) wrap under it rather than squeezing it. */
.vocab header{display:flex;flex-wrap:wrap;align-items:center;gap:var(--sp-2);margin-bottom:4px}
.vocab header h3{font-size:var(--f-md);flex:1 1 14em}
.vocab p{margin:0;color:var(--muted);font-size:var(--f-xs)}
.vocab .words{display:flex;flex-wrap:wrap;gap:var(--sp-1);margin-top:12px}
.vocab .word{display:flex;align-items:baseline;gap:var(--sp-2);padding:var(--sp-1) var(--sp-2);border-radius:var(--r-sm);
  background:var(--panel-2);border:1px solid var(--line);font-size:var(--f-xs);cursor:pointer}
.vocab .word:hover{border-color:var(--line-2)}
.vocab .word b{color:#ff8a8a;font-weight:600}
.vocab .word span{color:var(--muted)}
/* Compounds the scan threw away on its own: a count you cannot miss, the list
   only if you ask for it. */
.vocab .skipped{margin-top:10px;font-size:var(--f-xs)}
.vocab .skipped summary{color:var(--muted);cursor:pointer}
.vocab .skipped summary:hover{color:var(--dim)}
.vocab .skipped p{margin-top:6px;line-height:1.7}
.vocab .skipped em{color:var(--muted);opacity:.75;font-style:normal;margin-left:4px}

/* ---------- Claude limits, bottom left ----------
   On every page, from util.js. Faint until you look at it: it is a glance, not
   a control. Pages with something of their own down there lift it out of the
   way with --usage-lift. */
.usage{
  position:fixed;left:calc(12px + var(--sa-l));
  bottom:calc(12px + var(--sa-b) + var(--usage-lift,0px));z-index:35;
  display:flex;flex-direction:column;gap:3px;padding:var(--sp-2) var(--sp-2);
  background:#0d1124e0;border:1px solid var(--line);border-radius:var(--r-sm);
  backdrop-filter:blur(8px);font-size:var(--f-3xs);line-height:1.3;
  font-variant-numeric:tabular-nums;color:var(--muted);
  opacity:.55;transition:opacity .18s ease;user-select:none;
}
.usage:hover{opacity:1}
.usage.stale,.usage.empty{opacity:.32}
.usage .row{display:flex;align-items:center;gap:var(--sp-1);white-space:nowrap;--tone:var(--green)}
.usage .row.mid{--tone:var(--amber)}
.usage .row.high{--tone:var(--red)}
.usage .row.note{justify-content:flex-end;font-size:var(--f-3xs)}
.usage .tag{flex:none;width:36px;color:#767ca0}
.usage .meter{flex:none;width:44px;height:4px;border-radius:99px;
  background:#ffffff17;overflow:hidden}
.usage .meter i{display:block;height:100%;width:100%;border-radius:99px;background:var(--tone);
  transform:scaleX(var(--p,0));transform-origin:left center;transition:transform .35s ease}
.usage .pct{width:30px;text-align:right;color:var(--dim)}
.usage .left{flex:none;color:#767ca0}

/* ---------- the ☰ and its menu ----------
   Replaces the row of links that used to sit in the header on every page. It is
   the same control at every width; only its menu changes shape. */
.menubtn{
  flex:none;width:38px;height:38px;border-radius:var(--r-sm);
  border:1px solid var(--line);background:var(--panel-2);
  display:grid;place-content:center;gap:4px;box-shadow:var(--sh-1);
  transition:background .15s,border-color .15s;
}
.menubtn:hover{background:#1e2440;border-color:var(--line-2)}
.menubtn span{display:block;width:16px;height:1.5px;border-radius:var(--r-xs);background:var(--dim);
  transition:transform .2s ease,opacity .15s ease}
/* open → the three bars fold into an ×, so the button says how to undo itself */
body.nav-open .menubtn span:nth-child(1){transform:translateY(5.5px) rotate(45deg)}
body.nav-open .menubtn span:nth-child(2){opacity:0}
body.nav-open .menubtn span:nth-child(3){transform:translateY(-5.5px) rotate(-45deg)}

.navmenu{position:fixed;inset:0;z-index:80;visibility:hidden}
.navmenu.open{visibility:visible}
/* Transparent by default: on a wide screen this is only a click-catcher, and
   dimming the whole page behind a 272px dropdown would make it read as a modal.
   The phone drawer covers most of the screen, so there it does dim — see below. */
.navmenu-scrim{position:absolute;inset:0;opacity:0;transition:opacity .2s ease}
.navmenu.open .navmenu-scrim{opacity:1}

.navmenu-panel{
  position:absolute;background:var(--panel);border:1px solid var(--line);
  box-shadow:var(--sh-3);display:flex;flex-direction:column;
  transition:transform .2s ease,opacity .2s ease;
}
.navmenu-group{font-size:var(--f-3xs);text-transform:uppercase;letter-spacing:.7px;
  color:var(--muted);padding:var(--sp-3) var(--sp-4) var(--sp-1)}
.navmenu-item{
  display:flex;align-items:center;gap:var(--sp-3);margin:0 6px;padding:var(--sp-2) var(--sp-2);
  border-radius:var(--r-sm);color:var(--dim);font-size:var(--f-md);
  transition:background .13s,color .13s;
}
/* Inactive icons sit back so the current section is the brightest thing in the
   list — but only part way. Full greyscale at this size turns them into
   smudges you cannot tell apart. */
.navmenu-item i{font-style:normal;font-size:var(--f-md);width:22px;text-align:center;flex:none;
  filter:grayscale(.55);opacity:.85;transition:filter .13s,opacity .13s}
.navmenu-item:hover{background:var(--panel-2);color:var(--text)}
.navmenu-item.on{background:var(--accent-soft);color:var(--text);font-weight:600}
.navmenu-item:hover i,.navmenu-item.on i{filter:none;opacity:1}
/* 'Abmelden' is a button, not a link; make it sit in the list like one. */
button.navmenu-item{width:calc(100% - 12px);font-family:inherit;text-align:left}

/* wide — a dropdown hanging off the button */
@media (min-width:641px){
  .navmenu-panel{
    top:var(--nav-y,58px);left:var(--nav-x,14px);width:272px;
    border-radius:var(--r-lg);padding:2px 0 10px;
    transform:translateY(-6px);opacity:0;
  }
  .navmenu.open .navmenu-panel{transform:none;opacity:1}
}

/* phone — a drawer, because a 272px dropdown pinned to a corner wastes the
   screen and puts the last item out of thumb reach */
@media (max-width:640px){
  /* The drawer covers most of the screen, so what is left of the page behind it
     has to step back or the two compete. */
  .navmenu-scrim{background:#05070fc4;backdrop-filter:blur(3px)}
  .navmenu-panel{
    /* Narrow enough that the scrim beside it is still an obvious tap target —
       closing by tapping the page is how most people will dismiss this. */
    top:0;bottom:0;left:0;width:min(76vw,296px);border-left:0;
    border-radius:0 var(--r-xl) var(--r-xl) 0;
    padding:calc(16px + var(--sa-t)) var(--sa-l) calc(18px + var(--sa-b)) 0;
    overflow-y:auto;overscroll-behavior:contain;transform:translateX(-100%);
  }
  .navmenu.open .navmenu-panel{transform:none}
  .navmenu-item{padding:var(--sp-3) var(--sp-2);font-size:var(--f-md);min-height:var(--tap)}
  .navmenu-group{padding:var(--sp-4) var(--sp-4) var(--sp-1)}
}

/* ================= responsive ================= */

/* The header is one row at every width now — ☰, brand, then whatever the page
   puts on the right. It only ever gets smaller, never folds. A page title
   (file name, level name) still drops to its own line, because it is content
   rather than chrome. */
@media (max-width:820px){
  header.top{
    gap:var(--sp-2) var(--sp-2);padding:var(--sp-2) var(--sp-3);
    padding-top:calc(9px + var(--sa-t));
    padding-left:calc(14px + var(--sa-l));
    padding-right:calc(14px + var(--sa-r));
  }
  .brand{font-size:var(--f-md)}
  .title{order:9;flex:1 0 100%;font-size:var(--f-sm)}
  header.top .btn{padding:var(--sp-2) var(--sp-3);font-size:var(--f-xs)}
  header.top .tabs button{padding:var(--sp-1) var(--sp-3);font-size:var(--f-xs)}
}

/* Phone. The header is genuinely tight here, so labels swap to their short
   twins — see .short/.long. */
@media (max-width:640px){
  header.top{gap:var(--sp-2) var(--sp-2);padding:var(--sp-2) var(--sp-3);
    padding-top:calc(8px + var(--sa-t));
    padding-left:calc(11px + var(--sa-l));
    padding-right:calc(11px + var(--sa-r));
  }
  .brand{font-size:var(--f-md);gap:var(--sp-2)}
  .menubtn{width:34px;height:34px}
  /* A flex:1 spacer eats the whole first line and shoves the last control onto
     a second one. With it gone the controls pack tight behind the brand, and
     the last of them still hugs the right edge. */
  .spacer{display:none}
  header.top>.btn:last-child,
  header.top>.tabs:last-child,
  header.top>.chip:last-child{margin-left:auto}
  header.top .btn{padding:var(--sp-2) var(--sp-2);font-size:var(--f-xs)}
  header.top .tabs{padding:2px}
  header.top .tabs button{padding:var(--sp-1) var(--sp-2);font-size:var(--f-xs)}
  .short{display:inline}
  .long{display:none}
}

/* The teacher is a side panel on a desktop and a full sheet on a phone. Every
   panel in the app is built with closable:true, so there is always a way out. */
@media (max-width:900px){
  .teacher.side{position:fixed;inset:0;width:auto;z-index:70;border-left:0}
  .teacher.side .teacher-head{padding-top:calc(13px + var(--sa-t))}
  .teacher.side .teacher-head .close{font-size:var(--f-xl);padding:4px var(--sp-2)}
}

@media (max-width:640px){
  /* iOS zooms the page in whenever it focuses a field under 16px. */
  input,textarea{font-size:16px;padding:var(--sp-2) var(--sp-3)}
  /* A select still has to leave room for its own arrow — the shared rule above
     reserves 30px on the right, and a plain `padding` here would drop it. */
  select{font-size:var(--f-lg);padding:var(--sp-2) var(--sp-6) var(--sp-2) var(--sp-3)}
  .teacher-input textarea{font-size:16px}

  /* Dialogs become bottom sheets — reachable with a thumb, and the keyboard
     pushes them up instead of covering them. */
  .overlay{padding:0;place-items:end stretch}
  .modal{max-width:none;border-radius:var(--r-xl) var(--r-xl) 0 0;border-bottom:0;
    max-height:92vh;max-height:92dvh;padding:var(--sp-4) var(--sp-4);
    padding-bottom:calc(20px + var(--sa-b))}
  .modal .row{flex-wrap:wrap}
  .modal .row .field{flex:1 1 140px}
  .modal .actions{position:sticky;bottom:0;background:var(--panel);padding-top:14px;
    margin-top:14px;border-top:1px solid var(--line)}
  .modal .actions .btn{flex:1}

  .empty{padding:40px 0}
  /* The Satzweise options become a sheet over the bottom of the screen,
     where the thumb is, rather than a box hanging off a button. */
  .sent-pop{left:0;right:0;top:auto;bottom:0;width:auto;border-radius:var(--r-xl) var(--r-xl) 0 0;
    border-bottom:0;padding:var(--sp-4) var(--sp-4) calc(var(--sp-4) + var(--sa-b));z-index:65}
  .sent-option{min-height:38px;font-size:var(--f-sm)}
  .sent-keys{display:none}
  .sent-row{flex-wrap:nowrap}
  .sent-row .btn{flex:1 1 0;min-width:0;overflow:hidden;padding-left:var(--sp-1);padding-right:var(--sp-1)}
  .usage{font-size:var(--f-3xs);padding:var(--sp-1) var(--sp-2);left:calc(8px + var(--sa-l));
    bottom:calc(8px + var(--sa-b) + var(--usage-lift,0px));opacity:.38}
  .usage .meter{width:34px}
}

/* Anything driven by a finger rather than a cursor: hover states are pointless
   and every target has to be big enough to hit. */
@media (hover:none) and (pointer:coarse){
  .btn{min-height:var(--tap)}
  .btn.small{min-height:34px}
  .sent-row .btn.small{min-height:var(--tap)}
  .tabs button{min-height:36px}
  .teacher-input .send{min-height:var(--tap)}
  /* Row actions that only appear on hover would otherwise never appear. */
  .job .x,.vid .x,.doc .x,.item .acts{opacity:1 !important}
}

@media (prefers-reduced-motion:reduce){
  *,*::before,*::after{
    animation-duration:.01ms !important;animation-iteration-count:1 !important;
    transition-duration:.01ms !important;scroll-behavior:auto !important;
  }
}
