/* ---------- Question ---------- */
@property --angle{syntax:"<angle>";inherits:false;initial-value:0deg}
.qwrap{max-width:760px;margin:0 auto}
.qhead{display:flex;align-items:center;gap:10px;margin-bottom:17px;flex-wrap:wrap}
.qreading-head{display:flex;align-items:center;gap:10px;margin:0 0 12px;color:var(--ink-2);font-size:var(--fs-sm);font-weight:700}
.qreading-head .qprog{margin-left:auto}
.stem-tools{margin:-10px 0 16px}
/* The exam clock rides after .qprog, which already carries margin-left:auto,
   so the pair sits at the right end of the head together. Tabular figures
   because a proportional 1 is narrower than a 0 and the whole clock jitters
   sideways once a second otherwise. */
.exam-clock{
  font-variant-numeric:tabular-nums;font-weight:900;font-size:var(--fs-ui);letter-spacing:.02em;
  padding:var(--s-3) var(--s-6);border-radius:var(--r-lg);border:2px solid var(--line-2);background:var(--fill);
  color:var(--tx);white-space:nowrap;
}
.exam-clock.low{color:var(--warn);border-color:var(--warn)}
.exam-clock.out{color:var(--bad);border-color:var(--bad)}
.pill{
  font-size:var(--fs-xs);font-weight:900;letter-spacing:.06em;text-transform:uppercase;padding:var(--s-2) var(--s-5);
  border-radius:var(--r-lg);color:#fff;border:2px solid var(--line-2);box-shadow:0 5px 14px rgba(0,0,0,.18);
}
/* The source citation reuses .pill, but it is a SENTENCE, not a label — often a
   long one with a page number and a transcription note. Uppercase at 10px suits
   "ENDPOINT"; it turns a citation into a wall, and EB Garamond's capitals are
   wider than the sans this was designed against. Sentence case, slightly larger,
   and allowed to wrap. */
.pill.ghost{
  background:var(--bg-2);color:var(--ink-3);
  text-transform:none;font-size:var(--fs-sm);font-weight:600;letter-spacing:.005em;
  line-height:1.5;text-align:left;white-space:normal;border-radius:var(--r-lg);padding:var(--s-2) var(--s-5);
}
.qprog{
  margin-left:auto;font-size:var(--fs-sm);color:var(--ink-2);font-variant-numeric:tabular-nums;font-weight:800;
  background:var(--card);padding:var(--s-2) var(--s-5);border-radius:var(--r-lg);border:2px solid var(--line-2);
}

.qcard{background:var(--card);border:1px solid var(--neon);border-radius:var(--r-lg);padding:var(--s-11) var(--s-11);box-shadow:var(--neon-glow)}
.stem{
  font-family:"Iowan Old Style","Palatino Linotype",Palatino,Georgia,serif;
  font-size:var(--fs-body);line-height:1.64;color:var(--tx);margin-bottom:21px;
}
.stem p{margin:0 0 12px}
.stem p:last-child{margin-bottom:0}
.stem table{border-collapse:separate;border-spacing:0;width:100%;margin:13px 0;font-size:var(--fs-md);font-family:var(--font-body);font-variant-numeric:tabular-nums;border:2px solid var(--line-2);border-radius:var(--r-lg);overflow:hidden}
.stem th,.stem td{border-bottom:1.5px solid var(--line);padding:var(--s-3) var(--s-5);text-align:left}
.stem th{background:var(--bg-2);font-weight:800}
.stem tr:last-child td{border-bottom:none}

/* The clinical photograph some questions cannot be answered without. It sits
   between the stem and the options because that is where the paper prints it,
   and because an answer chosen before looking at the picture is not an answer.
   Capped rather than stretched: the crops top out around 560px and enlarging
   past their own resolution only makes a scan look worse. */
.qfig{margin:0 0 21px;display:flex;flex-direction:column;align-items:flex-start;gap:7px}
.qfig img{max-width:min(100%,430px);height:auto;display:block;border-radius:var(--r-lg);
  border:2px solid var(--line-2);background:#fff;box-shadow:0 8px 22px var(--sh-soft);cursor:zoom-in}
/* A failed figure takes the image's place in the flow rather than sitting over
   it, so the box it needs is sized on the message itself. Reserving that height
   on every figure instead would pad the two dozen crops that render shorter
   than the reserve — the shortest is 112px against a message that needs 150. */
.qfig-media{width:100%;max-width:430px}
.qfig.veil img{filter:blur(14px);pointer-events:none}
.qfig.veil .qfig-media{overflow:hidden;border-radius:var(--r-lg)}
.qfig-failure:not([hidden]){display:flex;align-items:center;justify-content:center;
  min-height:150px;margin:0;padding:var(--s-8);border:2px solid var(--line-2);border-radius:var(--r-lg);
  background:var(--bg);color:var(--tx);font-size:var(--fs-ui);line-height:1.55}
.qzoom .qfig-failure:not([hidden]){max-width:430px}
.qfig figcaption{font-size:var(--fs-sm);letter-spacing:.03em;color:var(--ink-2)}
/* Full-size look. The crops carry fine detail — a speck of debris, a leader
   line — that a 430px column cannot show. */
.qzoom{position:fixed;inset:0;z-index:var(--z-300);display:flex;align-items:center;justify-content:center;
  padding:var(--s-10);background:rgba(3,7,14,.86);cursor:zoom-out}
.qzoom img{max-width:100%;max-height:100%;border-radius:var(--r-lg);background:#fff;box-shadow:0 20px 60px rgba(0,0,0,.5)}

.opts{display:flex;flex-direction:column;gap:9px}
.opt{
  display:flex;gap:var(--s-3);align-items:flex-start;padding:var(--s-2) var(--s-2) var(--s-2) var(--s-8);
  border:1px solid var(--neon);border-radius:var(--r-lg);background:var(--card);box-shadow:var(--neon-glow);
  text-align:left;width:100%;line-height:1.5;font-weight:600;
  transition:border-color .15s var(--ease-standard),background-color .15s var(--ease-standard),box-shadow .15s var(--ease-standard);
}
.opt:hover:not(:has(.opt-answer:disabled)){border-color:var(--gold);background:var(--tint-2);box-shadow:var(--gold-glow)}
.opt-answer{display:flex;gap:13px;align-items:flex-start;padding:var(--s-4) 0;border:0;background:transparent;color:var(--tx);text-align:left;flex:1;min-width:0;font:inherit;line-height:inherit;font-weight:inherit}
.opt-answer:disabled{cursor:default}
.opt .ltr{
  width:26px;height:26px;border-radius:var(--r-lg);border:2.5px solid var(--line-2);
  display:flex;align-items:center;justify-content:center;font-size:var(--fs-sm);font-weight:900;
  flex-shrink:0;color:var(--ink-3);margin-top:1px;transition:all var(--dur-1);
}
.opt.sel{border-color:var(--neuro);background:rgba(109,76,125,.15);box-shadow:var(--neon-glow)}
.opt.sel .ltr{background:var(--neuro);border-color:var(--neuro);color:#fff}
.opt.correct{border-color:var(--ok);background:var(--ok-bg);box-shadow:var(--neon-glow)}
.opt.correct .ltr{background:var(--ok);border-color:var(--ok);color:#fff}
.opt.wrong{border-color:var(--bad);background:var(--bad-bg);box-shadow:var(--neon-glow)}
.opt.wrong .ltr{background:var(--bad);border-color:var(--bad);color:#fff}
.opt .mark{margin-left:auto;font-weight:900;font-size:var(--fs-lg);flex-shrink:0;line-height:1}
.opt.correct .mark{color:var(--ok)}
.opt.wrong .mark{color:var(--bad)}
.opt.struck .opt-text{color:var(--ink-3);text-decoration:line-through}
.strike-btn{width:var(--s-12);height:var(--s-12);padding:0;border:1px solid var(--line-2);border-radius:var(--r-md);background:var(--card);color:var(--ink-2);display:flex;align-items:center;justify-content:center;flex:0 0 var(--s-12);align-self:center;transition:transform var(--dur-1) ease,background var(--dur-1) ease}
.strike-btn:hover{background:var(--bg-2)}
.strike-btn:focus-visible,.opt-answer:focus-visible{outline:2px solid var(--tx);outline-offset:2px}

.qactions{display:flex;flex-direction:column;align-items:stretch;gap:10px;margin-top:22px}
.qaction-primary,.qaction-secondary{display:flex;gap:10px;align-items:center;flex-wrap:wrap}
.qaction-primary .btn{width:100%}
.qaction-secondary{justify-content:flex-start}
.qtools{margin-top:24px;padding-top:16px;border-top:1px solid var(--line)}
.qtools .qhead{margin-bottom:13px}
.btn{
  position:relative;isolation:isolate;padding:var(--s-5) var(--s-9);border:0;border-radius:var(--r-sm);font-size:var(--fs-ui);font-weight:800;
  background:var(--fill);color:#fff;
  transition:color var(--dur-2) var(--ease-standard),background-color var(--dur-2) var(--ease-standard);
}
.btn::before{content:"";position:absolute;inset:0;border-radius:var(--r-sm);padding:1px;background:conic-gradient(from var(--angle),transparent,transparent,var(--peds-hi),transparent,transparent);-webkit-mask:linear-gradient(#000 0 0) content-box,linear-gradient(#000 0 0);-webkit-mask-composite:xor;mask-composite:exclude;z-index:-1;animation:orbit 7s linear infinite}
.btn:hover:not(:disabled)::before{animation-duration:2.3s}
.btn:active:not(:disabled){background-color:var(--gold);color:var(--on-accent)}
.btn:active:not(:disabled)::before{background:var(--gold);animation:none}
.btn:disabled{opacity:.32;cursor:default;box-shadow:none}
.btn.sec{background:var(--card);color:var(--tx)}
.btn.go{background:var(--neuro)}
.btn.flag.on{background:var(--gold);color:var(--on-accent)}
/* A destructive button needs a glyph because its red border alone can be
   missed. ::before is now the travelling-light ring's masked pseudo-element
   (line above) — content set there is clipped by the mask and never paints.
   ::after is a plain, unmasked pseudo, absolutely placed at the left edge
   where the glyph sat before P4; padding-left makes room so it does not
   overlap the label. Fix round 2 item 3. */
.btn[style*="--bad"]{padding-left:calc(var(--s-9) + 12px)}
.btn[style*="--bad"]::after{content:"!";position:absolute;left:var(--s-7);top:50%;transform:translateY(-50%);font-weight:900}

@keyframes orbit{to{--angle:360deg}}
@keyframes pop{0%{transform:scale(.86)}60%{transform:scale(1.04)}100%{transform:scale(1)}}
@media(prefers-reduced-motion:reduce){.btn::before{animation:none}.opt,.opt.correct,.opt.wrong{transition:none}.verdict{animation:none}}
.verdict{
  margin-top:21px;padding:var(--s-7) var(--s-8);border-radius:var(--r-lg);font-weight:900;font-size:var(--fs-ui);
  display:flex;align-items:center;gap:11px;border:1px solid var(--neon);box-shadow:var(--neon-glow);animation:pop .32s var(--ease-standard);
}
.verdict.ok{background:var(--ok-bg);color:#5fe0a8;box-shadow:var(--pop) rgba(0,168,107,.3)}
.verdict.bad{background:var(--bad-bg);color:#ff9aa2;box-shadow:var(--pop) rgba(255,71,87,.3)}
.verdict.part{background:var(--gold-bg);color:#ffd27a;box-shadow:var(--pop) rgba(255,201,74,.45)}
.verdict .big{font-size:var(--fs-xl)}

/* ---------- Session results ---------- */
.session-results{max-width:760px;margin:0 auto}.result-grid{display:grid;gap:24px}
.result-account,.wrong-queue{border:1px solid var(--neon);border-radius:var(--r-lg);background:var(--card);box-shadow:var(--neon-glow);padding:var(--s-9)}
.result-grid{margin-top:18px}.score{font:var(--fs-3xl)/1 var(--font-display);color:var(--tx);font-variant-numeric:tabular-nums}
.quiet{color:var(--ink-2);line-height:1.55}.result-actions{display:flex;flex-direction:column;gap:10px;margin-top:22px}.result-actions .btn{width:100%}
.wrong-queue h2{margin-bottom:14px}.wrong-list{display:grid;gap:8px;margin:0 0 18px}.wrong-row{padding:var(--s-5) var(--s-6);border:1px solid var(--line);border-radius:var(--r-md);color:var(--ink-2);font-size:var(--fs-ui);line-height:1.45}
.result-mins{margin:18px 0 0}
@media(min-width:601px){.session-results{max-width:980px}.result-grid{grid-template-columns:1fr 1fr;align-items:start}.wrong-queue{margin-top:0}}

/* ---- free-text case questions ---- */
.caseask{margin-top:19px;font-weight:900;font-size:var(--fs-ui);color:var(--ink-2)}
.caseinput{
  width:100%;margin-top:9px;padding:var(--s-6) var(--s-7);font:inherit;font-size:var(--fs-ui);line-height:1.5;
  border:1px solid var(--line-2);border-radius:var(--r-lg);background:var(--card);color:var(--tx);
  box-shadow:var(--pop) rgba(0,0,0,.12);resize:vertical;
  transition:box-shadow var(--dur-4) ease,transform var(--dur-4) ease;
}
.caseinput:focus{outline:none;transform:translate(-1px,-1px);box-shadow:0 12px 28px rgba(0,0,0,.18)}
.caseinput:disabled{opacity:.75;cursor:default}
.caseinput::placeholder{color:var(--ink-2);opacity:.6}
.modelans{
  margin-top:15px;padding:var(--s-7) var(--s-8);border:1px solid var(--neon);border-radius:var(--r-lg);
  background:var(--ok-bg);box-shadow:var(--neon-glow);
}
.modelans>b{display:block;font-size:var(--fs-sm);letter-spacing:.08em;text-transform:uppercase;color:var(--ink-2);margin-bottom:5px}
.modelans>div{font-weight:800;font-size:var(--fs-ui)}
.keys{margin-top:14px;padding:var(--s-6) var(--s-8);border:2.5px dashed var(--line-2);border-radius:var(--r-lg)}
.keys-h{font-size:var(--fs-sm);letter-spacing:.08em;text-transform:uppercase;color:var(--ink-2);font-weight:800;margin-bottom:9px}
.key{display:flex;align-items:center;gap:9px;padding:var(--s-2) 0;font-size:var(--fs-ui);font-weight:700}
.key.hit>span:first-child{color:var(--ok);font-weight:900}
.key.miss{opacity:.55}
.lastgo{
  display:inline-flex;align-items:center;gap:7px;margin-top:9px;
  padding:var(--s-2) var(--s-6);border:2px dashed var(--line-2);border-radius:var(--r-lg);
  font-size:var(--fs-sm);font-weight:800;color:var(--ink-2);opacity:.85}

.two-right{margin-top:9px;padding:var(--s-2) var(--s-6);border:2px dashed var(--line-2);border-radius:var(--r-lg);
  font-size:var(--fs-sm);font-weight:800;color:var(--ink-2)}

.conf{margin-top:17px;padding:var(--s-7) var(--s-8);background:var(--gold-bg);border:1px solid var(--neon);border-radius:var(--r-lg);box-shadow:var(--neon-glow)}
.conf-q{font-size:var(--fs-md);color:var(--ink-2);margin-bottom:11px;font-weight:700}
.conf-btns{display:flex;gap:8px;flex-wrap:wrap}
.conf-b{
  padding:var(--s-4) var(--s-7);border:1px solid var(--line-2);border-radius:var(--r-lg);font-size:var(--fs-md);
  background:var(--card);font-weight:800;transition:transform var(--dur-1) ease,box-shadow var(--dur-1);
}
.conf-b:hover{background:var(--bg-2);box-shadow:0 7px 18px rgba(2,6,14,.25)}
.conf-b.on{background:var(--fill);color:#fff}

.explain{margin-top:18px;border:1px solid var(--neon);border-radius:var(--r-lg);overflow:hidden;box-shadow:var(--neon-glow)}

/* Two quiet routes out of a question: a note the student writes to themselves,
   and the one way to tell us the question itself is wrong. Both sit under the
   explanation as plain underlined text — a student who wants them will find
   them, and a student who does not is never asked anything. */
.qfoot{display:flex;align-items:center;gap:18px;flex-wrap:wrap;margin-top:16px}
.qfoot .lnk{background:none;border:none;padding:0;font:inherit;font-size:var(--fs-md);font-weight:700;
  color:var(--ink-2);border-bottom:2px solid var(--line);cursor:pointer;
  transition:color var(--dur-2),border-color var(--dur-2)}
.qfoot .lnk:hover{color:var(--tx);border-bottom-color:var(--tx)}
.qfoot .lnk.has{color:var(--gold);border-bottom-color:var(--gold)}
.qnote{margin-top:13px}
.qnote .lb{display:block;font-size:var(--fs-sm);font-weight:800;letter-spacing:.09em;
  text-transform:uppercase;color:var(--ink-2);margin-bottom:7px}
.qnote .say{font-size:var(--fs-sm);color:var(--ink-2);margin-top:7px;min-height:16px}
.ex-head{
  display:flex;align-items:center;gap:10px;padding:var(--s-6) var(--s-8);background:var(--bg-2);
  width:100%;text-align:left;font-weight:900;font-size:var(--fs-ui);transition:background var(--dur-2);
}
.ex-head:hover{background:rgba(255,255,255,.07)}
.ex-head .chev{margin-left:auto;transition:transform .22s ease}
.ex-head.open .chev{transform:rotate(180deg)}
.ex-body{padding:var(--s-9) var(--s-9);font-size:var(--fs-ui);line-height:1.68;color:var(--ink-2);display:none}
.ex-body.open{display:block;animation:pop .24s ease}
.ex-body p{margin:0 0 13px}
.ex-body p:last-child{margin-bottom:0}
.ex-body strong{color:var(--tx);font-weight:800}
.ex-body em{color:var(--ink-3)}
.objective{
  margin-top:17px;padding:var(--s-7) var(--s-8);background:linear-gradient(135deg,rgba(46,95,138,.13),rgba(109,76,125,.15));
  border:2.5px solid var(--neuro);border-radius:var(--r-lg);font-size:var(--fs-ui);line-height:1.6;
}
.objective b{display:block;font-size:var(--fs-xs);letter-spacing:.11em;text-transform:uppercase;color:var(--neuro);margin-bottom:5px;font-weight:900}
.noexp{padding:var(--s-8) var(--s-9);color:var(--ink-4);font-size:var(--fs-ui);font-style:italic}

/* ---------- Schedule ---------- */
.sched-legend{display:flex;gap:15px;flex-wrap:wrap;margin-bottom:20px;font-size:var(--fs-md);color:var(--ink-3);font-weight:700}
.leg{display:flex;align-items:center;gap:7px}
.leg .sw{width:13px;height:13px;border-radius:var(--r-md);border:2px solid var(--line-2)}
.day{
  display:flex;gap:14px;align-items:center;padding:var(--s-6) var(--s-7);background:var(--card);
  border:1px solid var(--line-2);border-radius:var(--r-lg);margin-bottom:9px;
  box-shadow:0 5px 14px rgba(0,0,0,.13);transition:transform var(--dur-2),box-shadow var(--dur-2);
}
.day:hover{border-color:var(--gold);box-shadow:0 10px 24px rgba(0,0,0,.18)}
.day.rest{background:var(--bg-2);opacity:.72;border-style:dashed}
.day.missed{background:var(--bad-bg);border-color:var(--bad)}
.day.done{background:var(--ok-bg);border-color:var(--ok)}
.day.today{border-color:var(--neuro);box-shadow:0 10px 24px rgba(125,88,144,.4)}
.day-date{width:74px;flex-shrink:0}
.day-d{font-weight:900;font-size:var(--fs-ui);font-variant-numeric:tabular-nums;letter-spacing:-.02em}
.day-w{font-size:var(--fs-xs);color:var(--ink-4);font-weight:700;text-transform:uppercase;letter-spacing:.05em}
.day-body{flex:1;min-width:0}
.day-line{font-size:var(--fs-md);margin-bottom:4px;line-height:1.45;font-weight:600}
.day-line:last-child{margin-bottom:0}
.day-line.faded{opacity:.45;text-decoration:line-through}
.tag{
  display:inline-block;font-size:var(--fs-xs);font-weight:900;letter-spacing:.07em;text-transform:uppercase;
  padding:var(--s-1) var(--s-5);border-radius:var(--r-lg);color:#fff;margin-right:10px;vertical-align:1.5px;
  border:1.5px solid var(--line-2);white-space:nowrap;box-shadow:0 3px 10px rgba(0,0,0,.22);
}
/* Owner ruling: Peds is a coloured word, never a boxed status tag. */
.tag[style*="#2e5f8a"]{background:transparent!important;border:0;box-shadow:none;padding:0;color:var(--peds-hi)}
.movednote{font-size:var(--fs-xs);color:var(--neuro);font-weight:800;margin-top:4px}
.day-marks{display:flex;gap:7px;flex-shrink:0}
.mk{
  width:38px;height:38px;border-radius:var(--r-lg);border:1px solid var(--line-2);background:var(--card);
  display:flex;align-items:center;justify-content:center;font-size:var(--fs-ui);
  transition:transform var(--dur-2) ease,box-shadow var(--dur-2),background var(--dur-2);
  box-shadow:0 5px 14px rgba(0,0,0,.16);
}
.mk:hover{background:var(--bg-2);box-shadow:0 7px 18px rgba(0,0,0,.25)}
.mk:active{transform:translate(1px,1px);box-shadow:0 2px 7px rgba(0,0,0,.2)}
.mk.done.on{background:var(--ok);color:#fff}
.mk.miss.on{background:var(--bad);color:#fff}
.mk .lbl{position:absolute;opacity:0;pointer-events:none}
/* Touch. These are 38px, under the 44px minimum, and the two of them sit 7px
   apart while meaning opposite things — the schedule renders 119 rows, so 238
   of them are on the page at once and a mis-tap is likeliest exactly here.
   Growing the target without growing the gap would trade one error for
   another, so both move. A portrait tablet is 768px and is still a finger, so
   this is keyed on the pointer as well as on the width, not on width alone. */
@media(max-width:700px),(hover:none){
  .day-marks{gap:10px}
  .mk{width:44px;height:44px;border-radius:var(--r-lg);font-size:var(--fs-body)}
}


.track{
  display:flex;align-items:center;gap:11px;padding:var(--s-4) 0;
  border-bottom:1.5px dashed var(--line);
}
.track:last-child{border-bottom:none}
.track-body{flex:1;min-width:0;font-size:var(--fs-ui);font-weight:600;line-height:1.55}
.track.is-done .track-body{opacity:.6}
.track.is-missed .track-body{opacity:.55;text-decoration:line-through}
.track.catchup{background:rgba(109,76,125,.12);border-radius:var(--r-lg);padding:var(--s-4) var(--s-5);margin-top:5px;border:1.5px dashed var(--neuro)}
.day{align-items:stretch}
.day-body{display:flex;flex-direction:column;justify-content:center}


/* ---------- Highlighter ---------- */
mark.hl{background:transparent;padding:var(--s-1) var(--s-1);border-radius:var(--r-md);color:inherit;
  box-decoration-break:clone;-webkit-box-decoration-break:clone}
/* On the old white paper these were solid pastels with dark text. On the dark
   ground a solid pastel would need dark text inside light-on-dark prose, which
   reads badly mid-sentence — so the highlight is now a translucent wash of the
   same hue and the text stays light. Colour still identifies the pen. */
mark.hl[data-c="y"]{background:rgba(255,220,90,.30);box-shadow:inset 0 -1px 0 rgba(255,220,90,.55)}
mark.hl[data-c="g"]{background:rgba(110,240,160,.28);box-shadow:inset 0 -1px 0 rgba(110,240,160,.5)}
mark.hl[data-c="p"]{background:rgba(255,150,200,.28);box-shadow:inset 0 -1px 0 rgba(255,150,200,.5)}
mark.hl[data-c="b"]{background:rgba(120,190,255,.28);box-shadow:inset 0 -1px 0 rgba(120,190,255,.5)}

.mkbar{
  display:flex;align-items:center;gap:7px;flex-wrap:wrap;margin-bottom:15px;
  padding:var(--s-4) var(--s-6);background:var(--bg-2);border:2px dashed var(--line-2);border-radius:var(--r-lg);
}
.mkbar .lb{font-size:var(--fs-xs);font-weight:900;letter-spacing:.09em;text-transform:uppercase;color:var(--ink-3);margin-right:2px}
.pen{
  width:29px;height:29px;border-radius:var(--r-lg);border:2.5px solid var(--line-2);
  box-shadow:0 5px 14px rgba(0,0,0,.18);transition:transform var(--dur-2) ease,box-shadow var(--dur-2);
  position:relative;
}
.pen:hover{background:var(--bg-2);box-shadow:0 7px 18px rgba(0,0,0,.26)}
.pen.on{transform:translateY(-3px);box-shadow:0 9px 22px rgba(0,0,0,.3)}
.pen.on::after{content:"";position:absolute;inset:-6px;border:2.5px solid var(--line-2);border-radius:var(--r-lg)}
.pen.y{background:#ffe97a}.pen.g{background:#a8f0bd}.pen.p{background:#ffc2dc}.pen.b{background:#a9d6ff}
.pen.er{background:var(--card);font-size:var(--fs-ui);display:flex;align-items:center;justify-content:center}
.mkbar .hint{font-size:var(--fs-sm);color:var(--ink-4);font-weight:600;margin-left:auto}
.mkbar .clr{
  font-size:var(--fs-sm);font-weight:800;color:var(--ink-3);padding:var(--s-2) var(--s-5);border-radius:var(--r-lg);
  border:2px solid var(--line-2);background:var(--card);
}
.mkbar .clr:hover{border-color:var(--line-2);color:var(--tx)}
.stem{-webkit-user-select:text;user-select:text}
.stem.arm{cursor:crosshair}


/* ---------- Jump navigator ---------- */
.modal.wide{max-width:660px}
.jumprow{display:flex;gap:9px;margin-bottom:16px;flex-wrap:wrap;align-items:center}
.jumpin{
  width:112px;padding:var(--s-4) var(--s-6);border:1px solid var(--line-2);border-radius:var(--r-lg);
  font-size:var(--fs-ui);font-weight:800;background:var(--card);color:var(--tx);
  box-shadow:0 5px 14px rgba(0,0,0,.15);
}
.jumpin::-webkit-outer-spin-button,.jumpin::-webkit-inner-spin-button{-webkit-appearance:none;margin:0}
.jumpin{-moz-appearance:textfield}
.jumpkey{display:flex;gap:13px;flex-wrap:wrap;font-size:var(--fs-sm);color:var(--ink-3);font-weight:700;margin-bottom:13px}
.jumpkey .k{display:flex;align-items:center;gap:6px}
.jumpkey .sw{width:13px;height:13px;border-radius:var(--r-md);border:2px solid var(--line-2)}
.qgrid{
  display:grid;grid-template-columns:repeat(auto-fill,minmax(46px,1fr));gap:8px;
  max-height:42vh;overflow-y:auto;padding:var(--s-2) var(--s-1);
}
.qg{
  aspect-ratio:1;border-radius:var(--r-lg);border:1px solid var(--line-2);font-weight:900;font-size:var(--fs-md);
  background:var(--card);display:flex;align-items:center;justify-content:center;position:relative;
  box-shadow:0 5px 14px rgba(0,0,0,.15);
  transition:transform var(--dur-1) ease,box-shadow var(--dur-1);
}
.qg:hover{background:var(--bg-2);box-shadow:0 10px 24px rgba(0,0,0,.24)}
.qg.ok{background:var(--ok-bg);color:#5fe0a8;border-color:var(--ok)}
.qg.bad{background:var(--bad-bg);color:#ff9aa2;border-color:var(--bad)}
/* Corner glyphs distinguish right and wrong when their colours look alike. */
.qg.ok::before{content:"\2713";position:absolute;left:2px;bottom:0;font-size:9px;line-height:1;color:var(--ok)}
.qg.bad::before{content:"\00d7";position:absolute;left:2px;bottom:0;font-size:var(--fs-xs);line-height:1;color:var(--bad)}
/* During a paper the grid may say only whether a question has been answered.
   Right and wrong are the marks, and the marks come out at hand-in. */
.qg.done{background:var(--fill);border-color:var(--tx);color:var(--tx)}
.qg.cur{background:var(--neuro);color:#fff;transform:translate(-2px,-2px);box-shadow:0 10px 24px rgba(0,0,0,.3)}
.qg.flagged::after{
  content:"";position:absolute;top:-5px;right:-5px;width:11px;height:11px;
  background:var(--gold);border:2px solid var(--line-2);border-radius:50%;
}
.qprog{border:2px solid var(--line-2);cursor:pointer;transition:transform var(--dur-1),box-shadow var(--dur-1)}
.qprog:hover{background:var(--bg-2);box-shadow:0 5px 14px rgba(0,0,0,.22)}



/* ---------- Schedule editor ---------- */
.edbar{display:flex;gap:9px;flex-wrap:wrap;align-items:center;margin-bottom:18px}
.day.editing{border-style:dashed;border-color:var(--neuro);background:rgba(109,76,125,.10)}
.ed-item{display:flex;gap:8px;align-items:center;padding:var(--s-3) 0;flex-wrap:wrap}
.ed-lab,.ed-txt{
  padding:var(--s-3) var(--s-5);border:2px solid var(--line-2);border-radius:var(--r-lg);font-size:var(--fs-md);
  background:var(--card);color:var(--tx);font-weight:600;font-family:inherit;
}
.ed-lab{width:132px;flex-shrink:0}
.ed-txt{flex:1;min-width:150px}
.ed-lab:focus,.ed-txt:focus{border-color:var(--neuro);outline:none}
.ed-col{width:30px;height:30px;border-radius:var(--r-lg);border:2.5px solid var(--line-2);flex-shrink:0;cursor:pointer}
.ed-x{
  width:29px;height:29px;border-radius:var(--r-lg);border:2px solid var(--bad);color:var(--bad);
  background:var(--card);font-weight:900;flex-shrink:0;font-size:var(--fs-md);
}
.ed-x:hover{background:var(--bad-bg)}
.ed-add{
  font-size:var(--fs-sm);font-weight:800;color:var(--neuro);padding:var(--s-3) var(--s-6);border-radius:var(--r-lg);
  border:2px dashed var(--neuro);background:transparent;margin-top:5px;
}
.ed-add:hover{background:rgba(109,76,125,.17)}
.ed-day-tools{display:flex;gap:7px;flex-shrink:0;flex-wrap:wrap}
.ed-t{
  font-size:var(--fs-xs);font-weight:800;padding:var(--s-2) var(--s-5);border-radius:var(--r-lg);border:2px solid var(--line-2);
  background:var(--card);color:var(--ink-3);
}
.ed-t:hover{border-color:var(--line-2);color:var(--tx)}
.ed-t.danger{border-color:var(--bad);color:var(--bad)}
.ed-t.on{background:var(--fill);color:#fff;border-color:var(--line-2)}
.newday{display:flex;gap:9px;align-items:center;flex-wrap:wrap;padding:var(--s-7);margin-top:10px;
  border:2.5px dashed var(--line-2);border-radius:var(--r-lg);background:var(--card)}
.newday input[type=date]{padding:var(--s-4) var(--s-5);border:2px solid var(--line-2);border-radius:var(--r-lg);
  font-family:inherit;font-size:var(--fs-md);font-weight:600;background:var(--card);color:var(--tx)}
.palette{display:flex;gap:6px;flex-wrap:wrap}


/* ---------- Profile gate ----------
   TWO STEPS: sign in, then choose or create a profile. showGate() renders one
   at a time and re-writes innerHTML to move between them, which is also what
   replays the entrance animations — new elements, so they run again.

   The gate sits at z-index 200, ABOVE .app, so the page's ambient orb field is
   hidden behind it and it needs its own. Tuned brighter and faster than the
   page's: this is a five-second screen, not one you read on, so the
   CALM-TO-READ half of the rule does not apply here.                          */
.gate{
  position:fixed;inset:0;z-index:var(--z-200);background:var(--bg);
  display:flex;flex-direction:column;align-items:center;justify-content:center;
  padding:var(--s-11) var(--s-9);overflow-y:auto;isolation:isolate;
}
.gate.hide{display:none}

.gate-amb{position:absolute;inset:0;overflow:hidden;pointer-events:none;z-index:0}
/* .5 before the film went in behind them. Same lesson as the page's orbs: two
   ambient systems at full strength cancel, and an 80px blur at half opacity
   simply fogs the room away. */
.gate-amb .gorb{position:absolute;border-radius:50%;filter:blur(80px);opacity:.34;will-change:transform}
/* one orb per module, in the module's own colour — the four colours of the app
   are the first thing you see */
.gate-amb .g1{width:44vw;height:44vw;left:-10vw;top:-16vh;background:radial-gradient(circle,var(--neuro),transparent 68%);animation:gd1 26s ease-in-out infinite}
.gate-amb .g2{width:38vw;height:38vw;right:-8vw;top:-6vh;background:radial-gradient(circle,var(--ent),transparent 68%);animation:gd2 31s ease-in-out infinite}
.gate-amb .g3{width:40vw;height:40vw;left:14vw;bottom:-22vh;background:radial-gradient(circle,var(--peds),transparent 70%);animation:gd3 37s ease-in-out infinite}
.gate-amb .g4{width:26vw;height:26vw;right:12vw;bottom:-14vh;background:radial-gradient(circle,var(--ophtho),transparent 72%);opacity:.34;animation:gd1 43s ease-in-out infinite reverse}
@keyframes gd1{0%,100%{transform:translate3d(0,0,0) scale(1)}50%{transform:translate3d(7vw,6vh,0) scale(1.14)}}
@keyframes gd2{0%,100%{transform:translate3d(0,0,0) scale(1)}50%{transform:translate3d(-8vw,8vh,0) scale(1.1)}}
@keyframes gd3{0%,100%{transform:translate3d(0,0,0) scale(1)}50%{transform:translate3d(6vw,-7vh,0) scale(1.16)}}
:root[data-theme="light"] .gate-amb .gorb{opacity:.26}
:root[data-theme="light"] .gate-amb .g4{opacity:.18}

/* THE GATE IS THE ONE PLACE TEXT SITS DIRECTLY ON THE FILM.
   Everywhere else in the app the type is on an opaque card, which is why the
   film cost nothing in contrast — measured, twice. Here there is no card, and
   the ground underneath MOVES: the scholar's pale robe drifts behind the small
   print, so contrast would rise and fall frame by frame and the worst frame is
   the one that matters. This pool fixes a constant ground under the centre
   column while leaving the lamp and the edges of the room fully visible. */
.gate-amb .film-scrim::after{
  content:'';position:absolute;inset:0;
  background:radial-gradient(46% 42% at 50% 52%,
    rgba(7,13,22,.88) 0%, rgba(7,13,22,.72) 45%, transparent 78%);
}
:root[data-theme="light"] .gate-amb .film-scrim::after{
  background:radial-gradient(46% 42% at 50% 52%,
    rgba(247,244,236,.92) 0%, rgba(247,244,236,.78) 45%, transparent 78%);
}

/* A faint grid, masked to fade out at the edges. Without it four big blurs on
   a flat ground read as soup; the straight lines give the blur something to be
   soft against. */
.gate::before{
  content:"";position:absolute;inset:0;z-index:0;pointer-events:none;
  background-image:linear-gradient(var(--line) 1px,transparent 1px),
                   linear-gradient(90deg,var(--line) 1px,transparent 1px);
  background-size:64px 64px;
  -webkit-mask-image:radial-gradient(ellipse 78% 62% at 50% 44%,#000 18%,transparent 76%);
          mask-image:radial-gradient(ellipse 78% 62% at 50% 44%,#000 18%,transparent 76%);
}

.gstep{position:relative;z-index:1;display:flex;flex-direction:column;align-items:center;width:100%;max-width:780px}
.gate-theme{position:absolute;top:16px;right:16px;z-index:3;font-size:var(--fs-ui)}

/* Each child rises in on its own --d delay, set inline where the markup is
   built. BACKWARDS, not BOTH: `both` would leave the final keyframe stuck on
   the element, and its transform:none then beats .pcard:hover. */
/* .pgrid is excluded: its own .pcard children stagger individually, and
   animating the container too makes the cards arrive twice. */
.gstep>*:not(.pgrid){animation:gRise .62s var(--ease-standard) var(--d,0ms) backwards}
@keyframes gRise{from{opacity:0;transform:translateY(18px)}to{opacity:1;transform:none}}

.gmark{
  position:relative;width:88px;height:88px;border-radius:50%;
  display:flex;align-items:center;justify-content:center;
  box-shadow:0 18px 44px rgba(0,0,0,.5),var(--glow);margin-bottom:22px;
}
/* a slow sonar ring — the one piece of continuous motion on the sign-in step.
   Gold, not ENT coral: it echoes the medallion's own rule instead of fighting it. */
.gmark::after{
  content:"";position:absolute;inset:-10px;border-radius:50%;
  border:2px solid var(--gold);animation:halo 3.6s ease-out infinite;
}
@keyframes halo{0%{transform:scale(.92);opacity:.55}70%,100%{transform:scale(1.25);opacity:0}}

/* The wordmark is set once, in the display face, and holds still. It used to
   carry a four-stop gradient walked across it by a nine-second loop: the one
   device on this page that announced itself before the app had said anything,
   and the first thing a reader takes for decoration rather than design. The
   tracking is positive because Cinzel is tracked out everywhere else it is
   used, and a titling face set tight is the tell that nobody chose it. */
.gtitle{
  font-size:clamp(28px,6vw,36px);font-weight:900;letter-spacing:.02em;margin:0 0 10px;
  color:var(--tx);
  animation:gRise .62s var(--ease-standard) var(--d,0ms) backwards;
}
.gtag{font-size:var(--fs-ui);color:var(--ink-3);font-weight:600;margin:0 0 30px;text-align:center;max-width:430px;line-height:1.6}
.glink{
  margin-top:18px;font-size:var(--fs-md);font-weight:800;color:var(--ink-3);
  padding:var(--s-4) var(--s-8);border-radius:var(--r-lg);transition:color .15s,background .15s;
}
.glink:hover{color:var(--tx);background:var(--tint-2)}
.gnote{font-size:var(--fs-sm);color:var(--ink-4);font-weight:600;max-width:400px;text-align:center;line-height:1.65;margin:26px 0 0}
.gnote.warn{color:var(--warn);border:2px solid var(--warn);border-radius:var(--r-lg);padding:var(--s-6) var(--s-7)}
.gwho{
  display:inline-flex;align-items:center;gap:8px;padding:var(--s-3) var(--s-7);border-radius:var(--r-lg);
  background:var(--ok-bg);color:var(--ok);border:1px solid var(--ok);
  font-size:var(--fs-md);font-weight:800;margin-bottom:22px;
}
.gate .glogo{display:flex;align-items:center;gap:11px;font-weight:900;font-size:var(--fs-xl);letter-spacing:-.03em;margin-bottom:6px}
.gate .glogo .blip,.splash .glogo .blip{
  width:38px;height:38px;border-radius:50%;overflow:hidden;
  display:flex;align-items:center;justify-content:center;
  box-shadow:0 7px 18px rgba(0,0,0,.35);flex-shrink:0;
}
.gate h2{font-size:var(--fs-lg);color:var(--ink-3);font-weight:700;margin:0 0 26px}
.pgrid{display:flex;gap:20px;flex-wrap:wrap;justify-content:center;max-width:760px}
.pcard{
  width:132px;display:flex;flex-direction:column;align-items:center;gap:10px;
  background:transparent;transition:transform .16s ease;
  animation:gRise .62s var(--ease-standard) var(--d,0ms) backwards;
}
.pcard:hover{border-color:var(--gold)}
.pav{
  width:96px;height:96px;border-radius:var(--r-lg);border:3px solid var(--line-2);
  display:flex;align-items:center;justify-content:center;font-size:var(--fs-3xl);overflow:hidden;
  box-shadow:0 12px 28px rgba(0,0,0,.2);position:relative;--fr-t:9px;
}
.pcard:hover .pav{box-shadow:0 17px 40px rgba(0,0,0,.28)}
.pav img{width:100%;height:100%;object-fit:cover;display:block}
.pav .lock{
  position:absolute;right:-6px;bottom:-6px;width:28px;height:28px;border-radius:50%;
  background:var(--gold);border:2.5px solid var(--line-2);display:flex;align-items:center;
  justify-content:center;font-size:var(--fs-sm);
}
.pname{font-weight:800;font-size:var(--fs-ui);text-align:center;line-height:1.3;word-break:break-word}
.pcard.add .pav{border-style:dashed;background:var(--card);font-size:var(--fs-3xl);color:var(--ink-4);box-shadow:none}
.pcard.add:hover .pav{border-color:var(--neuro);color:var(--neuro)}
.gate .gfoot{margin-top:34px;display:flex;gap:10px;flex-wrap:wrap;justify-content:center}
.gate .goffline{margin:20px auto 0;max-width:460px;text-align:center;font-size:var(--fs-md);line-height:1.6;color:var(--ink-4)}

/* Year and semester. This was two native <select> boxes — the only unstyled
   controls left in the app, and they landed on the gate, which is the first
   thing a new student sees. A white operating-system rectangle on a dark
   cinematic page does not read as a plain control; it reads as a page that was
   never finished. The chips borrow .chip's shape and .emb's selected state so
   this is the app's existing vocabulary rather than a third one. */
.tpick{display:flex;flex-direction:column;gap:18px;align-items:center;width:100%}
.tlab{
  font-size:var(--fs-xs);font-weight:800;letter-spacing:.14em;text-transform:uppercase;
  color:var(--ink-4);margin:0 0 9px;text-align:center;
}
.trow{display:flex;gap:9px;flex-wrap:wrap;justify-content:center}
.tchip{
  padding:var(--s-4) var(--s-8);border:1px solid var(--line-2);border-radius:var(--r-lg);
  background:var(--card);color:var(--tx);font-size:var(--fs-md);font-weight:800;
  transition:transform var(--dur-2) ease,box-shadow var(--dur-2),background var(--dur-2),color var(--dur-2);
}
.tchip:hover:not(.on){background:var(--bg-2);box-shadow:0 8px 20px var(--sh-soft)}
.tchip.on{background:var(--fill);color:#fff;box-shadow:0 9px 24px rgba(0,0,0,.35),var(--glow)}
/* The status line is what the old copy's "some semesters have no published
   content yet" was trying to say, except it says it about the semester the
   student is actually looking at, at the moment they select it. A student who
   picks an empty semester and gets an empty app otherwise has no way to tell a
   deliberate gap from a broken build. */
.tstat{
  font-size:var(--fs-md);font-weight:600;color:var(--ink-3);text-align:center;
  max-width:430px;line-height:1.6;margin:2px 0 0;min-height:20px;
}
.tstat.empty{color:var(--ink-4)}

/* Four across, not eight. A face needs room to be recognised, and eight tiles
   in a modal left each one about 44px wide — enough for a line emblem, not
   enough for a portrait. Two rows of four is the same eight choices at twice
   the size. */
.emgrid{display:grid;grid-template-columns:repeat(4,1fr);gap:8px;margin-bottom:16px}
.emb{
  aspect-ratio:1;border-radius:var(--r-lg);border:2.5px solid var(--line-2);background:var(--card);
  font-size:var(--fs-xl);display:flex;align-items:center;justify-content:center;overflow:hidden;
  transition:transform var(--dur-1) ease;
}
.emb:hover{background:var(--bg-2);border-color:var(--gold)}
.emb.on{border-color:var(--line-2);background:var(--fill);transform:translateY(-2px)}
.colrow{display:flex;gap:8px;flex-wrap:wrap;margin-bottom:16px}
.colb{width:34px;height:34px;border-radius:var(--r-lg);border:2.5px solid var(--line-2);
  transition:transform var(--dur-1) ease}
.colb:hover{background:var(--bg-2)}
.colb.on{transform:translateY(-4px);box-shadow:0 9px 22px rgba(2,6,14,.3)}
.avprev{
  width:88px;height:88px;border-radius:var(--r-lg);border:3px solid var(--line-2);margin:0 auto 16px;
  display:flex;align-items:center;justify-content:center;font-size:var(--fs-3xl);overflow:hidden;
  box-shadow:0 10px 24px rgba(0,0,0,.2);--fr-t:8px;
}
.avprev img{width:100%;height:100%;object-fit:cover}
/* A portrait fills its box at whatever size the box is, so one rule covers a
   96px gate card, an 88px editor preview, a 90px picker tile and a 32px rail
   chip. Unscoped on purpose: manageProfiles() renders a .rav outside .railprof
   and it must size the same there. */
.pav,.avprev,.rav{color:#fff}
.emb{color:var(--tx)}
.emb.on{color:#fff}
.pav img,.avprev img,.rav img,.emb img,.frtile img{width:100%;height:100%;object-fit:cover;display:block}
.emb img{border-radius:calc(var(--r-lg) - 2px)}
.pav .gi,.avprev .gi,.rav .gi{width:58%;height:58%;stroke-width:1.4}
.emb .gi{width:56%;height:56%;stroke-width:1.5}
.pinbox{
  display:flex;gap:9px;justify-content:center;margin:14px 0;
}
.pind{
  width:52px;height:60px;border:2.5px solid var(--line-2);border-radius:var(--r-lg);text-align:center;
  font-size:var(--fs-xl);font-weight:900;background:var(--card);color:var(--tx);
  box-shadow:0 5px 14px rgba(0,0,0,.18);
}
.pind:focus{border-color:var(--neuro);outline:none;box-shadow:0 7px 18px rgba(125,88,144,.4)}
.pinerr{color:var(--bad);font-weight:800;font-size:var(--fs-md);text-align:center;margin-top:4px;min-height:18px}

.railprof{
  display:flex;align-items:center;gap:10px;margin:4px 12px 8px;padding:var(--s-4) var(--s-5);border-radius:var(--r-lg);
  background:var(--rail-2);width:calc(100% - 24px);text-align:left;
  transition:background var(--dur-2);
}
.railprof:hover{background:var(--rail-3)}
.railprof .rav{
  width:32px;height:32px;border-radius:var(--r-lg);display:flex;align-items:center;justify-content:center;
  font-size:var(--fs-body);flex-shrink:0;overflow:hidden;border:2px solid rgba(255,255,255,.22);--fr-t:3px;
}
.railprof .rav img{width:100%;height:100%;object-fit:cover}
.railprof .rn{font-size:var(--fs-md);font-weight:800;color:#fff;line-height:1.2;flex:1;min-width:0;
  overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.railprof .rs{font-size:var(--fs-xs);color:var(--rail-dim);font-weight:700}

/* ---------- Frame treatments ----------
   Three ported frame styles sit alongside the flat ring, which keeps its
   original name internally ("plain") but needs no rule here at all — that
   ring is whatever border .pav/.avprev/.rav already draw, unchanged, so an
   existing profile with no av.f looks exactly as it did before this landed.
   The other three each draw inside a same-size inner wrapper instead of on
   the box's own border, sized off --fr-t (frame thickness) and --fr-c (the
   profile colour), both set by whichever render site is drawing them.
   .fr-sm is added by the two sites under ~40px, where a carved step or a
   walnut swirl stops reading as a step or a swirl and just reads as noise —
   dropped there, never drawn faint. */
.fr{display:block;width:100%;height:100%;border-radius:inherit;overflow:hidden;}

/* mount — a hairline in the app's own line token plus a mat of the profile
   colour at low alpha, the way a framer's mount sits between the moulding
   and the picture. The mat's depth holds the same two-thirds-of---fr-t
   ratio at every size, so it neither vanishes nor swallows the picture. */
.fr-mount{
  border:1px solid var(--line-2);
  padding:calc(var(--fr-t) * 2 / 3);
  background:color-mix(in srgb, var(--fr-c) 16%, transparent);
}

/* carved moulding — three wood steps plus a gilded bead, each step lit
   top/left and shadowed bottom/right with its own per-side border colour —
   no box-shadow anywhere in it. Below ~40px (.fr-sm) the steps flatten to
   one plain band, because a 1px bevel stops being a bevel at that size; the
   bead keeps a fixed width there instead of shrinking with the rest, since
   it is what carries the profile colour and must not thin into a hairline. */
.fr-moulding{
  --w1:calc(var(--fr-t) * .33);--w2:calc(var(--fr-t) * .28);
  --wb:calc(var(--fr-t) * .17);--w3:calc(var(--fr-t) * .22);
  background:#4a3319;
}
.fr-moulding span{display:block;width:100%;height:100%;box-sizing:border-box;}
.fr-moulding .b1{background:#86643a;padding:var(--w1);
  border-top:1px solid rgba(255,240,210,.5);border-left:1px solid rgba(255,240,210,.5);
  border-bottom:1px solid rgba(20,10,4,.45);border-right:1px solid rgba(20,10,4,.45);}
.fr-moulding .b2{background:#6f5029;padding:var(--w2);
  border-top:1px solid rgba(255,240,210,.42);border-left:1px solid rgba(255,240,210,.42);
  border-bottom:1px solid rgba(20,10,4,.4);border-right:1px solid rgba(20,10,4,.4);}
.fr-moulding .bead{background:var(--fr-c);padding:var(--wb);
  border-top:1px solid color-mix(in srgb, var(--fr-c) 55%, white 45%);
  border-left:1px solid color-mix(in srgb, var(--fr-c) 55%, white 45%);
  border-bottom:1px solid color-mix(in srgb, var(--fr-c) 60%, black 40%);
  border-right:1px solid color-mix(in srgb, var(--fr-c) 60%, black 40%);}
.fr-moulding .b3{background:#54391c;padding:var(--w3);
  border-top:1px solid rgba(255,240,210,.3);border-left:1px solid rgba(255,240,210,.3);
  border-bottom:1px solid rgba(20,10,4,.42);border-right:1px solid rgba(20,10,4,.42);}
.fr-moulding.fr-sm{--w1:.5px;--w2:.5px;--wb:1.5px;--w3:1px;}
.fr-moulding.fr-sm .b1,.fr-moulding.fr-sm .b2,.fr-moulding.fr-sm .b3{background:#6b4c2c;border:none;}

/* figured walnut — dark swirling wood from overlapping low-alpha radial
   gradients plus fine grain and one off-centre knot, with the profile
   colour as a thin inlay set in from the edge. Padding and inset both
   scale directly off --fr-t; below ~40px (.fr-sm) the swirl and knot stop
   being drawn but the inlay line stays, since that is what carries the
   profile colour. */
.fr-walnut{
  position:relative;background-color:#3a2416;
  background-image:
    repeating-linear-gradient(96deg, rgba(255,225,180,.10) 0 1px, transparent 1px 3px),
    repeating-linear-gradient(96deg, rgba(15,8,3,.13) 0 1px, transparent 1px 7px),
    radial-gradient(ellipse 95% 42% at 32% 28%, rgba(120,80,42,.16), transparent 62%),
    radial-gradient(ellipse 85% 38% at 68% 74%, rgba(10,5,2,.15), transparent 60%),
    radial-gradient(ellipse 110% 30% at 50% 50%, rgba(120,80,42,.10), transparent 55%),
    radial-gradient(circle at 72% 62%, rgba(12,6,2,.55) 0 1.5px, transparent 1.5px 3px),
    radial-gradient(circle at 72% 62%, rgba(12,6,2,.28) 3px 4px, transparent 4px 5px),
    linear-gradient(135deg, rgba(255,255,255,.06), transparent 45%, transparent 60%, rgba(0,0,0,.14));
  padding:var(--fr-t);
}
.fr-walnut::before{content:"";position:absolute;inset:calc(var(--fr-t) / 3);border:1px solid var(--fr-c);
  border-radius:inherit;pointer-events:none;}
.fr-walnut.fr-sm{background-image:none;background-color:#402917;}

/* the picker tile — the size the comparison page never needed, since it
   only ever showed one frame per cell. border-width stays --fr-t for every
   choice so the four tiles do not jump size as the selection changes; for
   "plain" the border carries --fr-c and IS the whole treatment, for the
   other three it goes transparent and the inner .fr span draws instead. */
.frtile{
  width:48px;height:48px;border-radius:var(--r-lg);overflow:hidden;position:relative;
  display:flex;align-items:center;justify-content:center;--fr-t:5px;
  border:var(--fr-t) solid var(--line-2);transition:transform var(--dur-1) ease;
}
.frtile:hover{border-color:var(--gold)}
.frtile.on{transform:translateY(-4px);}
.frgrid{display:flex;gap:10px;flex-wrap:wrap;margin-bottom:16px}

/* A ported frame draws its own complete edge, so wherever one is active the
   box's own border — the plain ring's 2/3px — is zeroed rather than left
   reserved as a blank gap outside it. Plain never carries this class and
   keeps the ring exactly as it always has. */
.pav.hasfr,.avprev.hasfr,.rav.hasfr,.frtile.hasfr{border-width:0}

/* ---------- Cloud sync ---------- */
.gsign{
  position:relative;overflow:hidden;
  display:flex;align-items:center;gap:11px;padding:var(--s-7) var(--s-10);border-radius:var(--r-lg);
  background:#fff;border:1px solid rgba(255,255,255,.5);font-weight:800;font-size:var(--fs-ui);color:#1f2430;
  box-shadow:0 10px 24px rgba(0,0,0,.2);transition:transform var(--dur-3) ease,box-shadow var(--dur-3);
}
.gsign:hover{background:var(--bg-2);box-shadow:0 18px 40px rgba(0,0,0,.3)}
.gsign:active{transform:translateY(-1px)}
/* a slow sheen crossing the button every few seconds. The button is white, so
   the sweep has to be a shadow rather than a light or it is invisible. */
.gsign::after{
  content:"";position:absolute;top:0;left:-60%;width:42%;height:100%;pointer-events:none;
  background:linear-gradient(100deg,transparent,rgba(60,90,140,.16),transparent);
  transform:skewX(-18deg);animation:sweep 5s ease-in-out 1.8s infinite;
}
@keyframes sweep{0%{left:-60%}24%{left:135%}100%{left:135%}}
/* the primary action when running from a file, where Google sign-in cannot work */
.gbig{font-size:var(--fs-ui);padding:var(--s-7) var(--s-11);border-radius:var(--r-lg)}
/* the cloud button in its unavailable state: shown, so the option is visible,
   but plainly inert — no pointer, no hover, no sheen */
.gsign.off{margin-top:18px;opacity:.42;cursor:not-allowed;box-shadow:none}
.gsign.off::after{animation:none;display:none}
.gsign.off:hover{box-shadow:none}
.gsign svg{width:19px;height:19px;flex-shrink:0}
.syncdot{
  display:inline-block;width:8px;height:8px;border-radius:50%;margin-right:6px;flex-shrink:0;
  background:var(--ink-4);vertical-align:1px;
}
.syncdot.ok{background:#4ade80}
.syncdot.busy{background:var(--gold);animation:blink 1s infinite}
.syncdot.err{background:var(--ent)}
.railsync{
  margin:0 12px 6px;padding:var(--s-3) var(--s-5);border-radius:var(--r-lg);background:rgba(255,255,255,.05);
  font-size:var(--fs-xs);font-weight:700;color:#b3a9dd;display:flex;align-items:center;
}
.cfgbox{
  width:100%;min-height:150px;padding:var(--s-6) var(--s-7);border:1px solid var(--line-2);border-radius:var(--r-lg);
  font-family:ui-monospace,Menlo,monospace;font-size:var(--fs-sm);background:var(--card);color:var(--tx);
  line-height:1.55;resize:vertical;
}
.steps{counter-reset:s;font-size:var(--fs-ui);line-height:1.65;color:var(--ink-2);font-weight:500}
.steps li{margin-bottom:9px}
.steps code{background:var(--bg-2);padding:var(--s-1) var(--s-3);border-radius:var(--r-md);font-size:var(--fs-sm);font-weight:700}


.splash{
  position:fixed;inset:0;z-index:210;background:var(--bg);
  display:flex;flex-direction:column;align-items:center;justify-content:center;gap:18px;
}
.splash.hide{display:none}
.splash .glogo{font-size:var(--fs-xl)}
.splash .msg{font-size:var(--fs-ui);color:var(--ink-3);font-weight:700}
@keyframes spin{to{transform:rotate(360deg)}}
.spinner{
  width:34px;height:34px;border:3.5px solid var(--line-2);border-top-color:var(--neuro);
  border-radius:50%;animation:spin .8s linear infinite;
}


/* ---------- Theory (document view) ---------- */
.th-intro{
  background:linear-gradient(135deg,rgba(46,95,138,.13),rgba(109,76,125,.15));border:2.5px solid var(--neuro);
  border-radius:var(--r-lg);padding:var(--s-8) var(--s-9);font-size:var(--fs-ui);line-height:1.68;margin-bottom:22px;
  box-shadow:var(--pop) rgba(109,76,125,.22);
}
/* ⚠️ "In this lecture" IS GONE FROM THE SCREEN TOO (user, 2026-08-19: "I don't
   need the in this lecture part I thought we changed this formula long ago").
   It was removed from PRINT on 2026-08-13 and the screen kept it — which is
   exactly why the user remembered the decision and still saw the block. The
   renderer no longer emits .th-toc, so its rules, its print rules and jumpSec()
   went with it. A section is still reachable by id: openTheoryFor() passes its
   section through go(), and bkInit() turns straight to that page. */

/* a section is now a chapter of a document, always open */
.th-sec{
  margin-bottom:34px;padding-left:var(--s-9);border-left:5px solid var(--c,var(--neuro));
  scroll-margin-top:80px;
}

/* ---------- The book reader ----------
   The user asked for a chapter to read like a BOOK rather than a long scroll
   (2026-08-13): fixed pages, page numbers, turned rather than scrolled.

   The pagination is done by CSS multi-column, not by measuring and slicing the
   DOM. The browser already knows how to flow prose into a fixed-height column
   and where it is legal to break; reimplementing that in JS would be fragile and
   would fight every table and figure. `.bk-flow` is one tall column-set as wide
   as it needs to be, and turning a page is a translateX of exactly one column
   plus one gap. Page count falls out of scrollWidth.

   ⚠️ EVERY RULE HERE IS GATED ON .bk-on, and @media print unsets the lot — a
   columned, translated, overflow-hidden container prints as ONE page and loses
   the whole chapter. The print block below is not optional. */
.bk-wrap{position:relative;--bk-max:820px}
/* ⚠️ THE PAGE GETS THE WINDOW (user, 2026-08-19: "I need when I press on the
   lecture's tab the whole pages appears and I surfe through pages not just
   separate points"). MEASURED before the change, op-glauc at 1280x860:
   461px of sheet against 399px of furniture — the page got 54 % of the window —
   and scrollHeight was 909 against an 860 window, so the turn buttons sat BELOW
   THE FOLD and a page had to be scrolled to before it could be turned. That is
   what made a chapter read as fragments: each "page" was a half-height sliver,
   and op-glauc needed 31 of them.
   Nothing below removes a word. It removes repetition and stacking:
     - the chapter title was printed TWICE, in .doc-head and again 20px lower in
       the running head, which is the one that stays;
     - the back link and the two action buttons moved into one strip beside each
       other instead of one band above the sheet and one below it.
   ⚠️ .doc-head STAYS IN THE DOM. On paper it is the cover — a full-page panel
   in the module's colour — so it is hidden for the screen only and the print
   block restores it. */
.bk-on .doc-head{display:none}
.bk-top{
  display:flex;align-items:center;gap:12px;flex-wrap:wrap;
  margin:0 auto 9px;
}
.bk-top .tb-btn{padding-left:0;margin:0;flex:0 0 auto}
.bk-acts{display:flex;gap:8px;margin-left:auto;flex-wrap:wrap}
.bk-acts .btn{padding:var(--s-3) var(--s-6);font-size:var(--fs-md)}
/* ⚠️ THE SHEET IS AN A4 PAGE (user, 2026-08-19: "I need it to appear like an
   A4 page"). The pass before this one gave the reader the window's HEIGHT and
   capped the measure at 820px, and that was still not a page: it was a wide
   landscape panel, which is what a browser window is shaped like, not what
   paper is shaped like. The box below is 210:297 exactly — set as an
   aspect-ratio so the two dimensions cannot drift apart — sized to whatever
   height the window can give it, with paper margins, a running head, a foot
   folio and a drop shadow, and the turn arrows out in the gutters where the
   edges of a page are.
   ⚠️ THE PAGE IS NOT WHITE, AND MUST NOT BE MADE WHITE. In dark mode --tx is
   near-white; a white sheet would set white type on white paper and lose the
   chapter. The A4-ness is carried by the PROPORTION, the margins, the folio and
   the shadow — never by the colour. The surface is --card, the same raised fill
   every other panel in the app uses, so both themes stay measured (CLAUDE.md
   §6) and the light theme inherits this rule unchanged.
   ⚠️ THE MARGINS ARE SET IN JS, AND PERCENTAGES ARE THE TRAP. Percentage
   padding resolves against the CONTAINING BLOCK's width — the stage, 1124px on
   a 1440 window — not against the sheet's own 510px. `padding:6.6%` therefore
   put 74px margins on a 510px page and left a 360px type block: margins wider
   than a real A4's, and they GREW when the window widened while the page did
   not. bkMeasure() sets these three from the sheet's own width instead, at
   6.8 % / 5.8 % / 3.4 % of it — 14.3mm and 12.2mm on a 210mm page, close to the
   15mm and 18mm the @page block uses on paper. */
.bk-stage{display:flex;align-items:center;justify-content:center;gap:16px}
.bk-on .bk-sheet{
  position:relative;display:flex;flex-direction:column;
  aspect-ratio:210/297;
  height:var(--bk-sheet-h,660px);
  flex:0 0 auto;
  background:var(--card);
  border:1px solid var(--line-2);
  border-radius:var(--r-md);
  padding:var(--bk-pad-t,30px) var(--bk-pad-x,35px) var(--bk-pad-b,17px);
  overflow:hidden;
}
/* ⚠️ THE CHAPTER OPENS WITH ITS OWN TITLE, AND IT LIVES INSIDE THE FLOW (user,
   2026-08-19: "with the lecture title at first like we agreed before" — the
   agreement is 391c440, 2026-08-13: "the opening ... lives INSIDE the flow as
   the first page", so it is turned to rather than scrolled past). Hiding
   .doc-head for the screen removed the title that was printed twice and took
   the opening with it; this puts the opening back and leaves the duplication
   gone.
   ⚠️ IT IS A HEAD, NOT A TITLE PAGE. The notes carry straight on underneath it
   on the same sheet — a whole page spent on a title is exactly the "one point
   in each page" the user asked not to have. break-after:avoid holds the first
   block of the chapter with it so the opening cannot end up alone.
   Screen only: on paper .doc-head is already a full-page cover, and printing
   both would open the chapter with its title twice. */
.bk-title{margin:0 0 15px;padding-bottom:var(--s-5);border-bottom:2px solid var(--line-2)}
.bk-title .eyebrow{margin-bottom:8px}
.bk-title h1{margin:0;font-size:var(--fs-xl);line-height:1.16;letter-spacing:-.012em;color:var(--tx)}
.bk-title .doc-meta{margin-top:5px;font-size:var(--fs-xs)}
.bk-on .bk-title{break-after:avoid;page-break-after:avoid}

/* The folio, at the foot of the page where a book puts it. */
.bk-foot{
  padding-top:var(--s-4);text-align:center;
  font-family:'EB Garamond',Georgia,serif;
  font-size:var(--fs-sm);font-weight:700;letter-spacing:.06em;
  color:var(--ink-4);font-variant-numeric:tabular-nums;
}
/* A narrower measure needs its indents back — the section rule was set for an
   820px column and costs 25mm of a 210mm page. */
.bk-on .th-sec{padding-left:var(--s-6);border-left-width:3px;margin-bottom:22px}
/* ⚠️ A PAGE IS SET TIGHTER THAN A SCROLLING COLUMN. 1.78 leading was chosen for
   a column you scroll, where air costs nothing; on a fixed page it costs pages,
   and the printed chapter — the thing this is meant to look like — is set at
   1.5. Meeting nearer the print figure buys about a tenth of the chapter back
   without touching the type size, which stays at 14.6px because the sheet is
   already only 510px wide and shrinking the face is the one thing that would
   make a page-shaped reader worse than the panel it replaced. */
.bk-on .th-body{line-height:1.62}
.bk-on .th-body p{margin:0 0 11px}
.bk-on .th-body ul{margin:0 0 11px}
/* The running head has a page's width, not a window's, so the section name is
   allowed to end in an ellipsis rather than wrap the head onto two lines. */
.bk-on .bk-run{font-size:var(--fs-xs);padding-bottom:var(--s-3);margin-bottom:11px;gap:10px}
.bk-on .bk-run>span{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.bk-on .bk-run>span:first-child{flex:0 0 auto}
.bk-on .bk-run .r{flex:1 1 auto;text-align:right}
.bk-on .bk-prog{max-width:var(--bk-sheet-w,520px);margin-left:auto;margin-right:auto}
.bk-on .th-intro{margin-top:0}
/* The intro opens the chapter, so it is a first page, not a tinted callout. */
.bk-on .th-intro.bk-first{
  border:none;background:none;box-shadow:none;padding:0;
}
.bk-on .bk-view{
  overflow:hidden;                    /* the window the pages slide behind */
  position:relative;
  flex:1 1 auto;min-height:0;         /* the type block of the page */
}
.bk-on .bk-flow{
  column-gap:var(--bk-gap,54px);
  column-fill:auto;                   /* fill each column fully before the next */
  height:var(--bk-h,70vh);
  transition:transform .42s var(--ease-standard);
  will-change:transform;
}
/* Small blocks stay whole; a heading never sits alone at the foot of a page.
   ⚠️ TALL blocks are NOT in this list and must not be — see bkMeasure(). An
   element with break-inside:avoid that is taller than its column overflows or
   is clipped, and clipped means the learner silently loses content. */
.bk-on .fbox,.bk-on .qfig,.bk-on .th-h{break-inside:avoid;page-break-inside:avoid}
.bk-on .th-keep{break-inside:avoid;page-break-inside:avoid}
/* ⚠️⚠️ overflow:hidden MAKES A TABLE MONOLITHIC, AND A MONOLITHIC TABLE IS
   CLIPPED RATHER THAN BROKEN. The base .th-table rule sets it to clip the corner
   cells against the border-radius; the cost was that a table taller than the
   reader's page could not fragment, so its last rows were never drawn and
   nothing on screen said so. MEASURED in op-lid: 782px and 741px tables in a
   613px page, getClientRects().length === 1, and "7. TRAUMATIC" and "⚠️ The rule
   — IT MUST BE NEVER SQUEEZED" both invisible. The corner cells carry the radius
   themselves instead, which costs nothing and clips nothing.
   ⚠️ SCREEN ONLY. The printed PDF was checked with pdftotext and holds both rows,
   so paper fragments these tables as it stands and the base rule is left alone.
   ⚠️ THE READER REPEATS A BROKEN TABLE'S COLUMN LABELS, exactly as the print
   block has done since the tables were tightened, so a table that breaks across
   a page turn keeps its headings. Rows still never split. */
.bk-on .th-table{overflow:visible}
.bk-on .th-table thead th:first-child{border-top-left-radius:var(--r-lg)}
.bk-on .th-table thead th:last-child{border-top-right-radius:var(--r-lg)}
.bk-on .th-table tbody tr:last-child td:first-child{border-bottom-left-radius:var(--r-lg)}
.bk-on .th-table tbody tr:last-child td:last-child{border-bottom-right-radius:var(--r-lg)}
.bk-on .th-table thead{display:table-header-group}
.bk-on .th-table tr{break-inside:avoid;page-break-inside:avoid}
/* A heading with nothing under it is the other half of the same fault — the
   section head and the markdown sub-heads both keep the block that follows. */
.bk-on .th-h,.bk-on .th-body .th-sub{break-after:avoid;page-break-after:avoid}
/* One line of a paragraph alone at the top or foot of a page reads as a slip. */
.bk-on .th-body p{orphans:2;widows:2}
.bk-on .th-sec{margin-bottom:26px}

/* Turning furniture */
.bk-btn{
  display:inline-flex;align-items:center;justify-content:center;
  width:44px;height:44px;border-radius:50%;cursor:pointer;
  border:1px solid var(--line-2);background:var(--card);color:var(--tx);
  box-shadow:0 1px 0 var(--sh-soft);
  transition:transform var(--dur-4) ease,background var(--dur-4) ease;
}
.bk-btn:hover:not(:disabled){background:var(--bg-2)}
.bk-btn:disabled{opacity:.32;cursor:default}
.bk-run{
  display:flex;justify-content:space-between;align-items:baseline;gap:16px;
  border-bottom:1px solid var(--line);padding-bottom:var(--s-3);margin-bottom:14px;
  font-size:var(--fs-xs);font-weight:900;letter-spacing:.12em;text-transform:uppercase;
  color:var(--ink-4);
}
.bk-run .r{color:var(--ink-3);text-transform:none;letter-spacing:.02em;font-weight:700}
/* A thin progress rule under the turning bar — how far through the chapter. */
.bk-prog{height:3px;border-radius:var(--r-md);background:var(--line);max-width:640px;margin:0 auto 6px}
.bk-prog i{display:block;height:100%;border-radius:var(--r-md);background:var(--gold);
  transition:width .42s var(--ease-standard)}
/* Click the outer thirds of the sheet to turn, as you would a page. */
.bk-on .bk-tap{position:absolute;top:0;bottom:0;width:11%;cursor:pointer;z-index:2}
.bk-on .bk-tap.l{left:0}
.bk-on .bk-tap.r{right:0}
@media (max-width:700px){
  .bk-on .bk-tap{width:16%}
  /* Arrows beside a page are a luxury of a wide window. On a phone they would
     take a third of the sheet's width, so they go under it and the page keeps
     the whole measure; the tap zones on the sheet itself are unchanged. */
  /* ⚠️ NO flex-basis ON THE SHEET. `flex:0 0 100%` is a definite width, and a
     definite width beats aspect-ratio — the page came out 347x346, square. The
     sheet keeps flex:0 0 auto and the width clamp in bkMeasure() is what stops
     it overflowing; at 375px wide it lands on exactly the row width, so the
     arrows wrap under it on their own line, which is what order:-1 is for. */
  .bk-stage{flex-wrap:wrap;gap:10px}
  .bk-on .bk-sheet{order:-1}
  .bk-stage>.bk-btn{width:40px;height:40px}
}
@media (prefers-reduced-motion:reduce){
  .bk-on .bk-flow,.bk-prog i{transition:none}
}
.th-h{display:flex;align-items:center;gap:11px;margin-bottom:13px;flex-wrap:wrap}
.th-h h2{
  margin:0;font-size:var(--fs-lg);font-weight:900;letter-spacing:-.028em;color:var(--tx);line-height:1.2;
}
.th-num{
  font-size:var(--fs-xs);font-weight:900;color:var(--c,var(--neuro));letter-spacing:.1em;
  text-transform:uppercase;
}
.wbadge{
  font-size:8.5px;font-weight:900;letter-spacing:.09em;text-transform:uppercase;padding:var(--s-1) var(--s-4);
  border-radius:var(--r-md);border:1.5px solid var(--line-2);flex-shrink:0;
}
.wbadge.must{background:var(--ent);color:#fff}
.wbadge.high{background:var(--gold);color:var(--tx)}
.wbadge.know{background:var(--bg-2);color:var(--ink-3)}
.th-body{font-size:var(--fs-ui);line-height:1.78;color:var(--ink-2)}
.th-body p{margin:0 0 14px}
.th-body strong{color:var(--tx);font-weight:800}
.th-body em{color:var(--ink-3)}
.th-body ul{margin:0 0 14px;padding-left:var(--s-9)}
.th-body li{margin-bottom:7px}
.th-body li::marker{color:var(--c,var(--neuro))}
/* Three line kinds mdBlock() now marks up. On screen they are STRUCTURAL only —
   the module colour they take in print is deliberately absent here, because
   those hues are chosen dark enough for white text to sit on them and are
   therefore poor as text on the dark ground (CLAUDE.md §6). */
.th-body .th-sub{
  margin:22px 0 10px;padding-bottom:var(--s-2);border-bottom:1px solid var(--line);
  font-family:var(--font-display),Georgia,serif;font-size:var(--fs-md);font-weight:700;
  letter-spacing:.12em;text-transform:uppercase;color:var(--tx)}
.th-body .th-sub:first-child{margin-top:0}
.th-body .src{
  margin:14px 0 0;padding-top:var(--s-3);border-top:1px solid var(--line);
  font-size:var(--fs-xs);letter-spacing:.1em;text-transform:uppercase;color:var(--ink-4)}
.th-body .xref{color:var(--ink-4);font-size:var(--fs-ui)}
.th-body .xref em{color:var(--ink-4)}
.th-body p.lead{margin-top:20px}
.th-body p.lead:first-child{margin-top:0}
.th-body blockquote{
  margin:0 0 15px;padding:var(--s-6) var(--s-8);background:var(--gold-bg);border-left:4px solid var(--gold);
  border-radius:0 var(--r-lg) var(--r-lg) 0;font-weight:700;color:var(--tx);font-size:var(--fs-ui);
}
.th-table{width:100%;border-collapse:separate;border-spacing:0;margin:4px 0 18px;font-size:var(--fs-ui);
  border:1px solid var(--line-2);border-radius:var(--r-lg);overflow:hidden}
.th-table th{background:var(--fill);color:#fff;text-align:left;padding:var(--s-5) var(--s-6);font-weight:800;
  font-size:var(--fs-xs);text-transform:uppercase;letter-spacing:.06em}
/* ⚠️ A header cell holding **bold** becomes <strong>, and `.th-body strong` sets
   it to the body ink — which is the SAME near-black as the header's own
   background, so the label vanishes. Invisible on screen and on paper both.
   Found by reading the printed PDF, not the source. */
.th-table th strong,.th-table th em{color:#fff}
.th-table td{padding:var(--s-5) var(--s-6);border-bottom:1.5px solid var(--line);vertical-align:top;line-height:1.55}
.th-table tr:last-child td{border-bottom:none}
.th-table tbody tr:nth-child(odd) td{background:rgba(255,255,255,.03)}

.flow{margin:4px 0 18px;border:2.5px dashed var(--neuro);border-radius:var(--r-lg);padding:var(--s-8) var(--s-8);background:rgba(109,76,125,.12)}
.flow-title{font-size:var(--fs-xs);font-weight:900;letter-spacing:.09em;text-transform:uppercase;color:var(--neuro);margin-bottom:14px}
.fstep{margin-bottom:9px}
.fbox{background:var(--card);border:1px solid var(--line-2);border-radius:var(--r-lg);padding:var(--s-5) var(--s-7);
  box-shadow:0 5px 14px rgba(0,0,0,.15)}
.fbox .ft{font-weight:800;font-size:var(--fs-ui);line-height:1.42}
.fbox .fn{font-size:var(--fs-md);color:var(--ink-3);margin-top:5px;line-height:1.5;font-weight:600}
.fbox.alert{background:rgba(255,90,95,.13);border-color:var(--ent);box-shadow:0 5px 14px rgba(255,90,95,.3)}
.fbox.alert .ft{color:#ff9aa2}
.fbox.dec{background:var(--gold-bg);border-color:var(--gold)}
.farrow{text-align:center;color:var(--neuro);font-size:var(--fs-body);line-height:1;margin:2px 0 7px;font-weight:900}
.fbranch{display:grid;grid-template-columns:1fr 1fr;gap:9px;margin-top:9px}
.fb{border:2px solid var(--line-2);border-radius:var(--r-lg);padding:var(--s-4) var(--s-6);font-size:var(--fs-md);font-weight:700;line-height:1.45}
.fb.y{background:rgba(0,168,107,.14);border-color:var(--ok)}
.fb.n{background:rgba(255,71,87,.13);border-color:var(--bad)}
.fb b{display:block;font-size:var(--fs-xs);letter-spacing:.09em;text-transform:uppercase;margin-bottom:4px}
.fb.y b{color:var(--ok)}
.fb.n b{color:var(--bad)}
@media(max-width:600px){.fbranch{grid-template-columns:1fr}}

.th-test{display:flex;align-items:center;gap:10px;flex-wrap:wrap;margin-top:4px}
.th-test .lbl{font-size:var(--fs-sm);font-weight:800;color:var(--ink-3)}
.readtheory{
  display:flex;align-items:center;gap:9px;width:100%;text-align:left;margin-top:16px;
  padding:var(--s-6) var(--s-8);background:linear-gradient(135deg,rgba(46,95,138,.13),rgba(109,76,125,.15));
  border:2.5px solid var(--neuro);border-radius:var(--r-lg);font-weight:800;font-size:var(--fs-ui);
  box-shadow:var(--pop) rgba(125,88,144,.3);
  transition:transform var(--dur-2) ease,box-shadow var(--dur-2);
}
.readtheory:hover{background:var(--bg-2);box-shadow:var(--pop-lg) rgba(125,88,144,.35)}
.th-ch{display:flex;align-items:center;gap:13px;padding:var(--s-7) var(--s-8);background:var(--card);
  border:1px solid var(--line-2);border-radius:var(--r-lg);margin-bottom:9px;width:100%;text-align:left;
  box-shadow:0 5px 14px rgba(0,0,0,.13);transition:transform var(--dur-2) ease,box-shadow var(--dur-2)}
.th-ch:hover:not(:disabled){background:var(--bg-2);box-shadow:0 10px 24px rgba(0,0,0,.2)}
.th-ch:disabled{opacity:.4;cursor:default;box-shadow:none;border-style:dashed}
.th-ch .n{flex:1;font-weight:700;font-size:var(--fs-ui)}
.th-ch .m{font-size:var(--fs-sm);color:var(--ink-4);font-weight:700}
.doc-head{border-bottom:3px solid var(--line-2);padding-bottom:var(--s-7);margin-bottom:22px}
.doc-meta{font-size:var(--fs-md);color:var(--ink-3);font-weight:700;margin-top:6px}
