/* Colours, sizes, radii, shadows and durations come from tokens.css (loaded first). */
* { box-sizing: border-box; }
[hidden] { display: none !important; }   /* the hidden attribute must win over element rules such as label { display: block } */
body { margin: 0; background: var(--bg); color: var(--ink); font: var(--fw) var(--fs-md)/var(--lh) system-ui, -apple-system, "Segoe UI", Roboto, sans-serif; padding-inline: var(--s4); padding-block: 0 var(--s5); }
a { color: var(--accent); }
h1 { font-size: var(--fs-xl); font-weight: var(--fw-b); line-height: var(--lh-t); margin: var(--s4) 0 var(--s2); }
h2 { font-size: var(--fs-md); font-weight: var(--fw-b); margin: var(--s4) 0 var(--s2); }
h3, h4 { font-size: var(--fs-md); font-weight: var(--fw-b); }
small, code, kbd, samp, pre { font-size: var(--fs-sm); }
b, strong { font-weight: var(--fw-b); }
.muted { color: var(--muted); }
.small { font-size: var(--fs-sm); }
.skip { position: absolute; left: -999px; }
.skip:focus { left: var(--s2); top: var(--s2); background: var(--surface); padding: var(--s2) var(--s3); z-index: 10; }

.top, main, .foot { max-width: var(--content); margin-inline: auto; }
.top { display: flex; align-items: center; gap: var(--s4); flex-wrap: wrap; padding: var(--s3) 0; border-bottom: 1px solid var(--line); position: sticky; top: 0; background: var(--bg); z-index: 5; }
.brand { display: flex; align-items: center; gap: var(--s2); font-weight: var(--fw-b); font-size: var(--fs-md); }
.wordmark { height: 28px; width: auto; display: block; color: var(--ink); }
.brand a.brandlink { display: inline-flex; color: var(--ink); }
.tabs { display: flex; gap: var(--s1); flex-wrap: wrap; }
.tab { border: 0; background: transparent; color: var(--muted); padding: var(--s2) var(--s3); border-radius: var(--r-pill); cursor: pointer; font: inherit; }
.tab[aria-selected="true"] { background: var(--surface); color: var(--ink); box-shadow: var(--e1); font-weight: var(--fw-b); }
.right { margin-left: auto; display: flex; gap: var(--s2); align-items: center; }
.icon { position: relative; border: 1px solid var(--line); background: var(--surface); border-radius: var(--r-pill); width: var(--tap); height: var(--tap); cursor: pointer; font-size: var(--fs-md); }
.badge { position: absolute; top: calc(var(--s1) * -1.5); right: calc(var(--s1) * -1.5); background: var(--bad); color: var(--on-accent); border-radius: var(--r-pill); font-size: var(--fs-xs); font-weight: var(--fw-b); min-width: 18px; height: 18px; line-height: 18px; padding: 0 var(--s1); }
select, input, textarea { font: inherit; color: var(--ink); background: var(--surface); border: 1px solid var(--field-line, var(--off)); border-radius: var(--r-s); padding: var(--s2); max-width: 100%; }
label { display: block; margin: var(--s2) 0; }
label > span { display: block; font-size: var(--fs-sm); color: var(--muted); margin-bottom: var(--s1); }
label.inline { display: flex; align-items: center; gap: var(--s2); }
label.inline > span { display: inline; margin: 0; color: var(--ink); font-size: var(--fs-md); }

.banner { margin: var(--s3) 0 0; padding: var(--s2) var(--s3); border-radius: var(--r-s); background: var(--accent-soft); font-size: var(--fs-sm); }
.banner:empty { display: none; }
.banner .btn.primary { background: var(--surface); color: var(--ink); border-color: var(--line); font-weight: var(--fw); }   /* one filled button per screen */
#mode { max-width: 960px; margin-inline: auto; }
.btn { display: inline-flex; align-items: center; gap: var(--s2); border: 1px solid var(--line); background: var(--surface); color: var(--ink); padding: var(--s2) var(--s4); border-radius: var(--r-s); cursor: pointer; font: inherit; text-decoration: none; transition: background-color var(--d-fast) var(--ease-std), transform var(--d-fast) var(--ease-std); }
.btn.primary { background: var(--fill); color: var(--on-accent); border-color: var(--fill); font-weight: var(--fw-b); }
.btn.danger { color: var(--bad); border-color: color-mix(in srgb, var(--bad) 40%, var(--line)); }
@media (hover: hover) {
  .btn:hover:not([disabled]) { background: color-mix(in srgb, var(--ink) 6%, var(--surface)); }
  .btn.primary:hover:not([disabled]) { background: color-mix(in srgb, var(--ink) 12%, var(--fill)); }
}
.btn:active:not([disabled]) { transform: scale(.98); }
/* disabled and busy buttons stay readable (no dimming) */
.btn[disabled], .btn[aria-busy="true"] { opacity: 1; color: var(--muted); background: var(--bg); cursor: default; }
.btn.primary[disabled], .btn.primary[aria-busy="true"] { background: var(--accent-soft); color: color-mix(in srgb, var(--accent) 75%, var(--ink)); border-color: var(--accent); }
.btn[aria-busy="true"] { cursor: progress; }
.row { display: flex; align-items: center; margin: var(--s3) 0; }
.gap { gap: var(--s3); }
.wrap { flex-wrap: wrap; }

.cards { display: grid; grid-template-columns: repeat(auto-fill, minmax(230px, 1fr)); gap: var(--s3); }
.card { background: var(--surface); border: 1px solid var(--line); border-radius: var(--r-m); padding: var(--s3) var(--s4); box-shadow: var(--e1); }
.card .when { font-size: var(--fs-lg); font-weight: var(--fw-b); }
.card .sub { color: var(--muted); font-size: var(--fs-sm); }
.card .bar { height: 6px; border-radius: var(--r-pill); background: var(--line); margin-top: var(--s2); overflow: hidden; }
.card .bar > i { display: block; height: 100%; background: var(--accent); }
.chip { border: 1px solid var(--line); background: var(--surface); color: var(--ink); padding: var(--s2) var(--s3); border-radius: var(--r-pill); cursor: pointer; font: inherit; white-space: nowrap; max-width: 100%; overflow: hidden; text-overflow: ellipsis; min-height: var(--tap); }
/* a long test name must never make the page wider than a phone (round 3: the page was 494 px on a 390 px phone) */
.chip[aria-pressed="true"] { background: var(--accent-soft); color: var(--ink); border-color: var(--accent); font-weight: var(--fw-b); }
.focusCard { background: var(--surface); border: 1px solid var(--line); border-radius: var(--r-m); padding: var(--s4); margin: var(--s4) 0; box-shadow: var(--e1); }
.gradeRow { margin: var(--s3) 0; max-width: none; width: 100%; }
.gradeSlider { display: grid; gap: var(--s1); }
.gradeHead { display: flex; justify-content: space-between; align-items: baseline; color: var(--muted); font-weight: var(--fw-b); }
.gradeHead output { font-size: var(--fs-xl); font-weight: var(--fw-b); color: var(--accent); line-height: var(--lh-t); }
.gradeEnds { display: flex; justify-content: space-between; color: var(--muted); font-size: var(--fs-xs); }
.gradeEnds > span { color: var(--muted); }
#gradeSlider { -webkit-appearance: none; appearance: none; width: 100%; height: var(--tap); margin: 0; padding: 0; border: 0; box-shadow: none; background: transparent; cursor: pointer; }
#gradeSlider::-webkit-slider-runnable-track { height: 8px; border-radius: var(--r-pill); background: linear-gradient(to right, var(--accent) var(--fill, 50%), var(--line) var(--fill, 50%)); }
#gradeSlider::-moz-range-track { height: 8px; border-radius: var(--r-pill); background: var(--line); }
#gradeSlider::-moz-range-progress { height: 8px; border-radius: var(--r-pill); background: var(--accent); }
#gradeSlider::-webkit-slider-thumb { -webkit-appearance: none; appearance: none; width: 28px; height: 28px; margin-top: -10px; border-radius: var(--r-pill); background: var(--surface); border: 3px solid var(--accent); box-shadow: var(--e1); transition: none; }
#gradeSlider:not(:active)::-webkit-slider-thumb { transition: transform var(--d-fast) var(--ease-std); }   /* a short ease only for keyboard steps, never while dragging */
#gradeSlider::-moz-range-thumb { width: 18px; height: 18px; border-radius: var(--r-pill); background: var(--surface); border: 3px solid var(--accent); box-shadow: var(--e1); }
#gradeSlider:active::-webkit-slider-thumb { transform: scale(1.15); }
.progressLine { font-weight: var(--fw-b); margin: var(--s2) 0 2px; }
.bar { height: 8px; border-radius: var(--r-pill); background: var(--line); margin-top: var(--s1); overflow: hidden; }
.bar > i { position: relative; display: block; height: 100%; width: 0; overflow: hidden; border-radius: var(--r-pill); background: var(--accent); }
/* the new fill: full width scaled by --p (0..1), only transform moves */
.bar > i[style*="--p"] { width: 100%; transform: scaleX(var(--p, 0)); transform-origin: left; transition: transform var(--d-base) var(--ease-std), background-color var(--d-base) var(--ease-std); }
.bar[data-full] > i { background: var(--ok); animation: none; }
/* the sheen plays only while real work runs (.is-busy), twice at most, never on an empty or full bar */
.bar.is-busy > i::after { content: ""; position: absolute; inset: 0; width: 45%; background: linear-gradient(90deg, transparent, color-mix(in srgb, var(--on-accent) 50%, transparent), transparent); animation: barSheen 1.3s var(--ease-std) 2; }
.bar.is-busy[data-full] > i::after, .bar.is-busy > i[style*="--p: 0;"]::after, .bar.is-busy > i[style*="--p:0;"]::after { display: none; }
@keyframes barSheen { from { transform: translateX(-120%); } to { transform: translateX(320%); } }
.taskDone { color: var(--muted); }
.taskDone .taskText { color: var(--muted); text-decoration: line-through; }
.taskDone:not(:has(.taskText)) { text-decoration: line-through; }   /* the old rows have no .taskText yet */
.taskSub { font-size: var(--fs-sm); color: var(--muted); }

.list { list-style: none; padding: 0; margin: 0; display: grid; gap: var(--s2); }
.list li { background: var(--surface); border: 1px solid var(--line); border-radius: var(--r-s); padding: var(--s3); display: grid; grid-template-columns: 112px minmax(0, 1fr) auto; gap: var(--s3); align-items: center; }
.list .date { font-weight: var(--fw-b); }
.list .date small { display: block; font-weight: var(--fw); color: var(--muted); }
.list .title { font-weight: var(--fw-b); }
.list .meta { color: var(--muted); font-size: var(--fs-sm); }
.list .acts { display: flex; align-items: center; align-self: center; gap: var(--s2); flex-wrap: nowrap; justify-content: flex-end; }
.list .acts .btn { padding: var(--s2) var(--s3); font-size: var(--fs-sm); }
.list.grey .title, .list.grey .meta, .list.grey .date { color: var(--muted); }   /* finished rows: muted text, never dimmed (contrast stays >= 4.5:1) */
.completed { margin-top: var(--s4); }
.completed summary { cursor: pointer; font-weight: var(--fw-b); margin-bottom: var(--s2); }
.count { background: var(--line); color: var(--ink); border-radius: var(--r-pill); padding: 1px var(--s2); font-size: var(--fs-xs); }
.tag { display: inline-block; border-radius: var(--r-pill); padding: 1px var(--s2); font-size: var(--fs-xs); background: var(--line); color: var(--ink); }
.tag.estimated { background: var(--warn-soft); color: var(--warn); }
.form { background: var(--surface); border: 1px solid var(--line); border-radius: var(--r-m); padding: var(--s3) var(--s4); margin: var(--s3) 0; display: grid; grid-template-columns: repeat(auto-fit, minmax(160px, 1fr)); gap: var(--s2) var(--s3); }
.form .row { grid-column: 1 / -1; }

.notice, .noticeCalm { background: var(--surface); border: 1px solid var(--line); border-radius: var(--r-s); padding: var(--s3) var(--s4); margin: var(--s3) 0; box-shadow: none; }
.notice.warn { background: var(--warn-soft); border-color: color-mix(in srgb, var(--warn) 40%, var(--line)); }
.qrBox { display: flex; justify-content: center; margin: var(--s2) 0; }
.qrBox img { background: #fff; padding: var(--s2); border-radius: var(--r-s); image-rendering: pixelated; max-width: 100%; height: auto; }   /* a QR code needs a white background, also in dark mode */
.calGroup { margin: var(--s4) 0; }
.calGroup h3 { margin: var(--s2) 0; font-size: var(--fs-md); }
.calGroup .list li { grid-template-columns: 112px minmax(0, 1fr) auto; }
/* By course rows: title, then the date line, then the Study button (no kind chip: By date already shows it) */
#calendarResults .list li { grid-template-columns: minmax(0, 1fr) auto; }
#calendarResults .list li > .tag { display: none; }
#calendarResults .list li b { display: block; }
#calendarResults .list li .meta { display: block; }
.tag.close { background: var(--bad-soft); color: var(--bad); }
.tag.due { background: var(--warn-soft); color: var(--warn); }
.tag.open { background: var(--ok-soft); color: var(--ok); }
.tableWrap { overflow-x: auto; background-color: var(--surface); border-radius: var(--r-s);
  /* a 12 px fade on the right edge, only while there is more to scroll (the local layer covers it at the end) */
  background-image: linear-gradient(to left, var(--surface) 30%, transparent), linear-gradient(to left, color-mix(in srgb, var(--ink) 14%, transparent), transparent);
  background-position: 100% 0, 100% 0; background-size: 24px 100%, 12px 100%; background-repeat: no-repeat; background-attachment: local, scroll; }
.table { width: 100%; border-collapse: collapse; background: transparent; border: 1px solid var(--line); border-radius: var(--r-s); }
.table th, .table td { text-align: left; padding: var(--s2) var(--s3); border-bottom: 1px solid var(--line); vertical-align: top; }
.table th { color: var(--muted); font-weight: var(--fw-b); font-size: var(--fs-xs); }
.v { display: inline-block; border-radius: var(--r-pill); padding: 1px var(--s2); font-size: var(--fs-sm); white-space: nowrap; }
.v-used, .v-checked, .v-cited { background: var(--ok-soft); color: var(--ok); }   /* one colour for every kind of used */
.v-skip, .v-seen { background: var(--line); color: var(--muted); }
.table td { transition: background-color var(--d-fast) var(--ease-std); }
.is-flash > td, .is-flash { background-color: var(--accent-soft); }
.v-unreachable { background: var(--bad-soft); color: var(--bad); }
.v-not-yet { background: var(--warn-soft); color: var(--warn); }

.grid2, .settingsCol { display: grid; gap: var(--s4); align-content: start; align-items: start; grid-template-columns: minmax(0, 1fr); }   /* one column inside main's 640 px; cards size to their content */
.setCard, fieldset, #dangerBox { background: var(--surface); border: 1px solid var(--line); border-radius: var(--r-m); padding: var(--s4); box-shadow: var(--e1); margin: 0; min-width: 0; }
.setCard h2, fieldset > legend, #dangerBox > summary { font-size: var(--fs-md); font-weight: var(--fw-b); margin: 0 0 var(--s2); padding: 0; }
#dangerBox > summary { cursor: pointer; display: flex; align-items: center; min-height: var(--tap); margin: 0; }
#dangerBox[open] > summary { margin-bottom: var(--s2); }
#dangerBox .btn.danger { border: 0; background: transparent; color: var(--bad); padding-inline: 0; box-shadow: none; }
.setRow { min-height: var(--tap); margin: 0 0 var(--s3); }   /* older markup: toggle line with help text below */
.setRow:not(:has(> p)) { display: flex; align-items: center; justify-content: space-between; gap: var(--s4); }
.setRow > .setText { flex: 1 1 auto; min-width: 0; }
.setRow > .switch { flex: none; }
.switch { display: inline-flex; align-items: center; cursor: pointer; }
.switch input { position: absolute; opacity: 0; }
.switch span { width: 44px; height: 26px; border-radius: var(--r-pill); background: var(--off); position: relative; transition: background-color var(--d-fast) var(--ease-std); }
.switch span::after { content: ""; position: absolute; top: 4px; left: 4px; width: 18px; height: 18px; border-radius: var(--r-pill); background: var(--on-accent); transform: translateX(0); transition: transform var(--d-fast) var(--ease-std); }
.switch input:checked + span { background: var(--ok); }
.switch input:checked + span::after { transform: translateX(18px); }
.switch input:focus-visible + span { outline: var(--focus); outline-offset: 2px; }

.drawer { position: fixed; top: 64px; right: var(--s4); width: min(380px, calc(100vw - 32px)); max-height: 70vh; overflow: auto; background: var(--surface); border: 1px solid var(--line); border-radius: var(--r-m); padding: var(--s3) var(--s4); box-shadow: var(--e2); z-index: 6; }
.drawer > .row:first-child { justify-content: space-between; margin: 0 0 var(--s2); }
.drawer > .row:first-child h2 { margin: 0; }
.drawer > .row:first-child .btn { width: var(--tap); padding: 0; justify-content: center; }
.drawer .list li { grid-template-columns: 1fr; }
.drawer[data-open="0"] { opacity: 0; transform: translateY(-6px); visibility: hidden; pointer-events: none; transition: opacity var(--d-fast) var(--ease-in), transform var(--d-fast) var(--ease-in), visibility var(--d-fast); }
.drawer[data-open="1"] { opacity: 1; transform: none; visibility: visible; transition: opacity var(--d-base) var(--ease-out), transform var(--d-base) var(--ease-out), visibility var(--d-base); }
.fab { position: fixed; right: var(--s4); bottom: var(--s4); width: var(--tap); height: var(--tap); border-radius: var(--r-pill); border: 1px solid var(--line); background: var(--surface); color: var(--ink); font-size: var(--fs-lg); line-height: 1; padding: 0; cursor: pointer; box-shadow: var(--e1); z-index: 6; }   /* quiet: the one filled button of a screen is the primary action */
dialog { border: 1px solid var(--line); border-radius: var(--r-m); background: var(--surface); color: var(--ink); padding: var(--s4); width: min(480px, calc(100vw - var(--s6))); margin: 10vh auto auto; box-shadow: var(--e2); opacity: 1; transform: none;
  transition: opacity var(--d-base) var(--ease-out), transform var(--d-base) var(--ease-out), overlay var(--d-base) allow-discrete, display var(--d-base) allow-discrete; }
dialog:not([open]) { opacity: 0; transform: translateY(8px); transition-duration: var(--d-fast); transition-timing-function: var(--ease-in); }
@starting-style { dialog[open] { opacity: 0; transform: translateY(8px); } }
dialog::backdrop { background: var(--scrim); opacity: 1; transition: opacity var(--d-base) var(--ease-out), overlay var(--d-base) allow-discrete, display var(--d-base) allow-discrete; }
dialog:not([open])::backdrop { opacity: 0; transition-duration: var(--d-fast); transition-timing-function: var(--ease-in); }
@starting-style { dialog[open]::backdrop { opacity: 0; } }
dialog textarea { width: 100%; }
dialog textarea:focus-visible { outline: var(--focus); outline-offset: 2px; }
dialog form { display: grid; gap: var(--s3); }
dialog form > * { margin-top: 0; margin-bottom: 0; }
dialog .row { display: flex; flex-direction: row; flex-wrap: wrap; gap: var(--s2); }
#recoveryCode { display: flex; flex-wrap: wrap; gap: var(--s2) var(--s3); font-family: ui-monospace, Menlo, Consolas, monospace; font-size: var(--fs-sm); word-break: normal; overflow-wrap: normal; }
#recoveryCode span { white-space: nowrap; }
body.dlg-open .tg-layer, body.dlg-open .tw-hint { display: none; }
.foot { margin-top: var(--s6); padding-top: var(--s3); border-top: 1px solid var(--line); color: var(--muted); font-size: var(--fs-sm); }

@media (max-width: 560px) {
  h1 { font-size: var(--fs-lg); }
  .tabs { order: 3; width: 100%; justify-content: space-between; }
  .tab { padding: var(--s2); font-size: var(--fs-sm); }
  .list li { grid-template-columns: minmax(0, 1fr); }
  .list li > .date { grid-column: 1 / -1; display: flex; gap: var(--s2); align-items: baseline; }
  .list .acts { justify-content: flex-end; flex-wrap: wrap; }
  #calendarResults .list li { grid-template-columns: minmax(0, 1fr) auto; }
  .table td:first-child .small { display: none; }
}

.upd { display: flex; gap: var(--s3); align-items: center; flex-wrap: wrap; }
.upd [hidden] { display: none; }
.upd.stale { background: var(--warn-soft); }

/* one message at a time: the welcome cards never sit on top of a guide label (the guide layer waits until they are closed) */
body.tw-open .tg-layer { display: none; }
/* the trial notice, the age read-back and the "next test" card use the shared notice look */
#ageConfirm .row { margin: var(--s2) 0 0; }
.nextCard .title { font-weight: var(--fw-b); margin: 0 0 var(--s2); }
/* a test is learnt: a check mark that draws itself (circle 400 ms, then the check 300 ms after 250 ms: 550 ms in all, once), the words and the button that finishes the test */
.learntBox { display: flex; align-items: center; gap: var(--s3); flex-wrap: wrap; margin: var(--s3) 0; padding: var(--s3); border-radius: var(--r-m); background: var(--ok-soft); color: var(--ok); border: 1px solid color-mix(in srgb, var(--ok) 30%, transparent); }
.learntBox b { flex: 1 1 160px; color: var(--ink); }
.learntMark circle { stroke-dasharray: 150; }
.learntMark path { stroke-dasharray: 40; }
.learntBox.play .learntMark circle { animation: learntCircle 400ms var(--ease-out) both; animation-iteration-count: 1; }
.learntBox.play .learntMark path { animation: learntCheck 300ms 250ms var(--ease-out) both; animation-iteration-count: 1; }
@keyframes learntCircle { from { stroke-dashoffset: 150; } to { stroke-dashoffset: 0; } }
@keyframes learntCheck { from { stroke-dashoffset: 40; } to { stroke-dashoffset: 0; } }
/* the whole mark pops once; a box that already played (.seen) shows the finished mark, still */
.learntBox.play .learntMark { animation: ts-pop 180ms var(--ease-out) 1 both; }
.learntBox.seen { animation-name: none; }
.learntBox.seen .learntMark, .learntBox.seen .learntMark circle, .learntBox.seen .learntMark path { animation: none; stroke-dasharray: none; }
li.task input[type="checkbox"]:checked { animation: ts-pop 180ms var(--ease-out) 1; }
@media (prefers-reduced-motion: reduce) { .learntBox.play .learntMark, .learntBox.play .learntMark circle, .learntBox.play .learntMark path { animation: none; stroke-dasharray: none; } li.task input[type="checkbox"]:checked { animation: none; } }
/* notification rows are real buttons */
.notifRow { display: block; min-height: var(--tap); width: 100%; text-align: left; background: none; border: 0; padding: 0; font: inherit; color: inherit; cursor: pointer; }
.notifRow .meta { display: block; }
.notifRow:focus-visible { border-radius: var(--r-s); }

/* Get started steps (guide.css draws the rest): readable marks and titles, no dimming of a whole row */
li.step.is-todo, li.step.is-done { opacity: 1; }
li.step.is-todo .stepTitle { color: var(--muted); }
li.step.is-done .stepTitle { color: var(--muted); }
li.step.is-done .stepMark { background: var(--ok); border-color: var(--ok); color: var(--on-accent); }

/* ---- shell-css B2-01: tap targets, wrapping, phone layout (bottom bar) ---- */
.btn, .chip, select, input:not([type="checkbox"], [type="radio"], [type="range"]), summary, label.inline, .list .acts .btn { min-height: var(--tap); }
input[type="checkbox"], input[type="radio"] { width: 24px; height: 24px; margin: 0; flex: none; }
.switch { min-width: var(--tap); min-height: var(--tap); padding-block: 8px; }
.table td { overflow-wrap: anywhere; }
.taskText, .list .title, .list .meta { overflow-wrap: break-word; hyphens: manual; }   /* a word is cut only when it cannot fit a line alone */
.tab { min-height: var(--tap); transition: background-color var(--d-fast) var(--ease-std); }
.tab:active { background: var(--accent-soft); }
.tabs { flex-wrap: nowrap; }
.top { max-width: 960px; flex-wrap: nowrap; }
.syncSlot { position: fixed; left: var(--s3); bottom: var(--s4); z-index: 6; max-width: calc(100vw - 24px); display: flex; align-items: center; gap: var(--s2); padding: var(--s1) var(--s3); background: var(--surface); border: 1px solid var(--line); border-radius: var(--r-pill); box-shadow: var(--e1); pointer-events: none; transition: opacity var(--d-base) var(--ease-std); }   /* a solid pill, never text drawn over the page */
.syncSlot span { font-size: var(--fs-sm); }
select { padding-right: var(--s5); }
#lang { min-width: 8.5em; }   /* the longest language name fits */
.syncSlot .btn { pointer-events: auto; }
.syncSlot:has(#syncMsg:empty):not(:has(button:not([hidden]))) { opacity: 0; }
@media (max-width: 640px), (pointer: coarse) {
  header.top { position: static; min-height: var(--header-h); padding: 0; gap: var(--s2); flex-wrap: nowrap; max-width: var(--content); }
  .top .brand { margin-right: auto; }
  .right { margin-left: 0; }
  .icon { width: var(--tap); height: var(--tap); }
  .right select { min-height: var(--tap); max-width: none; }
  nav.tabs { position: fixed; left: 0; right: 0; bottom: 0; z-index: 7; display: grid; grid-auto-flow: column; grid-auto-columns: 1fr; gap: 0; order: 0; width: auto; height: calc(var(--bar-h) + var(--safe-b)); padding-bottom: var(--safe-b); background: var(--surface); border-top: 1px solid var(--line); }
  nav.tabs .tab { display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 2px; height: var(--bar-h); min-height: var(--bar-h); min-width: 0; padding: 0 var(--s1); border-radius: 0; box-shadow: none; font-size: var(--fs-xs); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
  nav.tabs .tab[aria-selected="true"] { color: var(--accent); background: var(--accent-soft); }
  main { padding-bottom: var(--s4); }
  .foot { padding-bottom: calc(var(--bar-h) + 72px + var(--safe-b)); }   /* the last line scrolls clear of the bar and the chat button */
  .fab { width: var(--tap); height: var(--tap); bottom: calc(var(--bar-h) + var(--s3) + var(--safe-b)); }
  .syncSlot { left: 50%; translate: -50% 0; bottom: calc(var(--bar-h) + var(--s3) + var(--safe-b)); max-width: calc(100vw - 144px); }
  .syncSlot span { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
  .toast { bottom: calc(var(--bar-h) + var(--s3) + var(--safe-b)); }
}

/* ---- shell-css B2-02: skeleton, onboarding focus, step list ---- */
html:not([data-strings="ready"]) #main, html:not([data-strings="ready"]) .foot, html:not([data-strings="ready"]) #aiPanel { visibility: hidden; }
html[data-strings="ready"] #skeleton { display: none; }
.skeleton { max-width: var(--content); margin-inline: auto; animation: ts-fade-in var(--d-base) var(--ease-out) both; animation-iteration-count: 1; }
.skel-line { width: 40%; height: 28px; margin: var(--s4) 0 var(--s3); background: var(--line); border-radius: var(--r-m); }
.skel-card { height: 96px; margin: var(--s3) 0; background: var(--line); border-radius: var(--r-m); }
main[data-onboard="account"] :is(#mode, #testPicker, #focusCard, #aiPanel, #mathPanel, #noTestMsg, #noTestActions, #nextCard),
main[data-onboard="import"] :is(#mode, #testPicker, #focusCard, #aiPanel, #mathPanel, #noTestMsg, #noTestActions, #nextCard) { display: none; }
.startSteps { list-style: none; margin: var(--s3) 0 0; padding: 0; display: grid; gap: var(--s2); }
li.step { background: var(--surface); border: 1px solid var(--line); border-radius: var(--r-m); padding: var(--s3) var(--s4); }
li.step.is-current { border-color: var(--accent); }
li.step.is-done .stepBody { display: none; }
.stepHead { display: flex; align-items: center; gap: var(--s3); }
.stepMark { flex: none; width: 28px; height: 28px; border-radius: var(--r-pill); border: 2px solid var(--line); display: inline-flex; align-items: center; justify-content: center; font-weight: var(--fw-b); font-size: var(--fs-sm); }
li.step.is-current .stepMark { background: var(--accent); border-color: var(--accent); color: var(--on-accent); }
.stepTitle { font-weight: var(--fw-b); }
.stepNow { margin-inline-start: auto; font-size: var(--fs-xs); font-weight: var(--fw-b); color: var(--accent); }
.stepBody { margin: var(--s2) 0 0; margin-inline-start: 40px; }
#startFold, .stepFold { border: 0; background: none; padding: 0; font: inherit; color: var(--accent); text-decoration: underline; cursor: pointer; min-height: var(--tap); }   /* the real fold line is #startFold */
.signInSent { padding: var(--s3) var(--s4); border: 1px solid var(--line); border-radius: var(--r-s); background: var(--surface); }
.signInSent .signInSentText { margin: 0 0 var(--s2); overflow-wrap: anywhere; }
.signInSent .row { margin: 0; }

/* ---- shell-css B2-03: Today card, task rows, sticky progress, grade control ---- */
.list li.task, li.task { display: flex; align-items: stretch; gap: var(--s3); padding: 0; min-height: 56px; background: none; border: 0; border-bottom: 1px solid var(--line); border-radius: 0; }
li.task > label.inline { flex: 1; min-width: 0; min-height: 56px; align-items: center; margin: 0; padding-block: var(--s3); }
li.task input[type="checkbox"] { flex: none; width: 24px; }
.taskText { flex: 1; min-width: 0; }
.taskSub { display: block; font-size: var(--fs-xs); color: var(--muted); }
li.task .tag { align-self: center; }
#testMeta .title { font-size: var(--fs-lg); font-weight: var(--fw-b); line-height: var(--lh-t); }
#testMeta .meta { font-size: var(--fs-sm); color: var(--muted); margin-top: var(--s1); }
.progressSticky { position: sticky; top: 0; z-index: 4; background: var(--surface); padding: var(--s2) 0; }
.progressSticky .progressLine { font-size: var(--fs-sm); font-weight: var(--fw-b); margin: 0; }
.progressSticky .bar { height: 6px; }
@media (min-width: 641px) and (pointer: fine) { .progressSticky { top: var(--header-h); } }
.gradeHead { justify-content: space-between; align-items: center; gap: var(--s4); }   /* label left, [-] 8 [+] right */
.gradeBig { display: inline-flex; align-items: center; gap: var(--s3); }
.gradeHead output { font-size: var(--fs-xl); min-width: 2ch; text-align: center; }
.gradeStep { flex: none; display: inline-flex; align-items: center; justify-content: center; width: var(--tap); height: var(--tap); min-height: var(--tap); padding: 0; border: 1px solid var(--line); background: var(--surface); color: var(--ink); cursor: pointer; border-radius: var(--r-pill); font: inherit; font-size: var(--fs-lg); transition: background-color var(--d-fast) var(--ease-std), transform var(--d-fast) var(--ease-std); }
.gradeStep:active:not([aria-disabled="true"]) { transform: scale(.96); }
.gradeStep[aria-disabled="true"] { color: var(--muted); cursor: default; }
@media (hover: hover) { .gradeStep:hover:not([aria-disabled="true"]) { background: color-mix(in srgb, var(--ink) 6%, var(--surface)); } }
/* the grade number is the button that opens the slider (R3-cd-3): a full tap target, plain number, a quiet underline says it opens */
.gradeNum { flex: none; display: inline-flex; align-items: center; justify-content: center; min-width: var(--tap); min-height: var(--tap); padding: 0 var(--s2); border: 0; background: none; color: var(--ink); font: inherit; font-weight: 600; cursor: pointer; border-radius: var(--r-s); text-decoration: underline; text-decoration-color: var(--line); text-underline-offset: 4px; }
.gradeNum[aria-expanded="true"] { text-decoration-color: currentColor; }
#nextGradeHint { font-size: var(--fs-sm); color: var(--muted); }
#testPicker { display: flex; flex-wrap: wrap; gap: var(--s2); }

/* ---- the toast (shell-js) ---- */
/* loop L04-8: a quiet card of the page's own surface with a thin line (the inverted box was a white block over the dark page); the light theme keeps the inverse look. The text takes the room, Undo and Close stay at the end, two lines at most. */
.toast { position: fixed; left: 50%; bottom: var(--s5); transform: translateX(-50%); z-index: 8; display: flex; align-items: center; gap: var(--s2); max-width: min(520px, calc(100vw - 24px)); padding: var(--s2) var(--s3) var(--s2) var(--s4); background: var(--surface); color: var(--ink); border: 1px solid var(--line); border-radius: var(--r-s); box-shadow: var(--e2); font-size: var(--fs-sm); animation: toastIn var(--d-base) var(--ease-out) both; animation-iteration-count: 1; }
html[data-theme="light"] .toast { background: var(--ink); color: var(--bg); border-color: transparent; }
.toast #toastText { flex: 1 1 auto; min-width: 0; overflow-wrap: anywhere; display: block; max-height: 40vh; overflow-y: auto; }   /* round 3 L12-A6: never cut (it was two lines; at 200% zoom text was lost) */
.toast #toastAction, .toast .toast-close { flex: none; }
@keyframes toastIn { from { opacity: 0; transform: translate(-50%, 8px); } to { opacity: 1; transform: translateX(-50%); } }   /* ts-rise-in, keeping the centring */
.toast .btn { min-height: var(--tap); font-size: var(--fs-sm); }
.toast .toast-close { width: 44px; height: 44px; flex: none; padding: 0; justify-content: center; background: transparent; color: inherit; border: 0; font-size: var(--fs-lg); }
.toast .toast-close:hover:not([disabled]) { background: color-mix(in srgb, var(--bg) 16%, transparent); }

/* ---- shell-css B3: tests screen, motion, material ---- */
.seg { display: inline-flex; gap: var(--s1); }
.seg > button, .seg > .chip { background: transparent; border: 0; border-bottom: 2px solid transparent; border-radius: 0; color: var(--muted); padding: var(--s2) var(--s3); cursor: pointer; font: inherit; min-height: var(--tap); }
.seg > button[aria-pressed="true"], .seg > .chip[aria-pressed="true"] { background: transparent; color: var(--ink); border-bottom-color: var(--accent); font-weight: var(--fw-b); }
.moreMenu { position: relative; }
.moreMenu > summary { list-style: none; display: inline-flex; align-items: center; justify-content: center; min-width: var(--tap); cursor: pointer; }
.moreMenu > summary::-webkit-details-marker { display: none; }
.popmenu { position: absolute; right: 0; top: calc(100% + 4px); min-width: 168px; background: var(--surface); border: 1px solid var(--line); border-radius: var(--r-s); box-shadow: var(--e2); z-index: 8; display: flex; flex-direction: column; padding: var(--s1) 0; }
.popmenu button { min-height: 44px; border: 0; background: transparent; color: var(--ink); font: inherit; text-align: left; padding: 0 var(--s4); cursor: pointer; }
.popmenu button:hover { background: var(--accent-soft); }
.popmenu .danger, .popmenu button[data-act="delete"] { color: var(--bad); }
.view-enter { animation: ts-fade-in var(--d-base) var(--ease-out) both; animation-iteration-count: 1; }
.collapse { display: grid; grid-template-rows: 0fr; transition: grid-template-rows var(--d-base) var(--ease-std); }
.collapse[data-open="1"] { grid-template-rows: 1fr; }
.collapse-in { overflow: hidden; min-height: 0; }
@keyframes rowOut { to { opacity: 0; transform: translateX(-8px); } }
.is-leaving { animation: rowOut var(--d-base) var(--ease-in) both; animation-iteration-count: 1; }
.count.is-pulse { animation: ts-pop 180ms var(--ease-out) 1; }

/* Account card: sign-out panel, download button */
#accountBox .row, .setCard .row { flex-wrap: wrap; gap: var(--s2); }
#signOutPanel { background: var(--surface); border: 1px solid var(--line); border-radius: var(--r-s); padding: var(--s3) var(--s4); margin: var(--s3) 0; }
#signOutPanel .row { display: flex; flex-wrap: wrap; gap: var(--s2); }
#signOutPanel .btn, #exportBtn { min-height: var(--tap); }
#signOutPanel label { display: flex; align-items: center; gap: var(--s2); min-height: var(--tap); margin: 0; }
#exportMsg { font-size: var(--fs-sm); color: var(--muted); min-height: 20px; margin: var(--s1) 0 0; }
fieldset > legend { float: left; width: 100%; }   /* the title sits inside the card, not on its border */
fieldset > legend + * { clear: both; }
:is(.setCard, fieldset, #dangerBox) p:empty { display: none; }
.setRow > .row { margin: 0; }
.setRow > p { margin: var(--s1) 0 0; }

/* shell-css-B5-01: plan chooser, notice buttons */
.plans { display: flex; flex-direction: column; gap: var(--s2); margin: var(--s3) 0; }
label.planOpt { display: grid; grid-template-columns: 24px minmax(0, 1fr) auto; align-items: center; column-gap: var(--s3); min-height: var(--tap); box-sizing: border-box; background: var(--surface); border: 1px solid var(--line); border-radius: var(--r-m); padding: var(--s3) var(--s4); margin: 0; cursor: pointer; }
label.planOpt input[type=radio] { width: 24px; height: 24px; margin: 0; grid-column: 1; grid-row: 1 / span 2; }
.planName { grid-column: 2; font-weight: 600; min-width: 0; overflow-wrap: anywhere; }
.planPrice { grid-column: 3; grid-row: 1; font-size: var(--fs-md); font-weight: 600; text-align: right; white-space: normal; overflow-wrap: anywhere; max-width: 45%; justify-self: end; }
.planOpt .planNote { grid-column: 2 / -1; font-size: var(--fs-sm); color: var(--muted); }
label.planOpt:has(input:checked), label.planOpt.is-selected { border-color: var(--accent); background: var(--accent-soft); }
label.planOpt:has(input:focus-visible) { outline: 2px solid var(--accent); outline-offset: 2px; }
.planTerms { font-size: var(--fs-sm); color: var(--muted); }
.planTerms a { text-decoration: underline; }
.notice .row, .noticeCalm .row { display: flex; flex-wrap: wrap; gap: var(--s2); margin-top: var(--s2); }
.notice .row .btn, .noticeCalm .row .btn, .noticeCalm > .btn { min-height: var(--tap); }

/* shell-css-B5-02: invite card and plan note */
#inviteCard { max-width: 100%; box-sizing: border-box; }
#inviteLink { display: block; width: 100%; max-width: 100%; box-sizing: border-box; min-height: var(--tap); font-family: ui-monospace, Menlo, Consolas, monospace; font-size: var(--fs-sm); }
#inviteCard .row { display: flex; flex-wrap: wrap; gap: var(--s2); margin-top: var(--s2); }
#inviteCard .btn { min-height: var(--tap); }
#inviteCard .muted, #inviteMsg { min-height: 20px; }
#launchNote { display: flex; flex-direction: column; align-items: stretch; gap: var(--s2); }
#launchNote[hidden] { display: none; }
#launchNote .btn { min-height: var(--tap); align-self: flex-start; }
@media (min-width: 700px) { #launchNote { flex-direction: row; align-items: center; justify-content: space-between; } #launchNote .btn { align-self: center; margin-left: auto; flex: none; } }
.planLaunch { display: block; font-weight: 600; }
#planNote { display: flex; flex-direction: column; align-items: stretch; gap: var(--s2); }
#planNote[hidden] { display: none; }
#planNote .btn { min-height: var(--tap); align-self: flex-start; }
@media (min-width: 700px) { #planNote { flex-direction: row; align-items: center; justify-content: space-between; } #planNote .btn { align-self: center; margin-left: auto; flex: none; } }

/* ---- shell-css loop 6: first-look findings (R1) ---- */
/* Settings rows: the title on its own line, the help text under it in muted small text, "Saved" on its own line */
.setRow > .setText { margin: 0; color: var(--ink); font-size: var(--fs-md); }
.setText > b, .setText > span { display: block; }
.setText > .muted { color: var(--muted); font-size: var(--fs-sm); margin-top: 2px; }
.setText > .saved:empty { display: none; }
/* a row with nothing visible in it takes no space (no empty bands inside the cards) */
.setCard .row:not(:has(> :not([hidden], :empty, input, select))) { display: none; }
/* the agreement tick: box and sentence on one line, the sentence in ink at normal size */
label:has(> #bmAck) { display: flex; align-items: flex-start; gap: var(--s3); min-height: var(--tap); margin: 0; }
label:has(> #bmAck) > span { display: block; margin: 0; font-size: var(--fs-md); color: var(--ink); }
/* "Hide done tasks" is a small switch in the progress header, not a row that looks like a task */
label.hideDone { display: inline-flex; align-items: center; gap: var(--s2); min-height: var(--tap); margin: 0; font-size: var(--fs-sm); color: var(--muted); cursor: pointer; }
label.hideDone > span { display: inline; margin: 0; font-size: var(--fs-sm); color: var(--muted); }
label.hideDone input[type="checkbox"] { -webkit-appearance: none; appearance: none; flex: none; width: 40px; height: 24px; border-radius: var(--r-pill); background: var(--off); position: relative; cursor: pointer; transition: background-color var(--d-fast) var(--ease-std); }
label.hideDone input[type="checkbox"]::after { content: ""; position: absolute; top: 3px; left: 3px; width: 18px; height: 18px; border-radius: var(--r-pill); background: var(--on-accent); transition: transform var(--d-fast) var(--ease-std); }
label.hideDone input[type="checkbox"]:checked { background: var(--ok); }
label.hideDone input[type="checkbox"]:checked::after { transform: translateX(16px); }
/* the grading scale box opens with a short ease, not a jump */
html { interpolate-size: allow-keywords; }
details.scaleBox::details-content { block-size: 0; overflow: clip; transition: block-size var(--d-base) var(--ease-std), content-visibility var(--d-base) allow-discrete; }
details.scaleBox[open]::details-content { block-size: auto; }
details.scaleBox > summary { font-size: var(--fs-sm); display: flex; align-items: center; }
/* the bell panel: air above the quiet button */
#markRead { margin-top: var(--s3); }
.drawer .list { margin-bottom: 0; }
/* a busy button keeps its spinner beside the label */
.btn > .tl-spin { position: static; flex: none; }

/* ---- shell-css-B6-01: phone-first card and install hints (markup by shell-js, styled by name) ---- */
#phoneFirst { background: var(--surface); border: 1px solid var(--line); border-radius: var(--r-m); padding: var(--s5); margin: var(--s4) 0; }
#phoneFirst .btn { display: flex; justify-content: center; width: 100%; min-height: 48px; margin-top: var(--s3); }
#phoneFirst .btn:not(.primary) { margin-top: var(--s2); }
#iosHint { margin: var(--s3) 0; padding: 0; list-style-position: inside; font-size: var(--fs-sm); color: var(--muted); }
#iosHint li { margin: var(--s1) 0; overflow-wrap: break-word; }
#installBtn { min-height: var(--tap); }

/* ---- shell-css-B6-02: lost-key card ---- */
#keyLost { background: var(--surface); border: 1px solid var(--line); border-radius: var(--r-s); padding: var(--s3) var(--s4); margin: var(--s3) 0; box-shadow: none; }
#keyLost p { overflow-wrap: break-word; }
#keyLost .row, #keyLost .actions { display: flex; flex-wrap: wrap; gap: var(--s2); margin-top: var(--s2); }
#keyLost .btn { min-height: var(--tap); }
#keyLost .small, #keyLost [data-confirm] { font-size: var(--fs-sm); }
#keyLost .btn.danger, #keyLost [data-act="fresh"] { border-color: transparent; background: transparent; color: var(--bad); }

/* ---- shell-css loop 7: real markup of the plan chooser, invite card, phone-first card, install page; low R1 findings ---- */
#planOpts { display: flex; flex-direction: column; gap: var(--s2); }
#planOpts:empty { display: none; }
#planErr:empty, #planMsg:empty, #inviteStats:empty, #phoneSendMsg:empty { display: none; }
#planMsg, #inviteMsg { min-height: 20px; }
#planBox > h3 { margin: 0; }
#plansRetry { min-height: var(--tap); }
#subscribe { min-height: var(--tap); }
#inviteOut { margin-top: var(--s3); }
#inviteOut[hidden] { display: none; }
#inviteLink { margin: 0; }
#phoneFirst h3 { margin: 0 0 var(--s2); font-size: var(--fs-lg); line-height: var(--lh-t); }
#phoneFirst .row { display: block; margin: 0; }
#phoneMailRow { margin: var(--s2) 0 0; }
#phoneMailRow a { display: inline-flex; align-items: center; min-height: var(--tap); text-decoration: underline; }
/* install page: tick and sentence in one row, the button a pill, one calm "added" row, steps with numbers inside */
.ackRow, .bm-ack { display: flex; align-items: flex-start; gap: var(--s3); min-height: var(--tap); font-size: var(--fs-md); color: var(--ink); }
.ackRow input[type=checkbox], .bm-ack input[type=checkbox] { flex: none; width: 24px; height: 24px; margin: var(--s2) 0 0; }
.bm-ack label { margin: 0; padding: var(--s2) 0; font-size: var(--fs-md); color: var(--ink); min-height: var(--tap); box-sizing: border-box; }
#bookmarklet { display: inline-flex; align-items: center; justify-content: center; min-height: var(--tap); padding: var(--s2) var(--s5); border-radius: var(--r-pill); cursor: grab; text-decoration: none; }
#bookmarklet[aria-disabled="true"] { background: var(--accent-soft); color: var(--accent); border-color: var(--accent); cursor: not-allowed; }
#bmAdded { display: flex; flex-wrap: wrap; align-items: center; gap: var(--s3); margin: var(--s3) 0; padding: var(--s3) var(--s4); background: var(--accent-soft); border-radius: var(--r-s); }
#bmAdded[hidden] { display: none; }
#bmAdded p { margin: 0; }
#bmAddedBtn { min-height: var(--tap); }
#bmSteps { margin: var(--s2) 0; padding: 0; list-style-position: inside; }
#bmSteps li { margin: var(--s1) 0; overflow-wrap: break-word; }
#bmPhoneCard > summary, #bmPhoneDetails > summary { display: flex; align-items: center; min-height: var(--tap); cursor: pointer; font-weight: var(--fw-b); }
#bmFirstOnly, #bmNoClick { font-size: var(--fs-sm); color: var(--muted); }
/* low findings: "Now" label and the active tab keep 4.5:1 on their tinted background; the brand reads at a glance; long codes wrap only at group borders */
.stepNow, nav.tabs .tab[aria-selected="true"] { color: color-mix(in srgb, var(--accent) 70%, var(--ink)); }
.brand { font-size: var(--fs-lg); }
#recoveryCode { display: block; overflow-wrap: anywhere; word-break: normal; font-family: ui-monospace, Menlo, Consolas, monospace; font-size: var(--fs-sm); }
.tab > svg, .tab > .ico { display: block; flex: none; margin: 0; }
.table td, .table th { overflow-wrap: break-word; }

/* ---- loop 8 (shell-css): sign-out panel enters softly (R1-d29-2), slider ends sit clear of the thumb, task list does not jump ---- */
@keyframes panelIn { from { opacity: 0; transform: translateY(-4px); } to { opacity: 1; transform: none; } }
#signOutPanel:not([hidden]) { animation: panelIn var(--d-base) var(--ease-out) both; }
.gradeEnds { padding-inline: var(--s1); font-size: var(--fs-xs); }

/* ---- loop 8 (shell-css): R1 low findings and the four-view look ---- */
/* the brand reads at a glance: the mark is 44 px tall (text about 16 px); its empty right part is folded away so it takes little room (R1-d02-3, d08-4, d17-3, main-7) */
.wordmark { --wm-h: 44px; height: var(--wm-h); margin-right: calc(var(--wm-h) * -1.2); pointer-events: none; flex: none; }
/* the sample-data / status strip lines up with the page column */
#mode { max-width: var(--content); }
/* step bodies: tight, even spacing (R1-d05-3: no 50 px holes between the sentence, "More" and the fields) */
.stepBody p { margin: 0 0 var(--s2); }
.stepBody p:empty, .stepBody .row:empty { display: none; }
.stepBody label { margin: 0; }
.stepBody .row { margin: var(--s2) 0 0; align-items: flex-end; }
.stepBody details { margin: 0; }
.stepBody .btn.primary, .stepBody a.btn { margin-top: var(--s1); }
/* "2 of 5 steps done": a quiet muted line with a small chevron, not a link look */
#startFold { display: inline-flex; align-items: center; gap: var(--s2); color: var(--muted); font-size: var(--fs-sm); text-decoration: none; }
#startFold::before { content: ""; flex: none; width: 6px; height: 6px; border: solid currentColor; border-width: 0 2px 2px 0; transform: rotate(-45deg); transition: transform var(--d-fast) var(--ease-std); }
#startFold[aria-expanded="true"]::before { transform: rotate(45deg); }
/* cards: no dead band under the last visible row (R1-d14-4) */
.setCard > :not([hidden], :empty):not(:has(~ :not([hidden], :empty, div:not([class], [id])))) { margin-bottom: 0; }   /* plain script-made slots take no room */
#accountBox { padding-bottom: var(--s3); }   /* its last row is followed by rows that are only sometimes shown */
/* Danger zone is a quiet row with a small chevron, not an empty card */
#dangerBox > summary { list-style: none; justify-content: space-between; }
#dangerBox > summary::-webkit-details-marker { display: none; }
#dangerBox > summary::after { content: ""; flex: none; width: 8px; height: 8px; border: solid var(--muted); border-width: 0 2px 2px 0; transform: rotate(-45deg); transition: transform var(--d-fast) var(--ease-std); }
#dangerBox[open] > summary::after { transform: rotate(45deg); }
/* the target grade control: 16 px of air each side of the number, thin neutral rings, 44 px buttons (R1-d10-10) */
.gradeBig { gap: var(--s4); }
/* a table never squeezes its columns below a readable width: it scrolls sideways inside its own box (R1-d16-4) */
.tableWrap > .table { min-width: 30rem; }
/* radios and ticks use the app blue, rows in dialogs stay a tidy pair (R1-d20-2) */
input[type="radio"], input[type="checkbox"], input[type="range"] { accent-color: var(--accent); }
dialog label.inline { margin: 0; }
/* the bell panel: a faint dim behind it so the page does not look clickable (R1-d15-2); the close button shows a still ring (R1-d28-2) */
body:has(.drawer[data-open="1"])::after { content: ""; position: fixed; inset: 0; z-index: 5; pointer-events: none; background: color-mix(in srgb, var(--ink) 4%, transparent); animation: ts-fade-in var(--d-base) var(--ease-out) both; animation-iteration-count: 1; }
.drawer .btn { transition: none; }
.drawer .btn:focus-visible { outline: var(--focus); outline-offset: 2px; }
/* easing: nothing is linear (R1-d13-3, d21-2) */
@keyframes viewIn { from { opacity: .4; } to { opacity: 1; } }
.view-enter { animation: viewIn var(--d-base) var(--ease-std) both; animation-iteration-count: 1; }
@keyframes menuIn { from { opacity: 0; transform: translateY(-4px); } to { opacity: 1; transform: none; } }
.moreMenu[open] > .popmenu { animation: menuIn var(--d-base) var(--ease-out) both; animation-iteration-count: 1; }
/* a deleted row fades and closes up in the same 200 ms, so the page below does not jump (R1-d18-5) */
@keyframes rowShut { to { height: 0; min-height: 0; padding-block: 0; border-width: 0; margin-block: 0; } }
.list li.is-leaving { overflow: hidden; animation: rowOut var(--d-base) var(--ease-in) both, rowShut var(--d-base) var(--ease-std) both; animation-iteration-count: 1; }
/* the bell panel starts below the header (it is 88 px tall on a computer, 56 px on a phone) so it never covers the language select */
.drawer { top: calc(var(--header-h) + var(--s7) - var(--s4)); }
@media (max-width: 640px), (pointer: coarse) { .drawer { top: calc(var(--header-h) + var(--s2)); } }
/* an opened "Completed" list sits close under its title (R1-p08-3) */
.completed summary { margin-bottom: 0; }
.completed > .list { margin-top: var(--s1); }

/* ---- shell-css L09: "More" lines in Settings are centred in their 44 px row (the text sat at the top and left a dead band) ---- */
.setMore > summary { display: flex; align-items: center; gap: var(--s2); list-style: none; cursor: pointer; }
.setMore > summary::-webkit-details-marker { display: none; }
.setMore > summary::before { content: ""; flex: none; width: 0; height: 0; border-style: solid; border-width: 4px 0 4px 6px; border-color: transparent transparent transparent currentColor; transition: transform var(--d-fast) var(--ease-std); }
.setMore[open] > summary::before { transform: rotate(90deg); }

/* ---- shell-css L11: R2 findings ---- */
/* R2-trust-1, R2-ui-1: the plan row never crushes its name; on a phone the price drops under the name */
@media (max-width: 420px) {
  .planPrice { grid-column: 2 / -1; grid-row: 2; max-width: none; text-align: start; justify-self: start; font-weight: var(--fw); color: var(--muted); }
  label.planOpt input[type=radio] { grid-row: 1 / span 3; }
  .planOpt .planNote { grid-row: 3; }
}
/* R2-speed-8: the plan area keeps its height while the language swaps (no jump) */
.planTerms { min-height: 0; }   /* loop L04-5: no reserved blank lines under the terms; the box itself (#planBox) keeps its height while the text swaps */
/* R2-a11y-3, R2-look-8, R2-ftu-15: small blue text on the pale tint keeps 4.5:1 (guide.css loads after this file, so the selector is stronger) */
li.step .stepNow, html nav.tabs .tab[aria-selected="true"] { color: color-mix(in srgb, var(--accent) 60%, var(--ink)); }
/* R2-look-4: header and page share one column; the current tab is a flat pill, not a raised blob */
@media (min-width: 641px) and (pointer: fine) {
  .top, main, .foot, #mode { max-width: 720px; }
  .tab[aria-selected="true"] { background: color-mix(in srgb, var(--accent) 14%, var(--surface)); box-shadow: none; }
}
/* R2-look-15: the dark tab pill shows */
html:not([data-theme="light"]) .tab[aria-selected="true"], html:not([data-theme="light"]) nav.tabs .tab[aria-selected="true"] { background: rgb(110 160 255 / .16); }
/* R2-look-7: bell, tabs and the chat button share one 1.5 px stroke look; the chat bubble is drawn, not an emoji */
.tabIcon, .bellIcon { stroke-width: 1.5; }
.icon { color: var(--muted); }
.fab { text-indent: -999px; overflow: hidden; white-space: nowrap; color: var(--muted); }
.fab::before { content: ""; position: absolute; left: 50%; top: 50%; width: 20px; height: 15px; margin: calc(var(--s2) * -1.125) 0 0 calc(var(--s1) * -2.5); border: 1.5px solid currentColor; border-radius: var(--r-s); }
.fab::after { content: ""; position: absolute; left: 50%; top: 50%; width: 6px; height: 6px; margin: calc(var(--s1) * .75) 0 0 calc(var(--s1) * -1.5); border: solid currentColor; border-width: 0 1.5px 1.5px 0; background: var(--surface); transform: rotate(45deg); }
/* R2-look-6, R2-ftu-14: footer links use the text colour with a quiet underline; the sentence and the links sit on two lines */
.foot a { color: inherit; text-decoration: underline; text-decoration-color: var(--line); text-underline-offset: 3px; margin-inline-end: var(--s3); display: inline-block; min-height: var(--tap); line-height: var(--tap); }
.foot a:hover, .foot a:focus-visible { color: var(--accent); text-decoration-color: currentColor; }
.foot > span:first-child { display: block; margin-bottom: var(--s1); }
/* R2-ui-5: on a phone the last row scrolls clear of the round button */
@media (max-width: 640px), (pointer: coarse) { main { padding-bottom: var(--s4); } }   /* the footer below it already clears the tab bar: a second clearance here was dead space (loop L06-10) */
/* R2-look-3, R2-ui-3, R2-ui-4: one message look. .note is the base; .is-error has a warm bar and a "!" mark, .is-ok a tick, .is-news the soft accent.
   The result lines of the settings cards use it as soon as they hold text; the line fades in (shell-js sets is-error / is-ok / is-news). */
.note, :is(#planMsg, #exportMsg, #inviteMsg, #remindersMsg, #phoneMsg, #phoneSendMsg, #newMaterial):not(:empty) { display: block; margin: var(--s2) 0; padding: var(--s2) var(--s3); border-radius: var(--r-s); border-inline-start: 3px solid var(--line); background: var(--surface); color: var(--ink); font-size: var(--fs-sm); animation: ts-fade-in var(--d-base) var(--ease-out) both; animation-iteration-count: 1; }
.note.is-error, .is-error:is(#planMsg, #exportMsg, #inviteMsg, #remindersMsg, #phoneMsg, #phoneSendMsg) { border-inline-start-color: var(--bad); background: var(--bad-soft); font-size: var(--fs-md); color: var(--ink); }
.note.is-ok, .is-ok:is(#planMsg, #exportMsg, #inviteMsg, #remindersMsg, #phoneMsg, #phoneSendMsg) { border-inline-start-color: var(--ok); background: var(--ok-soft); color: var(--ink); }
.note.is-news, #newMaterial:not(:empty) { border-inline-start-color: var(--accent); background: var(--accent-soft); }
.note.is-error::before, .is-error:is(#planMsg, #exportMsg, #inviteMsg, #remindersMsg, #phoneMsg, #phoneSendMsg)::before { content: "!"; display: inline-block; width: 18px; height: 18px; margin-inline-end: var(--s2); border-radius: var(--r-pill); background: var(--bad); color: var(--on-accent); font-weight: var(--fw-b); font-size: var(--fs-xs); line-height: 18px; text-align: center; }
.note.is-ok::before, .is-ok:is(#planMsg, #exportMsg, #inviteMsg, #remindersMsg, #phoneMsg, #phoneSendMsg)::before { content: "\2713"; margin-inline-end: var(--s2); color: var(--ok); font-weight: var(--fw-b); }
/* R2-trust-10, R2-look-2, R2-ui-8: the Account card: one status line, a quiet second line, tight spacing; sign out and download are quiet text buttons away from the pay button */
#accountBox > p { margin: 0 0 var(--s1); }
#accountBox #planLine { color: var(--ink); font-weight: var(--fw-b); font-size: var(--fs-md); }
#accountBox > .row { margin: var(--s3) 0 0; }
#signOut, #exportBtn { border-color: transparent; background: transparent; color: var(--muted); text-decoration: underline; text-underline-offset: 3px; padding-inline: var(--s2); }
#signOut { margin-inline-start: auto; }
#exportBtn { margin-top: var(--s1); }
@media (max-width: 640px) { #subscribe { flex: 1 1 100%; justify-content: center; } #signOut { margin-inline-start: 0; } }
/* R2-look-10: even rhythm in Settings: 16 px between rows, 12 px inside; a switch that is on is the app blue; Danger zone actions are outlined red buttons */
.setCard .setRow { margin: 0 0 var(--s4); }
.setCard .setRow:last-child { margin-bottom: 0; }
.setMore { margin: 0 0 var(--s4); }
.setMore > :not(summary) { margin-top: var(--s3); }
.switch input:checked + span, label.hideDone input[type="checkbox"]:checked { background: var(--accent); }
#dangerBox .btn.danger { border: 1px solid color-mix(in srgb, var(--bad) 50%, var(--line)); background: transparent; color: var(--bad); padding-inline: var(--s4); box-shadow: none; }
/* the card trial (owner 2026-10-07): the Settings trial card and the words under the plan buttons */
#trialCard p { margin: 0 0 var(--s2); }
#trialCard .row, #trialCancelRow .row { margin: var(--s2) 0 0; }
#planTrial p, #planTrialPick { margin: var(--s2) 0 0; }
/* R2-ftu-1: the demo's sample test can open while the first steps run (shell-js sets data-sample="1" on main and builds .sampleCard) */
main[data-onboard][data-sample="1"] :is(#mode, #testPicker, #focusCard, #aiPanel, #nextCard) { display: revert; }
.sampleCard { background: var(--surface); border: 1px solid var(--line); border-radius: var(--r-m); padding: 0 var(--s4); margin: var(--s3) 0; }
.sampleCard > summary, .sampleCard > button { display: flex; align-items: center; min-height: var(--tap); cursor: pointer; font-weight: var(--fw-b); }
/* R2-ui-6: a busy button reserves room for the spinner at its start, so the label is never printed over */
.btn.tl-busy, .btn[aria-busy="true"] { position: relative; padding-inline-start: calc(var(--s4) + 24px); }
.btn.tl-busy > .tl-spin, .btn[aria-busy="true"] > .tl-spin { position: absolute; inset-inline-start: var(--s3); top: 50%; translate: 0 -50%; }
/* R2-ui-11: the view change eases out like the other enters */
@keyframes viewIn { from { opacity: .4; } to { opacity: 1; } }
.view-enter { animation: viewIn var(--d-base) var(--ease-out) both; animation-iteration-count: 1; }
/* L12: the one quiet row for Sign out and Download my data (shell-js puts both in .quietRow); result lines and the sample card keep their look */
.quietRow { display: flex; flex-wrap: wrap; align-items: center; gap: var(--s2) var(--s3); margin: var(--s3) 0 0; padding-top: var(--s2); border-top: 1px solid var(--line); }
.quietRow > :is(#signOut, #exportBtn) { margin: 0; margin-inline-start: 0; min-height: var(--tap); }
.quietRow > :is(#signOut, #exportBtn):hover { color: var(--ink); }
.quietRow > :is(#signOut, #exportBtn):focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
.is-error, .is-ok { overflow-wrap: anywhere; }
.sampleCard[data-sample="1"], main[data-sample="1"] .sampleCard { display: block; }
.sampleCard > :is(summary, button) { width: 100%; text-align: start; background: transparent; border: 0; color: var(--ink); font: inherit; font-weight: var(--fw-b); }
button.sampleCard { display: block; width: 100%; min-height: var(--tap); font: inherit; font-size: var(--fs-md); font-weight: var(--fw-b); text-align: start; color: var(--ink); cursor: pointer; }
button.sampleCard[hidden] { display: none; }
button.sampleCard:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
.sampleCard > :is(summary, button):focus-visible { outline: 2px solid var(--accent); outline-offset: -2px; border-radius: var(--r-m); }
@media (max-width: 640px) { .quietRow { justify-content: space-between; } .sampleCard { padding-inline: var(--s3); } }

/* ---- shell-css L14: a "More" line belongs to the row above it; two of them in a row sit together, 16 px only after the last ---- */
.setCard .setRow:has(+ .setMore) { margin-bottom: 0; }
.setMore:has(+ .setMore) { margin-bottom: 0; }

/* ---- shell-css L15: the items done/L14-shell-js.md left ---- */
/* the open tab is styled by aria-current as well as aria-selected (shell-js may drop aria-selected later) */
.tab[aria-current="page"] { background: var(--surface); color: var(--ink); box-shadow: var(--e1); font-weight: var(--fw-b); }
@media (max-width: 640px) { nav.tabs .tab[aria-current="page"] { color: var(--accent); background: var(--accent-soft); box-shadow: none; } }
html nav.tabs .tab[aria-current="page"] { color: color-mix(in srgb, var(--accent) 60%, var(--ink)); }
@media (min-width: 641px) and (pointer: fine) { .tab[aria-current="page"] { background: color-mix(in srgb, var(--accent) 14%, var(--surface)); box-shadow: none; } }
html:not([data-theme="light"]) .tab[aria-current="page"], html:not([data-theme="light"]) nav.tabs .tab[aria-current="page"] { background: rgb(110 160 255 / .16); }
/* the plan lines keep room for three lines while the language swaps (the options list stays hidden while empty) */
#planOpts:not(:empty) { min-height: calc(var(--fs-sm) * var(--lh) * 3); }
#planTerms { min-height: 0; }
/* phone: the update banner is one line, the text shortens with an ellipsis and Refresh is a quiet text button at its end */
@media (max-width: 640px) {
  .banner.upd { flex-wrap: nowrap; gap: var(--s2); }
  .banner.upd #updText { flex: 1 1 auto; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
  .banner.upd #updRefresh { flex: none; min-height: var(--tap); border-color: transparent; background: transparent; color: var(--accent); text-decoration: underline; text-underline-offset: 3px; padding-inline: var(--s2); box-shadow: none; }
  #subscribe, #planFirst ~ #subscribe { width: 100%; flex: 1 1 100%; justify-content: center; }
}
/* the Get started step 5 row: the one filled button sits alone, full width on a phone */
#startMakeRow:not([hidden]) { display: flex; margin: var(--s2) 0 0; }
#startMakeRow > .btn { min-height: var(--tap); }
@media (max-width: 640px) { #startMakeRow > .btn { flex: 1 1 100%; justify-content: center; } }
/* L16 R3-cd-1, R3-bench-1, R3-rage-1: a price, its period and "once" are never broken; the name takes the room that is left */
.planPrice { white-space: nowrap; overflow-wrap: normal; max-width: none; font-variant-numeric: tabular-nums; }
label.planOpt { grid-template-columns: 24px minmax(0, 1fr) max-content; }
@media (max-width: 420px) { .planPrice { max-width: 100%; } }

/* ---- shell-css L17: R3-cd-2/3/6 Today card, R3-cd-5 furniture, R3-cd-7 Settings weight, R3-bench-4/cd-4 sample look, R3-speed-16 ---- */
/* R3-cd-2: the test name is the big line; "in N days" (a real, school-set deadline) is the largest part of the line under it */
#testMeta .title { font-size: var(--fs-xl); }
#testMeta .meta > span.muted { font-size: var(--fs-lg); font-weight: var(--fw-b); color: var(--ink); }
/* R3-cd-6: no card in a card: the study-content panel is a plain section inside the Today card; one filled button (the link to the page), "Make it again" is a quiet text link */
.focusCard #aiPanel { background: transparent; border: 0; border-top: 1px solid var(--line); border-radius: 0; box-shadow: none; padding: var(--s3) 0 0; margin-top: var(--s3); }
.focusCard #aiPanel .aiAgain { border-color: transparent; background: transparent; color: color-mix(in srgb, var(--accent) 65%, var(--ink)); box-shadow: none; text-decoration: underline; text-underline-offset: 3px; padding-inline: var(--s2); }
.focusCard #aiPanel .aiAgain:hover { color: var(--ink); }
.focusCard #aiPanel .aiOpen { margin-top: 0; }
/* R3-cd-6: the test chips are one scrolling row, never an orphan on a second line */
#testPicker { flex-wrap: nowrap; overflow-x: auto; overscroll-behavior-x: contain; scrollbar-width: thin; padding-bottom: var(--s1); }
#testPicker > .chip { flex: none; max-width: 80%; }
/* R3-cd-3: the grade block reads as one calm row: the scale line is small and quiet, "Hide done tasks" is a small line at the end of the progress row */
.focusCard .scaleBox { margin: 0; }
#nextGradeHint { margin: var(--s1) 0; }
.progressSticky label.hideDone { justify-content: flex-end; margin: 0; font-size: var(--fs-sm); color: var(--muted); }
.progressSticky label.hideDone > span { font-size: var(--fs-sm); color: var(--muted); }
/* the learnt box: the two extra lines are plain muted text on their own row */
.learntBox .learntLook, .learntBox .learntAhead { flex: 1 1 100%; margin: 0; font-size: var(--fs-sm); color: var(--muted); overflow-wrap: anywhere; }
.learntBox .learntLook[hidden], .learntBox .learntAhead[hidden] { display: none; }
/* the store problem note: a calm strip, its button on its own row (shell-js builds it with .note.is-error) */
#storeNote { margin: var(--s3) 0; }
#storeNote > p { margin: 0 0 var(--s2); overflow-wrap: anywhere; }
#storeNote .row { margin: 0; }
#storeNote .btn { min-height: var(--tap); }
/* R3-cd-5: "Updated ..." is a small grey line, not a blue bar; the language select has no loud border; the long Moodle sentence shows in Settings only */
.banner.upd { background: transparent; padding: 0; justify-content: flex-end; text-align: end; color: var(--muted); font-size: var(--fs-xs); margin-top: var(--s1); }
.banner.upd.stale { background: var(--warn-soft); padding: var(--s2) var(--s3); color: var(--ink); }
.top #lang { border-color: transparent; background: transparent; color: var(--muted); min-width: 0; font-size: var(--fs-sm); }
.top #lang:hover, .top #lang:focus-visible { border-color: var(--line); color: var(--ink); }
.foot > span[data-i18n="legal.disclaimer"] { display: none; }
body:has(main section.view[data-view="settings"]:not([hidden])) .foot > span[data-i18n="legal.disclaimer"] { display: block; }
.foot { font-size: var(--fs-sm); }
/* R4-a11y-7: footer links are 44 px wide too */
.foot a { padding-inline: var(--s2); margin-inline-end: 0; min-width: var(--tap); text-align: center; }
/* R4-ftu-5: anchors and focus never hide under the sticky top bar */
html { scroll-padding-top: calc(var(--header-h) + var(--s3)); }
.learntBox .textLink, .learntBox a { color: color-mix(in srgb, var(--accent) 65%, var(--ink)); }
/* R3-cd-7: Settings is not seven equal cards: Account leads, the others are flat; the danger zone is a single quiet row until it is opened */
.settingsCol > .setCard:not(#accountBox, #dangerBox) { box-shadow: none; background: transparent; }
#dangerBox { background: transparent; border: 1px solid var(--line); border-radius: var(--r-m); padding: 0 var(--s4); }
#dangerBox[open] { padding-bottom: var(--s3); }
#dangerBox > summary { color: var(--muted); }
/* R3-speed-16: the plan chooser keeps its height while the slider/price lines are redrawn, so the page below does not jump */
#planBox:not([hidden]) { min-height: calc(var(--tap) * 5); }
/* R3-bench-4, R3-cd-4, R4-a11y-1: sample items keep full-contrast text; they are dashed, tagged "Sample" and flat, never faded */
#testPicker > .chip:is(.isSample, [data-sample="1"]) { border-style: dashed; }
main[data-sample="1"] :is(#focusCard, #nextCard) { background: transparent; box-shadow: none; }
.tag.isSample, .chip .tag { background: var(--accent-soft); color: color-mix(in srgb, var(--accent) 60%, var(--ink)); }

/* ---- shell-css L18: the last Today/Material/Settings touches (R3-cd-2/3/4/6/7/8) ---- */
/* the days left: the biggest, boldest part of the line under the test name (a real, school-set deadline only) */
#testMeta .meta .testWhen { font-size: var(--fs-lg); font-weight: var(--fw-b); color: var(--ink); }
/* the grade number is a button that opens the slider: 44 px tap box, a quiet underline that says "tap me" */
#gradeToggle { min-width: var(--tap); min-height: var(--tap); font-size: var(--fs-xl); font-variant-numeric: tabular-nums; text-decoration-thickness: 2px; }
#gradeToggle:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
@media (hover: hover) { #gradeToggle:hover { color: var(--accent); text-decoration-color: currentColor; } }
/* sample items: the "Sample" tag is a small quiet pill; the dashed look comes from .isSample (no fade) */
.sampleTag { vertical-align: middle; font-weight: var(--fw-b); white-space: nowrap; }
.isSample { border-style: dashed; }
/* a finished test in the picker: calm, with a quiet "Done" word, still readable and tappable */
.chip.chipDone { color: var(--muted); border-color: var(--line); background: transparent; }
.chip.chipDone[aria-pressed="true"] { color: var(--ink); background: var(--accent-soft); border-color: var(--accent); }
/* the study-content panel inside the Today card: no border of its own */
#aiPanel.flat { border: 0; box-shadow: none; background: transparent; }
/* "3 tasks to practise again" + its link: one small quiet line, the link a full tap target */
#practiseRow { margin: var(--s2) 0 0; color: var(--muted); overflow-wrap: anywhere; }
#practiseRow a { color: color-mix(in srgb, var(--accent) 65%, var(--ink)); display: inline-flex; align-items: center; min-height: var(--tap); font-weight: var(--fw-b); }
/* Material: the top summary is one calm line */
#materialSummary { margin: 0 0 var(--s3); color: var(--muted); font-size: var(--fs-sm); overflow-wrap: anywhere; }
#materialSummary:not([hidden]) { color: var(--ink); }
/* the test chips: one scrolling row at any width */
#testPicker { flex-wrap: nowrap; overflow-x: auto; -webkit-overflow-scrolling: touch; }
/* Settings: three folds (details.setGroup > summary > h2, R3-cd-7); the summary is a plain heading row with the same chevron as the danger zone */
.settingsCol > details.setGroup { display: flex; flex-direction: column; gap: var(--s4); margin: 0; }
details.setGroup > summary { list-style: none; display: flex; align-items: center; justify-content: space-between; min-height: var(--tap); margin: var(--s3) 0 0; cursor: pointer; }
details.setGroup > summary::-webkit-details-marker { display: none; }
details.setGroup > summary > h2 { margin: 0; font-size: var(--fs-md); font-weight: var(--fw-b); }
details.setGroup > summary::after { content: ""; flex: none; width: 8px; height: 8px; border: solid var(--muted); border-width: 0 2px 2px 0; transform: rotate(-45deg); transition: transform var(--d-fast) var(--ease-std); }
details.setGroup[open] > summary::after { transform: rotate(45deg); }
details.setGroup[open] > summary { margin-bottom: var(--s2); }
details.setGroup > .setCard + .setCard { margin-top: var(--s3); }
/* the language choice: one quiet row above the footer on every page (R3-cd-5) */
.langFoot { max-width: var(--content); margin: var(--s6) auto 0; display: flex; align-items: center; gap: var(--s2); }
.langFoot #lang { min-height: var(--tap); border-color: transparent; background: transparent; color: var(--muted); font-size: var(--fs-sm); min-width: 0; }
.langFoot #lang:hover, .langFoot #lang:focus-visible { border-color: var(--line); color: var(--ink); }
.langFoot + .foot { margin-top: var(--s2); }
/* "Hide done tasks": a small text link that is a switch; plain look, full 44 px tap box */
button.hideDone { display: inline-flex; align-items: center; min-height: var(--tap); padding: 0 var(--s2); border: 0; background: none; font: inherit; font-size: var(--fs-sm); color: var(--muted); text-decoration: underline; text-underline-offset: 3px; cursor: pointer; }
button.hideDone[aria-checked="true"] { color: var(--ink); font-weight: var(--fw-b); }
button.hideDone[aria-checked="true"]::before { content: "\2713\00a0"; content: "\2713\00a0" / ""; }   /* the tick is decoration: the alternative text keeps it out of the button name (L12-8) */
button.hideDone:is(:hover, :focus-visible) { color: var(--ink); }

/* ---- round 1 of the launch loops (job C: the app shell) ---- */
/* L01-A7: a quiet text link, for the choice that is not the main action ("Use another e-mail", "See Plus") */
.quietLink { display: inline-flex; align-items: center; min-height: var(--tap); padding: 0 var(--s2); border: 0; background: none; font: inherit; font-size: var(--fs-sm); color: color-mix(in srgb, var(--accent) 65%, var(--ink)); text-decoration: underline; text-underline-offset: 3px; cursor: pointer; }
.quietLink:hover { color: var(--ink); }
/* L05-I1 and L01-4: the sign-in step: birth month and year first, then a short honest strip, then the e-mail */
#preAge .row { margin: var(--s2) 0 0; align-items: flex-end; }
#preAgeLive { margin: var(--s1) 0 0; }
#preAgeMsg:not([hidden]) { display: block; margin: var(--s2) 0; }
.trustStrip { margin: var(--s3) 0 var(--s2); font-size: var(--fs-sm); color: var(--muted); }
.trustStrip ul { list-style: none; margin: 0 0 var(--s2); padding: 0; display: grid; gap: var(--s1); }
.trustStrip li, .trustStrip .trustLine { position: relative; padding-inline-start: var(--s4); margin: 0; }
.trustStrip li::before, .trustStrip .trustLine::before { content: "\2713"; position: absolute; inset-inline-start: 0; color: var(--ok); }
.trustMore { margin: 0; }
#blockedRow > p { margin: 0 0 var(--s2); }
/* L01-5: one clear next step on Today; L01-2: the phone's "import on your computer first" line can be read */
.firstWin { margin: var(--s3) 0 var(--s2); font-weight: var(--fw-b); }
.banner.upd.is-note { justify-content: flex-start; text-align: start; background: var(--accent-soft); padding: var(--s2) var(--s3); color: var(--ink); font-size: var(--fs-sm); }
/* L04-1: the maths row sits after the task list, outside the card */
.mathRow { margin: var(--s3) 0; }
/* L04-A1: the chip row says there is more beyond its edge */
#testPicker[data-more-end="1"] { -webkit-mask-image: linear-gradient(to right, black calc(100% - 28px), transparent); mask-image: linear-gradient(to right, black calc(100% - 28px), transparent); }
#testPicker[data-more-start="1"] { -webkit-mask-image: linear-gradient(to left, black calc(100% - 28px), transparent); mask-image: linear-gradient(to left, black calc(100% - 28px), transparent); }
#testPicker[data-more-start="1"][data-more-end="1"] { -webkit-mask-image: linear-gradient(to right, transparent, black 28px, black calc(100% - 28px), transparent); mask-image: linear-gradient(to right, transparent, black 28px, black calc(100% - 28px), transparent); }
/* L04-11: the Material summary is a strip with the page's soft colour, the filter has a visible name and the full width */
#materialSummary { padding: var(--s2) var(--s3); border-radius: var(--r-s); background: var(--accent-soft); }
.filterBox { display: block; flex: 1 1 100%; margin: 0; }
.filterBox > input { width: 100%; }
/* L04-6 / L04-7: Settings groups look alike: the Danger zone row has the weight, size and colour of the other group headings */
#dangerBox > summary { color: var(--ink); font-size: var(--fs-md); font-weight: var(--fw-b); }
/* L04-9: on a phone the grading-scale name sits above its select, not squeezed beside it */
@media (max-width: 480px) {
  #scaleBox label.inline { flex-direction: column; align-items: stretch; gap: var(--s1); }
  #scaleBox label.inline > span { display: block; }
  #scaleBox select { width: 100%; }
}
/* L01-A8 and L01-A9: on a phone the round chat button never sits on the last Get started row; the Today header takes less height */
@media (max-width: 640px), (pointer: coarse) {
  main[data-onboard] #startCard { margin-bottom: calc(var(--tap) + var(--s3)); }
  .view[data-view="today"] > h1 { margin-block: var(--s2) var(--s1); }
  .banner.upd { margin-top: 0; }
  .toast { width: calc(100vw - 24px); }   /* the message gets the whole width (loop L04-8) */
}
/* the plan card's Free / Trial / Plus table (planCompare 'table', loop L09-6): plain numbers, no colour of its own */
.planCmp { width: 100%; border-collapse: collapse; font-size: var(--fs-sm); margin: var(--s2) 0; }
.planCmp th, .planCmp td { padding: var(--s1) var(--s2); text-align: center; border-bottom: 1px solid var(--line); }
.planCmp tbody th { text-align: start; font-weight: normal; color: var(--muted); }
.planCmp thead th { font-weight: var(--fw-b); }

/* ---- round 3, job AX: accessibility (L12-1, 4, 6, 8, 10, 11, A6) ---- */
/* L12-1: the edge of a field is its only edge, so it is 3:1 against the card in both themes (--field-line is --off, tokens.css) */
input[type="checkbox"], input[type="radio"], input[type="range"] { border-color: initial; }
/* L12-4: a control that is busy or saving is aria-disabled, not disabled: focus stays on it and it looks the same as a disabled one */
.btn[aria-disabled="true"] { opacity: 1; color: var(--muted); background: var(--bg); cursor: default; }
.btn.primary[aria-disabled="true"] { background: var(--accent-soft); color: color-mix(in srgb, var(--accent) 75%, var(--ink)); border-color: var(--accent); }
.switch[aria-disabled="true"] { cursor: progress; }
/* L12-6: focus never ends up under a fixed bar (2.4.11): the sticky progress bar (about 100 px) and the phone's tab bar plus chat button */
li.task, #taskList > li { scroll-margin-top: calc(var(--header-h) + 100px); }
@media (max-width: 640px) { li.task, #taskList > li { scroll-margin-top: 110px; } html { scroll-padding-bottom: calc(var(--bar-h) + var(--s7) + var(--s4) + var(--safe-b)); } }
/* L12-10: Windows High Contrast: state that only a background drew is given a border or a system colour */
@media (forced-colors: active) {
  .switch span { border: 2px solid ButtonText; forced-color-adjust: none; background: Canvas; }
  .switch span::after { background: ButtonText; }
  .switch input:checked + span { background: Highlight; }
  .switch input:checked + span::after { background: HighlightText; }
  .bar, .card .bar { border: 1px solid CanvasText; background: Canvas; }
  .bar > i, .card .bar > i { background: Highlight; forced-color-adjust: none; }
  .tab[aria-current="page"] { border-bottom: 3px solid Highlight; }
  .chip[aria-pressed="true"] { border-width: 3px; }
  .btn[aria-disabled="true"], .btn[disabled] { color: GrayText; border-color: GrayText; }
  select, input, textarea { border-color: ButtonText; }
  .toast { border: 1px solid CanvasText; }
  button.hideDone[aria-checked="true"] { text-decoration: underline; }
}
