
html[data-chess-board="pulsar"]{--chess-light:#eeecfb;--chess-dark:#9d95e0;--chess-coordinates:#3d3790}
html[data-chess-board="forest"]{--chess-light:#eeeed2;--chess-dark:#769656;--chess-coordinates:#283d20}
html[data-chess-board="walnut"]{--chess-light:#f0d9b5;--chess-dark:#b58863;--chess-coordinates:#553c24}
html[data-chess-board="ocean"]{--chess-light:#deeaf3;--chess-dark:#7195b5;--chess-coordinates:#203c56}
html[data-chess-board="slate"]{--chess-light:#ececec;--chess-dark:#999fa7;--chess-coordinates:#303640}
:root{--bg:#0d1333;--bg2:#141c4a;--card:#fff;--ink:#161b3a;--muted:#5d6489;--line:#e6e4f4;--soft:#f6f5fd;--on-bg:#f4f2ff;--on-bg-muted:#aab0d6;
--accent:#ffb627;--accent-deep:#d98a00;--accent-soft:#fff6e0;--accent-ink:#2b1d00;--ok:#1fae7a;--ok-soft:#e3f8ef;--warn:#e5486b;--warn-soft:#fde8ee;
--violet:#7c6cff;--radius:24px;--shadow:0 1px 0 rgba(13,19,51,.04),0 14px 28px -16px rgba(13,19,51,.55);--stars:url("data:image/svg+xml,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20width%3D%22240%22%20height%3D%22240%22%3E%3Cg%20fill%3D%22%23fff%22%3E%3Ccircle%20cx%3D%2218%22%20cy%3D%2230%22%20r%3D%221.2%22%20opacity%3D%22.7%22%2F%3E%3Ccircle%20cx%3D%2296%22%20cy%3D%2212%22%20r%3D%22.8%22%20opacity%3D%22.5%22%2F%3E%3Ccircle%20cx%3D%22170%22%20cy%3D%2248%22%20r%3D%221.4%22%20opacity%3D%22.8%22%2F%3E%3Ccircle%20cx%3D%22214%22%20cy%3D%22140%22%20r%3D%22.9%22%20opacity%3D%22.5%22%2F%3E%3Ccircle%20cx%3D%22130%22%20cy%3D%22110%22%20r%3D%221%22%20opacity%3D%22.6%22%2F%3E%3Ccircle%20cx%3D%2252%22%20cy%3D%22160%22%20r%3D%221.3%22%20opacity%3D%22.7%22%2F%3E%3Ccircle%20cx%3D%22190%22%20cy%3D%22208%22%20r%3D%221%22%20opacity%3D%22.6%22%2F%3E%3Ccircle%20cx%3D%2286%22%20cy%3D%22222%22%20r%3D%22.8%22%20opacity%3D%22.4%22%2F%3E%3Ccircle%20cx%3D%2224%22%20cy%3D%2298%22%20r%3D%22.7%22%20opacity%3D%22.4%22%2F%3E%3C%2Fg%3E%3C%2Fsvg%3E")}
*{box-sizing:border-box}
html{touch-action:pan-x pan-y;scroll-behavior:smooth;scroll-padding-top:12px;font-size:20px;-webkit-text-size-adjust:100%;scrollbar-gutter:stable;scrollbar-width:thin;scrollbar-color:var(--accent) transparent;-webkit-tap-highlight-color:transparent}
/* no grey tap rectangle on Android: buttons press down instead (:active), keyboard focus outlines stay */
::-webkit-scrollbar{width:8px;height:8px}
::-webkit-scrollbar-track{background:transparent}
::-webkit-scrollbar-thumb{background:var(--accent);border-radius:8px;border:2px solid transparent;background-clip:padding-box}
::-webkit-scrollbar-thumb:hover{background:var(--accent-deep);background-clip:padding-box}
/* The page is at least as tall as what shows, measured as the root's own height (html 100%, the visible area), not with
   viewport units: 100vh is the height with a phone browser's bar hidden, and in the installed app on mtib's Android
   100dvh was 1088px for a 1032px screen (it counted the area behind the navigation buttons), so a short page's footer
   sat 56px below the screen (viewport probe, 2026-10-25). body is a column; main grows to fill it */
html{height:100%}
body{margin:0;min-height:100%;display:flex;flex-direction:column;font-family:"Nunito",ui-rounded,"Segoe UI",system-ui,sans-serif;line-height:1.45;color:var(--on-bg);
background:var(--bg)}
/* the stars on their own layer behind the page, drifting slower than it (parallax, pwa.ts): one star tile taller than
   the screen, so moving it by up to a tile never shows its edge */
.sky{position:fixed;top:0;left:0;right:0;height:calc(100vh + 240px);background:var(--stars) repeat;z-index:-1;pointer-events:none;will-change:transform}
main{max-width:660px;margin:0 auto;padding:12px 16px}
header{display:flex;align-items:center;justify-content:space-between;gap:12px;padding:10px 0 18px;flex-wrap:wrap}
.brand{display:flex;align-items:center;gap:10px;font-weight:900;font-size:1.35rem;letter-spacing:-.01em;color:var(--on-bg);text-decoration:none}
nav.tabs{display:flex;gap:4px;flex-wrap:wrap;max-width:100%}
nav.tabs a{color:var(--on-bg);font-weight:800;text-decoration:none;padding:8px 14px;border-radius:999px}
nav.tabs a:hover,nav.tabs a[aria-current]{background:rgba(255,255,255,.12)}
/* Count: a small red pill; on a tab it hangs on the top-right corner, so the header doesn't get wider */
.count{display:inline-grid;place-items:center;min-width:20px;height:20px;padding:0 6px;border-radius:999px;background:var(--warn);color:#fff;font-size:.72rem;font-weight:900;line-height:1;margin-left:8px;vertical-align:middle}
nav.tabs a{position:relative}nav.tabs a .count{position:absolute;top:-4px;right:-6px;margin:0;box-shadow:0 0 0 2px var(--bg)}
/* the profile is a round user icon at the right end of the brand row (the Me tab used to be the fourth tab) */
a.me{position:relative;order:2;display:grid;place-items:center;width:48px;height:48px;border-radius:50%;color:var(--on-bg);background:rgba(255,255,255,.12)}
a.me:hover,a.me[aria-current]{background:rgba(255,255,255,.24)}a.me .count{position:absolute;top:-2px;right:-4px;margin:0;box-shadow:0 0 0 2px var(--bg)}
/* on a phone the tabs wrap under the brand: spread them over the full width with tighter padding so three fit */
/* logo and profile on top, the tabs in a row of their own below, as soon as the page column gets narrower than its full
   width (628px inside main's padding; mtib, 2026-10-25, was a 520px media query). A container query, since a media
   query counts the scrollbar's gutter, which differs by browser; on the header itself (as wide as the column), not on
   main, so the fixed banners inside main stay placed against the screen in every browser */
header{container-name:top;container-type:inline-size}
@container top (max-width:627.5px){header .brand{margin-right:auto}nav.tabs{order:3;flex:1 1 100%;justify-content:space-between;gap:2px}nav.tabs a{padding:8px 10px}}
@media (max-width:360px){nav.tabs a{padding:8px 6px;font-size:.92rem}}

/* the pulsar: core, two rotating beams, expanding rings */
.pulsar{display:block;overflow:visible}
.pulsar .beams{animation:pulsar-spin 5s linear infinite}
.pulsar .ring{animation:pulsar-ring 2.6s cubic-bezier(.2,.6,.3,1) infinite;opacity:0}
.pulsar .ring.r2{animation-delay:1.3s}
.pulsar .core{animation:pulsar-beat 2.6s ease-in-out infinite}
@keyframes pulsar-spin{to{transform:rotate(360deg)}}
@keyframes pulsar-ring{0%{transform:scale(1);opacity:.9}100%{transform:scale(3.1);opacity:0}}
@keyframes pulsar-beat{0%,100%{transform:scale(1)}12%{transform:scale(1.12)}24%{transform:scale(1)}}
.pulsar.off .ring{display:none}
.pulsar.off .beams{animation-duration:60s}
.pulsar.off .beams path{fill:#b9bdd6}
.pulsar.off .core{fill:#9aa0c3;animation:none}
.pulsar.off .center{fill:#e4e6f2}
.wordmark{display:inline-flex;align-items:baseline;gap:.35em}
/* same size, weight and baseline as the app name next to it */
.streak{display:inline-block;font-size:1em;font-weight:900;line-height:1;padding:.12em .42em .14em;border-radius:999px;
background:#c9cce0;color:#4b5175;font-variant-numeric:tabular-nums}
.streak.on{background:var(--accent);color:var(--accent-ink)}
/* the page is at least the screen's height and the footer takes the space left, so on a short page Changelog and
   Report sit at the bottom of the screen (mtib, 2026-10-25); padding, not margin, as flex items' margins don't collapse */
main:not(.app){flex:1 0 auto;width:100%;display:flex;flex-direction:column}main:not(.app)>*{flex:0 0 auto}
.foot{display:flex;justify-content:center;align-items:flex-end;flex-wrap:wrap;gap:4px;margin:auto 0 0;padding:8px 0 4px}
.foot>a{display:inline-block;padding:12px 18px;border-radius:999px;color:var(--on-bg-muted);font-weight:700;text-decoration:none}
.foot>a:hover,.foot>a:focus-visible,.foot>a[aria-current]{background:var(--bg2);color:var(--on-bg)}
@media (prefers-reduced-motion: reduce){.pulsar *{animation:none!important}.pulsar .ring{display:none}}

.card{background:var(--card);color:var(--ink);border-radius:var(--radius);padding:22px;margin:0 0 16px;box-shadow:var(--shadow)}
h1{font-size:1.65rem;line-height:1.2;margin:0 0 12px;font-weight:900;letter-spacing:-.015em}
h2{font-size:1.2rem;margin:0 0 12px;font-weight:900}
p{margin:0 0 12px}
.muted{color:var(--muted)}
.big{font-size:1.2rem}
label{display:block;font-weight:800;margin:16px 0 6px}
input[type=text],input[type=email],input[type=search],input[type=number],input[type=datetime-local],input[inputmode],textarea,select{width:100%;font:inherit;padding:14px 16px;border:2px solid var(--line);
border-radius:16px;background:var(--soft);color:var(--ink);transition:border-color .15s,background .15s}
input:focus,textarea:focus,select:focus{outline:none;border-color:var(--accent);background-color:#fff}
/* a file field like the other fields, its button like a small quiet button (Settings, Chat emoji) */
input[type=file]{width:100%;font:inherit;font-size:.9rem;color:var(--muted);padding:8px;border:2px solid var(--line);border-radius:16px;background:var(--soft);cursor:pointer}
input[type=file]::file-selector-button{font:inherit;font-weight:900;color:var(--ink);margin:0 12px 0 0;padding:10px 16px;border:2px solid var(--line);border-radius:12px;background:#fff;box-shadow:0 3px 0 var(--line);cursor:pointer}
input[type=file]:focus{outline:none;border-color:var(--accent)}
/* one bold chevron in the ink colour instead of the browser's small caret (selects sit on white cards in both themes) */
select{appearance:none;-webkit-appearance:none;padding-right:52px;background-image:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3E%3Cpath d='M3 6l5 5 5-5' fill='none' stroke='%23161b3a' stroke-width='2.6' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E");background-repeat:no-repeat;background-position:right 20px center;background-size:16px}
textarea{min-height:110px}
.choices{display:grid;gap:10px}
.choices label{display:flex;align-items:center;gap:12px;margin:0;font-weight:700;padding:12px 16px;border:2px solid var(--line);border-radius:16px;cursor:pointer;background:#fff}
.choices input{width:22px;height:22px;margin:0;flex:0 0 auto;accent-color:var(--accent-deep)}
.choices label:has(input:checked){border-color:var(--accent);background:var(--accent-soft)}
button,.button{display:inline-flex;align-items:center;justify-content:center;gap:8px;min-height:56px;padding:0 24px;font:inherit;font-weight:900;
text-align:center;border:0;border-radius:18px;background:var(--accent);color:var(--accent-ink);box-shadow:0 4px 0 var(--accent-deep);cursor:pointer;text-decoration:none;
margin-top:14px;transition:transform .06s,box-shadow .06s,filter .15s}
button:hover,.button:hover{filter:brightness(1.04)}
button:active,.button:active{transform:translateY(3px);box-shadow:0 1px 0 var(--accent-deep)}
button:disabled{filter:grayscale(.6);opacity:.7;cursor:default}
button:focus,.button:focus{outline:none}
/* the ring hugs the button: an offset left a white gap on three sides (mtib, 2026-11-01) */
button:focus-visible,.button:focus-visible{outline:3px solid var(--violet);outline-offset:0}
dialog.picture-viewer button:focus-visible,dialog.picture-viewer .button:focus-visible{box-shadow:none}
/* a stack of buttons (or button forms): one gap between them, the first one keeps the usual space above */
.actions{display:grid;gap:12px;margin-top:14px}
.admin-hub h2{margin:30px 0 0}.admin-hub h1+h2{margin-top:18px}
.actions>*,.actions>form>button{margin:0}
.actions>form>button,.actions>.button,.actions>button{width:100%}
label.picker{margin:0;font-size:.85rem;color:var(--muted)}
label.picker select{margin-top:6px;min-height:56px;padding:0 52px 0 24px;font-weight:900;font-size:1rem;color:var(--ink);background-color:#fff;border-radius:18px;box-shadow:0 4px 0 var(--line);cursor:pointer}
/* exercise layout: one consistent gap between controls, no stacked margins */
.stack{display:grid;gap:14px}
.stack>*{margin:0}
.stack .row>*{margin:0}
.stack>div:empty,.stack>p:empty{display:none}
/* whatever follows a form in a card keeps the stack's gap from its last button (and that button's bottom edge): forms
   have no margin, so a line under one sat on the button (admin clips, mtib 2026-11-01) */
.card>form+*{margin-top:14px}
.card>.notes{margin:12px 0 0;gap:8px}.notes>#explain{font-weight:700}
.stack .flash{margin:0}
button.quiet,.button.quiet{background:#fff;color:var(--ink);box-shadow:0 4px 0 var(--line);border:2px solid var(--line)}
button.quiet:active,.button.quiet:active{box-shadow:0 1px 0 var(--line)}
.wide{width:100%}
a{color:var(--violet)}
ul.lessons{list-style:none;padding:0;margin:0;display:grid;grid-template-columns:minmax(0,1fr);gap:10px}
ul.lessons li{display:flex;align-items:center;justify-content:space-between;gap:12px;padding:12px 14px;border:2px solid var(--line);border-radius:18px}
ul.lessons li>span,ul.lessons li>div{flex:1 1 auto;min-width:0;overflow-wrap:break-word}
/* dictionary rows: the word, then its meaning and other meanings, each on its own line */
ul.words-list{margin-top:16px}ul.words-list li>div>span{display:block}
ul.lessons li .button,ul.lessons li>form>button{margin:0;min-height:48px;flex:0 0 auto;white-space:nowrap}
ul.lessons li>form{margin:0;flex:0 0 auto}
ul.lessons li.done{background:var(--soft)}
ul.lessons li.done span{color:var(--muted)}
/* a recorded call (ADR 0138): the video, the transcript that follows it (one language shown at a time), the steps */
.rec-video{display:block;width:100%;max-height:70vh;background:#000;border-radius:14px}
ul.transcript{list-style:none;margin:10px 0;padding:0;max-height:50vh;overflow-y:auto;position:relative;border:2px solid var(--line);border-radius:18px}
ul.transcript li{border-bottom:1px solid var(--line)}ul.transcript li:last-child{border-bottom:0}ul.transcript li.now{background:var(--soft)}
ul.transcript .tline{display:block;width:100%;margin:0;box-shadow:none;border-radius:0;transform:none;font-weight:600;padding:10px 14px;text-align:left;background:none;border:0;color:inherit;font:inherit;min-height:48px;cursor:pointer}
ul.transcript .tl{display:block}ul.transcript [data-l]{display:none}
ul.transcript[data-lang=und] [data-l=und],ul.transcript[data-lang=en] [data-l=en],ul.transcript[data-lang=de] [data-l=de]{display:inline}
ul.steps{list-style:none;margin:0;padding:0;display:grid;gap:8px}
.tag{display:inline-block;font-size:.72rem;font-weight:900;color:var(--muted);text-transform:uppercase;letter-spacing:.06em}
.msg{padding:12px 14px;border-radius:18px;margin:0 0 8px;white-space:pre-wrap}
body:has(main.app){overflow:hidden;overscroll-behavior:none}
/* the chat follows the keyboard at once: no smooth scrolling for iOS's pan to the field or for the fit (mtib, 2026-10-24) */
/* the page itself never scrolls (the log does): no gutter kept for a page scrollbar (mtib, 2026-10-25) */
html:has(main.app){scroll-behavior:auto;scrollbar-gutter:auto;overflow:hidden}
/* the same 12px on every side: the home bar's inset made the bottom twice the sides (mtib, 2026-10-25) */
main.app{position:fixed;top:0;left:0;right:0;bottom:0;height:auto;display:flex;flex-direction:column;padding:12px}
/* the keyboard left the messages too little room (family-chat.ts, fit): the heading and the way back make room */
main.app.cramped .chat-head{display:none}
/* position: the voice recorder overlays its bottom; the messages keep 8px at the sides, the heading and the field 12px
   (mtib, 2026-10-25) */
.chat-card{position:relative;flex:1 1 auto;min-height:0;display:flex;flex-direction:column;margin:0;padding:14px 8px 12px}.chat-card>.chat-head,.chat-card>.chat-compose{padding-left:4px;padding-right:4px}.chat-head{display:flex;align-items:center;gap:8px;margin:0 0 6px}.chat-head>h1{flex:1 1 auto;min-width:0;font-size:1.35rem;margin:0}
/* the way back, before the heading: a small quiet pill (the chat has no header or tabs) */
.chat-head>a.chat-back{flex:0 0 auto;display:inline-flex;align-items:center;min-height:36px;padding:0 12px;margin:0;border-radius:999px;background:var(--soft);color:var(--muted);font-weight:800;font-size:.85rem;text-decoration:none}
button.info-button,a.info-button{text-decoration:none;display:inline-flex;align-items:center;justify-content:center;width:36px;height:36px;min-height:0;padding:0;margin:0 0 0 4px;vertical-align:middle;background:none;border:0;box-shadow:none;border-radius:50%;color:var(--muted)}
button.info-button:hover,button.info-button:focus-visible,a.info-button:hover,a.info-button:focus-visible{background:var(--soft);color:var(--ink)}
/* the family's video call (ADR 0131): everyone's picture in a grid that fills the screen, and the controls under it */
html.in-call main.app{height:auto;bottom:0;max-width:none;margin:0}
.call-debug{position:fixed;left:6px;right:6px;top:6px;z-index:50;margin:0;padding:4px 8px;border-radius:8px;background:rgba(0,0,0,.75);color:#fff;font-size:.7rem;white-space:pre-wrap}
.call{flex:1 1 auto;min-height:0;display:flex;flex-direction:column;gap:10px;padding-top:max(0px,env(safe-area-inset-top))}
.call-status{flex:0 0 auto;margin:0;text-align:center;color:#fff;font-weight:800}.call-status .flash{margin:0}
.call-stage{position:relative;flex:1 1 auto;min-height:0;display:flex;flex-direction:column}
.call-dock{position:relative;flex:0 0 auto}
.call-menu{position:fixed;z-index:10;max-height:70dvh;overflow-y:auto;display:grid;gap:8px;padding:8px;border-radius:18px;background:var(--card);color:var(--ink);box-shadow:0 8px 28px rgba(0,0,0,.4)}
.call-menu[hidden]{display:none}
.call-menu-card{padding:4px;border:2px solid var(--line);border-radius:14px;background:var(--bg-soft,rgba(0,0,0,.03))}
.call-menu-head{margin:8px 10px 2px;font-size:.78rem;font-weight:900;text-transform:uppercase;letter-spacing:.06em;color:var(--muted)}
button.call-menu-row{display:flex;justify-content:space-between;align-items:center;width:100%;min-height:48px;margin:0;padding:0 12px;border:0;border-radius:12px;background:none;box-shadow:none;color:var(--ink);font-size:1rem;font-weight:700;text-align:left}
button.call-menu-row.on{background:var(--accent-soft)}button.call-menu-row .tick{font-weight:900}button.call-menu-row.disabled{opacity:.45}
.call-menu-note{margin:6px 12px;font-size:.85rem;color:var(--muted)}
/* the tiles are placed by the page's script (call-layout.ts, ADR 0153): rows of equal tiles, a single shared screen on a stage */
.call-grid{position:relative;flex:1 1 auto;min-height:0}
.call-tile{position:absolute;left:0;top:0;width:0;height:0;border-radius:14px;overflow:hidden;background:#0c1028;outline:3px solid transparent}.call-tile.speaking{outline-color:var(--accent)}
.call-tile video{display:block;width:100%;height:100%;object-fit:cover}.call-tile.screen video{object-fit:contain}.call-tile.camera-off video{visibility:hidden}
.call-tile .initial{position:absolute;inset:0;display:none;align-items:center;justify-content:center;font-size:clamp(2rem,12vw,4rem);font-weight:900;color:#fff;opacity:.75}.call-tile.camera-off .initial{display:flex}
.call-tile .who{position:absolute;left:8px;bottom:6px;padding:2px 8px;border-radius:999px;background:rgba(5,8,26,.6);color:#fff;font-size:.85rem;font-weight:800}
.call-tile .muted-badge{position:absolute;right:8px;top:8px;padding:2px 8px;border-radius:999px;background:rgba(5,8,26,.7);color:#fff;font-size:.75rem;font-weight:800}
.call-bar{flex:0 0 auto;display:flex;justify-content:center;gap:8px;padding-bottom:max(4px,env(safe-area-inset-bottom))}
button.call-control{display:flex;flex-direction:column;align-items:center;justify-content:center;gap:4px;flex:1 1 0;max-width:120px;min-height:64px;margin:0;padding:8px 6px;border-radius:16px;font-size:.8rem;font-weight:800}
button.call-control.off{background:#fff;color:var(--ink)}button.call-control.on{background:var(--accent);color:var(--accent-ink)}button.call-control.leave{background:var(--warn);color:#fff}
/* wide screens: the two icon buttons sit in the row, no caret and the whole title; narrow ones (and high zoom) fold them away */
.chat-tools{display:contents}.chat-head>h1 .short,.chat-head>.tools-toggle{display:none}
@media (max-width:430px){
.chat-head>h1 .full{display:none}.chat-head>h1 .short{display:inline}
.chat-head>.tools-toggle{display:inline-flex}.chat-head>.tools-toggle svg{transition:transform .15s}.chat-head.tools-open>.tools-toggle svg{transform:rotate(180deg)}
.chat-head>.chat-tools{display:none;flex:1 1 auto;min-width:0;align-items:center;gap:4px}
.chat-head.tools-open>.chat-tools{display:flex}.chat-head.tools-open>h1{display:none}
a.chat-call{padding:0 10px}
}
/* the call button next to the title (ADR 0131): the number is who is in the call now, and it lights up while someone is */
a.chat-call{position:relative;display:inline-flex;align-items:center;justify-content:center;gap:6px;flex:0 0 auto;min-height:36px;margin:0 0 0 4px;padding:0 14px;border-radius:999px;background:var(--soft);color:var(--ink);font-weight:800;font-size:.9rem;text-decoration:none}
a.chat-call:hover,a.chat-call:focus-visible{background:var(--accent-soft)}a.chat-call.live{background:var(--accent-soft)}
a.chat-call .count{position:absolute;top:-6px;right:-4px;margin:0;box-shadow:0 0 0 2px var(--card)}
.files-list h3{margin:14px 0 6px;font-size:.9rem;color:var(--muted)}.files-list h3:first-child{margin-top:0}
.files-list .grid{display:grid;grid-template-columns:repeat(auto-fill,minmax(110px,1fr));gap:4px}
.files-list h3{position:sticky;top:0;background:var(--card);padding:6px 0;z-index:1}
a.tile{position:relative;display:flex;aspect-ratio:1;overflow:hidden;border-radius:10px;background:var(--soft);color:var(--ink);text-decoration:none}
a.tile>img{width:100%;height:100%;object-fit:cover}
a.tile>.who{position:absolute;left:0;right:0;bottom:0;padding:14px 6px 4px;background:linear-gradient(transparent,rgba(5,8,26,.7));color:#fff;font-size:.72rem;font-weight:800;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
a.tile{-webkit-touch-callout:none;user-select:none;-webkit-user-select:none}
a.tile>.check{position:absolute;top:6px;left:6px;width:22px;height:22px;border-radius:50%;border:2px solid #fff;background:rgba(5,8,26,.35);display:none}
.files-list.picking a.tile>.check{display:block}a.tile.picked{outline:3px solid var(--accent);outline-offset:-3px}a.tile.picked>.check{background:var(--accent) url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'%3E%3Cpath d='M5 12.5l4.5 4.5L19 7.5' fill='none' stroke='%232b1d00' stroke-width='3' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E") center/16px no-repeat;border-color:var(--accent)}
.files-bar{flex:0 0 auto;display:flex;align-items:center;gap:8px;padding:10px 4px 0}.files-bar>span{flex:1 1 auto;font-weight:800}.files-bar>span,.files-bar>a.button,.files-bar>button{white-space:nowrap}.files-bar>a.button,.files-bar>button{margin:0;width:auto;padding-left:14px;padding-right:14px}.files-bar>span{font-size:.9rem}
a.tile>.play{position:absolute;left:50%;top:50%;width:44px;height:44px;margin:-22px 0 0 -22px;display:flex;align-items:center;justify-content:center;border-radius:50%;background:rgba(5,8,26,.6);color:#fff;pointer-events:none}a.tile>.play svg{width:24px;height:24px}
a.tile.doc{flex-direction:column;align-items:center;justify-content:center;gap:4px;padding:8px 6px 22px;text-align:center}a.tile.doc>.icon{color:var(--muted)}a.tile.doc>.name{user-select:none;-webkit-user-select:none;font-size:.72rem;font-weight:800;overflow-wrap:anywhere;max-height:3.2em;overflow:hidden}a.tile.doc>.who{background:none;color:var(--muted)}
dialog.info-dialog{max-width:min(560px,calc(100vw - 32px));max-height:calc(100dvh - 48px);overflow:hidden;border:0;margin:auto}dialog.info-dialog[open]{display:flex;flex-direction:column}
/* the text scrolls inside the card: reaching into its right padding puts the scrollbar 8px from the edge */
dialog.info-dialog>.info-body{flex:1 1 auto;min-height:0;overflow-y:auto;overscroll-behavior:contain;margin-right:-14px;padding-right:14px}dialog.info-dialog>h2,dialog.info-dialog>form{flex:0 0 auto}dialog.info-dialog::backdrop{background:rgba(8,12,38,.6)}
dialog.info-dialog h2{outline:none}dialog.info-dialog p{margin:0 0 12px}dialog.info-dialog form{margin:16px 0 0}
/* on a touch screen the log scrolls without a scrollbar (mtib, 2026-10-25); a mouse keeps it */
@media (pointer:coarse){.chat-log{scrollbar-width:none}.chat-log::-webkit-scrollbar{display:none}}
/* the card keeps 8px at the sides; the log reaches 4px into the right one, so its scrollbar runs 4px from the card's
   edge with 4px to the messages (mtib, 2026-10-25) */
.chat-log{-webkit-user-select:none;user-select:none;-webkit-touch-callout:none;flex:1 1 auto;min-height:0;overflow-y:auto;display:flex;flex-direction:column;overscroll-behavior:contain;margin-right:-4px;padding-right:4px}.chat-log>*:first-child{margin-top:auto}
.chat-log .chat-name{display:flex;flex-wrap:wrap;align-items:baseline;margin:12px 0 -4px;color:var(--ink);opacity:.8;font-weight:800;font-size:.85rem}.chat-log .msg{margin:0;display:flex;align-items:flex-end;gap:2px}.chat-log .msg>span{flex:1 1 auto;min-width:0}.chat-log .msg.more{margin-top:-5px}
.chat-log .chat-name button.place{display:inline-flex;align-items:center;min-height:0;height:22px;padding:0 8px;margin:0 0 0 6px;vertical-align:1px;border:0;box-shadow:none;border-radius:999px;font-size:.72rem;font-weight:900;opacity:1}
.chat-log .chat-name button.place.gold{background:#fff3c4;color:#8a5a00}.chat-log .chat-name button.place.silver{background:#e6e9f6;color:#4b5170}.chat-log .chat-name button.place.bronze{background:#fbe3cf;color:#7a4318}
.chat-log .chat-name .medal-tip{order:2;flex:0 0 100%;margin:2px 0 6px;font-weight:600;color:var(--muted);opacity:1}
.chat-log .chat-system{margin:10px 0;text-align:center;font-weight:800;color:var(--muted)}.chat-log .chat-system .goal-head{font-weight:600;font-size:.8rem}.chat-log .chat-system .goal-what{font-weight:700;font-size:.9rem}.chat-log .chat-system .small{font-weight:600;opacity:.75}
.chat-log .call-card{margin:12px auto;max-width:360px;padding:14px 16px;border-radius:20px;background:var(--accent-soft);color:var(--ink);text-align:left}
.call-card-text{display:flex;flex-direction:column;gap:2px;font-size:1.05rem;text-align:center}.call-card-text .small{font-weight:600;color:var(--muted);font-variant-numeric:tabular-nums}
a.call-join{display:flex;align-items:center;justify-content:center;width:max(80%,min(100%,260px));height:56px;margin:12px auto 0;border-radius:999px;background:var(--accent);color:var(--accent-ink);text-decoration:none;box-shadow:0 3px 0 var(--accent-deep)}a.call-join:active{transform:translateY(2px);box-shadow:none}
.call-card-people{margin:10px 0 0;text-align:center;font-weight:700;overflow-wrap:anywhere}
.chat-log .chat-name.mine{justify-content:flex-end}.chat-log .chat-when{order:1;margin-left:8px;font-size:.75rem;font-weight:600;color:var(--muted)}.chat-log .chat-name.mine .chat-when{order:-1;margin:0 8px 0 0}.chat-log .chat-system.milestone{font-weight:700;font-size:.85rem;opacity:.85}.chat-log .msg.waiting{color:var(--muted);font-style:italic}
.chat-log>.chat{padding-top:6px;grid-template-columns:minmax(0,1fr)}
.msg button.lang-toggle{display:inline-flex;flex:0 0 auto;align-items:center;justify-content:center;width:34px;height:30px;min-height:0;padding:0;margin:0 -6px -6px 0;vertical-align:bottom;background:none;border:0;box-shadow:none;border-radius:10px;color:var(--muted);opacity:.7}
.msg button.lang-toggle:hover,.msg button.lang-toggle:focus-visible{opacity:1;background:rgba(0,0,0,.06)}.msg button.lang-toggle[aria-pressed="true"]{opacity:1;color:var(--ink)}
/* reactions with the family's emoji (ADR 0119): pressing and holding a message (finger or mouse) opens the picker and holding again closes it under it;
   once there are reactions, a chip after them adds another and the picker opens in line with them (mtib, 2026-10-26) */
.chat-log button.reaction.add{width:34px;padding:0;justify-content:center;border-style:dashed;color:var(--muted)}
.chat-log .reactions{display:flex;flex-wrap:wrap;gap:6px;margin:-2px 12% 2px 0}.chat-log .reactions.mine{justify-content:flex-end;margin:-2px 0 2px 12%}
.chat-log button.reaction{display:inline-flex;align-items:center;gap:4px;min-height:0;height:30px;padding:0 9px 0 6px;margin:0;border:2px solid var(--line);box-shadow:none;border-radius:999px;background:var(--card);color:var(--ink);font-size:.8rem;font-weight:800}
.chat-log button.reaction.on{border-color:var(--accent);background:var(--accent-soft)}.chat-log button.reaction img,.chat-log .emoji-picker img{object-fit:contain;display:block}
/* the picker is as wide as the chat */
.chat-log .emoji-picker{display:flex;flex-wrap:wrap;gap:2px;box-sizing:border-box;width:100%;padding:3px;margin:2px 0 4px;border:2px solid var(--line);border-radius:14px}.chat-log .emoji-picker{position:relative}.chat-log .emoji-picker .picker-close{position:absolute;top:-10px;right:4px;width:24px;height:24px;min-height:0;padding:0;margin:0;display:flex;align-items:center;justify-content:center;border:2px solid var(--line);border-radius:50%;background:var(--card);color:var(--muted);box-shadow:none}.chat-log .emoji-picker>p{margin:4px 6px}
.chat-log .emoji-picker button{display:inline-flex;align-items:center;justify-content:center;width:40px;height:40px;min-height:0;padding:0;margin:0;background:none;border:0;box-shadow:none;border-radius:12px}
.chat-log .emoji-picker button:hover,.chat-log .emoji-picker button:focus-visible{background:rgba(0,0,0,.06)}
/* pictures and files in a message (ADR 0120): thumbnails at their own shape above the text, a file as a row to download;
   the tray above the field holds what goes with the next message, the paper clip sits before the field */
.chat-log .msg.has-files{padding:6px}.chat-log .msg-body{flex:1 1 auto;min-width:0;display:grid;grid-template-columns:minmax(0,1fr);gap:6px}.chat-log .msg.has-files .msg-body>span{padding:4px 8px 4px}
.chat-files{min-width:0;max-width:100%}
.chat-pictures{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:4px}.chat-pictures.one{grid-template-columns:minmax(0,1fr)}
.chat-pictures>a{display:block;border-radius:12px;overflow:hidden;background:rgba(0,0,0,.06)}.chat-pictures img{display:block;width:100%;height:auto;max-height:320px;object-fit:cover}
.chat-pictures:not(.one) img{aspect-ratio:1}
.chat-pictures>.chat-video{width:100%;max-width:none;aspect-ratio:1}
.chat-pictures>:last-child:nth-child(odd):not(:only-child){grid-column:1/-1}.chat-pictures>a:last-child:nth-child(odd):not(:only-child) img{aspect-ratio:2}.chat-pictures>.chat-video:last-child:nth-child(odd):not(:only-child){aspect-ratio:2}
.chat-log .msg.bare.learner,.chat-log .msg.bare.partner{background:none;padding:0}.chat-log .msg.bare .msg-body{justify-items:start}.chat-log .msg.bare.learner .msg-body{justify-items:end}
.chat-gif{display:block;max-width:100%}.chat-log .msg:not(.bare) .chat-gif{border-radius:12px;overflow:hidden}.chat-gif img{display:block;width:100%;height:auto}
.chat-video{--r:1.778;position:relative;display:block;width:calc(min(360px,55dvh) * var(--r));max-width:100%;aspect-ratio:var(--r);margin:0;padding:0;border:0;border-radius:12px;overflow:hidden;background:#000;box-shadow:none;cursor:pointer}
.chat-video img{display:block;width:100%;height:100%;object-fit:cover}
.chat-video .play{position:absolute;inset:0;margin:auto;width:56px;height:56px;border-radius:999px;background:rgba(5,8,26,.6);color:#fff;display:flex;align-items:center;justify-content:center}
.chat-file{display:flex;align-items:center;gap:8px;padding:6px 8px;border-radius:12px;background:rgba(0,0,0,.05);color:inherit;text-decoration:none;font-weight:700;overflow-wrap:break-word;min-width:0}.chat-file>span:last-child{min-width:0}.chat-file .icon{flex:0 0 auto;display:inline-flex;color:var(--muted)}
/* every bubble is as wide as what is in it, at most 88% of the chat (a poll always that wide): one's own on the right, the others' on the left (mtib, 2026-10-26) */
.chat-log .chat .msg{width:fit-content;max-width:88%}.chat-log .chat .msg.has-poll{width:88%}.chat-log .chat .msg.learner{margin-left:auto}.chat-log .chat .msg.partner{margin-right:auto}
/* never squeezed by the log above it (a flex column that gives up its own height first) */
/* the + menu above the field (ADR 0123), its entries as quiet buttons with an icon */
.compose-menu{flex:0 0 auto;display:grid;gap:8px;margin:8px 0 0;padding:0 4px}
.compose-menu>button{justify-content:flex-start;gap:12px;min-height:48px;margin:0;padding:0 16px}.compose-menu .icon{display:inline-flex;color:var(--muted)}
/* a poll in a bubble: each answer a row with a bar for its share, the reader's own ticked; who voted under it */
.chat-log .msg-body .poll-label{color:var(--muted);font-weight:900;text-transform:uppercase;letter-spacing:.04em;font-size:.68rem}
.chat-log .poll{display:grid;gap:6px;margin:6px 0 0;min-width:0}
.chat-log .poll-option{position:relative;display:flex;align-items:center;gap:8px;width:100%;min-height:40px;margin:0;padding:6px 10px;border:2px solid var(--line);border-radius:12px;background:var(--card);color:var(--ink);box-shadow:none;font-size:.9rem;font-weight:700;text-align:left;overflow:hidden}
.chat-log .poll-option.mine{border-color:var(--accent)}
.chat-log .poll-bar{position:absolute;left:0;top:0;bottom:0;background:var(--accent-soft);z-index:0}.chat-log .poll-option.mine .poll-bar{background:#ffe7a8}
.chat-log .poll-tick{position:relative;flex:0 0 auto;width:16px;height:16px;border-radius:50%;border:2px solid var(--line);background:var(--card)}
.chat-log .poll-option.mine .poll-tick{border-color:var(--accent-deep);background:var(--accent-deep);box-shadow:inset 0 0 0 3px var(--card)}
.chat-log .poll-text{position:relative;flex:1 1 auto;min-width:0}.chat-log .poll-n{position:relative;flex:0 0 auto;font-weight:900;font-variant-numeric:tabular-nums}
/* who voted, muted after the answer: up to 40% of the row on one line, cut with an ellipsis so it never grows the row */
.chat-log .poll-names{position:relative;flex:0 1 auto;max-width:40%;min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;text-align:right;color:var(--muted);font-weight:600;font-size:.8rem}
.chat-log .poll-names:empty{display:none}.chat-log .poll-foot{display:block;color:var(--muted);margin:2px 0 0}
/* the new-poll panel (the recorder's panel): the question, the answers, add one, several allowed */
.chat-recorder.poll-new{max-height:80%;overflow-y:auto}.chat-recorder.poll-new input[type=text]{margin:0;padding:10px 12px}
.chat-recorder .poll-answers{display:grid;gap:8px}.chat-recorder.poll-new>button.quiet{margin:0;min-height:44px}
.chat-recorder .choices.poll-multi{margin:0}
/* emoji suggestions while typing ":name": a row of chips above the field, scrolling sideways when there are many */
.emoji-suggest{flex:0 0 auto;display:flex;gap:6px;overflow-x:auto;margin:8px 0 0;padding:2px 4px}
.emoji-suggest>button{flex:0 0 auto;display:inline-flex;align-items:center;gap:6px;min-height:40px;padding:0 12px 0 8px;margin:0;border:2px solid var(--line);border-radius:999px;background:var(--card);color:var(--ink);box-shadow:none;font-size:.85rem;font-weight:800}
.msg strong.mention.me{color:#b86e00}.msg strong.mention,.emoji-suggest .mention{color:#1b2a8a;font-weight:900}
.emoji-suggest>button img{width:24px;height:24px;object-fit:contain}
.chat-tray{flex:0 0 auto;display:flex;gap:8px;overflow-x:auto;margin:10px 0 0;padding:8px 8px 2px 4px}
.chat-tray .tray-item{position:relative;flex:0 0 auto;width:76px;display:grid;justify-items:center;gap:2px;padding:6px 4px 4px;border:2px solid var(--line);border-radius:14px}
.chat-tray .tray-item>img{width:56px;height:56px;object-fit:cover;border-radius:8px}.chat-tray .tray-item>.icon{display:inline-flex;width:56px;height:56px;align-items:center;justify-content:center;color:var(--muted)}
.chat-tray .tray-item>.small{max-width:100%;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.chat-tray button.remove{position:absolute;top:-6px;right:-6px;width:24px;height:24px;min-height:0;padding:0;margin:0;border-radius:999px;border:0;box-shadow:none;background:var(--ink);color:var(--card);font-weight:900;line-height:1}
/* the compose button: a microphone or Send, an icon of the same size either way (ADR 0121) */
.chat-compose button#chat-send{width:48px;padding:0;display:inline-flex;align-items:center;justify-content:center}
/* recording a voice message: a panel over the bottom of the card (the field and the newest messages) */
.chat-recorder{position:absolute;left:0;right:0;bottom:0;z-index:3;display:grid;gap:10px;padding:14px 12px 12px;background:var(--card);border-top:2px solid var(--line);border-radius:0 0 var(--radius) var(--radius);box-shadow:0 -10px 24px rgba(8,12,38,.12)}
.chat-recorder .rec-head{display:flex;align-items:center;gap:8px;font-weight:800}.chat-recorder .rec-time{margin-left:auto;font-variant-numeric:tabular-nums;color:var(--muted)}
.chat-recorder .rec-dot{width:12px;height:12px;border-radius:50%;background:#e5484d;animation:rec-pulse 1.2s ease-in-out infinite}@keyframes rec-pulse{50%{opacity:.35}}
.chat-recorder .rec-wave{display:block;width:100%;height:56px;color:var(--accent)}.chat-recorder .row>button{flex:1 1 0;margin:0}
/* a voice message in its bubble: the transcript, then play, the waveform (filled as it plays) and the time */
.chat-log .msg-body .voice{display:flex;align-items:center;gap:6px;margin:2px 0 0;min-width:0}
/* compact and quiet: a soft tint, not a dark disc (mtib, 2026-10-25) */
.chat-log .voice button.play{flex:0 0 auto;width:28px;height:28px;min-height:0;padding:0;margin:0;display:inline-flex;align-items:center;justify-content:center;border-radius:50%;border:0;box-shadow:none;background:rgba(22,27,58,.08);color:var(--ink)}.chat-log .voice button.play:hover,.chat-log .voice button.play:focus-visible{background:rgba(22,27,58,.14)}
.chat-log .voice .wave{flex:0 1 auto;min-width:0;height:20px;display:flex;align-items:center;gap:1px;cursor:pointer;overflow:hidden}
.chat-log .voice .wave>span{flex:0 1 auto;width:3px;min-width:1px;border-radius:2px;background:var(--ink);opacity:.3}.chat-log .voice .wave>span.on{opacity:.9}
/* the time right after the waveform (mtib, 2026-10-25) */
.chat-log .voice .vtime{flex:0 0 auto;margin-left:2px;font-size:.75rem;font-weight:800;color:var(--muted);font-variant-numeric:tabular-nums}
.chat-compose button.attach{flex:0 0 auto;width:48px;padding:0;display:inline-flex;align-items:center;justify-content:center;color:var(--muted)}
.emoji-preview{width:64px;height:64px;object-fit:contain}
/* the chat's picture viewer: the picture as large as the screen allows, Download and Close above it, and when a message
   has several, the arrows at its sides and which one it is ("2 of 4") under it */
dialog.picture-viewer{position:fixed;inset:0;width:auto;height:auto;max-width:none;max-height:none;margin:0;padding:0;border:0;overflow:hidden;background:var(--bg);color:#fff}
dialog.picture-viewer:focus{outline:none}
dialog.picture-viewer::backdrop{background:var(--bg)}
/* the media runs edge to edge (as large as the screen allows, tall ones to the top and bottom, a matching shape to all four edges);
   Download, Close, the count and a video's controls float over it, and a tap on the media hides them or brings them back (viewer-chrome.ts) */
dialog.picture-viewer .stage{position:absolute;inset:0;display:flex;align-items:center;justify-content:center}
dialog.picture-viewer .bar,dialog.picture-viewer>.position,dialog.video-viewer .video-controls{position:absolute;left:0;right:0;z-index:2;display:flex;align-items:center;gap:8px;box-sizing:border-box;pointer-events:none;transition:opacity .2s}
dialog.picture-viewer .bar>*,dialog.picture-viewer>.position,dialog.video-viewer .video-controls>*{pointer-events:auto}
dialog.picture-viewer .bar{top:0;justify-content:flex-end;padding:max(12px,env(safe-area-inset-top)) 12px 0}
dialog.picture-viewer>.position{bottom:0;justify-content:center;margin:0;padding:0 12px max(12px,env(safe-area-inset-bottom));font-weight:800;color:#fff;pointer-events:none}
dialog.picture-viewer>.position:empty{display:none}
dialog.video-viewer>.position{bottom:84px;padding:6px 12px;background:none}
dialog.video-viewer .video-controls{bottom:0;padding:0 12px max(12px,env(safe-area-inset-bottom))}
dialog.picture-viewer.chrome-hidden .bar,dialog.picture-viewer.chrome-hidden>.position,dialog.video-viewer.chrome-hidden .video-controls,dialog.picture-viewer.chrome-hidden .stage button.prev,dialog.picture-viewer.chrome-hidden .stage button.next{opacity:0;pointer-events:none}
dialog.picture-viewer.chrome-hidden .bar>*,dialog.video-viewer.chrome-hidden .video-controls>*{pointer-events:none}
dialog.picture-viewer .bar .button,dialog.picture-viewer .bar button{flex:0 0 auto;width:auto;margin:0;min-height:44px;padding:0 14px;font-size:.95rem}
dialog.video-viewer .stage video{width:100%;height:100%;object-fit:contain;background:transparent}
dialog.video-viewer .video-controls button.play{flex:0 0 auto;width:48px;height:48px;min-height:0;padding:0;margin:0;display:inline-flex;align-items:center;justify-content:center;border:0;box-shadow:none;border-radius:999px;background:rgba(255,255,255,.14);color:#fff}
dialog.video-viewer .scrub{flex:1 1 auto;min-width:0;height:32px;margin:0;accent-color:#fff}dialog.video-viewer .time{flex:0 0 auto;font-weight:800;font-variant-numeric:tabular-nums;color:#fff}
dialog.picture-viewer:not(.video-viewer)>.video-controls{display:none}
dialog.picture-viewer .stage img[hidden],dialog.picture-viewer .stage video[hidden],dialog.picture-viewer .stage .file-card[hidden]{display:none}
dialog.picture-viewer:not(.video-viewer) .stage video{width:100%;height:100%;object-fit:contain;background:transparent}
dialog.picture-viewer .file-card{display:flex;flex-direction:column;align-items:center;gap:12px;max-width:80%;color:#fff;text-align:center}dialog.picture-viewer .file-card .file-info{margin:0;line-height:1.5;color:var(--on-bg-muted);user-select:none;-webkit-user-select:none}dialog.picture-viewer .file-card .file-name{margin:0;user-select:none;-webkit-user-select:none;font-weight:800;font-size:1.1rem;overflow-wrap:anywhere}
dialog.picture-viewer.playing .stage button.prev,dialog.picture-viewer.playing .stage button.next{opacity:0;pointer-events:none}
dialog.picture-viewer .stage img{width:100%;height:100%;object-fit:contain;user-select:none;touch-action:none}
/* zoomed in, the arrows give way to the picture (a drag moves it) */
dialog.picture-viewer.zoomed .stage button.prev,dialog.picture-viewer.zoomed .stage button.next{opacity:0;pointer-events:none}
dialog.picture-viewer .stage button.prev,dialog.picture-viewer .stage button.next{position:absolute;top:50%;transform:translateY(-50%);z-index:2;width:48px;height:48px;min-height:0;padding:0;margin:0;display:inline-flex;align-items:center;justify-content:center;border:0;box-shadow:none;border-radius:999px;background:rgba(5,8,26,.55);color:#fff;transition:opacity .2s}
dialog.picture-viewer .stage button.prev{left:4px}dialog.picture-viewer .stage button.next{right:4px}
/* the family's emoji written in a message: as high as the text, or bigger when they are the whole message */
/* every picture in the log on its own layer: an animated GIF repaints each frame, and on the log's layer that repainted
   the text with it, at the bottom's fractional scroll offset, so the text jittered (mtib, 2026-10-25, prod emoji) */
.chat-log img{will-change:transform}
/* a link in a message (as in blackhole's stream): its short label on one line, cut with an ellipsis */
.chat-log .msg a.bare{display:inline-block;max-width:100%;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;vertical-align:bottom;color:var(--accent-deep);font-weight:800;text-decoration:underline;text-underline-offset:2px}
.chat-log .msg img.emoji{display:inline-block;width:1.4em;height:1.4em;margin:-.2em .05em;vertical-align:middle;object-fit:contain}.chat-log .msg img.emoji.big{width:48px;height:48px;margin:0 2px}ul.emoji-list li>img{flex:0 0 auto;width:40px;height:40px;object-fit:contain}
.chat-compose{display:flex;gap:8px;align-items:flex-end;margin:12px 0 0}.chat-compose textarea{flex:1 1 auto;min-width:0;margin:0;min-height:48px;max-height:30vh;resize:none;padding:11px 12px;font-size:16px;line-height:1.4}.chat-compose button{flex:0 0 auto;min-height:48px;margin:0;padding:0 16px}
.msg.user,.chat .msg.learner{background:#eef0ff;margin-left:12%}
.msg.agent,.chat .msg.partner{background:var(--accent-soft);margin-right:12%}
.flash{background:var(--accent-soft);color:var(--ink);border-radius:16px;padding:14px 16px;margin:0 0 14px;font-weight:700}
.flash.ok{background:var(--ok-soft)}
.flash.bad{background:var(--warn-soft)}

table{width:100%;border-collapse:collapse;color:var(--ink);font-size:.95rem}
th{font-size:.7rem;font-weight:900;text-transform:uppercase;letter-spacing:.06em;color:var(--muted);text-align:left;padding:6px 8px;border-bottom:2px solid var(--line)}
td{padding:10px 8px;border-bottom:1px solid var(--line);font-weight:700}
tr:last-child td{border-bottom:0}
td.num,th.num{text-align:right;font-variant-numeric:tabular-nums}
/* CardHead: heading left, one compact fact right (wraps under it on narrow screens) */
.card-head{display:flex;align-items:baseline;justify-content:space-between;gap:4px 12px;flex-wrap:wrap;margin:0 0 12px}
.card-head>h1,.card-head>h2{margin:0;min-width:0}.card-head>.aside{white-space:nowrap;color:var(--muted);font-weight:700}
.card-head>.aside strong{color:var(--ink);font-size:1.15em}
tr.me td{background:var(--accent-soft)}
tr.me td:first-child{border-radius:12px 0 0 12px}
tr.me td:last-child{border-radius:0 12px 12px 0}
.rank{display:inline-grid;place-items:center;width:28px;height:28px;border-radius:50%;font-size:.8rem;font-weight:900;background:var(--soft);color:var(--muted)}
.rank.r1{background:#ffd34d;color:#5a3d00}.rank.r2{background:#d9dcef;color:#3d4366}.rank.r3{background:#f2b98a;color:#5b2e0a}
.rating{font-size:2.4rem;font-weight:900;letter-spacing:-.02em;line-height:1}
.hint{font-size:.85rem;color:var(--muted);margin:10px 0 0}
.small{font-size:.82rem}
.nowrap{white-space:nowrap}
tr.month td{background:var(--soft);font-weight:900}
.campaign h2{margin:4px 0 4px}
.campaign-desc{margin:0 0 12px;color:var(--muted);font-weight:700}
.countdown{font-weight:900;margin:0 0 10px}
.big-progress{height:20px;margin:0 0 8px}
.big-progress div{background:linear-gradient(90deg,var(--accent),#ffd166)}
/* the family goal bar: tall, in segments, with the count (or the finished word) on it */
.big-progress.segmented{position:relative;height:30px;border-radius:12px}.big-progress.segmented div{border-radius:12px 0 0 12px}
.big-progress.segmented::after{content:"";position:absolute;inset:0;pointer-events:none;border-radius:12px;
background:repeating-linear-gradient(to right,transparent 0 calc(100%/var(--segments) - 3px),var(--card) calc(100%/var(--segments) - 3px) calc(100%/var(--segments)))}
/* light colours on both sides, so the dark text reads on the fill and on the empty track */
.big-progress.segmented div{background:linear-gradient(90deg,#ffcc52,#ffda7a)}.big-progress.segmented{background:#efedf9}
.big-progress.finished div{border-radius:12px;background:#a3e3c5}
.big-progress.missed div{background:#c7cadb}
.progress-label{position:absolute;inset:0;z-index:1;display:grid;place-items:center;font-size:.8rem;font-weight:900;letter-spacing:.06em;color:var(--ink);font-variant-numeric:tabular-nums}
.big-progress.finished .progress-label{text-transform:uppercase;letter-spacing:.1em}
ul.contrib{list-style:none;padding:0;margin:10px 0 0;display:grid;gap:6px}
ul.contrib li{display:grid;grid-template-columns:32px 1fr auto;align-items:center;gap:10px;padding:6px 8px;border-radius:12px;font-weight:700}
ul.contrib li.me{background:var(--accent-soft)}
ul.badges{list-style:none;padding:0;margin:0;display:grid;gap:12px}
ul.badges li{display:grid;grid-template-columns:44px 1fr;align-items:center;gap:14px}
.movelist{margin:10px 0 0;font-variant-numeric:tabular-nums}ol.movelist{padding:0 0 0 2.2em}
/* the review's move table: White's move number, then both moves; the players head their columns */
div.movelist{display:grid;grid-template-columns:auto minmax(0,1fr) minmax(0,1fr);gap:2px 6px;align-items:center}
div.movelist .num{color:var(--muted);font-size:.8rem;text-align:right}
div.movelist .who{color:var(--muted);font-size:.85rem;font-weight:800;padding:0 8px 4px;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
.movelist li{display:grid;grid-template-columns:1fr 1fr;gap:6px;padding:1px 0}
.movelist li::marker{color:var(--muted);font-size:.8rem}
.movelist button{all:unset;cursor:pointer;display:block;padding:4px 8px;border-radius:10px;font-weight:800}
.movelist button.cur{background:var(--accent-soft);box-shadow:inset 0 0 0 2px var(--accent)}
.mk{font-weight:900;margin-left:3px}
.mk.best,.mk.good{color:var(--ok)}.mk.inaccuracy{color:#c99400}.mk.mistake{color:#e07a00}.mk.blunder{color:var(--warn)}
.turn{display:inline-block;margin:2px 0;padding:1px 8px;border-radius:999px;font-size:.8rem;font-weight:900;background:var(--soft);color:var(--muted)}
.turn.mine{background:var(--accent);color:var(--accent-ink)}
ul.games .game-info{min-width:0;display:grid;gap:2px}ul.games p{margin:0}
ul.games .game-head{display:flex;flex-wrap:wrap;align-items:center;gap:4px 8px}ul.games .game-head .turn{margin:0}
ul.games li.my-move{border-color:var(--accent)}.rating-cell{white-space:nowrap}

.board{width:100%;aspect-ratio:1;max-width:560px;margin:12px auto;overflow:hidden;box-shadow:0 0 0 3px var(--line);
touch-action:none;-webkit-user-select:none;user-select:none;-webkit-tap-highlight-color:transparent}
/* chessboard in the pulsar palette (replaces chessground's brown theme) */
.board cg-board{background-color:var(--chess-light,#eeecfb);background-image:conic-gradient(var(--chess-dark,#9d95e0) 25%,var(--chess-light,#eeecfb) 0 50%,var(--chess-dark,#9d95e0) 0 75%,var(--chess-light,#eeecfb) 0);background-size:25% 25%}
/* a hanging piece (src/client/chess-game.ts): its own outline, green for the opponent's, red for one's own */
.board cg-board piece.hang-green{filter:drop-shadow(2px 0 0 #1fa35a) drop-shadow(-2px 0 0 #1fa35a) drop-shadow(0 2px 0 #1fa35a) drop-shadow(0 -2px 0 #1fa35a)}
.board cg-board piece.hang-red{filter:drop-shadow(2px 0 0 #e5484d) drop-shadow(-2px 0 0 #e5484d) drop-shadow(0 2px 0 #e5484d) drop-shadow(0 -2px 0 #e5484d)}
.board cg-board square.last-move{background-color:rgba(255,182,39,.45)}
.board cg-board square.selected{background-color:rgba(255,182,39,.75);box-shadow:inset 0 0 0 3px #d98a00}
.board cg-board square.move-dest{background:radial-gradient(rgba(124,108,255,.55) 20%,transparent 22%)}
.board cg-board square.move-dest:hover{background:rgba(124,108,255,.3)}
.board cg-board square.oc.move-dest{background:radial-gradient(transparent 0%,transparent 79%,rgba(124,108,255,.5) 80%)}
.board cg-board square.check{background:radial-gradient(ellipse at center,rgba(229,72,107,1) 0%,rgba(229,72,107,.6) 30%,rgba(229,72,107,0) 75%)}
/* chessground rounds the board down to whole device pixels per square, which left a strip inside the frame: fill it */
.board cg-container{width:100%!important;height:100%!important}
/* coordinates under the pieces (z-index 2); file letters in each bottom square's right corner */
.board coords{font-family:inherit;font-weight:900;font-size:.62rem;z-index:1}
.board coords.files{left:0;bottom:0}
.board coords.files coord{flex:0 0 12.5%;text-align:right;padding-right:3px;box-sizing:border-box}
.board coords coord{color:var(--chess-coordinates,#3d3790)!important;text-shadow:-1px 0 var(--chess-light,#eeecfb),1px 0 var(--chess-light,#eeecfb),0 -1px var(--chess-light,#eeecfb),0 1px var(--chess-light,#eeecfb)}
.opening-tabs{display:flex;gap:10px;flex-wrap:wrap;margin:14px 0}.opening-tabs>*{flex:1;text-align:center}
.opening-plans{background:var(--soft);border-radius:12px;padding:12px;margin:12px 0}
.opening-plans summary{cursor:pointer;font-weight:800;min-height:44px;display:flex;align-items:center}
.opening-check{border-top:2px solid var(--line);margin-top:20px;padding-top:8px}
.opening-catalogue li{gap:12px}.opening-catalogue li>span{min-width:0;overflow-wrap:anywhere}
.row{display:flex;gap:10px}
.row>*{flex:1}
/* a line of text with a small control at its end (match: hint + sound toggle): one row, spaced like a paragraph */
/* four small steps through a game, always in one row */
.row.nav{flex-wrap:nowrap!important;gap:6px}.row.nav>button{flex:1 1 0;min-width:0;padding:0 4px!important;font-size:.9rem}
.row.spread{align-items:center;margin:0 0 12px}.row.spread>*{flex:0 0 auto;margin:0}.row.spread>p{flex:1}.row.spread>button{min-height:48px;padding:0 16px}
/* a changelog entry's small line (tags, then its link at the right end): right under the text */
.row.spread.changelog-meta{margin:2px 0 0;align-items:baseline}.row.spread.changelog-meta>p{flex:1 1 auto;min-width:0}
.pulse{display:flex;align-items:center;gap:16px}
/* minmax(0,1fr): a long word must never push a column out of the card on narrow phones */
.pairs{display:grid;grid-template-columns:minmax(0,1fr) minmax(0,1fr);gap:8px}
.pairs button{margin:0;width:100%;min-width:0;padding:10px 10px;background:#fff;color:var(--ink);border:2px solid var(--line);box-shadow:0 4px 0 var(--line)}
.pairs button.sel{border-color:var(--accent);background:var(--accent-soft);box-shadow:0 4px 0 var(--accent)}
.pairs button.gone{visibility:hidden}
.pairs button.bad{border-color:var(--warn);animation:shake .3s}
@keyframes shake{25%{transform:translateX(-4px)}75%{transform:translateX(4px)}}
.prompt{font-size:1.4rem;font-weight:900;margin:8px 0 4px}
/* clarification under a word or sentence (ADR 0037) */
small.note{display:block;font-size:.72rem;font-weight:700;color:var(--muted);line-height:1.2;margin-top:2px}
.prompt small.note{font-size:.85rem}
.pairs button .label{display:block;line-height:1.2;min-width:0;max-width:100%;overflow-wrap:anywhere;hyphens:auto;text-align:center;font-size:clamp(.9rem,4.2vw,1.05rem)}
/* a word over 12 letters gets a little smaller so it usually fits whole instead of breaking mid-word */
.pairs button.long .label{font-size:clamp(.8rem,3.6vw,1rem)}
.words span{display:inline-block;padding:2px 10px;margin:2px;border-radius:10px;font-weight:800}
.words .ok{background:var(--ok-soft)}
.words .no{background:var(--warn-soft)}
.words .close{background:var(--accent-soft)}
.progress{height:14px;background:var(--line);border-radius:10px;overflow:hidden;margin:0 0 16px}
.progress div{height:100%;background:var(--ok);border-radius:10px;transition:width .3s}
.chat{display:grid;gap:8px;margin:0}
.task{background:var(--accent-soft);border:2px solid var(--accent);border-radius:18px;padding:12px 14px;display:grid;gap:6px}
.task .prompt{margin:0;font-size:1.15rem}
.task button{margin:0;min-height:44px;justify-self:start}
.hint-text{margin:0;font-weight:800;color:var(--violet)}
.translation{margin:0;font-weight:600;color:var(--ink);opacity:.75}.chat .translation{margin:-2px 0 4px}.chat>button.small{justify-self:start;margin:0 0 4px}
/* read-aloud buttons on translate and chat lines; other correct answers one per line */
button.speak,button.show-translation{min-height:34px;padding:0 10px;font-size:.85rem;vertical-align:middle;margin:2px 0}
/* story lessons: the text grows chunk by chunk; answer options are full-width buttons */
.story-part{display:grid;gap:4px}.story-chunk{margin:0;font-size:1.1rem;line-height:1.5}
button.option{text-align:left;justify-content:flex-start}button.option:disabled{opacity:1;filter:none;cursor:default}
button.option.right{border-color:var(--ok);background:var(--ok-soft);box-shadow:0 4px 0 var(--ok)}
button.option.wrong{border-color:var(--warn);background:var(--warn-soft);box-shadow:0 4px 0 var(--warn)}
/* the status of every board: a head line (with a fact at its end), then a note; always two lines of room where it starts with one */
.board-status{display:grid;gap:4px;margin:14px 0 0}.board-status[hidden]{display:none}.board-status p{margin:0}
.move-feedback{display:grid;gap:4px;margin:14px 0 0}.move-feedback p{margin:0}.move-feedback p:empty{display:none}#feedback-head{font-weight:700}
/* something on its way (.skeleton on its box, e.g. the verdict while the undo time runs, mtib 2026-11-01): each line
   is a pastel bar in the sky's own blue (its hue, 230), breathing slightly, with specks of stars drifting slowly
   across it; two star layers, each moving by its own tile width per loop, at different speeds */
.skeleton>p{color:transparent;border-radius:6px;background:
  radial-gradient(circle at 11% 38%,#fff 0 1px,rgba(255,255,255,.55) 1.6px,transparent 3.2px) 0 0/151px 100%,
  radial-gradient(circle at 46% 71%,rgba(255,255,255,.8) 0 .6px,rgba(255,255,255,.35) 1.1px,transparent 2.2px) 0 0/151px 100%,
  radial-gradient(circle at 83% 29%,#fff 0 .8px,rgba(255,255,255,.45) 1.3px,transparent 2.6px) 0 0/151px 100%,
  radial-gradient(circle at 64% 58%,rgba(255,255,255,.7) 0 .6px,rgba(255,255,255,.3) 1px,transparent 2px) 0 0/89px 100% hsl(230 62% 89%);
  animation:skeleton-drift 26s linear infinite,skeleton-breathe 3.2s ease-in-out infinite}
/* the next line starts elsewhere in both loops: its stars never sit under the line above's */
.skeleton>p+p{animation-delay:-11s,-1.3s}
@keyframes skeleton-drift{to{background-position:151px 0,151px 0,151px 0,-89px 0}}
@keyframes skeleton-breathe{50%{opacity:.72}}
@media (prefers-reduced-motion: reduce){.skeleton>p{animation:none}}
.move-feedback.skeleton #feedback-head{width:60%}.move-feedback.skeleton #feedback-line{width:85%}
.board-head{display:flex;align-items:baseline;justify-content:space-between;gap:12px;font-weight:800;font-size:1.1rem;line-height:1.25}
#status-meta{white-space:nowrap;font-variant-numeric:tabular-nums}.board-note{color:var(--muted)}.board-note:empty{display:none}
.board-status.bad .board-note{color:var(--warn);font-weight:700}.board-status.ok #status-meta{color:var(--ok)}
.board-status.bad:has(.board-note:empty) .board-head{color:var(--warn)}.board-status.ok:has(.board-note:empty) .board-head{color:var(--ok)}
/* the side to move: a small disc in the piece colour */
.side-disc{display:inline-block;width:.8em;height:.8em;border-radius:50%;border:2px solid var(--ink);margin-right:.45em;vertical-align:-.08em}
.side-disc.white{background:#fff}.side-disc.black{background:var(--ink)}
.answers{display:grid;gap:6px;margin:10px 0 0}.answers>p{margin:0}.answers span.main{font-weight:800}.chat .answers{margin:4px 0 4px 12%}
ul.lessons li>button.speak{min-height:44px;padding:0 12px;font-size:.9rem;margin:0;flex:0 0 auto;white-space:nowrap}
.verdict{margin:-2px 0 0 12%;font-weight:800;font-size:.95rem;padding:6px 12px;border-radius:12px}
.verdict.ok{background:var(--ok-soft);color:#11704f}
.verdict.bad{background:var(--warn-soft);color:#a3203f}
/* tables scroll inside their card instead of overflowing on phones */
.table-wrap{overflow-x:auto;-webkit-overflow-scrolling:touch;margin:0 -4px}
@media (max-width:520px){th,td{padding:8px 5px}table{font-size:.85rem}th{font-size:.62rem}.card{padding:18px 14px}
#install,#notify{gap:10px;padding:10px 10px 10px 12px}:is(#install,#notify) p{font-size:.85rem}:is(#install,#notify) p small{font-size:.8rem}:is(#install,#notify)>svg{display:none}}
.fix{font-size:.95rem;color:var(--muted);margin:-4px 0 4px 12%}

.msg{white-space:normal}
.msg.user{white-space:pre-wrap}
.md p{margin:.35em 0}
/* the teacher's answer: plain text across the card, a little larger, under its tag */
#answer>.tag{margin:0 0 -8px}.answer-text{font-size:1.02rem;line-height:1.55}.answer-text>:first-child{margin-top:0}.answer-text>:last-child{margin-bottom:0}
.md ul,.md ol{margin:.35em 0;padding-left:1.3em}
.md code{background:rgba(22,27,58,.07);padding:0 .3em;border-radius:6px}
.md a{color:var(--violet)}
.spinner{display:inline-flex;align-items:center;gap:10px;color:var(--muted);font-weight:700}
.spinner.centered{display:flex;justify-content:center;padding:10px 0}
.ghost-spinner{width:32px;height:32px;animation:ghost-spin 1.4s linear infinite}
.ghost-spinner.lg{width:64px;height:64px}
.spinner.centered.lg{padding:22px 0}
@keyframes ghost-spin{to{transform:rotate(360deg)}}
@media (prefers-reduced-motion: reduce){.ghost-spinner{animation-duration:4s}}
.people{list-style:none;margin:0 0 12px;padding:0;display:grid;gap:10px}
.people>li{border:2px solid var(--line);border-radius:18px;padding:12px 14px;display:grid;grid-template-columns:minmax(0,1fr);gap:8px;min-width:0}
.people p{margin:0}
.person-head{display:flex;justify-content:space-between;align-items:baseline;flex-wrap:wrap;gap:4px 10px;font-weight:800;min-width:0}
.person-head .cost{font-variant-numeric:tabular-nums;color:var(--muted)}
.trend{display:inline-block;margin-left:.2em;font-size:.95em;line-height:1}
.trend.up{color:#1f9d55}.trend.down{color:#d64545}.trend.flat{color:var(--muted)}
.reports{list-style:none;margin:0;padding:0;display:grid;gap:12px}
.report{border:2px solid var(--line);border-radius:18px;padding:12px 14px;display:grid;grid-template-columns:minmax(0,1fr);gap:8px;min-width:0}
.report p{margin:0}
.report-head{display:flex;flex-wrap:wrap;align-items:center;gap:8px}
.report .row{flex-wrap:wrap;align-items:center}.report .row:empty{display:none}.report .row>*{flex:0 0 auto;white-space:nowrap}.report form{margin:0}
.report .cost{margin-left:auto;font-weight:800;color:var(--muted);font-variant-numeric:tabular-nums}
.chip{display:inline-block;border-radius:999px;padding:3px 10px;font-size:.78rem;font-weight:800;background:var(--line);color:var(--ink)}
.chip.kind-bug,.chip.status-failed{background:var(--warn-soft)}
.chip.kind-feature,.chip.status-filed{background:var(--accent-soft)}
.report>*{min-width:0}
.report-meta{font-size:.85rem;overflow-wrap:anywhere}
.report-body{white-space:pre-wrap;overflow-wrap:anywhere}
.report-error{font-size:.85rem;font-weight:600;overflow-wrap:anywhere}
.report .button{justify-self:start}
details>summary{list-style:none}
details>summary::-webkit-details-marker{display:none}
details[open]>summary{display:none}
/* fixed over the page, not sticky: a sticky banner stays in the flow and pushed a short page's footer off the screen by
   its own height (mtib, 2026-10-25); as wide as the page column, at the bottom of the screen */
:is(#install,#notify){position:fixed;z-index:20;bottom:max(12px,env(safe-area-inset-bottom));left:50%;transform:translateX(-50%);width:min(628px,calc(100% - 32px));display:none;align-items:center;gap:14px;background:var(--card);color:var(--ink);border-radius:20px;
padding:12px 14px;box-shadow:0 18px 40px -12px rgba(13,19,51,.6);border:2px solid var(--accent)}
:is(#install,#notify).show{display:flex}
:is(#install,#notify) p{margin:0;flex:1;font-weight:800;line-height:1.3}
:is(#install,#notify) p small{display:block;font-weight:600;color:var(--muted)}
:is(#install,#notify) button{margin:0;min-height:44px;padding:0 16px}
:is(#install,#notify) .x{background:transparent;box-shadow:none;color:var(--muted);padding:0 8px}
[hidden]{display:none!important}
.back{margin:0 0 12px}.back a{display:inline-flex;align-items:center;min-height:44px;padding:0 14px;border-radius:999px;color:var(--on-bg-muted);font-weight:800;text-decoration:none}
.back a:hover,.back a:focus-visible{background:var(--bg2);color:var(--on-bg)}
/* rarely needed settings fold away behind one button; open, the button disappears and the fields take its place */
#summary:not(:empty){margin:0 0 12px}#summary p{margin:0}
.eval-graph .eval-bg{fill:var(--soft)}.eval-graph .eval-white{fill:#fff;opacity:.8}.eval-graph .eval-black{fill:#1d2140;opacity:.3}
.eval-graph .eval-split{stroke:var(--muted);stroke-width:1.5;stroke-dasharray:6 5;vector-effect:non-scaling-stroke}
/* the move on the review board and the better one: one line each, always there */
.review-notes{display:grid;gap:2px;margin:12px 0 0}.review-notes p{margin:0;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.review-notes #explain{font-weight:700}
#graph-position{width:100%;min-height:44px;accent-color:var(--accent-deep)}
.rating-chart{margin:12px 0 26px;min-width:0}.rating-chart svg{display:block;width:100%;height:auto;overflow:visible;touch-action:pan-y}
.rating-chart svg text{font-family:inherit;font-size:14px;fill:var(--muted);font-variant-numeric:tabular-nums}
.rating-chart .chart-grid{stroke:var(--line);stroke-width:1}.rating-chart .chart-line{fill:none;stroke:var(--violet);stroke-width:2.5;vector-effect:non-scaling-stroke;stroke-linejoin:round}
[data-chart=puzzle-rating] .chart-line{stroke:#178073}[data-chart=puzzle-rating] .chart-area{fill:#178073}
.rating-chart .chart-area{fill:var(--violet);opacity:.08}.rating-chart .chart-marker{fill:var(--accent-deep);stroke:var(--card);stroke-width:2;vector-effect:non-scaling-stroke}
/* the rating as candles (chess-statistics): green when a span ended higher, red lower, grey unchanged, like .trend */
.rating-chart .candle line{stroke-width:1.5;vector-effect:non-scaling-stroke}
.rating-chart .candle.up{fill:#1f9d55;stroke:#1f9d55}.rating-chart .candle.down{fill:#d64545;stroke:#d64545}.rating-chart .candle.flat{fill:var(--muted);stroke:var(--muted)}
/* under the chart: the candle's span, then its four ratings side by side */
.chart-readout{margin:10px 0 0}.chart-readout p{margin:0 0 6px;font-weight:800}
.chart-readout dl{display:grid;grid-template-columns:repeat(4,minmax(0,1fr));gap:8px;margin:0}
.chart-readout dt{font-size:.8rem;color:var(--muted)}.chart-readout dd{margin:0;font-weight:800;font-variant-numeric:tabular-nums}
.chart-period{margin:16px 0 0}
.rating-chart .chart-crosshair{stroke:var(--muted);stroke-width:1;stroke-dasharray:4 4}
.rating-chart .chart-tip rect{fill:var(--ink);opacity:.9}.rating-chart .chart-tip text{fill:#fff;font-size:15px;font-weight:800}
@media(max-width:520px){.rating-chart .chart-tip text{font-size:24px}}
@media(max-width:520px){.rating-chart svg text{font-size:20px}}
/* radio cards in a fieldset: the legend reads like a form label */
.choices[hidden]{display:none}
fieldset.choices{border:0;padding:0;margin:16px 0;min-width:0}fieldset.choices>legend{display:block;font-weight:800;margin:0 0 6px;padding:0}
fieldset.choices>p{margin:0}
/* .choices.gallery: the same radio cards in two columns, each card stacked (heading, picture, lines) */
.choices.gallery{grid-template-columns:repeat(2,minmax(0,1fr))}
.choices.gallery>label{display:grid;align-content:start;gap:8px;padding:12px;min-width:0;overflow-wrap:break-word}
.choice-head{display:flex;gap:8px;align-items:center;min-width:0}.choice-head strong{min-width:0}
.choices .credit a{color:inherit}
.theme-swatch{width:48px;height:32px;border-radius:6px;flex-shrink:0}
.piece-sample{display:grid;grid-template-columns:repeat(3,minmax(0,1fr));gap:3px;background:conic-gradient(var(--chess-dark) 25%,var(--chess-light) 0 50%,var(--chess-dark) 0 75%,var(--chess-light) 0);background-size:66.667% 100%;border-radius:8px;overflow:hidden}
.piece-sample img{width:100%;height:auto;aspect-ratio:1;object-fit:contain}
@media(max-width:420px){.choices.gallery>label{padding:10px}.choice-head{gap:6px}.choices.gallery .choice-head strong{font-size:.92rem}}
/* very narrow phones: one card per row, so a set's name never breaks inside a word */
@media(max-width:360px){.choices.gallery{grid-template-columns:minmax(0,1fr)}}
@media(max-width:520px){.row{flex-wrap:wrap}.row>*{min-width:0}ul.lessons li .button,ul.lessons li>form>button{padding:0 14px}
.row>button,.row>.button,.row>form>button{padding:0 12px}}

/* the family's week (week.tsx): rows like list rows, the active days in the accent colour. Where a long name and the
   dots don't both fit (a phone), every row puts the dots under the name, spread over its width, so the rows match */
.week-list{list-style:none;padding:0;margin:0;display:grid;gap:10px;container-name:week;container-type:inline-size}
.week-list>li{display:flex;flex-wrap:wrap;align-items:center;gap:8px 12px;padding:10px 14px;border:2px solid var(--line);border-radius:18px}
.week-list .who{flex:1 1 auto;min-width:0;font-weight:800}
.week-dots{display:flex;gap:8px;flex:0 0 auto;margin-left:auto}
.week-dots .dot{width:24px;height:24px;border-radius:50%;display:inline-flex;align-items:center;justify-content:center;font-size:.68rem;font-weight:900;color:var(--muted);background:var(--soft);border:2px solid var(--line)}
.week-dots .dot.on{background:var(--accent);border-color:var(--accent);color:var(--accent-ink)}
.week-dots .dot.today{box-shadow:0 0 0 2px var(--card),0 0 0 4px var(--accent-deep)}
/* after the rules it overrides */
@container week (max-width:419.5px){.week-list .who{flex:1 1 100%}.week-dots{flex:1 1 100%;justify-content:space-between;margin-left:0}}

