@font-face{font-family:'Teneo Sans';src:url('/fonts/teneo-sans.woff2?r=2008e95d71a6') format('woff2');font-weight:200 900;font-display:block}
/* ── NOTICE: the one alert piece ── */
/* THE TOKENS, the one place for a value (the learner audit, 2026-09-25): colours, the type scale (8 steps and the door's display), 4 radii and the pill, the shadows, the hit, the frame and the bar's room; a rule names a token, never a typed size. THE SHADOWS, ONE SCHEME (2026-09-26, the owner: "the shadows around the popups are not uniform"): the outlines are rings (--ring and --ring-on, set per element by --rc; --ring-hair; --edge on glass, --edge-night on the night glass) and the elevation is three levels (--lift-card the card's frame, --lift-bar the bar, the pills and the rounds, --lift-sheet every popup); a box-shadow names these tokens alone (tools/clean.mjs) */
:root{--crest-inline:20px;--crest-row:34px;--crest-card:64px;--crest-hero:96px;color-scheme:only light;--act-row:768px;--wide:1500px;--read:880px;--lead:32px;--lead-box:22px;--dash:3 3;--chip-x:8px;--paper-min:320px;--rim-foot:#222345;--stage-foot:16px;--row:12px;--step-gap:14px;--it-y:8px;--it-x:10px;--kick:20px;--head:24px;--q-row:36px;--q-row-sm:28px;--ink:#232467;--ink-soft:#4a4b83;--mute:#67688d;--faint:#6a6c96;--paper:#f6f6fa;--sheet-foot:#f8f8fc;--sheet:linear-gradient(180deg,#fff,var(--sheet-foot)) var(--sheet-foot);--dim:rgba(10,11,40,.55);--chat-h:640px;--line:#d7d8e3;--ok:#1b6b4a;--warm:#b3261e;--warn:#8a4b00;--sky:#4a91e5;--sky-ink:#1a62b8;--sky-rgb:74,145,229;--sky-soft:rgba(74,145,229,.12);--sky-tint:rgba(74,145,229,.06);--sky-glow:rgba(74,145,229,.35);--ice-rgb:207,227,255;--glow-rgb:142,193,255;--night:rgba(255,255,255,.92);--night-mute:rgba(255,255,255,.72);--night-faint:rgba(255,255,255,.6);--field:linear-gradient(180deg,#25265a 0%,#141539 100%);--sans:'Teneo Sans',system-ui,-apple-system,'Segoe UI',sans-serif,'Apple Color Emoji','Segoe UI Emoji','Noto Color Emoji';--mono:ui-monospace,'SF Mono',Menlo,monospace;--tab:tabular-nums lining-nums;--t1:12px;--t2:13px;--t3:14px;--t4:15px;--t5:16px;--t6:18px;--t7:22px;--t8:28px;--t-display:clamp(30px,3.2vw,42px);--r1:6px;--r2:10px;--r3:14px;--r4:18px;--pill:999px;--edge:inset 0 0 0 1px rgba(20,21,58,.1),inset 0 1px 0 rgba(255,255,255,.8);--edge-night:inset 0 0 0 1px var(--isle-line),inset 0 1px 0 rgba(255,255,255,.16);--lift-card:0 12px 32px -14px rgba(4,6,32,.5);--lift-bar:0 12px 32px -14px rgba(4,6,32,.5);--lift-sheet:0 24px 48px -16px rgba(4,6,32,.6);--lift-knob:0 0 0 .5px rgba(20,21,58,.1),0 1px 3px rgba(20,21,58,.25);--glow:0 0 12px var(--sky-glow);--sheet-pad:22px;--sheet-in:sheet-in .35s cubic-bezier(.2,.8,.2,1);--sheet-out:sheet-out .22s cubic-bezier(.4,0,.6,1) forwards;--hit:44px;--band:7px;--round:25px;--rim-r:32px;--top-h:64px;--otty-row:138px;--bar-h:58px;--bar-edge:14px;--bar-gap:8px;--bar-room:calc(var(--bar-h) + 2 * var(--bar-edge) + env(safe-area-inset-bottom));--goal-bar:8px;--goal-act:44px;--badge-art:64px;--hair:2px;--chip-h:36px;--chip-h-sm:28px;--round-h:40px;--av-s:32px;--av-m:44px;--av-l:64px;--av-ground:radial-gradient(120% 100% at 50% 12%,#fff 0%,#e8effb 78%) #e8effb;--av-rim:rgba(35,36,103,.12);--face-pod:56px;--icon-s:18px;--icon-m:20px;--icon-sm:16px;--tok-s:28px;--meter-h:6px;--glass:rgba(255,255,255,.3);--bar-glass:rgba(255,255,255,.92);--glass-blur:blur(9px) saturate(1.4);--lens:rgba(255,255,255,.5);--lens-blur:blur(4px) saturate(1.6) brightness(1.04);--isle:rgba(28,29,74,.55);--isle-blur:blur(20px) brightness(.42) saturate(1.4);--isle-line:rgba(255,255,255,.14);--night-fill:rgba(255,255,255,.06);--hair-night:inset 0 1px 0 var(--isle-line);--night-lift:0 8px 24px rgba(5,6,30,.24);--rim:rgba(255,255,255,.06);--rim-blur:blur(18px) saturate(1.3);--glyph-i:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='black' stroke-width='1.8' stroke-linecap='round' stroke-linejoin='round'%3E%3Ccircle cx='12' cy='12' r='9'/%3E%3Cpath d='M12 8v5'/%3E%3Cpath d='M12 16.5v.01'/%3E%3C/svg%3E");--glyph-info:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='black' stroke-width='1.8' stroke-linecap='round' stroke-linejoin='round'%3E%3Ccircle cx='12' cy='12' r='9'/%3E%3Cpath d='M12 11v5.5'/%3E%3Cpath d='M12 7.6v.01'/%3E%3C/svg%3E");--glyph-ok:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='black' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M5 12.5l4.5 4.5L19 7.5'/%3E%3C/svg%3E");--glyph-way:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='black' stroke-width='3' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M9 6l6 6-6 6'/%3E%3C/svg%3E");--glyph-out:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='black' stroke-width='3' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M7 17 17 7M9 7h8v8'/%3E%3C/svg%3E");--way-s:16px;--way-r:32px;--page-head:124px;--g-spark:var(--sky);--g-spark-lt:rgb(var(--ice-rgb));--g-sparkle:#a13fc0;--g-sparkle-lt:#e0a0ff;--g-thanks:#d23c68;--g-thanks-lt:#ff8fb0;--g-flame:#dd6326;--g-flame-lt:#ff8a55;--g-chest:#bd7f0a;--g-chest-lt:#f6c350;--g-mission:#6a52de;--g-mission-lt:#b3a1ff;--g-rank:var(--ink);--g-rank-lt:var(--night);--g-on-gold:#5a3a08;--g-level-1:#a86437;--g-level-1-lt:#f0c29a;--g-level-2:#74819a;--g-level-2-lt:#e6ebf2;--g-level-3:#a87712;--g-level-3-lt:#f8d977;--g-dot-lit:#ffd166;--hold:linear-gradient(180deg,#ffd27a,#f2b347);--hold-ink:#3f2a06;--hair-lit:inset 0 1px 0 rgba(255,255,255,.45);--g-tile:32px;--g-dot:8px;--gold:linear-gradient(180deg,#fff4c8,#f3c850 55%,#e0a93a);--gold-lift:0 0 0 1px rgba(142,96,15,.35),0 4px 14px rgba(240,166,64,.35);--gold-glow:0 0 6px 1px rgba(255,209,102,.8)}
/* the two rings resolve on the element that wears them, so its --rc (the ring's colour) holds */
*,::before,::after{box-sizing:border-box;--ring:inset 0 0 0 1px var(--rc,var(--line));--ring-on:inset 0 0 0 1.5px var(--rc,var(--ink));--ring-hair:inset 0 0 0 var(--hair) var(--rc,var(--ink));--tint:linear-gradient(180deg,var(--tint-hi),var(--tint-lo)) var(--g,var(--sky));--wait:linear-gradient(180deg,var(--wait-hi),var(--wait-lo)) var(--g,var(--ink));--tint-edge:var(--ring);--tint-edge:inset 0 0 0 1px color-mix(in srgb,var(--g,var(--sky)) 24%,transparent),var(--top-light);--wait-edge:var(--ring);--wait-edge:inset 0 0 0 1px color-mix(in srgb,var(--g,var(--ink)) 30%,transparent),var(--top-light)}
/* THE SURFACES, ONE SYSTEM (2026-09-27, the owner, over the grey "Nothing to bank yet" pill: "I don't like this lazy undesigned gray background you put behind anything. You need to design this nicer, wherever there is a gray"; "you need to think designer, and lux"): no element takes a flat grey; a fill names one of six surfaces, its values the tokens here, in the night side's language (a fill with a top light, a precise hairline, a soft layered shadow). RAISED (--raised, --raised-edge): white with its top light, the hairline and a soft lift; the cards, tiles and cells on the paper. TINTED (--tint, --tint-edge): the concept's own colour (--g, the sky when none) at a breath, its hairline in the same colour, the top light; the game's items and the notes (the chest gold, the flame warm, a sparkle violet, done green). QUIET (--quiet-edge, no fill): a clear control, one hairline ring and the ink; the rounds, the arrows, the small tags. WAITING (--wait, --wait-edge): a promise, never a broken grey: the concept's colour at its faintest, its ring a little clearer, the glyph and the words muted; a hero one (the Bank pill) runs a slow light round its ring. TRACK (--track-fill, --track-edge; --track-line for a ring's track): a channel sunk in the paper, the ink at 7% with a rounded inner shadow; meters, bars, the switch, the flip. HOVER (--hover): the sky's breath under the pointer. TINTED and WAITING read --g on the element (so their two tokens resolve here beside the rings); tools/clean.mjs 10 refuses a grey fill anywhere else */
:root{--hairline:rgba(35,36,103,.09);--raised:linear-gradient(180deg,#fff 0%,#fafafd 100%) #fff;--lift-raised:0 1px 2px rgba(20,21,58,.05),0 6px 16px -10px rgba(20,21,58,.2);--raised-edge:inset 0 0 0 1px var(--hairline),var(--lift-raised);--top-light:inset 0 1px 0 rgba(255,255,255,.8);--tint-hi:rgba(255,255,255,.94);--tint-lo:rgba(255,255,255,.87);--wait-hi:rgba(255,255,255,.97);--wait-lo:rgba(255,255,255,.91);--quiet-edge:inset 0 0 0 1px rgba(35,36,103,.16);--hover:var(--sky-tint);--track-fill:rgba(35,36,103,.07);--track-edge:inset 0 1px 2px rgba(20,21,58,.13),inset 0 0 0 1px rgba(35,36,103,.05);--track-line:rgba(35,36,103,.12);--tile-art:56px;--h-1:#3553b5;--h-2:#11808a;--h-3:#a53c6b;--h-4:#6f7d1c;--h-5:#a4592b;--h-6:#52607a;--h-v:#6a52de;--hue-edge:var(--raised-edge);--crest-s:34px;--crest-m:44px;--crest-l:60px;--kind-s:34px;--act-h:36px;--tick-s:30px;--seg-h:6px;--ok-disc:linear-gradient(180deg,#2c8a63,#1b6b4a);--halo:0 0 0 5px rgba(74,145,229,.14);--lift-ok:0 2px 6px -2px rgba(27,107,74,.5);--sky-edge:inset 0 0 0 1.5px rgba(74,145,229,.5);--now-line:rgba(74,145,229,.6);--flame-edge:inset 0 0 0 1px rgba(221,99,38,.3);--dot-s:12px;--dot-edge:inset 0 0 0 2px rgba(35,36,103,.12);--act-way:26px;--ok-line:rgba(27,107,74,.45);--card-slot:52px;--round-s:26px;--tile-min:150px;--tile-h:108px;--tile-glyph:38px}
button{font:inherit}
/* THE HIT, one rule for every tap target (the learner audit, 2026-09-25: arrows at 28, words at 17, links at 15; 2026-09-27, the owner: the clear ring that grew the tap drew every small pill's ends as an ellipse): a target is --hit tall and wide at least; a target drawn smaller names its face (--face tall, --face-i wide: a round, a chip, a word) and its box IS the face, so its radius, ring, shadow, focus ring and room in the layout are the face's own; the tap grows to 44 px by one clear layer, --rb above and below and --ri at the sides: the target's ::after, or its ::before where the ::after draws (a summary's chevron, an outside link's arrow, the bank's ring); a target at full size has a layer of no size; every target is its layer's box (position: relative) */
:where(a[href],button,summary,[role=button]){--rb:max(0px,(var(--hit) - var(--face,var(--hit))) / 2);--ri:max(0px,(var(--hit) - var(--face-i,var(--hit))) / 2);position:relative;min-block-size:calc(var(--hit) - 2 * var(--rb));min-inline-size:calc(var(--hit) - 2 * var(--ri));border:0}
:where(a[href],button,[role=button]):not(:where(summary,.prose a[target='_blank'],.pbank))::after,:where(summary,.prose a[target='_blank'],.pbank)::before{content:'';position:absolute;right:calc(var(--ri) * -1);bottom:calc(var(--rb) * -1);width:min(100% + 2 * var(--ri),(var(--rb) + var(--ri)) * 9999);height:min(100% + 2 * var(--rb),(var(--rb) + var(--ri)) * 9999)}
/* THE TAP IS THE TARGET'S OWN (2026-09-27, the owner: "you click Back or Hand in and nothing happens; you need to re-click"): the parts inside a target (its word, its glyph, its svg) take no hit, so the press and the release land on the target itself even when a part is written again between them; a control inside a target, and a part with its own tip, keep theirs */
:where(a[href],button,summary,[role=button]) *{pointer-events:none}
:where(a[href],button,summary,[role=button]) :where(a[href],button,summary,input,select,textarea,label,video,audio,iframe,[role=button],[tabindex],[data-tip],.pinfo){pointer-events:auto}
:where(a:not([class]),.prose a:not(.ccard):not(.pic)){--face:1.3em;--face-i:calc(var(--hit) - 12px);min-inline-size:0}
:where(.prose summary){--face:23px}
/* A LINK THAT STANDS FOR ITS WHOLE BOX (the Timetable's cell; its link stood 31 to 41 px wide at narrow sizes): a static block whose ::after is the box, its own box never under the hit (the box's width or --hit, centred on it), its words in the box's width */
.cell:has(b a){position:relative}
.cell b a{--face-i:var(--hit);position:static;display:block;margin-inline:min(0px,(100% - var(--hit)) / 2);padding-inline:max(0px,(var(--hit) - 100%) / 2)}
.cell b a::after{content:'';position:absolute;inset:0;width:auto;height:auto;border-radius:inherit}
[hidden]{display:none!important}
html,body{height:100%;overflow-x:hidden;overscroll-behavior-x:none}
body{margin:0;background:#141539 var(--field);color:var(--ink);font:400 var(--t4)/22px var(--sans);-webkit-font-smoothing:antialiased}
/* the learner's head holds his three-line bubble over his 44 px row (quest.css: 10 + 79 + 6 + 44 + 12), so his row is taller than the guardian's; the guardian's holds his longest line from the first paint (138 px, 2026-09-27: at 126 the Learners pages' line landed and moved the card 12 px under the finger) */
.app.learn{--otty-row:151px}
/* a 100vh line before each 100dvh and a plain colour before each color-mix: the floor (tools/floor.mjs, 2026-09-19) */
.app{--card-r:var(--round);--gut:clamp(20px,3vw,40px);height:100vh;height:100dvh;background:var(--field);display:flex;flex-direction:column;overflow:hidden;overscroll-behavior-x:none}
html:not([data-fill=on]) .app:not(:has(> .top))::before{content:'';flex:none;height:var(--top-h)}
html[data-fill=on] .app:not(.door):not(.enroldoor) .stage:not(:has(> .otty))::before{content:'';flex:none;height:calc(max(var(--otty-row), var(--corner-y, 0px) + 12px) + env(safe-area-inset-top))}
/* OTTY STAYS BETWEEN PAGES (the owner, 2026-09-15 20:2x: "let Otty stay continuous as we click through pages and just change the rest"): every page is its own document, so the browser's cross-document transition carries the top bar, him, his words and the pill over the change in place, the rest crossfading; the new page holds the old page's picture (html.hold, set by the shell) until its own reveal, 700 ms at most; reduced motion cuts straight */
@view-transition{navigation:auto}
/* a tap during the change reaches the page, never the transition's picture (the owner, 2026-09-16 01:5x: "buttons need a double click; something blocks the first click"): the pseudo-tree lets pointer events through */
::view-transition{pointer-events:none}
::view-transition-group(*),::view-transition-old(*),::view-transition-new(*){animation-duration:.12s}
/* ── TOP BAR (frozen) ── */
.top{view-transition-name:top;position:relative;z-index:6;height:var(--top-h);display:flex;align-items:center;justify-content:space-between;padding:0 16px;color:#fff;flex:none;background:none}
/* no room is reserved for the jump: otty rises over whatever stands above him */
.otty .mount{view-transition-name:otty;align-self:start;grid-area:mount;width:var(--osz);aspect-ratio:1;flex:none;position:relative;margin:0;z-index:3}
/* his still for good when no painted frame was proven (shell.js proof): the films stand down, so he is never an empty box nor a double */
.otty .mount[data-still] > :not(.say){visibility:hidden}
/* ── THE SPEECH BUBBLE (the owner, 2026-09-15 16:4x to 17:1x: "an elegant thin glassy speech bubble, shown after the size of the text is known, for the phone and the desk; appear magically"; "the colour doesn't match the theme"; "the triangle has a closed back where it connects"; "the speech and the pill are not aligned") ── */
/* the slot (.say) keeps its place and size; the bubble inside hugs the words and wears the island's navy glass (the theme's box on the field), a tail towards him drawn as one piece (the tail's solid the glass's own composite, its two outer edges ringed, its base over the bubble's ring); it is drawn once the whole line is known (a ghost of the whole line sizes it while the words stream in over it) and rises in once; on a phone its top is the island's top */
.otty .say{view-transition-name:say;grid-area:say;color:#fff;font:400 var(--t2)/19px var(--sans);max-width:none;margin:0;min-width:0;min-height:41px;display:flex;align-items:flex-start;justify-content:flex-start;align-self:start}
/* ONE PILL (the owner, 2026-09-15): the same glass as the card's shell (the faint white fill, the white ring, the deep blur, the cast shadow), and the icons follow the ground: white over the navy field, ink where the white box stands under the bar (the fill) */
.bar{view-transition-name:pill;pointer-events:auto;display:flex;align-items:center;gap:2px;max-width:100%;border:0;border-radius:var(--pill);padding:4px;background:var(--bar-glass);-webkit-backdrop-filter:var(--glass-blur);backdrop-filter:var(--glass-blur);box-shadow:var(--edge),var(--lift-bar)}
html.hold::view-transition-group(*),html.hold::view-transition-old(*),html.hold::view-transition-new(*){animation-play-state:paused}
/* before the reveal every change lands instantly (no transition); the frame stands from the first paint, so an arrival plays where it lands and never again at the reveal */
.app:not(.up) *{transition:none!important}
.logo{--face:42px;--face-i:42px;display:flex;align-items:center;text-decoration:none}
.logo img{height:28px;width:auto;display:none}
/* the phone's mark: the same glass, round */
.logo .mark{display:grid;place-items:center;background:var(--isle);backdrop-filter:var(--isle-blur);-webkit-backdrop-filter:var(--isle-blur);box-shadow:var(--edge-night);border-radius:50%;width:42px;height:42px}
.logo .mark svg{width:14px;height:auto;display:block;fill:#fff}
.tright{display:flex;align-items:center;gap:16px}
/* the top island: the same pill as the bottom bar, two segments, the bell with its counter and the name with its chevron */
.me{position:relative}
.isl{display:inline-flex;align-items:center;gap:2px;padding:4px;border-radius:calc(var(--hit) / 2);background:var(--isle);backdrop-filter:var(--isle-blur);-webkit-backdrop-filter:var(--isle-blur);box-shadow:var(--edge-night),var(--lift-bar)}
/* the now button, the slot it seats within (day.js compact; shell.js seat under 1000 px): drawn only where the fill's rules below draw it */
.top #now{display:none}
.isl .seg{--face:36px;display:inline-flex;align-items:center;gap:8px;height:var(--face);padding:0 12px;border-radius:var(--pill);font:500 var(--t2)/20px var(--sans);color:var(--night);background:none;border:0;cursor:pointer;position:relative;max-width:min(52vw,280px);outline:0}
.isl .seg.ico{--face-i:40px;width:var(--face-i);padding:0;justify-content:center}
.isl .seg svg{width:20px;height:20px;stroke:currentColor;fill:none;stroke-width:1.5;stroke-linecap:round;stroke-linejoin:round;flex:none}
.isl .seg .nm{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.isl .seg .chev{width:12px;height:12px;stroke-width:2;color:var(--night-faint);transition:transform .3s ease}
/* the bell's one counter (shell.js tally): the unread count, 9+ above nine, ink on the glow (7.4:1) */
.isl .seg .pip{position:absolute;top:1px;right:1px;box-sizing:border-box;min-width:18px;height:18px;padding:0 4px;border-radius:9px;font:700 var(--t1)/18px var(--sans);font-variant-numeric:var(--tab);text-align:center;color:var(--ink);background:rgb(var(--glow-rgb))}
.me.open .isl .seg[aria-expanded="true"]{color:#fff}
.isl .rule{width:1px;height:20px;background:rgba(255,255,255,.14);margin:0 2px}
/* THE DRAWER (the owner, 2026-09-15 23:2x: "grown from the capsule"; 2026-09-16 01:0x: "make the tabbing stay in place; just under it expands, not the actual element"): the island stays as it is, its size, place and spacing untouched; the bell and the name are its two tabs, the open one white and the other muted; the paper pane opens right under it, right-aligned and wider, and the glass is one shape: the pane's top edge meets the capsule's bottom edge, the capsule's bottom corners square at the join, one outline around both (the capsule's ring open at its foot, the pane's open at its head and drawn left of the capsule by its ::before); the chevron turns while open; the rows scroll (16:5x: "these long notifications go out of the screen and don't even scroll") */
.me.open .isl{position:relative;z-index:10;border-radius:calc(var(--hit) / 2) calc(var(--hit) / 2) 0 0;box-shadow:var(--edge-night)}
.me.open .isl::after{content:'';position:absolute;inset:0;border-radius:inherit;border:1px solid var(--isle-line);border-bottom-color:transparent;pointer-events:none}
.me.open #me .chev{transform:rotate(180deg)}
/* the open tab shows by its colour alone (the owner, 01:2x: "don't put a tab line under it"): the open one white, the other muted, the chevron turned */
.me.open .isl .seg{color:var(--night-faint)}
.drawer::before{content:'';position:absolute;top:0;left:0;right:var(--cap,0px);height:var(--rim-r);border-top:1px solid var(--isle-line);border-left:1px solid var(--isle-line);border-top-left-radius:var(--rim-r);pointer-events:none}
.drawer::after{content:'';position:absolute;inset:0;border-radius:inherit;border:1px solid var(--isle-line);border-top-color:transparent;pointer-events:none}
.drawer .paper{min-height:0;display:flex;flex-direction:column;padding:8px;border-radius:var(--round);color:var(--ink);background:var(--sheet);box-shadow:var(--ring);overflow:auto;overscroll-behavior:contain;scrollbar-width:thin;scrollbar-color:var(--line) transparent}
/* ONE OPENING FOR BOTH SHEETS ON EVERY SIZE (the owner, 2026-09-21: "elegant opening etc on mobile very clean"): the pane rises from the widget's foot (its origin the widget's corner, the theme's curve, 0.35 s) and goes back the same way (pane-out, 0.22 s; shell.js hides it once the way out ends, at once where no way out runs), the widget's corners square with it (the island's transition above), the dim under it fades (the shell's rule below); reduced motion gives the pane no animation at all (the sheet's foot) and the rest a hundredth of a millisecond; the page behind takes no touch while the pane stands, so nothing under it scrolls or follows a tap, and the tap closes the pane alone */
.drawer{position:absolute;top:100%;right:0;z-index:9;min-width:100%;width:min(calc(100vw - 32px),360px);max-height:calc(100vh - 136px - env(safe-area-inset-top));max-height:calc(var(--vv-h,100dvh) - 136px - env(safe-area-inset-top));padding:var(--band);border-radius:var(--rim-r) 0 var(--rim-r) var(--rim-r);display:flex;flex-direction:column;background:var(--isle);-webkit-backdrop-filter:var(--isle-blur);backdrop-filter:var(--isle-blur);box-shadow:var(--lift-sheet);transform-origin:top right;animation:pane-in .35s cubic-bezier(.2,.8,.2,1)}
.drawer.away{animation:pane-out .22s cubic-bezier(.4,0,.6,1) forwards;pointer-events:none}
.drawer .dhead{display:none}
@keyframes pane-in{from{opacity:0;transform:translateY(-6px) scaleY(.96)}}
@keyframes pane-out{to{opacity:0;transform:translateY(-6px) scaleY(.96)}}
.sheet:focus{outline:0}
html.draweropen .app:not(.door):not(.enroldoor) .stage{pointer-events:none}
.sheet{display:flex;flex-direction:column;min-height:0}
/* the first-open offer (push.js) keeps the paper-in-the-rim dress on the veil */
.offer .sheet{position:relative;width:min(100%,440px);color:var(--ink);border-radius:var(--round);padding:var(--sheet-pad) var(--sheet-pad) 18px;z-index:9}
.offer .sheet::before{content:'';position:absolute;inset:calc(var(--band) * -1);border-radius:var(--rim-r);background:var(--rim);backdrop-filter:var(--rim-blur);-webkit-backdrop-filter:var(--rim-blur);box-shadow:var(--edge-night),var(--lift-sheet);z-index:-2}
.offer .sheet::after{content:'';position:absolute;inset:0;border-radius:var(--round);background:var(--sheet);box-shadow:var(--ring);z-index:-1}
.sheet[hidden]{display:none}
.sheet .n{display:grid;grid-template-columns:auto minmax(0,1fr) auto;gap:0 10px;align-items:start;padding:10px 14px;border-radius:var(--r2);text-decoration:none;color:var(--ink)}
.sheet .n:hover{background:var(--hover)}
/* a row that opens its sheet is a button in the link row's own dress (2026-10-16, README 3 A NOTIFICATION ROW ALWAYS OPENS ITS THING; shell.js row) */
.sheet button.n{width:100%;text-align:left;background:none;border:0;cursor:pointer}
/* the row's own dot is its child alone (2026-09-21: as a descendant rule it reached the orb of the act pill inside the row, a dot under the arrow) */
.sheet .n > i:not(.way){width:8px;height:8px;border-radius:50%;background:var(--sky);margin-top:7px}
.sheet .n > i.off{background:var(--line)}
/* a row's way in (round 3 review): the one mark at the row's end, in its middle; on the Notifications page the rows start at the title's edge, their hover ground reaching out past it */
.sheet .n > .way{align-self:center;color:var(--mute)}
.sheet.notices .n{margin-inline:calc(var(--it-x) * -1);padding-inline:var(--it-x)}
.sheet .n b{display:block;font:500 var(--t3)/20px var(--sans)}
.sheet .n small{display:block;font:400 var(--t1)/17px var(--sans);color:var(--mute)}
.sheet .r{display:block;padding:12px 14px;border-radius:var(--r2);font:500 var(--t3)/20px var(--sans);color:var(--ink);text-decoration:none}
.sheet .r:hover{background:var(--hover)}
/* ── THE STAGE: otty left, the card ── */
/* ONE SCROLLER PER PAGE (the owner, 2026-10-06, the Timetable at a desk: "when you go down or up it gets stuck until you move the mouse to a different section"): the stage is the page's one vertical scroller, and nothing inside it owns a vertical scroll (a pane grows to its content). A browser latches a wheel gesture to the scroller it picks under the pointer at the gesture's first tick and keeps it there to the gesture's end, so an inner pane that can take the first tick (a sideways scroller takes a slanted first tick) holds the whole gesture and the page stands still until the pointer leaves it. So: THE SIDEWAYS SCROLLERS, ONE LIST, below: a block wider than the card scrolls sideways only here, its vertical axis closed, its overscroll the default (auto) so a gesture that ends at its edge hands the next one to the stage; no wheel handler anywhere (shell.js and payment.js only listen, passive). The timetable grid is not on the list: it fits the card at every width (.tt columns minmax(0,1fr), no min-width), so it is no scroller at all. A sheet, a drawer or a popup over the page is its own layer and keeps overscroll-behavior:contain: nothing under it should move. */
.stage{flex:1;min-height:0;overflow:auto;display:grid;grid-template-columns:minmax(0,1fr);gap:0;padding:0 16px 24px;max-width:1760px;width:100%;margin:0 auto;align-content:start;padding-bottom:var(--stage-foot)}
/* THE SIDEWAYS SCROLLERS, ONE LIST (the rule above; :where, so the list is the default and a block's own stated exception wins, the Tasks filter row wrapping under 1280 px): a wide table, formula or code block in a page's body, the buddy row, the phone's day strip, the Tasks filter row; nowhere else does a block scroll */
:where(.prose .scroll,.prose math,.prose pre,.pbuddy .row,.ttday .days,.tfilters,.flow .cmp-wrap){overflow:auto hidden;overscroll-behavior:auto}
.otty{position:sticky;top:0;z-index:4;display:grid;grid-template-columns:auto minmax(0,1fr);grid-template-areas:'mount say';align-items:center;column-gap:12px;margin:0 -16px;padding:6px 16px 12px;background:var(--field);background-size:100% 100vh;background-size:100% 100dvh;background-position:0 -56px;background-repeat:no-repeat;--osz:72px}
.otty .barwrap{grid-area:bar}
.shell{background:var(--rim);border-radius:var(--rim-r);padding:var(--band);box-shadow:var(--edge-night),var(--lift-card);width:min(100%,774px);margin:0 auto;min-width:0}
/* THE RIM BAND (the owner, 2026-09-16 the staff mark; 2026-10-03 one component, two uses): a slim band on the rim above the card. .staff while a staff access link's session stands (claims.staff): who the page is viewed as and until when, in the rim's own night tone; .held while the learner is suspended: the hold tone (--hold, a warm amber of the palette's chest gold with dark ink, readable on any ground), filled, rounded as the card, the sentence allowed its lines on a phone, the whole band the one act (the Support chat); the staff band stands first, the held band under it; nothing else of the page changes */
.shell .band{font:500 var(--t1)/16px var(--sans);letter-spacing:.02em;color:var(--night);text-align:center;padding:3px 12px 9px;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
.shell .band.held{display:block;margin:0 0 var(--band);padding:9px 16px;border-radius:var(--round);background:var(--hold);color:var(--hold-ink);font-size:var(--t2);line-height:18px;letter-spacing:0;white-space:normal;text-wrap:pretty;text-decoration:none;box-shadow:var(--hair-lit)}
.shell .band.held:hover{filter:brightness(1.03)}
.card{background:var(--sheet);border-radius:var(--card-r);box-shadow:none;padding:clamp(24px,3vw,40px) var(--gut) clamp(24px,3vw,40px);min-width:0}
/* the door card's shape before its words (the owner, 2026-09-15 22:4x: "a placeholder"): the module's paint replaces it */
/* ── CONTENT: no lines, space and alignment ── */
.who{margin-top:36px}
.who:first-child{margin-top:0}
:where(h1,h2).nm{margin:0}
.who .nm{font:600 var(--t7)/28px var(--sans);letter-spacing:-.02em;color:var(--ink)}
.who .fx{font:400 var(--t3)/20px var(--sans);color:var(--mute);margin-top:3px}
.who .fx a{color:var(--ink);text-decoration:underline;text-underline-offset:2px}
/* THE TRAIL (2026-10-01, the Phase Heads' feedback 5; kit.js Head up, study.js Trail): the kicker's type over the head in the head's own column, Courses › the course › the module, each a quiet link that darkens on hover, a long name cut with an ellipsis */
.who .trail{display:flex;flex-wrap:wrap;align-items:center;gap:0 6px;margin:0 0 6px;font:500 var(--t2)/18px var(--sans);color:var(--mute)}
.who .trail a{color:var(--mute);text-decoration:none;min-width:0;max-width:36ch;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.who .trail a:hover{color:var(--ink);text-decoration:underline;text-underline-offset:2px}
.who .trail .way{--way-s:12px;color:var(--faint)}
/* THE STATE LINE (kit.js StateLine; the Phase Heads' feedback 8): the state icon of the one set beside its word, in the facts line and in a row's key */
.pst{display:inline-flex;align-items:center;gap:6px;min-width:0}.pst .pstate{flex:none}
.pst[data-state="handed"],.pst[data-state="marked"]{color:var(--ink)}
/* THE STUDENT NUMBER at the head (2026-09-27): a small ID tag on the name's left edge, the facts line's gap above it; the chips' pill radius and hairline, no fill; the tiny uppercase label (Copied in the same cell, so nothing moves), the number a step under the facts line in tabular figures, a hairline divider, the quiet copy glyph */
.who .sn{display:flex;flex-wrap:wrap;align-items:center;gap:8px 14px;margin-top:8px}
.who .sn .pill{display:inline-flex;align-items:center;gap:8px;height:30px;padding:0 3px 0 10px;border-radius:var(--pill);box-shadow:var(--ring);color:var(--ink)}.who .sn .copy{margin-left:-6px}
.who .sn .k{display:inline-grid;font:500 11px/1 var(--sans);letter-spacing:.06em;text-transform:uppercase;color:var(--mute)}.who .sn .k > *{grid-area:1/1}.who .sn .k .off{visibility:hidden}.who .sn .did .k{color:var(--ok)}
.who .sn .n{display:inline-flex;gap:.32em;padding-right:6px;border-right:1px solid var(--line);font:500 var(--t2)/14px var(--sans);font-variant-numeric:var(--tab);white-space:nowrap}
.who .sn .sd{font:400 var(--t2)/18px var(--sans);color:var(--mute)}
/* THE SHINE, one rule for a light running round an edge (2026-09-27): a conic ring masked to the element's edge, above its fill and under its words; .shine turns for ever on a sky hairline (the re-enrol pill), .shine.now runs one lap every 12 s on no hairline and rests between (his question chips: "a minimal occasional glow"); reduced motion: .shine a still soft glow, .shine.now nothing */
@property --spin{syntax:'<angle>';inherits:false;initial-value:0deg}
@keyframes shine-spin{to{--spin:360deg}}
@keyframes shine-now{0%{--spin:0deg;opacity:0}3%{opacity:1}20%{opacity:1}24%,100%{--spin:360deg;opacity:0}}
.shine{position:relative;isolation:isolate}
.shine::before{content:'';position:absolute;inset:0;z-index:-1;pointer-events:none;border-radius:inherit;padding:1.5px;background:conic-gradient(from var(--spin),rgba(var(--sky-rgb),.28) 0deg 250deg,var(--sky) 310deg,rgb(var(--ice-rgb)) 330deg,rgba(var(--sky-rgb),.28) 360deg);-webkit-mask:linear-gradient(#000 0 0) content-box,linear-gradient(#000 0 0);-webkit-mask-composite:xor;mask:linear-gradient(#000 0 0) content-box exclude,linear-gradient(#000 0 0);animation:shine-spin 5s linear infinite}
.shine.now::before{opacity:0;background:conic-gradient(from var(--spin),transparent 0deg 250deg,var(--sky) 310deg,rgb(var(--ice-rgb)) 330deg,transparent 360deg);animation:shine-now 12s linear 2s infinite}
@media (prefers-reduced-motion:reduce){.shine::before{animation:none;background:rgba(var(--sky-rgb),.55)}.shine:not(.now){box-shadow:var(--glow)}.shine.now::before{content:none}}
/* AT REST THE DECORATIVE LOOPS STAND STILL (2026-10-13, the rest lane; shell.js marks the root data-rest when the tab is hidden or nothing was touched for 20 s): the shine's lap, the chest's dot and glow, the sparkle's twinkle and Otty's breath pause until the next input; a waiting cue (the dots, the scanbar, the skeleton's light) keeps running */
:root[data-rest] :is(.shine,.pchestart.s-ready)::before,:root[data-rest] .gtile.on::after,:root[data-rest] :is(.pspark .tw,.otty-stage){animation-play-state:paused}
/* RE-ENROL AT THE HEAD (2026-09-27): the number pill's twin in white, THE SHINE turning round its edge; once re-enrolled, a quiet line with a tick */
.who .sn .renew{--face:30px;display:inline-flex;align-items:center;gap:6px;height:30px;padding:0 14px 0 10px;border-radius:var(--pill);background:#fff;color:var(--ink);font:500 var(--t2)/1 var(--sans);text-decoration:none;white-space:nowrap;transition:background .2s}
.who .sn .renew:hover{background:var(--sky-tint)}
.who .sn .renew.busy{opacity:.6;cursor:progress}
.who .sn .renew svg{width:16px;height:16px;fill:none;stroke:currentColor;stroke-width:1.8;stroke-linecap:round;stroke-linejoin:round;flex:none}
.who .sn .renew.done{background:none;padding:0;gap:6px;font-weight:400;color:var(--mute)}.who .sn .renew.done::before{content:none}.who .sn .renew.done svg{color:var(--ok)}
/* THE PILL DISABLED WHILE RE-ENROLMENT IS CLOSED (the owner, 2026-10-08): the same pill held, no shine, the muted ink, the quiet word beside its text, no pointer */
.who .sn .renew.off{background:rgba(255,255,255,.55);color:var(--mute);cursor:default;--rc:rgba(var(--sky-rgb),.28);box-shadow:var(--ring)}.who .sn .renew.off:hover{background:rgba(255,255,255,.55)}.who .sn .renew.off b{font:400 var(--t1)/1 var(--sans);color:var(--mute);margin-left:2px}.who .sn .renew.off b::before{content:'·';margin-right:6px}
/* THE RHYTHM: a row wears --row, a kicker stands --kick over its rows, the head --head over the first kicker; one step tighter on a phone */
.gt{font:500 var(--t2)/18px var(--sans);color:var(--mute);margin:var(--kick) 0 8px}
.fx .dot::before{content:'·';margin:0 6px}
/* THE TERM PATH (daycard.js TermMap; the learner's desk and the guardian's card): the wavy road faint, the weeks gone in the sky, a dot a week, this week's dot ringed */
.today .qmap .map{display:block;width:100%;height:16px;overflow:visible}
.qmap .road{fill:none;stroke:rgba(255,255,255,.18);stroke-width:2;stroke-linecap:round}
.qmap .done{fill:none;stroke:var(--sky);stroke-width:2;stroke-linecap:round}
.qmap circle{fill:rgba(255,255,255,.3)}.qmap circle.on{fill:var(--sky)}.qmap circle.now{fill:white;stroke:var(--sky);stroke-width:2}
/* THE PROGRESS PAGE (stats.js Progress; the owner, 2026-09-27: "more like a timeline ... using space well and mobile-friendly"): the last-seen line; the subjects as the Tasks page's rows (.trow: the subject and its small line, the mark at the right with its overdue count under it, a way to its marks) and the timeline beside them on a desk (5 to 7 of the width), under them on a phone. THE TIMELINE: a quiet dotted spine at the left; a band's head in the page's kicker; a row the day in a narrow column, the title over its subject, the state or the mark at the right, its bullet the learner card's small ring on the spine (a mark filled in the soft ink, a live lesson in the sky); TODAY a sky dot on the spine with the day in the small caps; the warm tone only on what is overdue */
.who .seen{margin:var(--kick) 0 0;font:400 var(--t2)/18px var(--sans);color:var(--mute)}
.prog{display:grid;gap:0 44px;align-items:start}
.trow.psub{grid-template-columns:minmax(0,1fr) auto}.trow.psub:has(> .way){grid-template-columns:minmax(0,1fr) auto 16px}
.trow.psub .ttl{font-size:var(--t3)}
.trow.psub .ttl small{white-space:normal;overflow:visible;text-overflow:clip}
.trow.psub > .way{--way-s:16px;justify-self:end}
.tdue small.late,.gt .n.late{display:inline-flex;align-items:center;gap:6px}
.tdue small.late::before,.gt .n.late::before{content:'';width:6px;height:6px;border-radius:50%;background:var(--warm)}
.ptl{position:relative;padding-left:22px}
.ptl::before{content:'';position:absolute;left:5px;top:var(--kick);bottom:6px;border-left:2px dotted var(--track-line)}
.tband > .gt{margin-bottom:4px}
.tli{position:relative;display:grid;grid-template-columns:52px minmax(0,1fr) auto;align-items:center;column-gap:12px;min-height:44px;padding:8px 0;border-top:1px solid var(--hairline)}
.tband > .gt + .tli{border-top:0}
.tli::before{content:'';position:absolute;left:-20px;top:50%;width:8px;height:8px;margin-top:-4px;border-radius:50%;background:var(--sheet-foot);--rc:var(--faint);box-shadow:var(--ring-on)}
.tli.mark::before{background:var(--ink-soft);box-shadow:none}.tli.live::before{background:var(--sky);box-shadow:var(--glow)}
.tli .td{font:500 var(--t2)/18px var(--sans);font-variant-numeric:var(--tab);color:var(--mute);white-space:nowrap}
.tli .ttl{font-size:var(--t3);line-height:20px}
.tli .tst{font:500 var(--t2)/18px var(--sans);font-variant-numeric:var(--tab);color:var(--ink-soft);white-space:nowrap}
.tli.mark .tst{color:var(--ink)}
.tnow{position:relative;display:flex;gap:10px;margin:var(--kick) 0 0}
.tnow::before{content:'';position:absolute;left:-21px;top:4px;width:10px;height:10px;border-radius:50%;background:#fff;--rc:var(--sky);box-shadow:var(--ring-hair)}
.tnow .k{font:500 var(--t1)/18px var(--sans);letter-spacing:.08em;text-transform:uppercase;color:var(--sky-ink)}
.ptl .tmore{margin:4px 0 0}
@media (min-width:1000px){.prog{grid-template-columns:minmax(0,5fr) minmax(0,7fr)}}
.who > :is(.nm,.fx,.sn) + .gt,.ask + .gt{margin-top:var(--head)}
.who > :is(.nm,.fx,.sn) + .cols{margin-top:calc(var(--head) - var(--kick))}
.who > :is(.nm,.fx,.sn) + .ask{margin-top:var(--head)}
.kv{display:grid;grid-template-columns:minmax(0,1fr) auto;align-items:baseline;column-gap:20px;padding:var(--row) 0}
.kv .k{font:400 var(--t5)/22px var(--sans);color:var(--ink);min-width:0}
.kv .k small{display:block;min-height:18px;font:400 var(--t2)/18px var(--sans);color:var(--mute);margin-top:1px}
.kv .v{font:400 var(--t5)/22px var(--sans);color:var(--ink);text-align:right;white-space:nowrap}
.kv .v.num{font-family:var(--sans);font-variant-numeric:var(--tab);font-size:var(--t4)}
.copy{--face:var(--chip-h-sm);--face-i:var(--face);display:inline-grid;place-items:center;width:var(--face);height:var(--face);background:none;border-radius:50%;color:var(--mute);cursor:pointer}.copy svg{width:16px;height:16px;fill:none;stroke:currentColor;stroke-width:1.6;stroke-linecap:round;stroke-linejoin:round}.copy:hover{background:var(--hover);color:var(--ink)}.copy .ck{display:none}.did .copy{color:var(--ok)}.did .copy .cp{display:none}.did .copy .ck{display:block}
.kv .v.need{color:var(--warm);font-weight:500}
/* THE INVIGILATOR'S FRAME (tasks.js ExamFrame, 2026-10-12): the tool's page in its frame above the exam row, the vendor's Canvas placement size (800 by 400) on the row's own width */
.examframe{margin:var(--row) 0}.examframe iframe{display:block;width:100%;height:400px;border:1px solid var(--line);border-radius:var(--r4);background:var(--raised)}
.kv .v.ok{color:var(--ok);font-weight:500}
.kv .v.mu{color:var(--mute)}
.ask{margin-top:16px;background:var(--raised);box-shadow:var(--raised-edge);border-radius:var(--r4);padding:20px 20px 18px;display:grid;grid-template-columns:minmax(0,1fr) auto;align-items:center;column-gap:16px;row-gap:14px}
.ask .t{font:500 var(--t5)/22px var(--sans);color:var(--ink)}
.ask .t small{display:block;font:400 var(--t2)/19px var(--sans);color:var(--mute);margin-top:3px}
.ask .amt,.grp > .kv.due .amt{display:block;font-family:var(--sans);font-variant-numeric:var(--tab);font-size:var(--t8);line-height:34px;letter-spacing:-.02em;color:var(--ink);margin-top:10px}
/* THE LIFT LAYER (the learner audit, 2026-09-25: no shadow animates): a ring or shadow that changes stands on ::before (--lift-to) and fades in by opacity */
:is(.bar .sg,.bar .sup,.dcard,.go.pale,.upl .take,.ptok,.sheet .lang .flip button,.qpick button)::before{content:'';position:absolute;inset:0;border-radius:inherit;box-shadow:var(--lift-to);opacity:0;pointer-events:none;transition:opacity .2s ease}
:is(.bar .sg:hover,.bar .sup:hover,.bar .sg.on,.bar .sup.open,.dcard.on,.go.pale:hover,.upl .take:hover,.ptok.lit,.sheet .lang .flip button[aria-checked="true"],.qpick button:not(:disabled):hover,.qpick button.on:disabled)::before{opacity:1}
/* ── ACTS: one shape, the pill ── */
/* a pill is a link or a button: the button's border and pointer go here, once (2026-09-21: they stood in 17 rules); A PILL NEVER SHRINKS UNDER ITS WORDS (round 2 of the audit: the hit's min-width let a flex row squeeze "Try again ›" past the notice's edge; its words never wrap) */
.go{display:inline-flex;flex-shrink:0;border:0;cursor:pointer;align-items:center;gap:12px;background:var(--ink);color:#fff;font:500 var(--t4)/1 var(--sans);border-radius:var(--pill);padding:8px 8px 8px 22px;white-space:nowrap;justify-self:end;text-decoration:none;box-shadow:var(--lift-bar);transition:transform .5s cubic-bezier(.32,.72,0,1)}
.go:active{transform:scale(.98)}
/* THE PILL'S FOCUS RING, ONE RULE (the owner, 2026-10-06, a staff view as on a desk: the blocked notifications sheet's Not now wore the browser's own ring; the ring stood under three parents, .guide, .announce and .confirm, and a pill outside them had none): the keyboard's focus draws the ink ring, 2 px at 3 px out, on every pill of every sheet and page; a quiet pill rounds its ring with its words */
.go:focus-visible{outline:2px solid var(--ink);outline-offset:3px}
.go.quiet:focus-visible{border-radius:var(--r1)}
/* A PILL THAT DOES NOTHING SHOWS IT, one rule (2026-09-21: five parents, four numbers, and full ink outside them: the quiz's Start, a bell row's act, Send again in Settings); --go-off: a door's pill is off only after a send, and dims lightly */
.go:disabled,.go[aria-disabled=true]{opacity:var(--go-off,.4);cursor:default}
.door,.enroldoor{--go-off:.7}
.go i{width:var(--way-r);height:var(--way-r);border-radius:50%;background:rgba(255,255,255,.3);display:inline-flex;align-items:center;justify-content:center;font-style:normal;font-size:var(--t5)}
.go.quiet{--face:1.2em;background:none;color:var(--ink);box-shadow:none;padding:0;gap:6px}
/* every quiet act's chevron steps right under the pointer, as the pending rows' does (the owner, 2026-09-15 18:5x: "this upload that you hover and its arrow moves, use that in all other places with an arrow") */
.go.quiet i{width:auto;height:auto;background:none;font-size:var(--t5);transition:transform .2s ease}
.go.quiet:hover i{transform:translateX(2px)}
/* THE WAY'S MARK, ONE LOOK (the owner, 2026-09-25: "all right arrows like the one in 'See missions' are too small and invisible", a › of 8 px, grey in a pale round): parts.js Way (GLYPH.right into the portal, GLYPH.open out of it) as its mask (--glyph-way, --glyph-out), --way-s (16 px, 18 on a desk) at a 2 px stroke in its place's own ink, so it stands as clear as the words beside it; on a filled pill its round is --way-r (28 px at least) of a ground apart from the pill; a fold's chevron is the same glyph turned */
.way{display:inline-grid;place-items:center;flex:none;font-style:normal;vertical-align:middle}
.way::after,:is(.mod,.prose) summary::after,.prose a.ccard .c-word::after{content:'';display:inline-block;flex:none;inline-size:var(--way-s);block-size:var(--way-s);background:currentColor;-webkit-mask:var(--glyph-way) center/100% 100% no-repeat;mask:var(--glyph-way) center/100% 100% no-repeat}
.way.out::after{-webkit-mask-image:var(--glyph-out);mask-image:var(--glyph-out)}
.go.pale i,.pgoal .go.pwhite i{background:var(--ink);color:#fff}
/* a quiet act is the pill's 15px; THE SMALL FORM, 14px, one rule (2026-09-21: five rules, and the pill's font and ground written again under ten parents); kept for the owner's ruling: a flow page's row act is 15px outside a hand-in well */
.flow .kv .v .go.quiet{font-size:var(--t4)}
.ways .go.quiet,.kv .v .go.quiet,.paybox .kv .v .go.quiet,.paybox .kv .v.acts .go.quiet,.paybox .acts .go.quiet,.notice .go.quiet,.uplink .lk .go.quiet,.upl .or .go.quiet,.upl .again .go.quiet,.docs .review .go.quiet{font-size:var(--t3)}
/* THE SHOW-MORE WORD, one rule for every Show {n} more and Show fewer (the owner, 2026-09-29: the learner card's Show 4 more stood in a grey box; its rule was scoped to .kv.subj and the Ask Support row draws the same names in a Kv without that class, so the browser's button face and THE HIT's full height drew the box): a word in its row's own type at weight 500, ink, no face, no box, underlined under the pointer; its face is its line, so THE HIT grows the tap to 44 px by the clear layer alone */
.more{--face:1.3em;--face-i:calc(var(--hit) - 12px);min-inline-size:0;margin:0;padding:0;background:none;box-shadow:none;border-radius:0;color:var(--ink);font:inherit;font-weight:500;cursor:pointer;white-space:nowrap;text-align:start;justify-self:start;-webkit-appearance:none;appearance:none}
.more:hover{text-decoration:underline;text-underline-offset:var(--hair)}
/* ── BOTTOM BAR (frozen) ── */
/* the bar is the same light glass pill everywhere (the owner, 2026-09-15: "the same on desktop too, same elements, same colour"): white glass, our icons and words in ink, the open place a soft ink tint, the dot blue */
.barwrap{position:fixed;left:0;right:0;bottom:0;height:calc(76px + env(safe-area-inset-bottom));padding:6px 12px env(safe-area-inset-bottom);display:flex;align-items:center;justify-content:center;container-type:inline-size;z-index:5;background:none}
.bar.full{width:auto}
.bar>.nav{flex:1 1 auto;min-width:0;display:flex;align-items:center;gap:2px}
.bar>.rule{flex:0 0 auto}
.sg,.sup{display:flex;flex-direction:column;align-items:center;justify-content:center;gap:4px;height:50px;min-width:0;padding:0 14px;border-radius:var(--pill);font:500 var(--t1)/14px var(--sans);letter-spacing:.01em;color:var(--night-mute);position:relative;text-align:center;text-decoration:none}
.sg{flex:0 0 auto;min-width:78px}
.sup{flex:0 1 auto;min-width:82px}
.sg svg,.sup svg{width:24px;height:24px;stroke:currentColor;fill:none;stroke-width:1.5;stroke-linecap:round;stroke-linejoin:round}
/* a place under the pointer is a lens (the owner, 2026-09-15: "the hover more glassy, like a magnifying glass, without much blue"): a lighter, clearer, slightly brighter glass with a fine ring; the same lens on the Back and Support rounds */
.bar .sg.on:hover,.bar .sup.open:hover{background:rgba(255,255,255,.7)}
.sg.on svg{stroke-width:1.8}
.sg>span,.sup>span{max-width:100%;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.pip{position:absolute;top:8px;right:14px;width:6px;height:6px;border-radius:50%;background:rgb(var(--glow-rgb))}
.rule{width:1px;height:28px;background:rgba(255,255,255,.18);margin:0 4px}
/* ── THE LEARNER: a wider box, and otty opens the character sheet ── */
/* one split on every page: a third to otty, two thirds to the white box */
.learn .otty .mount{cursor:pointer}
/* the veil and the dialog's backdrop are one solid dim, never a blur (THE SHEET: no glass behind a sheet) */
.veil{position:fixed;inset:0;background:var(--dim);z-index:20;display:flex;align-items:flex-end;justify-content:center;padding:0}
.veil[hidden]{display:none}
/* ── THE ENROLMENT FLOW: one page, eight steps, the payment in the middle ── */
/* the white box is a frame: the rail at its head, the body scrolling between, the foot pinned at its bottom */
/* ONE SIDE MARGIN FOR EVERY PAGE (round 3 review): a flow's card pads by the one gutter, --gut, as every other card */
.flow .card{--pad:var(--gut);padding:0;display:flex;flex-direction:column;overflow:clip}
/* the rail: one thin track in eight parts that fills as the enrolment goes, each part named under it (only the open one named where the box is narrow); a filled part is the way back; .rail .tk, not .flow .tk (2026-09-21: that reached the points sheet's rail on a flow page) */
.flow .rail{flex:none;align-items:center;gap:6px;position:relative;z-index:2;display:block;padding:calc(var(--pad) - 4px) var(--pad) 8px}
/* THE RAIL HOLDS ITS HEIGHT FROM THE FIRST PAINT (2026-09-27, the re-enrolment's capture: the body moved 0.03 to 0.045 of the screen as the rail came in and its names were fitted): the box stands before its parts, the track as tall with every name as with the open one alone */
.flow .rail{min-height:calc(var(--pad) + 37px)}
.flow .rail .track{min-height:33px}
.flow .body:first-child{padding-top:calc(var(--pad) - 4px)}
.flow .body{flex:1 0 auto;padding:14px var(--pad) 0}
/* the foot: back on the left, the one act on the right */
.flow .card > .foot{flex:none;padding:12px var(--pad) calc(var(--pad) - 8px);position:relative;z-index:2;pointer-events:none;display:flex;justify-content:space-between;align-items:center;gap:16px;margin-top:36px}
.flow .card > .foot>*{pointer-events:auto}
.flow .card > .foot [data-line][data-warn]{color:var(--warm);font-weight:500}
.rail .track{display:flex;gap:6px}
.rail .tk{--face:29px;flex:1;min-width:0;position:relative;display:flex;flex-direction:column;align-items:stretch;gap:9px;padding:0 0 4px;background:none;cursor:default;font:inherit}
.rail .tk i{display:block;height:4px;border-radius:var(--pill);background:var(--track-fill);box-shadow:var(--track-edge);position:relative;overflow:hidden}
.rail .tk i::before{content:'';position:absolute;inset:0;border-radius:var(--pill);background:var(--ink);transform:scaleX(0);transform-origin:left;transition:transform .7s cubic-bezier(.2,.8,.2,1)}
.rail .tk.done i::before,.rail .tk.on i::before{transform:scaleX(1)}
/* the names show when every one fits under its own part on one line; otherwise only the open step's name, under its part (the rail's `names` class, measured by the page) */
.rail .tk span{font:400 var(--t1)/16px var(--sans);color:var(--faint);text-align:left;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;display:none}
.rail .tk.done span{color:var(--ink-soft)}
.rail .tk.on span{color:var(--ink);font-weight:500}
.rail .tk.done{cursor:pointer}
.flow .rail.names .tk span{display:block}
.flow .rail:not(.names) .tk.on span{display:block;overflow:visible;position:absolute;top:13px;left:0}
.flow .rail:not(.names) .tk:nth-child(n+5).on span{left:auto;right:0}
.flow .rail:not(.names) .track{padding-bottom:20px}
.flow .rail:not(.names) .tk{--face:8px}
/* the foot: the way back as a round, the one act as the pill, the quiet way beside it; on the payment the total stands where the act would, the act is the form's own */
.flow .card > .foot .acts{display:flex;align-items:center;gap:22px;margin-top:0}
:is(.barwrap, .acts) > .back svg{width:20px;height:20px;fill:none;stroke:currentColor;stroke-width:1.8;stroke-linecap:round;stroke-linejoin:round}
.flow .paybox{background:var(--raised);box-shadow:var(--raised-edge);border-radius:var(--r4);padding:18px 20px 20px;margin-top:20px}
.go.wide{width:100%;margin-top:18px}
/* THE WIDE ACT, ONE RULE (the owner, 2026-10-15 14:5x SAST, the adviser's sign-up sheet at 1440: the Sign up pill, wide as the row's share, drew "Sign up ●›" centred as one lump, the round floating mid-pill; before it .go.wide and the phone's stretched acts drew the word at the left edge, space-between): a filled pill wider than its words (.go.wide: the doors' pills, the pay and upload pages', the hand-in's; a sheet row's filled act; on a phone the ask box's and the next row's stretched act, the same declarations in the phone block) centres its WORD in the pill and pins its round at the right edge, inset 6 px, whatever the width; the pill keeps the round's room on both sides (44 px), so the word never meets it. A wide pill without a round (Install) centres its word the same. A new wide act is one of these or it is a defect */
:is(.go.wide,:is(.offer .foot,.announce .foot,.offer .guide .acts) > .go:not(.quiet)){position:relative;justify-content:center;padding-inline:44px}
:is(.go.wide,:is(.offer .foot,.announce .foot,.offer .guide .acts) > .go:not(.quiet)) > i{position:absolute;right:6px;top:50%;transform:translateY(-50%)}
.flow .step{margin-top:0;max-width:720px}
.flow .step[hidden]{display:none}
.flow .fx{max-width:56ch;text-wrap:pretty}
/* the learner's course, task and quiz heads carry a fact line, not a lede (the sweep, 2026-09-15): no measure on it */
.app.learn.flow .fx{max-width:none}
.fields{display:grid;grid-template-columns:1fr;gap:14px 16px;margin-top:20px}
/* a field's line: held under the input so the box never grows; the meta tone for a notice, the warm with the circle-i for a refusal, the green when it is right; the field keeps its ring in the warm, focused too */
.fld{display:grid;gap:6px;min-width:0;align-content:start}
.fld>span{font:500 var(--t2)/18px var(--sans);color:var(--mute)}.fld>span:empty{display:none}
.fld input{height:46px;border:0;border-radius:var(--r3);background:#fff;box-shadow:var(--ring);padding:0 14px;font:400 var(--t5)/22px var(--sans);color:var(--ink);width:100%;font-variant-numeric:var(--tab)}
/* THE ONE FIELD FOCUS (the owner: the Ask field glowed while the learner typed): a text field in focus turns its ring the accent, one pixel and a half, no halo, blur or shadow; buttons and chips keep their ring for the keyboard alone (:focus-visible) */
:is(.fld input,.fld select,.fld textarea):focus{outline:0;--rc:var(--sky);box-shadow:var(--ring-on)}
/* THE ONE FOCUS RING (the owner, 2026-09-26: the Standings tab wore a heavy box after a click): a pointer's focus shows none; the keyboard's focus (:focus-visible) keeps the ring, a tab's the accent inside its box */
:is(button,a,[role=tab],[tabindex]):focus:not(:focus-visible){outline:none}
/* THE ROWS' FOCUS (the owner's QA, 2026-09-27: a task row wore the browser's thin ring): a course's and a task's rows, cards, chips and folds take the one sky ring on the keyboard's focus, round with the row */
:is(.trow,.tupc,.cit,.crow,.cnext,.tch,.tfold,.tchip,.tfilter,.pastfold > summary,.tiles > a.kv.it):focus-visible{outline:2px solid var(--sky-ink);outline-offset:2px;border-radius:var(--r3)}
.tab:focus-visible{outline:2px solid var(--sky-ink);outline-offset:-2px;border-radius:var(--r1)}
.fld input::placeholder{color:var(--faint)}
.chips{display:flex;flex-wrap:wrap;gap:8px;margin-top:8px}
.chips.grid{display:grid;grid-template-columns:repeat(auto-fill,minmax(58px,1fr))}
/* THE CHIP CENTRES ITS OWN WORDS (the owner, 2026-10-16, the subjects sheet: the words at the top of each box, empty room under them): the class centres by its own flex rule and keeps no fixed height, so a span wears it as a button does; the box is the face at least and grows with a second line on even padding */
.chip{--face:40px;display:inline-flex;align-items:center;justify-content:center;min-height:var(--face);padding:6px 16px;border-radius:var(--pill);background:var(--chip-fill,#fff);box-shadow:var(--ring);font:500 var(--t3)/18px var(--sans);color:var(--ink);text-align:center;cursor:pointer;overflow-wrap:anywhere}
.chips.grid .chip{padding:0}
.chip.usual{--rc:var(--faint);box-shadow:var(--ring-on)}
.chip.on{box-shadow:var(--ring-on)}
.chip:disabled{opacity:.45;cursor:default}
.meta{font:400 var(--t2)/18px var(--sans);color:var(--mute);margin:10px 0 0}
.meta.ok{color:var(--ok);font-weight:500}
.meta.warn{color:var(--warm);font-weight:500}
.flow .opts{display:grid;gap:8px;margin-top:8px}
.flow .opt{display:flex;align-items:flex-start;gap:12px;width:100%;min-width:0;padding:14px 16px;border:0;border-radius:var(--r3);background:#fff;box-shadow:var(--ring);color:var(--ink);text-align:left;cursor:pointer;font:inherit}
.flow .opt .t{flex:1;min-width:0;display:grid;gap:2px}
.flow .opt .title{font:500 var(--t5)/22px var(--sans);display:flex;flex-wrap:wrap;gap:0 8px;align-items:baseline}
.flow .opt .sub{font:400 var(--t2)/18px var(--sans);color:var(--mute);text-wrap:pretty}
.flow .opt .num{font:500 var(--t4)/22px var(--sans);font-variant-numeric:var(--tab);white-space:nowrap}
.flow .opt .num small{display:block;font:400 var(--t1)/16px var(--sans);color:var(--mute);text-align:right}
.flow .ring{flex:none;width:18px;height:18px;border-radius:50%;--rc:var(--faint);box-shadow:var(--ring-on);margin-top:2px}
.flow .opt.on{box-shadow:var(--ring-on)}
.flow .opt.on .ring{box-shadow:none;background:radial-gradient(circle,#fff 3px,var(--ink) 3.5px)}
.flow .flag{font:500 var(--t1)/16px var(--sans);letter-spacing:.02em;color:var(--sky-ink)}
/* the week cards, as approved on the pages kit (P4), in this design's colours */
.flow .head{display:flex;flex-wrap:wrap;align-items:baseline;justify-content:space-between;gap:6px 16px;margin:22px 0 10px}
.flow .head .label{font:500 var(--t2)/18px var(--sans);color:var(--mute)}
/* THE NEW ENROLMENT WIZARD (the owner, 2026-10-20; README 3 THE NEW ENROLMENT WIZARD; /enrol/ alone): the mock's pieces inside the flow card at the shell's width, on the tokens: the numbered rail, the route cards, the pick row (the grade, the language, the board), the one comparison matrix (a column per format, the chosen column lit head to foot; it scrolls inside its own box under 640 px), the subject tiles, the summary and the total */
.flow .rail.steps .track{display:flex;flex-wrap:wrap;align-items:center;gap:4px 2px;min-height:40px;padding:0}
.flow .rail.steps .tk{--face:40px;--face-i:36px;flex:none;flex-direction:row;align-items:center;gap:8px;min-height:var(--face);padding:0 12px 0 6px;border-radius:var(--pill);background:none;color:var(--mute);font:500 var(--t2)/18px var(--sans);cursor:default}
.flow .rail.steps .tk+.tk::before{content:'';width:10px;height:1px;background:var(--line);margin:0 6px 0 2px;flex:none}
.flow .rail.steps .tk i{display:inline-grid;place-items:center;width:28px;height:28px;border-radius:50%;background:var(--track-fill);box-shadow:none;color:var(--mute);font:600 var(--t1)/1 var(--sans);font-style:normal;overflow:visible;flex:none}
.flow .rail.steps .tk i::before{content:none}
.flow .rail.steps .tk i svg{width:12px;height:12px;fill:none;stroke:currentColor;stroke-width:3;stroke-linecap:round;stroke-linejoin:round}
.flow .rail.steps .tk.done,.flow .rail.steps .tk.on{color:var(--ink)}
.flow .rail.steps .tk.done i,.flow .rail.steps .tk.on i{background:var(--ink);color:#fff}
.flow .rail.steps .tk.on{font-weight:600}
.flow .rail.steps .tk.done{cursor:pointer}
.flow .rail.steps .tk.done:hover{background:var(--hover)}
.flow .rail.steps.names .tk span{display:block;font:inherit;color:inherit;overflow:visible;white-space:nowrap;position:static}
.flow .saved{margin-left:auto;display:inline-flex;align-items:center;gap:6px;font:500 var(--t2)/18px var(--sans);color:var(--mute);padding:0 6px}
.flow .saved[data-state=saved]{color:var(--ok)}
.flow .saved svg{width:14px;height:14px;fill:none;stroke:currentColor;stroke-width:3;stroke-linecap:round;stroke-linejoin:round}
.flow .step[data-step="format"],.flow .step[data-step="subjects"]{max-width:none}
.flow .step[data-step="format"] .nm,.flow .step[data-step="subjects"] .nm,.flow .step[data-step="subjects"] .fx{max-width:720px}
.flow .routes{display:grid;grid-template-columns:repeat(auto-fit,minmax(240px,1fr));gap:12px;margin-top:8px}
.flow .route{position:relative;display:flex;gap:14px;align-items:flex-start;text-align:left;padding:16px 18px;border:0;border-radius:var(--r4);background:#fff;box-shadow:var(--ring);cursor:pointer;font:inherit;color:var(--ink);min-height:92px;min-width:0}
.flow .route:hover{--rc:var(--sky);box-shadow:var(--ring)}
.flow .route.on{--rc:var(--ink);box-shadow:var(--ring-on)}
.flow .route:disabled{cursor:default}
.flow .route .ic{flex:none;width:52px;height:52px;border-radius:var(--r3);background:var(--hover);color:var(--ink);display:grid;place-items:center}
.flow .route.on .ic{background:var(--ink);color:#fff}
.flow .route .ic svg{width:28px;height:28px;fill:none;stroke:currentColor;stroke-width:1.7;stroke-linecap:round;stroke-linejoin:round}
.flow .route b{display:block;font:600 var(--t5)/22px var(--sans);padding-right:28px;margin-bottom:2px}
.flow .route .say{display:block;color:var(--mute);font:400 var(--t2)/18px var(--sans);text-wrap:pretty}
.flow .route .tick{position:absolute;top:12px;right:12px;width:22px;height:22px;border-radius:50%;background:var(--ok);color:#fff;display:none;place-items:center}
.flow .route.on .tick{display:grid}
.flow .route .tick svg,.flow .cmp .hd .tick svg{width:12px;height:12px;fill:none;stroke:currentColor;stroke-width:3;stroke-linecap:round;stroke-linejoin:round}
.flow .pick{display:flex;flex-wrap:wrap;gap:20px 40px;align-items:flex-start;margin-top:14px}
.flow .pick>div{display:flex;flex-direction:column;gap:8px;min-width:0;flex:1 1 auto} /* a block as wide as its chips on one row (the years 7 to 13); the row wraps under 640 px alone */
@media (min-width:641px){.flow .pick .chips{flex-wrap:nowrap}}
.flow .pick .k,.flow .payrow .k{font:600 var(--t1)/16px var(--sans);letter-spacing:.06em;text-transform:uppercase;color:var(--mute)}
.flow .pick .say{font:400 var(--t2)/18px var(--sans);color:var(--mute);min-height:18px}
.flow .pick .chips,.flow .payrow .chips{margin-top:0}
.flow .pick .meta{margin:0}
.flow .pick .chip.on,.flow .payrow .chip.on,.flow .addons .chip.on,.flow .terms .chip.on{background:var(--ink);color:#fff;box-shadow:none}
.flow .terms .chip{--face:36px;min-height:36px;gap:5px;padding:0 13px;font:600 var(--t3)/18px var(--sans)} /* the term chips in the grade chips' style (the owner, 2026-10-20 night): the name and the first day in one pill */
.flow .terms .chip small{font:400 var(--t1)/14px var(--sans);color:var(--mute);white-space:nowrap}
.flow .terms .chip.on small{color:rgba(255,255,255,.8)}
.flow .terms+.meta{font:400 var(--t2)/18px var(--sans);color:var(--mute);margin:6px 0 0}
.flow .pick .chip.usual{--rc:var(--line);box-shadow:var(--ring)}
.flow .pick .chip.usual.on{box-shadow:none}
.flow .pick .chip.usual.on::before{content:none}
.flow .h2row{display:flex;flex-wrap:wrap;align-items:center;justify-content:space-between;gap:8px 16px;margin:28px 0 12px}
.flow .nm2{font:600 var(--t6)/24px var(--sans);letter-spacing:-.01em;margin:0}
.flow .payrow{display:flex;align-items:center;gap:10px}
.flow .cmp-wrap{margin:6px calc(var(--pad) * -1) 0;padding:0 var(--pad);container-type:inline-size}
.flow .cmp{display:grid;grid-template-columns:minmax(140px,1.2fr) repeat(var(--cols,1),minmax(124px,1fr));width:100%}
.flow .cmp>*{min-width:0}
.flow .cmp .corner{align-self:end}
.flow .cmp .hd{display:flex;flex-direction:column;align-items:center;gap:6px;padding:26px 10px 10px;margin:0 4px;border:0;border-radius:var(--r4) var(--r4) 0 0;background:#fff;box-shadow:var(--ring);text-align:center;cursor:pointer;position:relative;font:inherit;color:var(--ink);min-width:0}
.flow .cmp .hd:hover{--rc:var(--sky);box-shadow:var(--ring)}
.flow .cmp .hd.on{--rc:var(--ink);box-shadow:var(--ring-on)}
.flow .cmp .hd:disabled{cursor:default}
.flow .cmp .lab svg{width:20px;height:20px;fill:none;stroke:currentColor;stroke-width:1.7;stroke-linecap:round;stroke-linejoin:round}
.flow .cmp .hd b{display:block;font:600 clamp(13px,1.5cqw,16px)/1.3 var(--sans);letter-spacing:-.01em;text-wrap:balance}
/* THE HEAD IN OPTION H (the owner, 2026-10-20): the week's tiles (14 by 22, a live lesson filled ink, a recorded one white with a 2 px ink edge, none dotted), the day letters, the two marks, the name, the one line, Choose or the tick; the tile states carry the tile's own selector, or the base rule's border wins (the fault of the first live hour: every tile an empty outline) */
.flow .cmp .hd .week{display:flex;gap:4px;align-items:flex-end;margin-bottom:3px}
.flow .cmp .hd .week i{display:block;width:14px;height:22px;border-radius:4px;border:1.5px solid var(--line);background:none}
.flow .cmp .hd .week i.day-live{background:var(--ink);border-color:var(--ink)}
.flow .cmp .hd .week i.day-rec{background:#fff;border:2px solid var(--ink)}
.flow .cmp .hd .week i.day-none{border-style:dashed}
.flow .cmp .hd .days{display:flex;gap:4px;font:500 10px/12px var(--sans);letter-spacing:.04em;text-transform:uppercase;color:var(--mute);margin-bottom:6px}
.flow .cmp .hd .days span{width:14px;text-align:center}
.flow .cmp .hd .marks{display:flex;flex-wrap:wrap;justify-content:center;gap:4px 6px;margin-bottom:8px}
.flow .cmp .hd .marks span{font:500 10px/14px var(--sans);letter-spacing:.04em;text-transform:uppercase;color:var(--mute);border:1.5px solid var(--line);border-radius:6px;padding:2px 7px;white-space:nowrap}
.flow .cmp .hd .marks span.in{color:var(--ink);border-color:var(--ink);background:#fff}
.flow .cmp .hd .marks span.in::before{content:"\2713 ";color:var(--ok)}
.flow .cmp .hd .marks span.out{text-decoration:line-through;opacity:.7}
.flow .cmp .hd .sub{display:block;font:400 var(--t2)/17px var(--sans);color:var(--mute);text-wrap:pretty;margin:2px 0 4px}
.flow .cmp .hd .pick{display:grid;place-items:center;min-height:22px;margin-top:auto}
.flow .cmp .hd .tick{width:20px;height:20px;border-radius:50%;background:var(--ok);color:#fff;display:grid;place-items:center}
.flow .cmp .hd .choose{font:600 var(--t1)/16px var(--sans);letter-spacing:.02em;color:var(--mute);box-shadow:var(--ring);border-radius:var(--pill);padding:2px 10px}
.flow .cmp .hd.hov .choose{--rc:var(--ink);color:var(--ink)}
.flow .cmp .hd .choose.off,.flow .cmp .hd.hov .choose.off{opacity:.45;--rc:var(--line);color:var(--mute)}
.flow .cmp .hd:disabled{cursor:default;opacity:1}
.flow .cmp [data-col]{cursor:pointer}
.flow .cmp .cell.hov,.flow .cmp .hd.hov{background:var(--hover)}
@keyframes hdpulse{0%{outline:2px solid var(--sky);outline-offset:0}100%{outline:2px solid transparent;outline-offset:10px}}
.flow .cmp .hd.pulse{animation:hdpulse 1s ease-out}
@media (prefers-reduced-motion:reduce){.flow .cmp .hd.pulse{animation:none;--rc:var(--sky);box-shadow:var(--ring-on)}}
.flow .cmp .lab{display:flex;align-items:flex-start;gap:8px;min-height:52px;padding:12px 8px 12px 0;font:400 var(--t2)/18px var(--sans);color:var(--ink);border-top:1px solid var(--line)}
.flow .cmp .lab svg{width:18px;height:18px;flex:none;color:var(--mute);margin-top:1px}
.flow .cmp .cell{display:flex;align-items:center;justify-content:center;gap:6px;min-height:52px;padding:12px 6px;margin:0 4px;border:0 solid var(--line);border-width:1px 1px 0;background:#fff;font:400 var(--t2)/18px var(--sans);color:var(--mute)}
.flow .cmp .cell.on{border-color:var(--ink);border-left-width:1.5px;border-right-width:1.5px}
.flow .cmp .cell{position:relative}
.flow .cmp .cell .chip.count{--face:22px;--face-i:22px;min-height:22px;min-inline-size:22px;padding:0 7px;font:600 var(--t1)/16px var(--sans);margin-left:4px}
.flow .cmp .cell .more{--face:22px;--face-i:22px;width:22px;height:22px;padding:0;display:grid;place-items:center;color:var(--mute);background:none}
.flow .cmp .cell .more svg{width:14px;height:14px;fill:none;stroke:currentColor;stroke-width:1.7;stroke-linecap:round;stroke-linejoin:round}
.flow .cmp .cell .pop{display:none;position:absolute;left:50%;bottom:calc(100% + 6px);transform:translateX(-50%);width:320px;max-width:80vw;z-index:3;padding:12px 14px;border-radius:var(--r3);background:var(--raised);box-shadow:var(--lift-sheet);text-align:left;color:var(--ink)}
@media (hover:hover){.flow .cmp .cell:hover .pop{display:block}}
.flow .cmp .subpanel{grid-column:1/-1;margin:0 4px;padding:12px 14px;border-radius:0 0 var(--r4) var(--r4);background:var(--raised);box-shadow:var(--raised-edge);text-align:left}
.flow .cmp .pop p,.flow .cmp .subpanel p{margin:0 0 8px;font:400 var(--t2)/18px var(--sans);color:var(--ink);text-wrap:pretty}
.flow .cmp .pop p:last-child,.flow .cmp .subpanel p:last-child{margin-bottom:0}
.flow .cmp .pop .k,.flow .cmp .subpanel .k{display:block;font:600 var(--t1)/16px var(--sans);letter-spacing:.06em;text-transform:uppercase;color:var(--mute)}
.flow .cmp .cell.last{border-bottom-width:1px;border-radius:0 0 var(--r4) var(--r4);padding-bottom:14px}
.flow .cmp .cell.last.on{border-bottom-width:1.5px}
.flow .cmp .cell .yes,.flow .legend .yes{width:20px;height:20px;color:var(--ink);display:grid;place-items:center}
.flow .cmp .cell .yes svg,.flow .legend .yes svg,.flow .stile i svg{width:14px;height:14px;fill:none;stroke:currentColor;stroke-width:3;stroke-linecap:round;stroke-linejoin:round}
.flow .cmp .cell .no,.flow .legend .no{width:12px;height:2px;border-radius:var(--pill);background:var(--line)}
.flow .cmp .cell .add,.flow .legend .add{font:600 10px/14px var(--sans);letter-spacing:.02em;color:var(--mute);box-shadow:var(--ring);border-radius:var(--pill);padding:2px 7px;white-space:nowrap}
.flow .cmp .cell .word{font:400 var(--t2)/17px var(--sans);color:var(--ink);text-align:center;padding:0 2px}
.flow .cmp .pc{display:flex;flex-direction:column;align-items:center;text-align:center;gap:1px;min-width:0}
.flow .cmp .pc b{font:600 clamp(14px,1.7cqw,18px)/1.15 var(--sans);color:var(--ink);font-variant-numeric:var(--tab)} /* no tightening on a figure: the formatter's thousands space is the narrow one (U+202F), and a negative letter-spacing closed it to the eye (the review, 2026-10-20: "R4180") */
.flow .cmp .pc.wait b{width:56px;height:14px;border-radius:var(--pill);background:var(--track-fill)}
.flow .cmp .pc.wait small{width:36px;height:10px;border-radius:var(--pill);background:var(--track-fill);margin-top:4px}
.flow .cmp .pc b.tbc{font:500 var(--t2)/18px var(--sans);color:var(--mute);letter-spacing:0}
.flow .cmp .pc small{font:400 var(--t1)/16px var(--sans);color:var(--mute)}
.flow .cmp .modes{display:flex;flex-direction:column;gap:4px;margin-top:6px;align-items:stretch}
.flow .radio{--face:28px;display:flex;align-items:center;gap:6px;border:0;background:none;padding:4px 2px;cursor:pointer;text-align:left;border-radius:var(--r2);font:400 var(--t1)/16px var(--sans);color:var(--ink)}
.flow .radio:hover{background:var(--hover)}
.flow .radio i{flex:none;width:14px;height:14px;border-radius:50%;box-shadow:var(--ring)}
.flow .radio.on i{box-shadow:none;background:radial-gradient(circle,#fff 2.5px,var(--ink) 3px)}
.flow .step[data-step="format"] > .meta,.flow .step[data-step="format"] > .gt{max-width:720px}
.flow .cmp[data-cols="1"]{max-width:560px}
.pprog{display:inline-flex;align-items:center;gap:5px;margin-top:4px}
.pprog i{width:8px;height:8px;border-radius:50%;background:var(--track-fill)}
.pprog i.done{background:var(--ink)}
.pprog i.on{--rc:var(--ink);box-shadow:var(--ring-on)}
.pprog span{margin-left:4px}
.flow .coupon{display:flex;align-items:flex-end;gap:8px;max-width:420px}
.flow .coupon .fld{flex:1;min-width:0}
.flow .coupon .cfield{position:relative;display:block}
.flow .coupon .cfield input{padding-right:92px}
.flow .coupon .cfield .apply{--face:34px;position:absolute;right:6px;top:6px;height:34px;padding:0 14px;font:500 var(--t3)/1 var(--sans);border-radius:var(--pill)}
.flow .coupon .cfield .moment{position:absolute;right:10px;top:10px}
.flow .coupon .fold{--face:46px;width:46px;height:46px;border:0;border-radius:var(--r3);background:#fff;box-shadow:var(--ring);color:var(--mute);display:grid;place-items:center;cursor:pointer;flex:none}
.flow .coupon .fold:hover{--rc:var(--ink);color:var(--ink)}
.flow .payhost .kv,.flow .kv.coupon-row{padding:10px 0;column-gap:20px}
.flow .kv.coupon-row .k{font:400 var(--t5)/22px var(--sans)}
.flow .legend{display:flex;flex-wrap:wrap;gap:6px 18px;font:400 var(--t2)/18px var(--sans);color:var(--mute);margin-top:12px;align-items:center}
.flow .legend span{display:inline-flex;align-items:center;gap:6px}
.flow .sgrp{margin-top:22px}
.flow .sgrp:first-of-type{margin-top:4px}
.flow .sgrp .nm2{margin:0 0 10px}
.flow .sgrp .nm2 small{font:400 var(--t2)/18px var(--sans);color:var(--mute);margin-left:8px}
.flow .subs{display:grid;grid-template-columns:repeat(auto-fill,minmax(230px,1fr));gap:8px}
.flow .stile{display:grid;grid-template-columns:22px minmax(0,1fr);column-gap:10px;align-items:start;min-height:46px;padding:11px 12px;border:0;border-radius:var(--r3);background:#fff;box-shadow:var(--ring);cursor:pointer;text-align:left;font:400 var(--t3)/20px var(--sans);color:var(--ink);min-width:0;margin:0}
.flow .stile span{min-width:0;text-wrap:pretty;grid-column:2}
.flow .stile i{width:22px;height:22px;border-radius:7px;--rc:var(--line);box-shadow:var(--ring-on);display:grid;place-items:center;font-style:normal;margin-top:-1px;color:#fff}
.flow .stile:hover{--rc:var(--sky);box-shadow:var(--ring)}
.flow .stile.on{--rc:var(--ink);box-shadow:var(--ring-on)}
.flow .stile.on i,.flow .stile.fixed i{background:var(--ink);box-shadow:none}
.flow .stile.fixed{cursor:default;background:var(--raised);box-shadow:var(--raised-edge)}
.flow .stile[aria-disabled=true]{opacity:.45;cursor:default}
.flow .sum{display:grid;grid-template-columns:repeat(auto-fit,minmax(180px,1fr));gap:12px 24px;margin-top:12px}
.flow .sum>div{display:flex;flex-direction:column;gap:2px;border-top:1px solid var(--line);padding-top:8px;min-width:0}
.flow .sum .k{font:400 var(--t2)/18px var(--sans);color:var(--mute)}
.flow .sum .v{font:600 var(--t4)/22px var(--sans);color:var(--ink);text-wrap:pretty}
.flow .sum.wide{grid-template-columns:1fr;margin-top:0}
.flow .tags{display:flex;flex-wrap:wrap;gap:6px}
.flow .tag{font:500 var(--t2)/18px var(--sans);padding:4px 10px;border-radius:var(--pill);box-shadow:var(--ring);background:#fff;white-space:nowrap}
.flow .tag.open{box-shadow:none;border:1px dashed var(--line);color:var(--mute);background:none;font-weight:400}
.flow .sumtotal{margin-top:16px;padding:16px 18px;border-radius:var(--r4);background:var(--raised);box-shadow:var(--raised-edge);display:flex;flex-wrap:wrap;gap:8px 28px;align-items:baseline;font:400 var(--t2)/18px var(--sans);color:var(--mute)}
.flow .sumtotal .m{font:600 var(--t7)/28px var(--sans);letter-spacing:-.02em;color:var(--ink);font-variant-numeric:var(--tab)}
.flow .sumtotal .m small{font:400 var(--t2)/18px var(--sans);color:var(--mute);letter-spacing:0}
.flow .badge{display:inline-block;font:600 var(--t2)/18px var(--sans);color:var(--mute);box-shadow:var(--ring);border-radius:var(--pill);padding:2px 10px}
@media (max-width:1000px){.flow .rail.steps .tk{padding:0 4px;min-inline-size:36px}.flow .rail.steps.names .tk span{display:none}.flow .rail.steps.names .tk.on span{display:block;margin:0 4px 0 0}.flow .rail.steps .tk.on{padding-right:10px}.flow .rail.steps .tk+.tk::before{width:6px;margin:0 2px}}
@media (max-width:640px){.flow .rail.steps .tk{--face-i:32px;padding:0 2px;min-inline-size:32px}.flow .rail.steps .tk+.tk::before{content:none}.flow .rail.steps.names .tk.on span{margin:0 2px 0 6px}.flow .rail.steps .tk.on{padding-right:6px}.flow .cmp{grid-template-columns:116px repeat(var(--cols,1),148px);width:max-content;min-width:100%;padding-right:max(0px,calc(100cqw - 264px))} /* the trailing room is padding, never a track: a track is filled by the next row's label */.flow .cmp-wrap{margin:6px 0 0;padding:0;scroll-snap-type:x mandatory;scroll-padding-left:116px}.flow .cmp .hd{scroll-snap-align:start}.flow .cmp .lab,.flow .cmp .corner{position:sticky;left:0;z-index:2;background:#fff;padding-right:6px}.flow .cmp .corner{align-self:stretch}.flow .cmp .cell{margin:0}.flow .cmp .hd{margin:0}.flow .cmp .subpanel{position:sticky;left:0;justify-self:start;width:calc(100cqw - 8px);margin:0 4px} /* the panel stays in the window while the matrix scrolls under it */.flow .pick .chip{--face-i:40px;min-inline-size:40px;padding:0 10px}.flow .cmp .hd b{font-size:var(--t2)}.flow .cmp .pc b{font-size:var(--t3)}.flow .pick{gap:16px 24px}}
/* the payment */
.flow .lines{margin-top:12px}
.flow .ln{display:grid;grid-template-columns:minmax(0,1fr) auto;align-items:baseline;column-gap:20px;padding:10px 0}
.flow .ln .k{font:400 var(--t5)/22px var(--sans)}
.flow .ln .k small{display:block;font:400 var(--t2)/18px var(--sans);color:var(--mute);margin-top:1px}
.flow .ln .num{font:400 var(--t4)/22px var(--sans);font-variant-numeric:var(--tab);white-space:nowrap;text-align:right}
.flow .ln.total{margin-top:6px}
.flow .ln.total .k{font-weight:500}
.flow .ln.total .num{font-size:var(--t7);line-height:28px;letter-spacing:-.02em}
/* THE QUIET WELL (kit.js Said), one rule (2026-09-21: under .flow alone it left the unlinked learner's well bare on Study, Tasks, Marks and the Timetable); bare on purpose in a popup and beside the phone page's tick */
.said{display:flex;gap:10px;align-items:baseline;background:var(--raised);box-shadow:var(--raised-edge);border-radius:var(--r3);padding:14px 18px;margin-top:20px;font:400 var(--t4)/22px var(--sans)}
.said b{font-weight:500;color:var(--ok);white-space:nowrap}
.said b.mu{color:var(--mute)}
.said span{text-wrap:pretty}
.upl .said{align-items:flex-start;background:none;box-shadow:none;padding:0;margin-top:22px}
/* ── DOCUMENTS ── */
/* THE DOCUMENTS STEP (the owner, 2026-09-16 00:2x: "tabs like cards with the ID images; opened, they show the captured data"; 18:4x SAST: "I do not understand why half of the screen is not used for this; this needs a better UX ... and better design"): the slots as picture cards (the file's picture or the slot's drawing, the name, the one status word toned: verified in the green, admissions in the sky, resend and needed in the warm, none muted; the picked one ringed in the ink on white); the picked slot's panel: its head (the name, its line), the facts line, the file as a quiet row (the glyph, the name with its kind and size, the download arrow), the read rows as the design's kv rows with the marks as tone glyphs at the value's end and the mark's word under a differing value, the reason as one notice row, the acts at the foot; narrow, the cards in a row and the panel under them with the read folded under its kicker; wide, the 1400px block below */
.dcards{display:grid;grid-template-columns:repeat(auto-fill,minmax(150px,1fr));gap:12px;margin:10px 0 20px}
.dcard{display:grid;grid-template-columns:minmax(0,1fr);gap:8px;padding:10px;border-radius:var(--r3);background:var(--raised);border:0;text-align:left;cursor:pointer;font:inherit;color:var(--ink);box-shadow:var(--raised-edge);position:relative;--lift-to:var(--ring-on);transition:background .2s}
.dcard:hover{background:var(--hover)}.dcard.on{background:#fff}
.dcard .pic{aspect-ratio:4/3;border-radius:var(--r2);background:#fff;overflow:hidden;display:grid;place-items:center;box-shadow:var(--ring)}.dcard.on .pic{background:var(--hover)}
.dcard .pic img{width:100%;height:100%;object-fit:cover}.dcard .pic img.sample{width:64px;height:auto;object-fit:contain}
.dcard .t{font:500 var(--t3)/18px var(--sans);display:-webkit-box;-webkit-line-clamp:2;-webkit-box-orient:vertical;overflow:hidden}
.dcard .s{font:500 var(--t2)/18px var(--sans);color:var(--warm)}.dcard .s.ok{color:var(--ok)}.dcard .s.chk{color:var(--sky-ink)}.dcard .s.mu{color:var(--mute)}
.doc{padding:4px 0 0;min-width:0}
.doc .dh .k{display:block;font:500 var(--t5)/24px var(--sans);color:var(--ink)}
.doc .dh .k small{display:block;font:400 var(--t2)/18px var(--sans);color:var(--mute);margin-top:2px;max-width:60ch;text-wrap:pretty}
.doc .facts{font:400 var(--t2)/18px var(--sans);color:var(--mute);margin-top:6px}
.doc .line{font:400 var(--t2)/18px var(--sans);color:var(--mute);text-wrap:pretty;margin-top:14px}
.doc .line.warn{color:var(--warm);font-weight:500}
/* the acts as the wizard's own pills (the owner, 2026-09-16 19:0x SAST: "the buttons are like text links; they are not nice"): the ink pill for the act the slot asks for, the pale one (white, the Back round's ring, the ink ring on hover) for the other choice, at the row's size */
.go.pale{position:relative;background:#fff;color:var(--ink);box-shadow:var(--ring);--lift-to:var(--ring-on)}
.doc .go{--face:42px;height:var(--face);padding:0 7px 0 18px;font-size:var(--t3)}
.doc .go:not(:has(i)){padding:0 18px}
.doc .go i{width:28px;height:28px;font-size:var(--t4)}
.doc .taps{display:flex;flex-wrap:wrap;align-items:center;gap:10px 12px;margin-top:14px}
.doc .taps.end{margin-top:20px}
.who[data-locked] .taps.end,.who .part .taps.end{display:flex;flex-wrap:wrap;gap:10px 12px;margin-top:20px}
.doc .taps span{font:400 var(--t2)/18px var(--sans);color:var(--mute)}
.doc .fold{--face:18px;cursor:pointer;background:none;padding:0}
.doc.busy .taps{opacity:.5;pointer-events:none}
.doc .file{--face:36px;display:grid;grid-template-columns:22px minmax(0,1fr) auto;column-gap:12px;align-items:center;padding:8px 0;margin-top:14px;text-decoration:none;color:var(--ink)}
.doc .file .fi{display:grid;place-items:center;color:var(--mute)}.doc .file .fi svg{width:20px;height:20px;fill:none;stroke:currentColor;stroke-width:1.6;stroke-linecap:round;stroke-linejoin:round}
.doc .file .fn{display:flex;align-items:baseline;gap:8px;min-width:0;font:400 var(--t4)/20px var(--sans)}
.doc .file .fn b{font-weight:400;min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.doc .file .fn small{flex:none;font:400 var(--t1)/17px var(--sans);color:var(--mute)}
.doc .file .dl{font-style:normal;color:var(--mute);font-size:var(--t5);line-height:22px;transition:transform .2s ease,color .2s ease}
.doc .file a{color:inherit;text-decoration:none;min-width:0}.doc .file a.dl:hover{transform:translateY(2px);color:var(--ink)}
.doc .read .fold{display:flex;align-items:center;gap:6px;width:100%;text-align:left;margin:calc(var(--kick) + 13px) 0 15px;font:500 var(--t2)/18px var(--sans);color:var(--mute)}
.doc .read .fold small{font:inherit;color:inherit}.doc .read .fold small.warm{color:var(--warm)}
.doc .read .fold i{font-style:normal;font-size:var(--t5);line-height:18px;color:var(--mute);transition:transform .2s}
.doc .read[data-open=false] .rows{display:none}
/* a read value may wrap (a school's name): the value's track is a share of the row, never the auto track a wrapping value collapses */
.kv.rd{grid-template-columns:minmax(0,1fr) minmax(0,1.4fr);padding:9px 0;align-items:start}
.kv.rd .v{white-space:normal;overflow-wrap:anywhere}
.kv.rd .v em{font-style:normal;font-weight:600;margin-left:8px;color:var(--mute)}
.kv.rd[data-mark=ok] .v em{color:var(--ok)}
.kv.rd[data-mark=differs] .v em,.kv.rd[data-mark=near] .v em{color:var(--warm)}
.kv.rd[data-mark=differs] .v small,.kv.rd[data-mark=near] .v small{color:var(--warm);font-weight:500}
.doc .read .marks{display:grid;grid-template-columns:minmax(0,1fr) auto auto;gap:4px 18px;align-items:baseline;padding:9px 0;font:400 var(--t4)/20px var(--sans)}
.doc .read .marks b{grid-column:1/-1;font:500 var(--t2)/18px var(--sans);color:var(--mute)}
.doc .read .marks span:nth-child(3n){font-variant-numeric:var(--tab);text-align:right}
.doc .read .marks span:nth-child(3n+1){color:var(--mute)}
.doc .notice{margin:14px 0 0}
.doc .notice>span{min-width:0}
/* THE RECEIPT OF A PAYMENT (the owner, 2026-09-17: "make it use and look similar to the doc upload of the documents part"): the documents step's one card and panel on the pay step's bank tab, and under an invoice awaiting a transfer on Billing; the card a tile (the receipt drawing beside the name over the word, as the wide documents step draws its cards), the panel under it */
.receipt .dcards{grid-template-columns:minmax(0,1fr);max-width:340px;margin:16px 0 14px}
.receipt .dcard{grid-template-columns:88px minmax(0,1fr);grid-template-rows:auto auto;column-gap:14px;row-gap:1px;align-content:center;padding:10px 14px 10px 10px;border-radius:var(--r3);cursor:default}
.receipt .dcard .pic{grid-row:1/3;width:88px}
.receipt .dcard .pic img.sample{width:48px}
.receipt .dcard .t{align-self:end;font-size:var(--t4);line-height:20px}
.receipt .dcard .s{align-self:start}
.receipt .doc{padding-top:0}
.receipt .doc .moment{margin-top:14px}
.receipt-row{margin:0 0 18px}
.check{position:relative;display:flex;gap:12px;align-items:flex-start;margin-top:20px;cursor:pointer;font:400 var(--t4)/22px var(--sans);color:var(--ink)}
.check .box{flex:none;width:22px;height:22px;border-radius:var(--r1);--rc:var(--faint);box-shadow:var(--ring-on);display:grid;place-items:center;color:#fff;font-size:var(--t2);margin-top:0}
.check.on .box{background:var(--ink);box-shadow:none}
.flow .done .kv{padding:12px 0}
.flow button.go{font:500 var(--t4)/1 var(--sans)}
.check input{position:absolute;opacity:0;width:0;height:0}.check input:focus-visible+.box{outline:2px solid var(--ink);outline-offset:2px}
.check small{display:block;font:400 var(--t2)/18px var(--sans);color:var(--mute);margin-top:2px}.check:has(input:disabled){cursor:default;opacity:.6}
.kv .check{margin-top:10px;font:400 var(--t3)/20px var(--sans);color:var(--ink)}.kv .check small{min-height:0;margin-top:2px}
/* ── THE DOORS: the sign-in card centred in the white box, the ways in on the bar ── */
/* the box keeps one height whatever the way or the moment */
/* THE CARD'S CONTENT STANDS AT ITS TOP (2026-10-16, the night lane web-door-2; the shooter's doors at 393 and 852x393: the skeleton stood centred in the way's box and the module's taller tree was centred again, its top 68 to 90 px higher, a shift of 0.02 to 0.036 on every door): the skeleton and the tree share their top, so the tree grows down inside the box and nothing moves */
.door .card{justify-content:flex-start;min-height:420px;display:flex;flex-direction:column}
/* the mailed link's landing (auth: Is that you?, Done, an outcome) sizes its card to its content: it changes no way, so it keeps no height (the owner, 2026-09-16: the 1440 card stood at the door's height with an empty band) */
/* A PAGE WITH NO COLUMN KEEPS ITS CONTENT'S HEIGHT (the 404 page, the auth landing: data-no-otty; one rule since 2026-10-16, the lane web-door-2: the 404 page stood on the 420 px box with its three lines at the top once the card's content stood at its top) */
.door[data-no-otty] .card{min-height:0}.door[data-no-otty] .card .who{flex:none}
.door .who{width:100%;margin:0 auto;max-width:none}
.door .fields{margin-top:22px}
.door .go.wide{margin-top:16px}
.door .ways{margin-top:18px}
/* THE HELP ROW (the owner, 2026-10-05): the ways out (Enrol, Ask support) apart from the request's acts, under a hairline, one quiet link per line at the door's row spacing */
.door .help{display:flex;flex-direction:column;align-items:flex-start;gap:8px;margin-top:18px;padding-top:16px;border-top:1px solid var(--hairline)}
/* a help line wraps inside the card (the pill rule's nowrap is for pills: at 393 the support line ran past the card's edge, the shooter's first read of 2026-10-05); the arrow follows the last word */
.door .help .go.quiet{display:block;font-size:var(--t3);line-height:1.35;white-space:normal;text-align:left}
.door .help .go.quiet i{margin-left:6px;vertical-align:middle}
.door .meta a{color:inherit}
.isl.one{text-decoration:none;color:#fff}
.isl.one .seg{display:inline-flex;align-items:center;gap:6px;padding:0 10px 0 16px;height:36px;font:500 var(--t3)/1 var(--sans);color:#fff}
.isl.one .chev{width:16px;height:16px;fill:none;stroke:currentColor;stroke-width:1.8;stroke-linecap:round;stroke-linejoin:round;opacity:.7}
/* ── ROWS THAT OPEN: an invoice or a receipt, the figure with its state under it, the chevron at the end ── */
.kv .v small{display:block;font:400 var(--t1)/17px var(--sans);color:var(--mute);margin-top:1px}
.kv :is(.k,.v) small.ok{color:var(--ok);font-weight:500}
.kv :is(.k,.v) small.need{color:var(--warm);font-weight:500}
.seg:focus{outline:none}
.seg:focus-visible{outline:2px solid rgba(255,255,255,.7);outline-offset:-2px;border-radius:var(--pill)}
/* ── LEARNER TABS: one per learner, only where the family has more than one; the ink slides under the open name ── */
.tabs{position:relative;display:flex;margin:-4px 0 24px}
.tab{--face:38px;flex:1;background:none;padding:6px 8px 12px;font:500 var(--t4)/20px var(--sans);color:var(--faint);cursor:pointer;text-align:center;white-space:nowrap;overflow:hidden;overflow:clip visible;text-overflow:ellipsis;transition:color .3s}
.tab.on{color:var(--ink)}
.tabs .ink{position:absolute;bottom:0;left:0;height:3px;border-radius:var(--pill);background:var(--ink);transform-origin:left center;transition:transform .25s ease}
.doc .pmeter{margin-top:14px}
.who.tabbed{margin-top:0}
/* an invoice row: what and for whom, the figure with its state, the last column the act — the Pay pill on an open one, the way into the receipt on a paid one; one set of columns for every row */
.kv.inv{grid-template-columns:minmax(0,1fr) auto 20px;column-gap:16px;align-items:center;text-decoration:none;color:inherit;padding:var(--row) 0}
.kv.inv .a{display:flex;justify-content:flex-end;align-items:center;color:var(--faint);font-size:var(--t6);line-height:22px}
.paybox .kv{padding:8px 0}
.paybox .meta{margin-top:12px}
/* a row that is not yet open: the same row, quiet, no act */
.kv.off .k{color:var(--mute)}
.kv .k small.num{font-family:var(--sans);font-variant-numeric:var(--tab)}
.flow .fields+.meta b{font-weight:500;color:var(--ink)}
/* a row that is an act: the word and its chevron, nothing else */
.kv.act{display:flex;align-items:center;gap:6px;font:500 var(--t5)/22px var(--sans);color:var(--ink);text-decoration:none}
.kv.act i{font-style:normal;color:var(--mute);font-size:var(--t5)}
/* a phone number always has its country code chosen beside it */
.fld select{height:46px;border:0;border-radius:var(--r3);background:#fff;box-shadow:var(--ring);padding:0 40px 0 14px;font:400 var(--t5)/22px var(--sans);color:var(--ink);width:100%;appearance:none;-webkit-appearance:none;background-image:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%236b6c93' stroke-width='1.8' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M6 9l6 6 6-6'/%3E%3C/svg%3E");background-repeat:no-repeat;background-position:right 12px center;background-size:18px}
/* the add control is a tab of the row: the plus glyph alone, the tabs' muted ink, no circle, ink on hover */
.tabs.one{justify-content:flex-end}
.tab.plus svg{width:18px;height:18px;fill:none;stroke:currentColor;stroke-width:1.9;stroke-linecap:round}
.tab.plus:hover{color:var(--ink)}
/* THE WEEK AS A TABLE: days across, periods down, today's date in the filled round, the now line at the clock; wider than a phone inside its own scroll */
.ttwrap{margin:6px 0 0;padding:0 0 6px}
.ttday{display:none}
.tt{--col:minmax(0,1fr);--col-quiet:minmax(48px,.45fr);display:grid;gap:5px;min-width:0;grid-template-columns:auto var(--cols)}
/* THE KEY under the week head (2026-09-29): every sign a chip can carry, the kinds' glyphs and the state signs, each with its word, one quiet line */
/* THE KEY UNDER THE WEEK (timetable.js WeekKey; the owner 2026-10-07, after the teachers: "the symbols above the word Timetable look like buttons"): one quiet line below the grid, the word Key first, plain glyph and word, no border and no fill */
.ttkey{display:flex;flex-wrap:wrap;gap:4px 16px;margin:14px 0 0;font:400 var(--t1)/16px var(--sans);color:var(--faint)}
.ttkey .kw{font-weight:600;color:var(--mute)}
.ttkey span{display:inline-flex;align-items:center;gap:5px}
.ttkey svg{flex:none;width:13px;height:13px;fill:none;stroke:currentColor;stroke-width:1.7;stroke-linecap:round;stroke-linejoin:round}
.ttkey [data-kind=play] svg{fill:currentColor;stroke:none}
.ttkey .pstate{display:inline-grid;width:13px;height:13px;color:inherit}.ttkey .pstate svg{width:13px;height:13px}
/* THE LEAD CARD (the owner, 2026-09-29 14:2x: "I don't like this blue background idea and the blue left bar lines"): the raised card as any, its head word Now or Next in the small caps over the title, the live dot before Now, its Join pill the one accent */
.ask.lead .t .hd{display:flex;align-items:center;gap:5px;font:500 var(--t1)/16px var(--sans);letter-spacing:.08em;text-transform:uppercase;color:var(--ink-soft);margin:0 0 4px}
.ask.lead.live .t .hd::before{content:'';flex:none;width:7px;height:7px;border-radius:50%;background:var(--sky);box-shadow:var(--glow)}
/* TODAY (the owner, 2026-09-29 14:3x: "highlight the current date only, in the day head, the way Apple Calendar marks today"; before it the column's light, which tinted the days after today too): the date in the filled round, the day's name in the ink; every other head in the quiet ink; room over the head row; no column and no day carries a ground */
.tt .th{font:500 var(--t1)/22px var(--sans);color:var(--faint);text-align:center;padding:8px 0 6px}
.tt .th b{display:inline-grid;place-items:center;min-width:22px;height:22px;padding:0 5px;border-radius:var(--pill);font-weight:500;font-variant-numeric:var(--tab)}
.tt .th.now{color:var(--ink)}
.tt .th.now b{background:var(--ink);color:#fff}
.tt .th.zone{text-align:left;white-space:nowrap}
.tt .tm{font:400 var(--t1)/16px var(--sans);font-variant-numeric:var(--tab);color:var(--mute);align-self:start;padding:8px 8px 0 0}
/* THE NOW LINE: the clock's time in the hours column, a sky hairline across the days with its dot at the left, moved by the page's half-minute clock */
.tt .nowtm{color:var(--sky-ink);font-weight:600;letter-spacing:.06em;padding-top:0;align-self:center;line-height:10px}
.tt .nowline{grid-column:2 / -1;position:relative;height:10px;align-self:center;background:linear-gradient(var(--now-line),var(--now-line)) center / 100% 1px no-repeat;pointer-events:none}
.tt .nowline::before{content:'';position:absolute;left:-3px;top:2px;width:6px;height:6px;border-radius:50%;background:var(--sky)}
.tt .cell{background:var(--raised);border-radius:var(--r2);padding:8px 10px;min-height:52px;display:grid;align-content:start;gap:1px;box-shadow:var(--raised-edge)}
/* a lesson's card on its subject's wash (the owner, 2026-10-07, THE SUBJECT FAMILIES: the hue follows the subject on the timetable too; the state keeps its own marks, the Join pill, the now line and the ink border of the one on now) */
.tt .cell.lesson{background:var(--wash,var(--raised));box-shadow:var(--hue-edge)}
/* THE CARD'S HEAD (2026-09-29): the kind's glyph and its word (Now, Next, Later today, Done) or its hour; the Now head wears the live dot */
.tt .cell .hd{display:inline-flex;align-items:center;gap:5px;font:500 var(--t1)/16px var(--sans);color:var(--ink-soft);font-variant-numeric:var(--tab);letter-spacing:.01em;margin:0 0 2px;white-space:nowrap;min-width:0;overflow:hidden}
.tt .cell .hd svg{flex:none;width:13px;height:13px;fill:none;stroke:currentColor;stroke-width:1.7;stroke-linecap:round;stroke-linejoin:round}
.tt .cell.live .hd{color:var(--ink)}
.tt .cell.live .hd::before{content:'';flex:none;width:7px;height:7px;border-radius:50%;background:var(--sky);box-shadow:var(--glow)}
.tt .cell b{font:500 var(--t2)/17px var(--sans);color:var(--ink);display:-webkit-box;-webkit-box-orient:vertical;-webkit-line-clamp:2;overflow:hidden}
/* a lesson's name is its course's door (the owner, 2026-09-15 23:0x: "can these courses link to the corresponding course page?") */
.tt .cell b a{color:inherit;text-decoration:none;position:static}
.tt .cell b a:hover{text-decoration:underline;text-underline-offset:2px}
.tt .cell small{font:400 var(--t1)/15px var(--sans);color:var(--ink-soft)}
/* EVEN CARDS (the owner, 2026-09-29 14:3x: "a card never grows taller than its neighbours because of a long description"): one padding and one min-height for every card, the title on two lines at most, each detail line on one; the full text stands on the course page */
.tt .cell > small:not(.hd){display:-webkit-box;-webkit-box-orient:vertical;-webkit-line-clamp:1;overflow:hidden}
/* the lesson's hour in every box (the owner, 2026-09-15 17:0x: "add the exact time from and to on each box, elegantly"): one faint tabular line under the name */
.tt .cell .tm{display:block;font:400 var(--t1)/15px var(--sans);color:var(--ink-soft);font-variant-numeric:tabular-nums;letter-spacing:.01em;margin-top:1px}
/* THE CELL'S KINDS, one ordered set (the owner, 2026-09-29: "what is a task, what is a meeting, what is a recording, what is today, what is past; think designer, within the theme"; then 14:2x, on the sky band and the column's light: "vibe-cody, not enough real-world designer", so a kind is told by the glyph in its head and the card's weight alone, as a calendar app does; before it the learner audit of 2026-09-25: the hairline rule outranked the empty day's): A LESSON is the raised card with the live glyph in its head, the title in the full ink; A RECORDING is the same card on no fill with a faint dotted edge, the play glyph in its head and filled in its Watch, on a past day too; A DONE LESSON (today's, or a past day's) keeps its card at 60 % ink with the tick in its head; the next one is said by its head word alone; a break is a quiet ring; an empty day is nothing; the time and the line in a cell wear the soft ink, which reads on the well; no colour on the grid but the Join pill and the now line */
.tt .cell.rec{background:none;box-shadow:none;border:1.5px dotted rgba(35,36,103,.32);padding:6.5px 8.5px}
.tt .cell.rec b{color:var(--ink-soft)}
.tt .cell.rec .hd svg,.tt .cell.rec .mini svg{fill:currentColor;stroke:none}
.tt .cell.past{opacity:.6}
.tt .cell.past b{font-weight:400}
.tt .cell.brk{background:none;box-shadow:var(--quiet-edge)}
.tt .cell.brk b{color:var(--mute);font-weight:400}
.tt .cell.off{background:none;box-shadow:none}
/* A DAY'S TASKS IN THE WEEK: the cell a plain stack of compact row-chips (the owner, 2026-09-29: a task is never a lesson-sized card; 2026-09-27: "this box under the big one is without enough padding"), each the kind's glyph, its title in two lines at most and Due 14:00 under it, a quiet ring, a tap of the hit's height; "Show more" a quiet word under them */
.tt .cell.tasks{display:grid;gap:calc(var(--hair) * 2);padding:0;background:none;box-shadow:none;align-content:start;font:400 var(--t1)/16px var(--sans)}
.tt .tchip{display:grid;grid-template-columns:minmax(0,1fr) auto;align-items:center;column-gap:8px;padding:5px 10px 5px 8px;border-radius:var(--r2);background:none;box-shadow:var(--quiet-edge);text-decoration:none;color:var(--ink);min-height:var(--hit)}
.tt .tchip.kv .k{display:grid;grid-template-columns:auto minmax(0,1fr);column-gap:7px;align-items:center;padding-left:0;font:500 var(--t1)/15px var(--sans)}
.tt .tchip.kv .ki{position:static;width:14px;height:14px;color:var(--ink-soft)}
.tt .tchip .ki svg{width:14px;height:14px;fill:none;stroke:currentColor;stroke-width:1.7;stroke-linecap:round;stroke-linejoin:round}
.tt .tchip .t{min-width:0;display:-webkit-box;-webkit-box-orient:vertical;-webkit-line-clamp:2;overflow:hidden;overflow-wrap:anywhere}
.tt .tchip small{grid-column:2;font:400 var(--t1)/14px var(--sans);color:var(--mute);margin:0;min-height:0}
/* the lesson on now: the cell fades from the top as the lesson goes — the part that has passed lightens toward the done tone, the rest keeps the well; its way in bolder */
.tt .cell.live{position:relative;overflow:hidden;border:1.5px solid var(--ink);padding:6.5px 8.5px}
.tt .cell.live::before{content:'';position:absolute;left:0;right:0;top:0;height:var(--p,0%);background:linear-gradient(180deg,rgba(255,255,255,.75),rgba(255,255,255,.75) calc(100% - 10px),rgba(255,255,255,0));pointer-events:none}
.tt .cell.live .mini{font-weight:600}
.tt .cell.live b,.tt .cell.live small{position:relative}
/* the lesson's own way in, inside its cell: Watch on a done one, Join on the one now and the next */
.tt .cell .mini{--face:16px;position:relative;margin-top:20px;font:500 var(--t1)/16px var(--sans);color:var(--ink);text-decoration:none;display:inline-flex;align-items:center;gap:5px;justify-self:start}
.tt .cell .mini svg{width:14px;height:14px;fill:none;stroke:currentColor;stroke-width:1.6;stroke-linejoin:round;stroke-linecap:round}
.tt .cell.rec .mini{color:var(--ink-soft)}
/* A BLOCK OF RECORDINGS IS ONE TAP (2026-10-12): the card is a button in the cell's own clothes (the dotted edge of .cell.rec), the whole card the tap on every size; it firms its edge under the pointer and rings on the keyboard */
.tt button.cell{appearance:none;-webkit-appearance:none;font:inherit;text-align:left;color:inherit;cursor:pointer;width:100%;margin:0}
.tt button.cell:hover{border-style:solid}
.tt button.cell:focus-visible{outline:var(--hair) solid var(--sky-ink);outline-offset:var(--hair)}
/* THE DAY'S RECORDINGS (parts.js Sheet drawn by timetable.js RecSheet): each row the course over its topic, the one control on the right (Watch with the play glyph, or the muted state word) */
dialog.list[data-recordings] .rows > .kv{align-items:center}
dialog.list[data-recordings] .mini{font:500 var(--t2)/18px var(--sans);color:var(--ink);text-decoration:none;display:inline-flex;align-items:center;gap:5px}
dialog.list[data-recordings] .mini svg{width:14px;height:14px;fill:currentColor;stroke:none}
dialog.list[data-recordings] .mini.follows{color:var(--mute);font-weight:500}
/* the work's groups carry their count; overdue and today read warm */
.tt .cell .short{display:none}
/* a marks row (marks.js): name and teacher, the term average as the figure, the way in */
.kv.course{grid-template-columns:minmax(0,1fr) auto 12px;row-gap:8px;align-items:center;text-decoration:none;color:inherit;padding:11px 0}
.kv.course .a{color:var(--faint);font-size:var(--t5);line-height:22px;justify-self:end}
.kv.course .v small{display:block;font:400 var(--t1)/15px var(--sans);color:var(--mute);text-align:right;margin-top:1px}
/* a question: its words, then the answers as choices */
.q{margin-top:18px}
.who > .acts[data-act-end]{justify-content:flex-end;margin-top:var(--head)}
.q .qt{font:500 var(--t5)/22px var(--sans);color:var(--ink);margin-bottom:8px}
.paybox.up{padding:8px 20px 12px}
.paybox.up .kv{padding:10px 0}
/* THE MODULE LIST (the owner's audit, 2026-09-25: "is this the best spacing, font sizes and design?"): a module is ONE row of 64 px, the whole row the tap: its step badge (parts.js Step), its clean name in ink over ONE muted line, the chevron turning by transform alone; hairlines between the rows, never air; the open one in the accent, never another layout. A group's head (tasks.js Overdue) is the same row with its title (.gt) and count (.n) */
.mod summary{--face:var(--hit);list-style:none;cursor:pointer;display:flex;align-items:center;gap:var(--step-gap);padding:var(--row) 0;user-select:none;-webkit-user-select:none}
.mod summary::-webkit-details-marker,.prose summary::-webkit-details-marker{display:none}
/* one chevron, one size (the owner, 2026-09-16 00:3x: "arrows too much and two sizes"): the disclosure is the list's small chevron at the row's end; it turns by transform alone */
.mod summary::after,.prose summary::after{color:var(--mute);transform:rotate(90deg);margin:0 0 0 var(--it-x);transition:transform .3s}
.mod[open] > summary::after,.prose details[open] > summary::after{transform:rotate(-90deg)}
/* THE MODULE ROWS ARE LIGHT (round 3b review): the step badge says the state, so a module's fold and its items' way marks are the small chevron (--fold-s) */
.mod{--fold-s:12px}.mod summary::after,.mod .kv.it .v > .way::after{inline-size:var(--fold-s);block-size:var(--fold-s)}
/* ONE FOLD (the learner audit, 2026-09-25): every disclosure's chevron points down while it is closed and up while it is open */
.doc .read .fold i{transform:rotate(90deg)}
.doc .read[data-open=true] .fold i{transform:rotate(-90deg)}
.mod summary .gt{margin:0;flex:1 1 auto;min-width:0;font:500 var(--t5)/22px var(--sans);color:var(--ink)}
.mod summary .gt .n{display:flex;margin:2px 0 0}
details.mod + .mod{border-top:1px solid var(--line)}
/* THE OPEN MODULE SHOWS (the owner): its head and its items stand on one soft tint of the accent, its badge and title in the accent, the chevron turned; its items come in with a short fade and rise (opacity and transform alone) */
details.mod[open]{margin-inline:calc(var(--gut) * -1);padding-inline:var(--gut)}
details.mod[open] + .mod,.mod + details.mod[open]{border-top-color:transparent}
details.mod[open] > .its{animation:mod-in .18s ease-out both}
@keyframes mod-in{from{opacity:0;transform:translateY(4px)}}
/* ONE PAGE WIDTH (the owner, 2026-09-27: "is the tasks design correct, to not take the full width"): every list and dashboard page (Timetable, Courses, a course, Tasks, Marks, Notifications and the guardian's pages) takes the card's full width with the same side padding; its rows keep their columns (the title, the day, the act) in one grid across it; a page's own reading keeps its own rule (.prose) */
/* ONE BAND WIDTH (round 3b review: at 1440 the open module's tint ran 40 px past its rows): a course at its reading width (the query's 880 is --read) keeps the open module's band inside the rows, every module inset by one row gap so nothing moves as it opens */
.course-page{container-type:inline-size}
@container (min-width:880px){.course-page details.mod{padding-inline:var(--it-x)}.course-page details.mod[open]{margin-inline:0;border-radius:var(--r3)}}
.mod summary .mt{flex:1 1 auto;min-width:0;display:grid}
.mod summary .mt .t{font:500 var(--t5)/22px var(--sans);color:var(--ink);display:-webkit-box;-webkit-box-orient:vertical;-webkit-line-clamp:2;overflow:hidden}
.mod summary .mt small{font:400 var(--t2)/18px var(--sans);color:var(--mute);white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
.mod[open] > summary .mt .t{color:var(--sky-ink)}
/* A HEAD'S STATUS LINE, ONE LOOK (2026-09-24: the module lines in the body font): the module's, the work's and the marks' */
:is(.mod,.work,[data-marks]) .gt .n{display:inline-flex;align-items:center;gap:6px;font:400 var(--t2)/18px var(--sans);color:var(--ink-soft);margin-left:8px}
.kv.it.later .k{color:var(--mute)}
.kv.it.next .k{font-weight:500}
/* the courses on a wide screen: the list at the left, the open course at the right, one box */
.card.wide .body{padding-top:calc(var(--pad) - 4px)}
.split{display:grid;grid-template-columns:minmax(300px,2fr) minmax(0,3fr);gap:0 48px;align-items:start}
/* THE FULL PAGE (study.js full$, the owner 2026-10-07, the proposal's Option A): the list hidden, the pane across the stage; the pane's head (.panehead) the tabs with the Full page or Half page control at its end, a quiet pill on the words' baseline; the course's head (.chead) its crest in the subject's full hue before the title, on the course page alone and on the full page */
.split.full{grid-template-columns:minmax(0,1fr)}.split.full .side{display:none}
.panehead{display:flex;align-items:flex-start;gap:16px}.panehead .tabs{flex:1 1 auto;min-width:0}
.pfull{--face:var(--hit);flex:none;display:inline-flex;align-items:center;gap:6px;height:30px;margin-top:2px;padding:0 12px 0 10px;border-radius:var(--pill);background:none;box-shadow:var(--quiet-edge);font:500 var(--t2)/1 var(--sans);color:var(--ink-soft);cursor:pointer;white-space:nowrap}
.pfull:hover{background:var(--hover);color:var(--ink)}.pfull svg{width:14px;height:14px;fill:none;stroke:currentColor;stroke-width:1.8;stroke-linecap:round;stroke-linejoin:round}
.chead{display:flex;align-items:center;gap:14px}.chead > div{min-width:0}.chead .nm{margin-top:0}.chead .trail{margin-bottom:4px}
/* THE TERM GROUPS (study.js byTerm, the owner 2026-10-07, the proposal's Option A: no ordinal digit, a Term head over its modules): a quiet head over each group, each group its own path, so the joined line runs inside a term */
.pterm + .pterm{margin-top:var(--row)}.gt.mterm{margin:0 0 2px}
.split .main .course-page .nm{margin-top:0}
/* A MODULE'S ITEMS (the owner's audit, 2026-09-25: huge grey padded cards): compact rows at the name's edge, no cards: the kind's glyph, the clean title on one line, ONE muted line (the assessment's code a quiet chip at its head), the state's icon at the end; a lesson's label a small word over its rows */
details.mod > .its{padding:0 0 var(--row) calc(var(--round-h) + var(--step-gap))}
.mod .sub{font:500 var(--t2)/18px var(--sans);color:var(--mute);margin:var(--row) 0 2px}
.mod .kv.it{align-items:center;padding:var(--it-y) var(--it-x);margin:0 calc(var(--it-x) * -1);border-radius:var(--r2)}
.mod a.kv.it:hover{background:var(--hover)}
.mod .kv.it .ki{top:50%;transform:translateY(-50%)}
:is(.mod,.tools) .kv.it .k > .t{display:-webkit-box;-webkit-box-orient:vertical;-webkit-line-clamp:2;font:400 var(--t4)/20px var(--sans);overflow:hidden;overflow-wrap:break-word}
.mod .kv.it .k small{display:-webkit-box;-webkit-box-orient:vertical;-webkit-line-clamp:2;overflow:hidden}
/* THE CHIP, one look (.pill): a quiet word in a small pill of the well; the assessment's code beside a title wears it, the door's last-account word the same with more room */
.pill{display:inline-block;white-space:nowrap;padding:0 var(--chip-x);border-radius:var(--pill);box-shadow:var(--quiet-edge);font:500 var(--t1)/16px var(--sans);color:var(--ink-soft)}
.kv.it .k{display:block;position:relative;padding-left:var(--lead)}
.kv.it .ki{position:absolute;left:0;top:0;display:grid;place-items:center;width:var(--lead-box);height:var(--lead-box)}
/* ONE HEIGHT PER ROW KIND (the learner audit, 2026-09-25: task rows stood 62 to 101 px and a module's items 79 to 105): a list row's title and its line under it are one line each, the rest cut with an ellipsis (the row opens to the whole), so every row of a kind stands one height */
:is(.kv.task,.kv.course,.kv.file) .k,:is(.kv.task,.kv.course,.kv.file) .k small{white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
.kv.it .ki svg{width:var(--icon-s);height:var(--icon-s);fill:none;stroke:currentColor;stroke-width:1.6;stroke-linecap:round;stroke-linejoin:round;color:var(--mute)}
.kv.it.later .ki svg{color:var(--faint)}
/* ONE END COLUMN (round 2 of the audit: the › stood 7 px right of the state icons and smaller): the way's mark in a row's end stands in the state icon's own box, centred, the icon's size */
.kv .v > i{display:inline-grid;place-items:center;inline-size:var(--way-s);block-size:var(--way-s);font:400 var(--t6)/1 var(--sans);color:var(--mute);vertical-align:-.2em}.kv .v > i .pglyph{inline-size:var(--icon-sm);block-size:var(--icon-sm)}
/* THE TOOLS (study.js Tools): the rows under their kicker as the list's rows stand; under a course's modules the items' tiles, the kicker on the module heads' own line */
.mod.tools{padding-top:var(--row)}.mod.tools > .gt{margin:0 0 4px}
/* THE COURSE CARDS (study.js CourseCard and SchoolRow; the Courses and Tasks proposal the owner approved, 2026-09-27: "make sure everything is for a reason and works elegantly"; his bar "think designer, and lux"): each subject a raised card on a grid (one across on a phone and beside the list, more on a wide list), the whole card on the subject's wash with the crest in the full hue and the bar in it (the owner, 2026-10-07, THE SUBJECT FAMILIES; ~~its hue on the crest alone, 2026-09-27~~: the edge read as a warning, a band or a side alone is refused; Nothing due one calm line with its tick); the name's link stands for the whole card (its ::after the card's box) and the next pill stands over it as the second target; ONE HEIGHT FROM THE FIRST PAINT: the progress row and the pill's room (--card-slot) stand before their lines land, so a line landing moves nothing; a school course a quiet row */
:is(.courses,.work) .gt .n{display:inline-grid;place-items:center;min-width:22px;height:18px;margin-left:8px;padding:0 7px;border-radius:var(--pill);box-shadow:var(--quiet-edge);font:500 var(--t1)/1 var(--sans);color:var(--ink-soft);font-variant-numeric:var(--tab);vertical-align:1px}
.cards{display:grid;grid-template-columns:repeat(auto-fill,minmax(min(100%,320px),1fr));gap:12px}
.ccard{position:relative;display:grid;grid-template-columns:var(--crest-m) minmax(0,1fr) auto;align-items:center;gap:10px 14px;padding:16px 16px 14px;border-radius:var(--r4);background:var(--wash,var(--raised));box-shadow:var(--hue-edge),var(--top-light)}
.ccard:has(a.cl):hover{background:linear-gradient(var(--hover),var(--hover)),var(--wash,var(--raised))}
.ccard.on{box-shadow:var(--sky-edge),var(--lift-raised)}
.ccard:has(.cl:focus-visible){outline:var(--hair) solid var(--sky-ink);outline-offset:2px}
.ccard .ct{min-width:0;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;font:600 var(--t5)/22px var(--sans);color:var(--ink)}
.ccard .ct small{display:block;overflow:hidden;text-overflow:ellipsis;font:400 var(--t2)/18px var(--sans);color:var(--mute)}
.ccard .cl{position:static;color:var(--ink);text-decoration:none;outline:0}
.ccard .cl::after{content:'';position:absolute;inset:0;width:auto;height:auto;border-radius:inherit}
.ccard .flag{font:500 var(--t1)/16px var(--sans);color:var(--sky-ink);margin-left:6px}
.ccard .cs{text-align:right;font:600 var(--t6)/22px var(--sans);color:var(--ink)}
.ccard .cs small{display:block;font:400 var(--t1)/15px var(--sans);color:var(--mute)}
.ccard .cp{grid-column:2 / 4;display:flex;align-items:center;gap:10px;min-height:18px;font:500 var(--t1)/16px var(--sans);color:var(--ink-soft);font-variant-numeric:var(--tab);white-space:nowrap}
.ccard .cp .pseg{max-width:180px}
.ccard .cp .ch{color:var(--mute);font-weight:400}
.ccard .cn{grid-column:1 / 4;display:grid;align-content:center;min-height:var(--card-slot)}
.cnext{position:relative;z-index:1;display:flex;align-items:flex-start;gap:10px;min-height:var(--card-slot);padding:8px 12px 8px 8px;border-radius:var(--r3);--g:var(--sky);background:var(--tint);box-shadow:var(--tint-edge);font:500 var(--t2)/18px var(--sans);color:var(--ink);text-decoration:none}
.cnext .tx{min-width:0;overflow-wrap:anywhere}
.cnext small{display:block;font:400 var(--t1)/16px var(--sans);color:var(--ink-soft)}
.cnext .gr{display:grid;place-items:center;flex:none;width:var(--round-s);height:var(--round-s);margin-top:4px;border-radius:50%;background:white;box-shadow:var(--sky-edge);color:var(--sky-ink);transition:transform .2s ease}
.cnext .gr svg{width:14px;height:14px;fill:none;stroke:currentColor;stroke-width:2;stroke-linecap:round;stroke-linejoin:round}
.cnext:hover .gr{transform:translateX(2px)}
.cnext.soon{--g:var(--g-flame)}.cnext.soon .gr{box-shadow:var(--flame-edge);color:var(--g-flame)}.cnext.soon small{color:var(--warn);font-weight:500}
.ccard .cq{display:flex;align-items:center;gap:10px;min-height:var(--card-slot);padding:8px 12px 8px 8px;border-radius:var(--r3);font:400 var(--t2)/18px var(--sans);color:var(--ink-soft)}
.ccard .cq.ok{padding-left:2px;font-weight:500;color:var(--ink-soft)}
.ccard .cq svg{flex:none;width:18px;height:18px;fill:none;stroke:var(--ok);stroke-width:2.4;stroke-linecap:round;stroke-linejoin:round}
.ccard .cn:has(> .go){grid-template-columns:minmax(0,1fr) auto;align-items:center}
.ccard .cq + .go{position:relative;z-index:1;justify-self:start}
.crows{display:grid;gap:8px}
.crow{display:flex;align-items:center;gap:14px;min-height:56px;padding:10px 16px 10px 12px;border-radius:var(--r4);background:var(--wash,var(--raised));box-shadow:var(--hue-edge),var(--top-light);text-decoration:none;color:var(--ink);font:500 var(--t4)/20px var(--sans)}
.crow .t{flex:1;min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.crow .way{color:var(--faint)}
.crow:hover{background:linear-gradient(var(--hover),var(--hover)),var(--wash,var(--raised))}
.crow.on{box-shadow:var(--sky-edge),var(--lift-raised)}
/* THE TOOLS AS APP TILES (study.js Tools; the proposal: "tools read as apps to open, not as more homework rows"): the same rows as tiles on a grid, the glyph of the one set on the ink's tint at the top, the name and where it comes from under it, the way out in the corner; Google Drive first */
.tools .tiles{display:grid;grid-template-columns:repeat(auto-fill,minmax(var(--tile-min),1fr));gap:10px}
.tiles > .kv.it{position:relative;display:block;min-height:var(--tile-h);margin:0;padding:14px 14px 12px;border-radius:var(--r3);background:var(--raised);box-shadow:var(--raised-edge),var(--top-light)}
.tiles > a.kv.it:hover{background:linear-gradient(var(--hover),var(--hover)),var(--raised)}
.tiles > .kv.it .k{padding:0;display:grid;gap:2px}
.tiles > .kv.it .ki{position:static;transform:none;width:var(--tile-glyph);height:var(--tile-glyph);margin-bottom:8px;border-radius:var(--r2);--g:var(--ink-soft);background:var(--tint);box-shadow:var(--tint-edge)}
.tiles > .kv.it .ki svg{width:20px;height:20px;color:var(--ink)}
.tools .tiles > .kv.it .k > .t{font:500 var(--t3)/18px var(--sans);color:var(--ink)}
.tiles > .kv.it .k small{font:400 var(--t1)/16px var(--sans);color:var(--mute)}
.tiles > .kv.it .v{position:absolute;top:12px;right:12px;color:var(--faint)}
/* THE NEXT ROWS (study.js NextRow; the owner, 2026-09-27: no hero): a card's Due next and Continue, and the course page's rows under its title; an act at a row's end (the selected card's and the page's) */
.ccard .cn{gap:8px}.cnrows{display:grid;gap:8px;margin-top:14px}
.cnext .pact{flex:none;align-self:center;margin-left:auto}
.course-page > .nm ~ .who .tabs{margin-top:var(--head)}
/* THE MODULES AS A PATH (the proposal): one joined line down the step badges, green after a done one, no hairlines between the rows; the state a chip on a wide page (Now in the sky, Done in green, Past quiet), the line's first word on a narrow one; a past module opened stands on the waiting surface, quiet */
.path details.mod{position:relative;--mx:0px}
.path details.mod + .mod{border-top:0}
.path details.mod:not(:last-of-type)::before{content:'';position:absolute;left:calc(var(--mx) + var(--round-h) / 2 - 1px);top:calc(var(--row) + var(--round-h) / 2);bottom:calc(var(--row) * -1 - var(--round-h) / 2);width:2px;border-radius:2px;background:var(--track-line)}
.path .pstep:not(.s-done):not(.s-info){border-radius:50%;background:white}
.path details.mod.m-done:not(:last-of-type)::before{background:var(--ok-line)}
/* AN OPEN MODULE (the owner, 2026-09-27: "this vertical line is a bit too close to the icons, and generally the design can be refined to be more elegant"): no rule down its side and no panel; its items under the title's text with air, the kind's glyph without a tile, one act or state at the right, Show past a quiet link */
.path details.mod[open]::before{display:none}
.mod .cit .pkind{background:none;box-shadow:none}
/* THE ITEM'S ICON ONE STEP UP (2026-10-01, the Phase Heads: "some icons small"): a module item's kind glyph at the medium icon size, the one token */
.mod .cit .pkind svg{width:var(--icon-m);height:var(--icon-m)}
.mod details.mod[open] > .its,details.mod[open] > .its{padding-top:4px}
.path details.mod[open]{--mx:var(--gut)}
.path details.mod.m-past[open]{--g:var(--ink);background:var(--wait)}
.path .m-past > summary .mt .t{color:var(--ink-soft)}
.path details.m-past[open] > summary .pstep{color:var(--faint)}
.mchip{flex:none;display:none;align-items:center;gap:5px;height:24px;padding:0 10px;border-radius:var(--pill);font:500 var(--t1)/1 var(--sans);white-space:nowrap}
.mchip.c-now{background:var(--sky);color:white;box-shadow:var(--glow)}
.mchip.c-done{--g:var(--ok);background:var(--tint);box-shadow:var(--tint-edge);color:var(--ok)}
.mchip.c-past{box-shadow:var(--quiet-edge);color:var(--ink-soft)}
.mchip.c-locked{box-shadow:var(--quiet-edge);color:var(--mute)}
.mchip svg{width:13px;height:13px;fill:none;stroke:currentColor;stroke-width:2.2;stroke-linecap:round;stroke-linejoin:round}
.mod summary .stw{color:var(--ink-soft)}
@container (min-width:560px){.course-page .mchip{display:inline-flex}.course-page .mod summary .stw{display:none}}
@container (min-width:880px){.path details.mod{--mx:var(--it-x)}}
/* THE ITEM ROWS (study.js Item): the kind's round, the title over its one line, ONE act or the drawn tick at the end, one row a hairline from the next; the now item a white card in the sky's ring, one due within 48 hours on the flame's warm tint with its day said in words; done a step softer, past quieter, locked with its lock in the line */
.mod .kv.it.cit{grid-template-columns:var(--kind-s) minmax(0,1fr) auto;column-gap:12px;align-items:center;min-height:60px}
.mod .kv.it.cit .k{padding-left:0}
.mod .kv.it.cit .k > .t{font:500 var(--t4)/20px var(--sans);color:var(--ink)}
.mod .kv.it.cit .k small{color:var(--mute)}
.cit small .lk{width:13px;height:13px;margin-right:4px;vertical-align:-2px;stroke:currentColor;stroke-width:1.8;stroke-linecap:round;stroke-linejoin:round}
.mod .cit + .cit{border-top:1px solid var(--hairline);border-radius:0}
.mod .kv.it.cit.s-now{background:var(--sky-tint);border-radius:var(--r3);margin-block:4px}
.mod .kv.it.cit.s-soon{--g:var(--g-flame);background:var(--tint);border-radius:var(--r3);margin-block:4px}
.mod .cit.s-soon .k small{color:var(--warn);font-weight:500}
.mod .cit:is(.s-now,.s-soon) + .cit,.mod .cit + .cit:is(.s-now,.s-soon){border-top-color:transparent}
.mod .kv.it.cit.s-done .k > .t,.mod .kv.it.cit.s-lock .k > .t{color:var(--ink-soft)}
.mod .kv.it.cit.s-past .k > .t{color:var(--mute);font-weight:400}
.mod .pastfold{margin-top:6px}
.mod .pastfold > summary{gap:6px;min-height:var(--hit);padding:0;font:500 var(--t2)/18px var(--sans);color:var(--sky-ink)}
.mod .pastfold > summary::after{margin-left:0}
.mod .pastfold[open] > summary::after{transform:rotate(-90deg)}
.mod .pastfold > .its{padding-top:4px}
.mod .pastfold[open] > .its{animation:mod-in .18s ease-out both}
@media (prefers-reduced-motion:reduce){.pseg.grow > i,.pseg.grow > i > b,.ptick path,.mod[open] > .its,.mod .pastfold[open] > .its{animation:none}.pact,.cnext .gr{transition:none}}
/* a course page fills the white box (the owner, 2026-09-15 16:5x: "courses are not taking the whole screen"); the reading cap of 70ch is gone and a table takes the row */
/* THE BODY IS DRAWN AS CANVAS DRAWS IT (2026-10-01, the Phase Heads beside Canvas: the bars, the highlights, the bold, the spacing, the tables, the centred pictures and the frames' sizes differed): the rules under .prose on the author's own elements are Canvas's content stylesheet, read from its common bundle on 2026-10-01 (16px/1.5; p 12px 0; headings bold, 6px 0, 38/32/24/18/16/12; lists 0 0 6px 25px; hr 20px 0; blockquote a 15px left rule; a cell 2px unless cellpadding; no cell line unless the author drew one; mark the browser's yellow; a picture inline, at most the column; a frame at most the column, in the author's box). Ours are the column, the cards, the scroll box, the code face and the equations alone */
.prose{font:400 var(--t4)/24px var(--sans);color:var(--ink);margin-top:18px;max-width:none;overflow-wrap:anywhere;min-width:0}
.prose table{max-width:100%;table-layout:auto;font:inherit;color:inherit;background-color:transparent}
.prose table:not([cellspacing]){border-collapse:collapse;border-spacing:0}
.prose table:not([cellpadding]) td{padding:2px}
/* THE ONE DEPARTURE FROM CANVAS'S SHEET (2026-10-01, the concord page at a desk: the editor bakes a row's height in pixels from the author's own column, so at ours the words were shorter than the row and the browser's middle put a hole above them): a cell the author did not align sits at the top; the author's own valign or vertical-align wins */
.prose :is(td,th):not([valign]):not([style*="vertical-align"]){vertical-align:top}
.prose p{margin:12px 0}
.prose :is(figure,.scroll,a.ccard,pre,details,.chips){margin:12px 0}
.prose .media .frame{position:relative;aspect-ratio:var(--ratio,16/9);border-radius:var(--r3);background:linear-gradient(180deg,#25265a,#141539);overflow:hidden}
/* THE FRAME'S BOX IS THE AUTHOR'S (2026-10-01, prose.js boxOf): its width (--w, pixels or a percentage) never past the column nor past what the view can hold, its ratio (--ratio); left as Canvas places it, centred when the author centred its block (.mid) */
/* ONE MEDIA RULE FOR EVERY EMBED (the owner, on a short laptop screen: "why do videos and content show like this"; a video stood as a wide band cut at its foot, its controls under the bar): a frame keeps its own ratio (--ratio, prose.js; 16:9 when none) and fits both ways, the column's width or the room the screen has over the bar (--fit: the viewport less the frame round the card, --frame-h), centred, never cropped; a player, a paper and a picture stand within the same room */
.prose{--frame-h:calc(var(--otty-row) + var(--bar-room) + 2 * var(--band) + var(--row) + env(safe-area-inset-top))}
.prose .media .frame,.prose .paper,.prose img:not(.emoji){--fit:calc(100vh - var(--frame-h));--fit:calc(100dvh - var(--frame-h))}
.prose .media .frame{width:min(100%,100cqw,var(--w,100%),calc(var(--fit) * (var(--ratio,16/9))));max-height:var(--fit);margin:0}
/* a frame in a table cell never widens the cell past the column (a percentage in an auto cell is no cap, 2026-10-01: a 560 px YouTube in a cell made a 357 px phone column scroll): the body and the scroll box are inline-size containers, so 100cqw is the column */
.prose,.prose .scroll{container-type:inline-size}
.prose .media.mid .frame{margin-inline:auto}
.prose .media figcaption{font:400 var(--t1)/17px var(--sans);color:var(--mute);margin-top:8px;display:flex;flex-wrap:wrap;align-items:center;gap:4px 8px}
.prose .media figcaption .go{margin-left:auto}
.prose a.ccard{display:flex;align-items:center;gap:12px;padding:12px 14px;border-radius:var(--r3);background:#fff;box-shadow:var(--ring);text-decoration:none;color:var(--ink);margin-top:10px}
.prose a.ccard .glyph{flex:none;width:22px;height:22px;color:var(--mute)}
.prose a.ccard .glyph svg{width:22px;height:22px;fill:none;stroke:currentColor;stroke-width:1.6;stroke-linecap:round;stroke-linejoin:round}
.prose a.ccard .c-t{flex:1;min-width:0;display:grid}
.prose a.ccard .c-title{font:500 var(--t4)/20px var(--sans)}
.prose a.ccard .c-meta{font:400 var(--t1)/17px var(--sans);color:var(--mute)}
.prose a.ccard .c-word{font:500 var(--t3)/20px var(--sans);color:var(--ink)}
.prose a.ccard .c-word::after{color:var(--mute);margin-left:.25em;vertical-align:-.2em}
.prose a.ccard .c-word:has(svg)::after{content:none}
.prose a.ccard .c-word svg{width:var(--icon-sm);height:var(--icon-sm);vertical-align:-.2em;margin-left:.25em}
.mod summary:focus{outline:none}
.mod summary:focus-visible{outline:2px solid var(--ink);outline-offset:4px;border-radius:var(--r1)}
.split .side .nm{margin-top:0}
/* the door's own moments: the last account as one row with its pill, the wait as a line with the scanning bar */
.lastrow{display:flex;align-items:center;gap:14px;width:100%;margin-top:22px;padding:14px 16px;border:0;border-radius:var(--r3);background:#fff;box-shadow:var(--ring);cursor:pointer;text-align:left;font:inherit;color:var(--ink)}
.lastrow .who{flex:1;min-width:0;display:grid;grid-template-columns:minmax(0,1fr)}
.lastrow .who b,.lastrow .who small{min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.lastrow .pill{flex:none;line-height:1;color:var(--ink);padding:var(--band) var(--it-x)}
.lastrow .who b{font:500 var(--t5)/22px var(--sans)}
.lastrow .who small{font:400 var(--t2)/18px var(--sans);color:var(--mute)}
.lastrow i{font-style:normal;color:var(--mute);font-size:var(--t6)}
.moment{margin-top:22px}
.moment p{margin:0 0 10px;font:400 var(--t3)/20px var(--sans);color:var(--mute)}
/* THE BUSY BAR, OPTION A, ONE RULE FOR EVERY BUSY BAR (the owner, 2026-09-30, chosen from the coordinator's page; the connect moment's too): the track clearly seen (the ink at 12% on the paper with the track's inset edge, the white at 12% on the night ground, --bar-track), the bar sweeping to and fro for as long as the wait lasts (the ink on the paper, the sky on the night ground, --bar-ink); the page's own wait line still speaks after about 19 s */
.scanbar{--bar-track:rgba(35,36,103,.12);--bar-ink:var(--ink);position:relative;height:4px;border-radius:var(--pill);background:var(--bar-track);box-shadow:var(--track-edge);overflow:hidden}
.otty .scanbar,.connect .scanbar{--bar-track:rgba(255,255,255,.12);--bar-ink:var(--sky);box-shadow:none}
.scanbar i{position:absolute;top:0;bottom:0;left:0;width:38%;border-radius:var(--pill);background:var(--bar-ink);animation:scan 1.6s cubic-bezier(.4,0,.6,1) infinite alternate both}
/* THE MOTION RULE (the learner audit, 2026-09-25; the busy bar's sweep since 2026-09-30): a thing moves by transform or opacity alone, never by its size or place; the busy bar sweeps for as long as its wait lasts (before 2026-09-30 it rested after 12 sweeps), a meter's fill arrives from the left once; reduced motion: no sweep, the bar shown whole at a third of its ink */
@keyframes scan{from{transform:translateX(-100%)}to{transform:translateX(264%)}}
.pbar > :is(i,b){transform-origin:left center;animation:fill .6s cubic-bezier(.2,.8,.2,1) both}
@keyframes fill{from{transform:scaleX(0)}}
.door .fx b{font-weight:500;color:var(--ink)}
/* the door as the live door stands: the statement at the left, the box in the middle, otty at the right with his words in the runtime's glass bubble only when he speaks (the OTTY AT THE DOOR block below); no bar */
.door .shell{align-self:center;justify-self:center;width:min(100%,540px)}
/* A DOOR'S STATEMENT, one rule for the sign-in doors and the enrol door (2026-09-21: written twice); each door's own under it */
.statement{color:#fff}
.statement .eyebrow{display:block;font:500 var(--t1)/16px var(--sans);letter-spacing:.14em;text-transform:uppercase;color:var(--night-faint)}
.statement h1{margin:14px 0;font:600 var(--t-display)/1.12 var(--sans);letter-spacing:-.025em}
.statement p{margin:0;font:400 var(--t5)/26px var(--sans);color:var(--night-mute)}
.door .statement{align-self:center}
.door .statement p{max-width:26ch}
/* THE DOOR'S BOXES STAND FROM THE FIRST PAINT (2026-10-16, the night lane web-door; /guardian/ CLS p75 0.11 in the field, the shooter's door-class sheet 0.026 to 0.039: the statement's words land after the first paint and the card grows out of its skeleton, and the stage's centring moves everything by half of each): the headline keeps its two lines (one on a phone) and the lead its line while empty, the card each way's settled box under 1000 px (the learner's 333 px, the email way's 391, the SMS way's 325; under 600 px 352, 410 and 407: the shooter's measured shells less the rim band, off the boxlog of the run web-door-2; the first run's one 300 px box left half of each way's growth as the shift; the three sign-in ways alone: the 404 page and the auth landing wear .door too and keep their content's height); his box is his column's --fig from the first paint already */
.door .statement h1{min-height:2.24em}
.door .statement p{min-height:26px}
/* HIS ROW'S ROOM IS THE STYLESHEET'S (2026-10-16, the night lane web-door-2; the shooter's box log at 820 and 393: a frame was laid out before shell.js ran, with the statement and the card and no column, and the shell's row of 318 px (820) or 155 px (393) then moved the centred statement up and the card down by half, 0.07 to 0.077): under 1000 px his box is the stylesheet's curve (--fig: never under 130 px, 38 vw, the shell's fit() curve for the top band, now written here alone) and the stage keeps his row (--his-row: the box, its 6 px of foot and its 12 px of margins) from the first paint, so the column lands in room already kept; a page with no column (the 404 page, the auth landing: data-no-otty) and a short screen (his column gone) keep no row */
.door{--fig:max(130px,38vw);--his-row:calc(var(--fig) + 18px)}
.door[data-no-otty]{--his-row:0px}
.door .card .who{flex:1}
/* the ways in as the learner tabs: names with the ink under the open one; the box one height whatever the way */
.door .tabs.ways{margin:-4px 0 22px}
.door .tabs.ways .tab{text-decoration:none}
/* the enrol door, laid out as the live door: the statement over the week at the left (three parts of five), the compact card at the right, the terms under it; no otty, the chat bubble instead */
.enroldoor .stage{display:grid;grid-template-columns:minmax(0,1fr) minmax(480px,820px);grid-template-rows:auto 1fr;gap:0 clamp(24px,4vw,56px);align-items:start;align-content:start;overflow:auto;padding-top:16px;padding-bottom:24px}
.enroldoor .statement{grid-column:1;grid-row:1;padding:24px 0 22px}
.enroldoor .statement h1{margin-bottom:12px;font-size:var(--t-display);line-height:1.1}
.enroldoor .shell{grid-column:2;grid-row:1/3;align-self:start;width:100%;margin:0}
/* the enrol form on one screen: a tighter rhythm, six rows of fields */
.enroldoor .card{display:block;min-height:0;overflow:visible;padding:28px clamp(20px,3vw,36px) 26px}
/* the code piece and the offer at a hand's width inside the wide card (the owner, 2026-09-16 08:0x SAST: "Enter your code on desktop is a bit stretched") */
.enroldoor #code,.enroldoor #offer{max-width:480px}
.enroldoor .go.wide{margin-top:16px}
.enroldoor .fields{gap:8px 16px;margin-top:8px}
.enroldoor .gt{margin:16px 0 2px}
.enroldoor .fields .fld.code{grid-column:1}
.dwm .cel.lv .fw{color:var(--night)}
.isl.two{display:inline-flex}
.isl.two .seg{display:inline-flex;align-items:center;gap:8px;padding:0 14px;height:36px;font:500 var(--t3)/1 var(--sans);color:#fff;text-decoration:none}
.isl.two .seg svg{width:18px;height:18px;fill:none;stroke:currentColor;stroke-width:1.6;stroke-linecap:round;stroke-linejoin:round}
/* the chat bubble: the bar's glass as one round, at the foot of the page where the bar would be */
.chatbubble{position:fixed;right:20px;bottom:calc(20px + env(safe-area-inset-bottom));width:56px;height:56px;border-radius:50%;display:grid;place-items:center;background:var(--isle);backdrop-filter:var(--isle-blur);-webkit-backdrop-filter:var(--isle-blur);box-shadow:var(--edge-night),var(--lift-bar);color:#fff;z-index:7;transition:transform .5s cubic-bezier(.2,.8,.2,1)}
.chatbubble:hover{transform:translateY(-2px)}
.chatbubble svg{width:24px;height:24px;fill:none;stroke:currentColor;stroke-width:1.5;stroke-linecap:round;stroke-linejoin:round} /* the bar's icon exactly (.sup svg): THE ONE GLYPH, 2026-10-14 */
/* the enrol door is a page that scrolls, not the fixed frame: the box and the week take their own height, nothing goes under anything */
.app.enroldoor{height:auto;min-height:100vh;min-height:100dvh;overflow:auto}
.app.enroldoor .stage{flex:none;height:auto;min-height:0;overflow:visible}
.app.enroldoor .shell{min-height:auto;display:block}
.app.enroldoor .card{flex:none;min-height:auto;height:auto;overflow:visible}
/* the week is the door's background: a faint calendar behind the words and the box, a hint of school, nothing to read */
.enroldoor .dwm{position:fixed;left:50%;top:58%;width:max(118vw,1000px);transform:translate(-50%,-50%) rotate(-7deg);z-index:0;pointer-events:none;user-select:none;color:#fff}
.enroldoor .dwm .cap,.enroldoor .dwm .facts,.enroldoor .dwm .sj .short{display:none}
.enroldoor .dwm .g{display:grid;grid-template-columns:auto repeat(5,minmax(0,1fr));gap:12px}
.enroldoor .dwm .hd{font:500 var(--t1)/16px var(--sans);letter-spacing:.14em;color:rgba(255,255,255,.09);padding:0 0 4px 14px}
.enroldoor .dwm .pr{font:400 var(--t2)/16px var(--sans);color:rgba(255,255,255,.09);padding:16px 14px 0 0;font-variant-numeric:var(--tab)}
.enroldoor .dwm .cel{display:grid;gap:6px;align-content:start;padding:12px 16px;border-radius:var(--r3);background:rgba(255,255,255,.014);--rc:rgba(255,255,255,.03);box-shadow:var(--ring);min-height:clamp(64px,9vh,92px)}
.enroldoor .dwm .cel.lv{background:rgba(255,255,255,.032)}
.enroldoor .dwm .sj{font:500 var(--t4)/1.25 var(--sans);color:rgba(255,255,255,.10)}
.enroldoor .dwm .fw{font:500 var(--t1)/12px var(--sans);letter-spacing:.14em;color:rgba(255,255,255,.10)}
.enroldoor .statement,.enroldoor .shell{position:relative;z-index:1}
/* ══ OTTY AT THE DOOR as the live door draws him (src/styles.scss .dotty and .obubble, src/app/door/door.ts fit): his column is a cell of the door's grid, never a layer over it; his box stands out of the flow at the size the shell writes (--fig, the live door's MAX OTTY by the drawn body: the cell's height, or the cell's width less the desk's gutters at his widest pose, at most 600px), his feet on the cell's foot (52 of the frame's 600 rows under them, so the box drops by that much); the runtime's stage over his words, so the sign-in's jump crosses them ══ */
.door .otty{--band:top;--feet:0.0867;position:relative;z-index:5;pointer-events:none;display:block;min-height:100px;height:auto;align-self:stretch;justify-self:stretch;margin:0;padding:0;background:none;container-type:normal;overflow:visible}
.door .otty .mount{position:absolute;left:calc(var(--fig) * -0.18);bottom:calc(var(--fig) * var(--feet) * -1);width:var(--fig);height:auto;aspect-ratio:1;margin:0;cursor:default;isolation:isolate}
/* HE STANDS IN FRONT OF HIS WORDS (the owner, 2026-10-01: "when Otty jumps up it should be in front of the text above it, so it covers it when jumping; now the bubble shows transparently through Otty"): his box is its own stack (isolation above), his words the lower layer (.say, 1) and the runtime's stage the upper (2), so the top of the sign-in's jump crosses the bubble in front of it; one rule for every door (the guardian's, the learner's, recover, the phone page, the authorise page), his placement and the clip untouched; proven by the shooter's doors flow (tools/shots.mjs door-jump-in-front) */
.door .otty .mount > .otty-stage{z-index:2}
/* the door's bubble in the app's glass (the owner, 2026-09-15 23:0x: "the speech bubble a bit higher and aligned with the glass design of the other pages; the slip behind him is fine") */
/* the door's slot places the bubble alone: no size, no fill, no padding of its own (THE BUBBLE HUGS ITS WORDS, below) */
.door .otty .say{position:absolute;left:50%;right:auto;top:auto;bottom:calc(100% + 48px);z-index:1;display:block;width:max-content;max-width:min(90%,var(--room,100vw));min-width:0;min-height:0;transform:translateX(-50%);margin:0;padding:0;font:400 var(--t4)/21px var(--sans);color:var(--night);border:0;text-wrap:pretty;overflow:visible;contain:none}
.enroldoor .fld{gap:4px}
.enroldoor .fld input,.enroldoor .fld select{height:44px}
.fld .line{display:flex;gap:6px;align-items:flex-start;min-height:18px;font:400 var(--t2)/18px var(--sans);color:var(--mute);text-wrap:pretty}
.fld.warn .line{color:var(--warm);font-weight:500}
.fld.ok .line{color:var(--ok);font-weight:500}
.fld.warn .line:not(:empty)::before{content:'';flex:none;width:16px;height:16px;margin-top:1px;background:currentColor;-webkit-mask:var(--glyph-i) center/16px no-repeat;mask:var(--glyph-i) center/16px no-repeat}
.fld.warn input,.fld.warn select,.fld.warn input:focus,.fld.warn select:focus{--rc:var(--warm);box-shadow:var(--ring-on)}
/* the card's row: the ask's soft well with the glyph leading the words; attention and info on the well, a refusal in the warm on a faint warm tint, a success in the green on a faint green tint; no border, no fade */
.notice{display:flex;gap:10px;align-items:flex-start;margin:0 0 18px;padding:13px 16px;border-radius:var(--r3);background:var(--tint);box-shadow:var(--tint-edge);font:400 var(--t3)/20px var(--sans);color:var(--ink);text-wrap:pretty}
.notice::before{content:'';flex:none;width:18px;height:18px;margin-top:1px;background:currentColor;-webkit-mask:var(--glyph-i) center/18px no-repeat;mask:var(--glyph-i) center/18px no-repeat}
.notice.info{color:var(--ink-soft)}
.notice.error{--g:var(--warm);color:var(--warm);font-weight:500}
.notice.success{--g:var(--ok);color:var(--ok);font-weight:500}
.notice.success::before{-webkit-mask-image:var(--glyph-ok);mask-image:var(--glyph-ok)}
.notice .go.quiet{margin-left:auto}
.notice.end{flex-wrap:wrap}
.notice.end > span{flex:1 1 0;min-width:0}
.notice > .way-back{flex-basis:100%;box-sizing:border-box;display:inline-flex;align-items:center;gap:6px;padding-left:28px;color:var(--ink);font-weight:500;text-decoration:none}
.notice > .way-back .pglyph{width:18px;height:18px}
.notice > .way-back:hover{text-decoration:underline}
/* on a door the row is bare, as the live door's: one line in its tone, no well */
.door .notice{background:none;box-shadow:none;padding:0;margin:0 0 14px;border-radius:0;font-size:var(--t2);line-height:18px;gap:6px}
.door .notice::before{width:16px;height:16px;-webkit-mask-size:16px;mask-size:16px}
/* the wait stands where the action button stood, the button's 48 px: the words over the scanning bar; in a flow's foot it takes the acts' room */
.moment.busy{margin-top:16px;min-height:48px;padding:7px 0;flex:1;min-width:160px}
.moment.busy p{margin:0 0 10px}
.flow .card > .foot .moment.busy,.shell > .barwrap > .acts .moment.busy{margin:0}
/* the door while it sends: the tabs, the ways and the last-used row are held; Google's reCAPTCHA badge never shows (its notice is in the terms line) */
.door .held{opacity:.5;pointer-events:none}
.grecaptcha-badge{visibility:hidden}
/* ── ENROL DOOR: facts and the code step ── */
/* the three quiet facts under the statement: the design's small uppercase label over each value, no box, no line */
.enroldoor .glance{display:flex;flex-wrap:wrap;gap:10px 28px;margin-top:20px}
.enroldoor .glance span{display:grid;gap:3px;font:400 var(--t3)/20px var(--sans);color:var(--night);min-width:0}
.enroldoor .glance b{font:500 var(--t1)/12px var(--sans);letter-spacing:.08em;text-transform:uppercase;color:var(--night-faint)}
/* the actions under a moment: quiet links in one row, the same on every door */
.acts{display:flex;flex-wrap:wrap;align-items:center;gap:10px 24px;margin-top:14px}
.code-or{margin:16px 0 0;color:var(--mute)}
/* the one code field: six digits, wide-set and centred, no placeholder; the value is the digits alone so a phone's or a mail's one-time code fills it; one rule for every code step (2026-09-21: Settings' wore a second one, 24px at .16em, off centre on this rule's padding) */
.fld input.otp{font:500 var(--t8)/28px var(--mono);letter-spacing:.42em;text-align:center;height:56px;padding-left:calc(14px + .42em);font-variant-numeric:var(--tab)}
.enroldoor .captcha{height:0}
/* ── CHAT: the Desk widget under the design ── */
/* the vendor's launcher never shows: the design's bubble on a door and the bar's Support item are the launchers (chat.js), and its hidden disc still carries the vendor's unread class, which chat.js reads onto them as the design's dot (the bar's pip on Support; a sky disc in the bubble's navy ring on the bubble's edge) */
.woot--bubble-holder{display:none!important}
/* THE PEEK (the owner, 2026-09-16 00:1x: a new Desk message while the panel is closed shows in our own dress, never the vendor's unread bubble): the vendor's unread view is held unseen; the island's glass bubble stands over the pill (over a door's disc) with the sender's name and words; a tap opens the panel, its X or 8 s closes it (chat.js) */
.woot-widget-holder.has-unread-view{visibility:hidden!important;pointer-events:none!important}
/* the panel's paper is opaque under the frame (the owner, 2026-09-16 01:1x: "because it is transparent too it does not look good"): white at the card's round inside the band (chat.js sets --band and --paper off the shell and the card) */
.woot-widget-holder::before{content:'';position:absolute;inset:var(--band,7px);border-radius:var(--paper,25px);background:#fff;z-index:-1}
.peek{position:absolute;left:50%;bottom:calc(100% + 10px);transform:translateX(-50%);z-index:6;pointer-events:auto;width:max-content;max-width:min(360px,calc(100% - 16px));display:grid;grid-template-columns:minmax(0,1fr) auto;align-items:center;column-gap:10px;padding:10px 10px 10px 16px;border:0;border-radius:var(--r4);color:#fff;font:400 var(--t3)/19px var(--sans);text-align:left;cursor:pointer;background:var(--isle);-webkit-backdrop-filter:var(--isle-blur);backdrop-filter:var(--isle-blur);box-shadow:var(--edge-night),var(--lift-bar);animation:bub-in .34s cubic-bezier(.2,.7,.2,1)}
.peek small{display:block;color:var(--night-faint);font:500 var(--t1)/16px var(--sans)}
.peek b{display:-webkit-box;-webkit-line-clamp:2;-webkit-box-orient:vertical;overflow:hidden;font-weight:400}
.peek .x{width:30px;height:30px;border-radius:50%;display:grid;place-items:center;background:rgba(255,255,255,.1)}
.peek .x svg{width:14px;height:14px;fill:none;stroke:currentColor;stroke-width:1.8;stroke-linecap:round}
.app > .peek{position:fixed;left:auto;right:20px;bottom:calc(88px + env(safe-area-inset-bottom));transform:none;z-index:7}
.chatbubble.unread::after{content:'';position:absolute;top:2px;right:2px;box-sizing:border-box;width:12px;height:12px;border-radius:50%;border:2px solid #1c1d38;background:rgb(var(--glow-rgb))}
.sup.unread::after{content:'';position:absolute;top:8px;right:14px;width:6px;height:6px;border-radius:50%;background:rgb(var(--glow-rgb))}
/* THE ONE BADGE RULE (2026-10-15, chat.js unreadCount): more than one unseen thing draws the count in the bell's pip shape */
.sup.unread[data-n]::after{content:attr(data-n);top:2px;right:6px;box-sizing:border-box;width:auto;min-width:18px;height:18px;padding:0 4px;border-radius:9px;font:700 var(--t1)/18px var(--sans);font-variant-numeric:var(--tab);text-align:center;color:var(--ink)}
/* THE LOADING DRESS (2026-10-06, chat.js open): a launcher tapped before the widget stands dims with its own label (no spinner, no second word) until the panel opens or the help line shows */
.sup.loading,.chatbubble.loading,.band.held.loading,[data-chat].loading{opacity:.55;cursor:progress;transition:opacity .2s ease}
/* ── PAGE: the offline lede and the waiting state ── */
/* the one quiet line while the connection is down (page.js): between the top bar and the stage on the field's own ground, no band, no modal */
/* THE NOTIFICATIONS STRIP OF THE INSTALLED APP (2026-10-03, push.js Strip): one glass pill under the head, the app's bar glass and night edge, white words, the way arrow, the small night round to close */
.app > .stripper{flex:none;padding:0 16px 8px}
.strip{display:flex;align-items:center;gap:6px;border-radius:var(--pill);padding:4px 4px 4px 16px;background:var(--bar-glass);-webkit-backdrop-filter:var(--glass-blur);backdrop-filter:var(--glass-blur);box-shadow:var(--edge-night);color:#fff}
.strip .ask{flex:1;min-width:0;display:flex;align-items:center;justify-content:space-between;gap:10px;min-height:36px;padding:6px 0;border:0;background:none;color:inherit;text-align:left;font:500 var(--t2)/18px var(--sans);cursor:pointer;text-wrap:pretty}
.strip .ask:disabled{opacity:.6;cursor:default}
.app > .offline{margin:0;padding:0 16px 10px;text-align:center;font:400 var(--t2)/18px var(--sans);color:var(--night-mute)}
/* THE ONE WAIT (the owner, 2026-09-30, first: "one elegant, glassy, minimal thing for all"; then, on the glass: "the skeletons are glass on white, which is not normal. Just do the normal skeleton the sites show — not glassy, but within our theme and colours"): every wait in the portal is ONE system in this one place: the page's shape while it loads (the skeleton: shell.js MY.skel, in the real shape of what comes, a form's fields, a list's rows, a card), the busy bar under an act's word (above) and the Canvas connect moment (below). The skeleton's block is the quiet muted tone of the theme, the ink at a few percent on the paper and the white at a few percent on the night ground, the cards' own radii, one gentle light across it; no glass, no blur, no white block; reduced motion stills the light */
/* the block is the house's own WAITING surface (THE SURFACES, ONE SYSTEM above: the ink at its faintest with its ring and its top light), as the door's shape draws it; on the night ground the white at a breath with its hairline */
/* THE SKELETON IS FLAT (the owner, 2026-10-16: "make sure all the skeletons resemble the actual pages; the skeletons are a little bit off; also they are not that elegant, or minimal"; the night lane skeletons): one tone from the theme, the track's fill (the ink at 7 %) on the paper and the night fill on the night ground; no ring, no top light, no gradient, no icon, no word; one soft light across it; the doors' shape (door.html and the token pages, which paint before any script) is the same catalogue's markup, stamped into their cards at the deploy's build by tools/skel.mjs (2026-10-16, the night lane skeletons-2; before it each door carried a hand-drawn block of its own, the hand-in's 344 px short of its page); a text part is a bar with the small radius, a boxed part the cards' own radius; every part's measure is its real element's (the list under the parts) */
:root{--wait-light:linear-gradient(100deg,transparent 35%,rgba(255,255,255,.5) 50%,transparent 65%)}
.skel .sk{position:relative;overflow:hidden;background:var(--track-fill);box-shadow:none}
.otty .skel .sk,.connect .skel .sk{--wait-light:linear-gradient(100deg,transparent 35%,rgba(255,255,255,.1) 50%,transparent 65%);background:var(--night-fill)}
.skel .sk::after{content:'';position:absolute;inset:0;background:var(--wait-light);transform:translateX(-100%);animation:sk-light 2.6s ease-in-out infinite;pointer-events:none}
@keyframes sk-light{to{transform:translateX(100%)}}
/* the card waits for the state at its full size (2026-09-28, the owner: "a small card ... then it turns into a large card"): the title line and the page's blocks in its own order */
.card .moment.wait{margin:0;min-height:160px;display:grid;align-content:center}
.card .moment.wait.skel{display:block;min-height:0;margin:0;padding-top:4px}
.skel .sk{display:block;border-radius:var(--r3)}
.skel > .sk.t{width:min(36%,240px);height:20px;border-radius:var(--r1);margin:4px 0 28px}
/* the parts of a page's shape (shell.js SKEL, 2026-09-30; 2026-10-16 the item row, the day strip, the courses' own grid, a lone card at half width): the line under the title (kit.js Head, the h1's 28 px line and the fx's 18), a label (.hd 16), a small pill, the tab bar (.tab 38), chips (.chip 40), the day strip (timetable.js, seven tiles on a phone and a line of day names from 900 px), the wide block, the week, a boxed row (.crow 56, the r4 radius), an item row (a row of the list pages: its lines over the hairline, tasks.js, marks.js, talk.js), the summary, cards side by side (two columns from 900 px, so one card alone stands at half width as Billing's does; each its own height `h:200,100`), course cards in the courses' own grid (.cards above: 320 px columns, auto-fill), two columns of rows, the pay box's rows beside its pill, a form as one grid of its fields (f*6), the doors' own parts under THE DOORS' PARTS below; a part's own height rides inline (`w:120`) */
.skel > .sk.s{width:min(28%,180px);height:12px;border-radius:var(--r1);margin:-16px 0 24px}
.skel .sk.l{width:min(22%,140px);height:12px;border-radius:var(--r1);margin:6px 0 12px}
.skel > .sk.x{width:min(30%,170px);height:26px;border-radius:var(--pill);margin:-12px 0 24px}
.skel > .sk-b{display:flex;gap:28px;height:36px;border-bottom:1px solid var(--hairline);margin:-4px 0 24px}.skel > .sk-b > .sk{width:64px;height:12px;border-radius:var(--r1);margin-top:12px}
.skel > .sk-k{display:flex;flex-wrap:wrap;gap:8px;margin:0 0 16px;overflow:hidden}@media (max-width:899px){.skel > .sk-k{flex-wrap:nowrap}}.skel > .sk-k > .sk{flex:none;width:72px;height:28px;border-radius:var(--pill)}
.skel > .sk.w{height:76px;margin:0 0 24px}
.skel > .sk.g{height:clamp(220px,50vh,560px)}
.skel .sk.r{height:56px;margin:0 0 12px;border-radius:var(--r4)}
.skel .sk-i{display:grid;align-content:center;gap:6px;height:40px;border-bottom:1px solid var(--hairline)}.skel .sk-i > .sk{height:11px;width:min(38%,260px);border-radius:var(--r1)}.skel .sk-i > .sk + .sk{height:9px;width:min(58%,380px)}
.skel > .sk-y{display:grid;grid-template-columns:repeat(7,minmax(0,1fr));gap:4px;margin:0 0 16px}.skel > .sk-y > .sk{height:44px;border-radius:var(--r2)}@media (min-width:900px){.skel > .sk-y{gap:0;margin:0 0 8px}.skel > .sk-y > .sk{height:14px;width:48px;justify-self:center;border-radius:var(--r1)}}
.skel > .sk-h{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:20px;margin:0 0 20px}@media (max-width:899px){.skel > .sk-h{grid-template-columns:1fr}}
.skel > .sk-h > .sk{height:clamp(150px,24vh,230px);border-radius:var(--r4)}
.skel > .sk-h > div{min-width:0}.skel > .sk-h.d{gap:32px}
.skel > .sk-h.q{grid-template-columns:minmax(0,3fr) minmax(0,2fr);gap:32px}.skel > .sk-h.q .sk.p{margin-top:0}@media (max-width:899px){.skel > .sk-h.q{grid-template-columns:1fr}}
.skel > .sk.m{height:128px;margin:0 0 46px}
.skel > .sk-h.c{grid-template-columns:repeat(auto-fill,minmax(min(100%,320px),1fr));gap:12px;margin:0 0 12px}.skel > .sk-h.c > .sk{height:102px}
/* a form's field: its label line and its box, the field's own height (kit.js Field); a pill: the act's own height and width */
.skel .sk-f{display:grid;gap:6px}.skel .sk-f > .sk.l{width:min(28%,160px);height:12px;border-radius:var(--r1);margin:3px 0}.skel .sk-f > .sk.i{height:46px;border-radius:var(--r3)}
/* a form is one grid of its fields at the .fields' own measures (2026-10-16, the night lane skeletons-2; the address sheet at 393 stood 30 px over its form when each field carried a margin of its own): the field is the .fld (an 18 px label line, 6 px, the 46 px box), the grid the .fields (one column, two from 600 px, the gap 14 by 16 and 12 on a phone, the margin above it the head's); a door's form sits 22 px under its name line */
.skel > .sk-h.f{grid-template-columns:1fr;gap:14px 16px;margin:var(--head) 0 0}@media (min-width:600px){.skel > .sk-h.f{grid-template-columns:1fr 1fr}}@media (max-width:599px){.skel > .sk-h.f{gap:12px}}
/* a sheet's title (the 30 px name line of a page with a way back) is the one followed by its form; the form then sits the head's margin under the line's bottom (the bar's 5 px inset and the margin, as one margin above: sibling block margins collapse, so no part of the doors or the sheets carries a margin below) */
.skel > .sk.t:has(+ .sk-h.f){margin:1px 0 0}.skel > .sk.t:has(+ .sk-h.f) + .sk-h.f{margin-top:calc(var(--head) + 5px)}
.door .skel > .sk.t:has(+ .sk-h.f){margin:0}.door .skel > .sk-h.f{margin-top:26px}
.skel .sk.p{width:min(48%,220px);height:48px;border-radius:var(--pill);margin:10px 0 0}
/* THE DOORS' PARTS (2026-10-16, the night lane skeletons-2; shell.js SKEL door, hand-in, upload): every part carries its margin ABOVE alone (sibling block margins collapse, so a margin below would merge with the next part's: the first run stood 11 px short), the margin being the real gap plus the inset of the bar before it; on a door the title is the card's name line (.who .nm, 28 px: the section's 4 px of padding above the 20 px bar, the 4 below in the next margin) and the sub line its .fx (3 px on a phone 2, and a 20 px line); u the capture box (.upl .take: 22 px under the line, 152 tall), o a choice row (.pchoice: 8 px above, 44 tall), n a note line centred (.count: 8 px above, an 18 px line, two of them on a phone where the sentence wraps), a a quiet act centred (.upl .or: 12 px above, 22 tall, the last part on the upload page so it keeps its 5 px below), p the wide act (.go.wide: 44 tall, 16 px above on a door and 22 on a token page) */
.door .skel > .sk.t{margin:0}.door .skel > .sk.s{margin:11px 0 0}@media (max-width:599px){.door .skel > .sk.s{margin-top:10px}}
.skel > .sk.u{height:152px;margin:26px 0 0;border-radius:var(--r4)}.skel > .sk.o{height:44px;margin:8px 0 0;border-radius:var(--r3)}
.skel > .sk-n{display:grid;justify-items:center;gap:6px;margin:11px 0 0}.skel > .sk-n > .sk{width:min(46%,230px);height:12px;border-radius:var(--r1)}.skel > .sk-n > .sk + .sk{width:min(30%,150px)}@media (min-width:600px){.skel > .sk-n > .sk + .sk{display:none}}
.skel > .sk.a{width:min(32%,160px);height:12px;margin:17px auto 5px;border-radius:var(--r1)}
.door .skel > .sk.p{width:100%;height:44px;margin:16px 0 0}.upl .skel > .sk.p{margin-top:25px}
.card[data-landed] > :not(.moment),.card[data-landed] > .body > :not(.moment){animation:t-in .24s ease-out both}
/* THE CANVAS CONNECT MOMENT (the owner, 2026-09-30: "nothing for the Canvas authorise redirect"; then: a full-screen calm surface in the theme's colours, not glass; learn.js authorise and connectBack, shell.js MY.connect): the whole page under the app's own night ground, Otty's round, one calm line and the busy bar in the sky; no button, nothing to tap */
.veil.connect{z-index:40;align-items:center;padding:16px;background:var(--field);animation:t-in .24s ease-out both}
.connect .pane{display:grid;justify-items:center;gap:16px;width:min(100%,360px);padding:24px;text-align:center}
.connect .pane img{width:96px;height:96px;border-radius:50%;box-shadow:var(--edge-night),var(--night-lift)}
.connect .pane p{margin:0;font:400 var(--t4)/22px var(--sans);color:var(--night)}
.connect .pane .scanbar{width:120px;margin-top:2px}
/* the small way out for a shared device (the owner's ruling, 2026-09-30): one quiet link under the bar, the night's muted ink */
.connect .pane a.out{margin-top:18px;font:400 var(--t2)/18px var(--sans);color:var(--night-mute);text-decoration:underline;text-underline-offset:3px}.connect .pane a.out:hover{color:var(--night)}
@media (prefers-reduced-motion:reduce){.skel .sk::after{content:none}.scanbar i{animation:none;width:100%;opacity:.35}.veil.connect{animation:none}.card[data-landed] > *,.card[data-landed] > .body > *{animation:none}}
/* a row whose value holds two acts (a device asks: the quiet decline and Approve): on one line, wrapping on a phone; held while its act runs */
.kv .v.acts{white-space:normal;display:inline-flex;align-items:center;justify-content:flex-end;gap:8px 18px;flex-wrap:wrap}
.kv.held .go{opacity:.5;pointer-events:none}
/* the busy moment standing in a row's value keeps to the row */
.kv .v .moment.busy{margin:0;min-width:120px;text-align:left}
/* ── MONEY ── */
[data-pay]{margin-top:16px;scroll-margin-top:12px}
[data-pay] [data-block]>.fields{margin-top:0}
[data-pay] .kv{padding:8px 0}
[data-pay] .ways{margin-top:14px}
[data-pay] .go.wide{margin-top:16px}
/* THE PROCESSOR'S FRAME IN THE DESIGN'S WELL (the payskin lane, 2026-09-16, the owner: "is payment nice looking on all devices and robust and professional?"): the box is the pay well (the well's tone, 20 px corners, no ring) under the tree's own kicker; the vendor's form stands on the well with its 24 px gutter shown as the well's 20 (a phone 14) and its top at 14 (a phone 10); the SDK's root fills the frame's height (its own document, 334 with the heading blanked, given 358 so one of the vendor's hint lines keeps the Pay button in view, plus the chrome read from the drawn iframe), 640 for a bank's challenge; the box at 440 under the cover, then grown to the frame */
.paycard{position:relative;box-sizing:content-box;margin-top:0;padding-top:14px;background:var(--raised);box-shadow:var(--raised-edge);border-radius:var(--r4);overflow:hidden;container-type:inline-size;height:440px}
.paycard .payframe{--sdk-doc:358px;--sdk-form:calc(var(--sdk-doc) + var(--sdk-chrome,37px));height:var(--sdk-form);margin:0 -4px}
.paycard.challenge .payframe{height:max(var(--sdk-form),640px)}
.paycard .payframe iframe{display:block;width:100%;border:0}
.paycard .cover{position:absolute;inset:0;display:grid;align-content:center;gap:10px;padding:0 20px;background:var(--raised)}
/* THE VENDOR'S CHROME IN OUR DOCUMENT: the SDK draws three things around its frame in our page, an empty amount-and-cancel row (both hidden by the options), the box holding the frame, and the Secured by Peach Payments line; the row is collapsed and the line wears our small mute face with the padlock in the same ink (important, since the vendor's utilities are important in adopted sheets); the form inside the frame is the vendor's own page on another origin, so only its theme (our ink, our face's name) reaches it */
.payframe [data-testid=checkout-root]>div>[px-gutter]{display:none}
.payframe [data-testid=checkout-secured-by-peach]{font:400 var(--t1)/17px var(--sans)!important;color:var(--mute)!important;padding:4px 0 16px!important;box-shadow:none!important}
.payframe [data-testid=checkout-secured-by-peach] span{color:var(--ink-soft)!important;font-weight:500!important;margin:0 0 0 4px!important}
.payframe [data-testid=checkout-secured-by-peach] svg{width:12px;height:12px;margin-left:5px}
.payframe [data-testid=checkout-secured-by-peach] svg path{fill:currentColor}
.paycard .cover .moment{margin:0;min-height:0;padding:0}
.paycard .cover p{margin:0;min-height:18px}
/* THE SLOW MOMENT (notice.js Slow, 2026-10-03): the line, the bar and, after 20 s, the acts under it; the height held from the first instant so the acts' row moves nothing */
.moment.slow{min-height:104px}
.moment.slow .taps{display:flex;flex-wrap:wrap;gap:10px 12px;margin-top:14px}
.paycard .cover .moment.slow{min-height:0}
.uplink .qr .moment,.sheet .qr .moment{margin:0;min-height:0;width:100%;align-self:center}
.paycard.up .cover{display:none}
.paycard:not(.up) .payframe{visibility:hidden}
/* THE PROCESSING MOMENT (the paywait lane, 2026-09-16, the owner: "when you pay it just disappears for minutes or seconds with nothing showing while it is processing, and you never know when done"): from the tap on Pay to the answer the pay box is one moment in its place, the well with the head, the line that changes with time, the door's scan bar and the amount; the vendor's frame stays mounted under it, collapsed (held) while its redirect pages fly and shown under the moment when a bank's page stands (the challenge); the paid well answers with its tick drawn in */
.paywait{display:grid;gap:10px;padding:22px 20px 20px;background:var(--raised);box-shadow:var(--raised-edge);border-radius:var(--r4)}
.paywait .head{font:500 var(--t5)/24px var(--sans);letter-spacing:-.01em;color:var(--ink)}
.paywait .meta{margin:0}
.paywait .scanbar{margin:4px 0 2px}
.paywait .facts{color:var(--ink-soft)}
.paywait + .paycard.challenge{margin-top:10px}
.paycard.held{height:0!important;padding:0;visibility:hidden}
.said.paid .tick{flex:none;align-self:flex-start;display:grid;place-items:center;width:22px;height:22px;border-radius:50%;background:var(--ok);animation:tick-pop .5s cubic-bezier(.2,.8,.2,1) both}
.said.paid .tick svg{width:14px;height:14px;fill:none;stroke:#fff;stroke-width:2.6;stroke-linecap:round;stroke-linejoin:round;stroke-dasharray:24;animation:tick-draw .45s .2s cubic-bezier(.2,.8,.2,1) both}
@keyframes tick-pop{from{transform:scale(.4);opacity:0}60%{transform:scale(1.12);opacity:1}to{transform:scale(1);opacity:1}}
@keyframes tick-draw{from{stroke-dashoffset:24}to{stroke-dashoffset:0}}
.flow .ln.off .k,.flow .ln.off .num,.flow .ln.sub .k,.flow .ln.sub .num{color:var(--mute)}
.flow .coupon{display:flex;gap:16px;align-items:end;margin-top:12px}
.flow .coupon .fld{flex:1}
.flow .coupon .go.quiet{padding-bottom:12px}
.flow [data-fee]>.go.quiet{margin-top:14px}
.sheet-doc{margin-top:14px;overflow:hidden}
.sheetframe{display:block;width:100%;border:0;background:#fff;border-radius:var(--r3)}
.kv.inv.mute .k,.kv.inv.mute .v{color:var(--mute)}
.kv.inv .a:empty{min-height:22px}
.flow .later{margin-top:18px}
/* ── ENROL FLOW ── */
/* a part the lane does not offer is not drawn; the identity line's one tap stands at the row's end; the foot's acts stay on the Back round's line (the doors' .acts rule adds a top margin) */
.rail .tk[hidden]{display:none}
/* the terms line under a door's box: one small line; Google's reCAPTCHA notice, where the SMS way needs it, a fainter line under it */
.door .terms,.enroldoor .terms{font-size:var(--t1);line-height:15px;margin:14px 0 0;color:var(--mute)}
.door .terms a,.enroldoor .terms a{color:inherit}
/* ── SETTINGS ── */
.flow .body .acts{display:flex;flex-wrap:wrap;align-items:center;gap:10px 22px}
/* the first-open offer: the shell's sheet glass on the veil, risen from the foot on a phone, centred on a desk */
.veil.offer{z-index:21;align-items:flex-end;padding:0 10px calc(14px + env(safe-area-inset-bottom))}
.offer .gt{margin:0 0 6px}
/* a sheet's title, one rule (2026-09-21: the announcement carried a copy); kept for the owner's ruling: the points sheet's is 500 20/26 (.popup .hd .nm) */
.offer .nm,.announce .title,#dtitle,#pb-title{font:600 var(--t7)/26px var(--sans);letter-spacing:-.02em;color:var(--ink)}
/* THE SHEET'S HEAD, ONE RULE (2026-09-26, the owner: "2 Notifications titles"): one title and one close round on its line, the round's face at the paper's padding (--sheet-pad) from its top and right edges; the notifications and menu sheet's head (ui.css THE SHEET), the picker's, the confirm's, the offer's .x */
:is(.sheet > .hd,html[data-fill=on] .drawer .dhead){display:flex;align-items:center;justify-content:space-between;gap:var(--row);width:100%}
html[data-fill=on] .drawer .dhead{padding:var(--sheet-pad) var(--sheet-pad) 6px}
/* THE CHAT'S ONE CLOSE ON A PHONE AND A TABLET, the one exception to the sheet's head (the owner, 2026-09-26: "this Support title above the blue looks bad, exceptionally just keep the top, the logo part"): no title band; the vendor's navy head is the sheet's top, and one white X on it at the top right, a 44 px tap (chat.js headOf) */
/* THE CHAT'S ONE CLOSE (chat.js headOf, 2026-09-29: one control on every page at every size): the white X over the vendor's navy head at its right, in the vendor's holder, wherever the panel stands (the sheet, a door's rim panel, the desk's column) */
html .woot-widget-holder > .chatshut{--face:var(--hit);display:grid;place-items:center;position:absolute;top:12px;right:10px;z-index:1;width:var(--hit);height:var(--hit);padding:0;border:0;border-radius:50%;background:none;color:white;cursor:pointer;-webkit-tap-highlight-color:transparent}
html .woot-widget-holder > .chatshut:hover{background:rgba(255,255,255,.12)}
html .woot-widget-holder > .chatshut:focus-visible{outline:2px solid white;outline-offset:-4px}
html .woot-widget-holder > .chatshut svg{width:20px;height:20px;fill:none;stroke:currentColor;stroke-width:2;stroke-linecap:round}
.offer .fx{display:block;margin:6px 0 0;font:400 var(--t3)/20px var(--sans);color:var(--mute);text-wrap:pretty}
.offer .why{display:flex;gap:6px;align-items:flex-start;margin:10px 0 0;font:500 var(--t2)/18px var(--sans);color:var(--warm);text-wrap:pretty}
.offer .why::before{content:'';flex:none;width:16px;height:16px;margin-top:1px;background:currentColor;-webkit-mask:var(--glyph-i) center/16px no-repeat;mask:var(--glyph-i) center/16px no-repeat}
/* THE SHEET'S ACTION ROW, ONE RULE (the owner, 2026-10-15: "the Install button on the 'install the app' popup is not centred"; before it the offer's foot was a right-aligned row on a desk and, on a phone, a stretched pill with its word at the left and its round at the right (space-between), so a pill with no round, Install, stood with its word at the left): every sheet's action row (the offer sheet's foot, push.js Ask; the confirm dialogs' foot, parts.js Confirm, List and Camera, asks.js, subjects.js, uploadlink.js, stages.js; the announcement's foot, popup.js; the install guide's acts, push.js Guide) lays its acts side by side in equal shares, each act's words centred in its share, so one act alone fills the row and stands centred; a pill with no round wears the pill's padding on both sides, so its word is truly centred; on a phone the acts stack, the filled act on top (the markup puts the quiet acts first, the filled act last). A new sheet's action row is one of these or it is a defect */
:is(.offer .foot,.announce .foot,.offer .guide .acts){display:flex;flex-wrap:wrap;align-items:center;justify-content:center;gap:12px 18px;margin-top:20px}
:is(.offer .foot,.announce .foot,.offer .guide .acts) > .go{flex:1 1 0;margin:0} /* the filled act wears THE WIDE ACT (below .go.wide): the word centred, the round pinned right */
:is(.offer .foot,.announce .foot,.offer .guide .acts) > .go.quiet{justify-content:flex-start} /* the quiet act at the left of its share, on the pill's line (the owner, 2026-10-15 14:5x SAST: the adviser's "Already an adviser? Log in" beside Sign up); alone in the row, centred */
:is(.offer .foot,.announce .foot,.offer .guide .acts) > .go.quiet:only-child{justify-content:center}
.offer .foot .go i svg{width:18px;height:18px;stroke:currentColor;fill:none;stroke-width:1.6;stroke-linecap:round;stroke-linejoin:round}
/* the chat's one close over the desk's panel: the paper round (chat.js places it from 1000 px; under it the sheet's head holds it) */
.offer .x{position:absolute;top:var(--sheet-pad);right:var(--sheet-pad);z-index:1}
.uplink .gt{margin-right:48px}
.uplink .qr{display:grid;place-items:center;width:212px;max-width:100%;aspect-ratio:1;margin:18px auto 0;padding:14px;border-radius:var(--r4);background:#fff;box-shadow:var(--ring);color:var(--ink)}
.uplink .qr svg{width:100%;height:100%;display:block}
.uplink .qr.minting,.uplink .qr.refused{background:var(--wait);box-shadow:var(--wait-edge)}
.uplink .qr.expired svg{opacity:.14}
.uplink .qr .tick{display:grid;place-items:center;width:56px;height:56px;border-radius:50%;background:var(--ok);animation:tick-pop .5s cubic-bezier(.2,.8,.2,1) both}
.uplink .qr .tick svg{width:28px;height:28px;fill:none;stroke:#fff;stroke-width:2.6;stroke-linecap:round;stroke-linejoin:round;stroke-dasharray:24;animation:tick-draw .45s .2s cubic-bezier(.2,.8,.2,1) both}
.uplink .fx.got{color:var(--ok);font-weight:500}
.uplink .lk{display:flex;align-items:center;justify-content:center;gap:14px;margin-top:14px;min-width:0}
.uplink .lk code{min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;font:400 var(--t2)/18px var(--mono);color:var(--mute)}
.uplink .lk .go.quiet{flex:none}
/* THE PHONE'S PAGE (upload.html, /upload/): the door's card with no sign-in, otty small; the take control as one big well with the camera glyph, the file's preview, Send as the wide pill, the track, the sent moment with the count, or the one line */
.door.upl .otty{max-height:220px;align-self:end}
.upl .card{min-height:0}.upl .card .who{flex:none}
.upl .who > .gt:first-child{margin-top:0}
.upl .over{margin:0;font:400 var(--t5)/24px var(--sans);color:var(--ink-soft);text-wrap:pretty}
.upl .take{display:grid;place-items:center;gap:8px;margin-top:22px;padding:30px 16px 26px;border-radius:var(--r4);background:var(--raised);box-shadow:var(--raised-edge);cursor:pointer;text-align:center;color:var(--ink);font:500 var(--t5)/22px var(--sans);position:relative;--lift-to:var(--ring-on)}
.upl .take svg{width:46px;height:46px;fill:none;stroke:currentColor;stroke-width:1.5;stroke-linecap:round;stroke-linejoin:round;color:var(--ink-soft)}
.upl .take small{display:block;margin-top:2px;font:400 var(--t2)/18px var(--sans);color:var(--mute);text-wrap:pretty}
.upl .or{margin-top:12px;text-align:center}
.upl .prev{margin-top:20px;border-radius:var(--r4);overflow:hidden;background:var(--raised);box-shadow:var(--raised-edge);display:grid;place-items:center}
.upl .prev img{display:block;max-width:100%;max-height:min(46vh,420px);object-fit:contain}
.upl .prev .pdf{display:grid;place-items:center;gap:8px;padding:36px 16px;color:var(--mute);font:400 var(--t3)/20px var(--sans)}
.upl .prev .pdf svg{width:40px;height:40px;fill:none;stroke:currentColor;stroke-width:1.5;stroke-linecap:round;stroke-linejoin:round}
.upl .fn{margin-top:10px;font:400 var(--t3)/20px var(--sans);color:var(--mute);overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.upl .notice{margin:14px 0 0}
.upl .again{margin-top:14px;text-align:center}
.upl .said .tick{margin-top:1px}
.upl .count{margin-top:8px;font:400 var(--t2)/18px var(--sans);color:var(--mute)}
/* the install guide: an iPhone or an iPad in a browser tab installs first; the three steps in the well, under the offer's title or the settings row */
.guide b{display:block;font:500 var(--t4)/20px var(--sans);color:var(--ink)}
/* THE SWITCH, one for every on/off (parts.js Switch; the owner 2026-09-27: the oval "not aligned with other buttons"). The button is the track, a pill 44 by 26 whose radius is half its height like every pill (the hit rule's clear layer grows its tap to 44 by 44); off the soft well with a hairline, on the ink; the knob is its ::before, 20 px white at a 3 px inset, a soft shadow, a 150 ms slide; in a row it stands 44 px tall, its hit's room; the ring for the keyboard alone; disabled faded */
.switch{--face:26px;width:var(--hit);height:var(--face);margin-block:var(--rb);padding:0;border-radius:var(--pill);background:var(--track-fill);box-shadow:var(--track-edge);cursor:pointer;vertical-align:middle;flex:none;-webkit-tap-highlight-color:transparent;transition:background .15s,box-shadow .15s}
.switch::before{content:'';position:absolute;top:3px;left:3px;width:20px;height:20px;border-radius:50%;background:#fff;box-shadow:var(--lift-knob);transition:transform .15s cubic-bezier(.2,.8,.2,1)}
.switch[aria-checked="true"]{--rc:var(--ink);background:var(--ink)}
.switch[aria-checked="true"]::before{transform:translateX(18px)}
.switch:disabled{opacity:.45;cursor:default}
.switch.busy{opacity:.6;pointer-events:none}
.switch:focus-visible{outline:2px solid var(--sky-ink);outline-offset:2px}
@media (prefers-reduced-motion:reduce){.switch,.switch::before{transition:none}}
/* a switch whose state is not known yet holds blank: the track without its knob, never off then on */
.switch:not([aria-checked])::before{visibility:hidden}
.kv:has(>.v>.switch){align-items:center}
.kv .k small.warn{color:var(--warm);font-weight:500}
/* ── MARKS AND REWARDS ── */
/* THE MARKS ROWS (marks.js Mk; the owner, 2026-09-27: "this needs a redesign, minimally and elegantly"): the Tasks row with two columns, the title with its line and the figure over its word; the head the figure with its counts */
.trow.mrow{grid-template-columns:minmax(0,1fr) auto}.trow.mrow:has(> .way){grid-template-columns:minmax(0,1fr) auto auto}
div.trow.mrow:hover{background:none}.trow.mrow > .way{color:var(--faint)}
.tdue b.mu{font-weight:400;color:var(--ink-soft)}
.mhead{display:flex;flex-wrap:wrap;align-items:baseline;gap:4px 12px;margin:18px 0 6px}.mhead b{font:600 var(--t8)/32px var(--sans);letter-spacing:-.02em;color:var(--ink)}.mhead span{font:400 var(--t2)/18px var(--sans);color:var(--ink-soft)}
.mrows > .trow:first-child{border-top:0}
/* THE TERM MARKS (marks.js TermMarks; the owner, 2026-10-12: the moderated term marks alone, never Canvas): the small line under the head, a subject's block with its term mark row, a part row (a group or a skill) and its task rows one step in */
.mnote{margin:4px 0 0;font:400 var(--t2)/18px var(--sans);color:var(--mute)}
.who .mnote + .tabs{margin-top:16px}
.msub + .msub{margin-top:var(--kick)}
.msub > .trow:first-child{border-top:0}
.msub .mpart .ttl{font-size:var(--t3)}
.msub .mtask{margin-left:16px}
.msub .mtask .ttl{font-weight:400;font-size:var(--t3)}
/* THE PAGER at a page's foot (study.js Leg; the owner, 2026-09-29, over two stacked "Previous … Open ›" rows, then over the first pager's slabs and dots: "not aligned, the dots are ugly, the sizes are bad"): the module's slim segmented track (the rail's 4 px channel, one segment an item, done in the sky, this item's with the halo) and the small caps "Term 3 · 2 of 5 done" on one line; then one row of the control height in the content column: "‹ Previous · name" the quiet text control with the small round, "Next · name ›" the foot's own pill at its content width, ink when it leads, pale otherwise; the two wrap to their own rows on a phone, never stretched */
.ptrack{display:flex;align-items:center;gap:12px;margin-top:var(--lead)}
.pbar{display:flex;flex:none;gap:4px;width:min(160px,40%);height:4px}
.pbar i{flex:1;border-radius:var(--pill);background:var(--track-fill);box-shadow:var(--track-edge)}
.pbar i.on{background:var(--sky);box-shadow:none}
.pbar i.now{box-shadow:var(--halo)}
.pcap{font:500 var(--t1)/16px var(--sans);letter-spacing:.06em;text-transform:uppercase;color:var(--mute);white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
.pager{display:flex;flex-wrap:wrap;justify-content:space-between;align-items:center;gap:10px 16px;min-height:var(--hit);margin-top:10px}
.pager .next{margin-left:auto;justify-self:auto}
.pager span{min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;max-width:36ch}
.pager small{font:inherit;color:var(--mute)}
.pager .go.next small{color:var(--night-mute)}.pager .go.pale.next small{color:var(--mute)}
.pager .prev{font-size:var(--t4);gap:10px}
.pager .prev .way{width:var(--round-s);height:var(--round-s);border-radius:50%;box-shadow:var(--quiet-edge)}
.pager .prev:hover .way{transform:translateX(-2px)}
.way.back::after{transform:scaleX(-1)}
/* THE POINTS DOOR under Otty: one plate on the field's glass at the bar's width, the label and the facts as targets that open the sheet on their cell; none reads its one line; on a phone the plate takes its own line under him and his words */
.otty .door{padding:12px 14px 14px;border-radius:var(--r4);background:var(--isle);backdrop-filter:var(--isle-blur);-webkit-backdrop-filter:var(--isle-blur);box-shadow:var(--edge-night);color:#fff}
.door>.k,.door .yc>.k,.today .k{display:block;font:500 var(--t1)/16px var(--sans);letter-spacing:.08em;text-transform:uppercase;color:var(--night-faint)}
/* ── PROSE ── */
/* a Canvas body through the one pass (prose.js): the classes the pass writes that the design's prose rules above do not carry; the tokens are the design's */
.kv .k small .warm,.ask .t small .warm{color:var(--warm);font-weight:500}
.gt .n{font:400 var(--t1)/18px var(--sans);font-variant-numeric:var(--tab);color:var(--faint);margin-left:8px}
a.kv.it,.who a.kv{text-decoration:none;color:inherit}
.kv.it.open .k{color:var(--ink)}
.prose>*{min-width:0;max-width:100%}
.prose :is(h1,h2,h3,h4,h5,h6){margin:6px 0;font-weight:700;line-height:1.5}
.prose h1{font-size:38px}
.prose h2{font-size:32px}
.prose h3{font-size:24px}
.prose h4{font-size:18px}
.prose h5{font-size:16px}
.prose h6{font-size:12px}
.prose :is(p,td,th,div)>.sm:only-child,.prose :is(p,div).sm,.prose figcaption{font:400 var(--t1)/17px var(--sans);color:var(--mute)}
.prose :is(strong,b){font-weight:700}
.prose :is(sup,sub){font-size:.75em;line-height:0;position:relative;vertical-align:baseline}
.prose sup{top:-.35em}
.prose sub{top:.2em}
.prose .screenreader-only{position:absolute;width:1px;height:1px;overflow:hidden;clip:rect(0 0 0 0)}
.prose>:first-child{margin-top:0}
.prose>:last-child{margin-bottom:0}
/* the arrangement: the section mark beside the first lines, the levels, the pause */
/* AN ICON BEFORE WORDS IS A ROW (2026-09-28, prose.js iconRows): the icon left at the mark's size, the words stacked right on its centre, never a float with words under it */
.prose hr{margin:20px 0;border:0;border-top:1px solid #e8eaec}
.prose :is(ul,ol){padding:0;margin:0 0 6px 25px}
/* a definition list (2026-09-28: the browser's 40 px indent stood): the list's own gap, its term bold, its lines at the list indent */
/* A CANVAS BODY WEARS OUR LIST LOOK (the learner audit, 2026-09-25: its square bullets and the browser's triangles stood): our round marker in the mute ink, a fold our one small chevron, a teacher's font colour and face never reach the words */
.prose summary{list-style:none;display:flex;align-items:center;cursor:pointer;font-weight:500}
/* ONE RHYTHM FOR A CANVAS BODY (the owner, 2026-09-28: "spacing is bad anyway"; prose.js flatten puts every block of a fold in one flow): a fold is a list row, its head at the row's padding with a hairline between two folds, its blocks 8 px apart and indented under the head, the open fold closed by the row's padding; a meta line and its pill row one group; the page's module line and its Previous and Next a quiet foot after the lead's gap */
.prose summary{padding:var(--row) 0}
.prose>details+details{margin-top:0;border-top:1px solid var(--line)}
.prose details>:not(summary){margin-top:8px;padding-left:var(--chip-x)}
.prose details>summary+*{margin-top:0}
.prose details[open]{padding-bottom:var(--row)}
.prose .sm+.chips{margin-top:8px}
.prose .chips a.chip{display:inline-flex;align-items:center;text-decoration:none}
.who>.prose~.gt{margin-top:var(--lead)}
/* links: the underline, the outside mark glued to the last word, the Zoom link with its glyph (one look, alone or in a sentence; 2026-09-21: a lone one was named `btn sm zoom`, and sm made it small print), the gap of a quiz blank */
.prose a:not(.ccard):not(.pic):not(.chip):not(.go){color:var(--ink);text-decoration:underline;text-decoration-color:var(--faint);text-decoration-thickness:1px;text-underline-offset:3px}
.prose a:not(.ccard):not(.pic):not(.chip):not(.go):hover{text-decoration-color:currentColor}
.prose a[target='_blank']:not(.ccard):not(.zoom):not(.go)::after{content:'\2060';display:inline;padding-left:15px;background:currentColor;-webkit-mask:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='black' stroke-width='1.8' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M14 4h6v6M20 4l-9 9M18 13v5a2 2 0 0 1-2 2H6a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h5'/%3E%3C/svg%3E") right center/12px 12px no-repeat;mask:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='black' stroke-width='1.8' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M14 4h6v6M20 4l-9 9M18 13v5a2 2 0 0 1-2 2H6a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h5'/%3E%3C/svg%3E") right center/12px 12px no-repeat}
.prose a.zoom{display:inline-flex;align-items:center;gap:6px;text-decoration:none;font-weight:500}
/* A HELD WAY IN is muted wherever it stands: a Canvas body, a lesson's cell, the Timetable's well (2026-09-21: the body's alone was written) */
.prose .zoom.hold,.tt .cell .mini.hold,.tt .cell .mini.follows,.ask [data-hold]{color:var(--mute);font-weight:500}
/* THE CARD'S OWN WAYS (2026-09-29; 2026-10-06 ONE CONTROL PER STATE): the well's act carries the lesson's glyph before its word (live, or play on a recording); a recording not linked yet stands as the muted word alone in the cell (.mini.follows), no glyph (~~before the join window the way stood muted with the moment it opens, .mini.soon and [data-soon]~~) */
.ask .go .pglyph{flex:none;width:16px;height:16px;fill:none;stroke:currentColor;stroke-width:1.6;stroke-linecap:round;stroke-linejoin:round;margin-right:-4px}
/* THE LIVE WAY IS THE CARD'S ONE CALL (the owner, 2026-09-29 09:5x: "make the join icon a bit more visible to not miss"): in the join window the cell's Join is the primary pill in the button's own tokens (the ink fill, the white word, the pill radius, the bar's lift; never an outline, never a grey), the card's title line tall, with the live dot (the sky on its glow, the Today pane's own dot); the well's Join wears the same dot and stretches on a phone (the .go rule under 600 px); a recording's Watch stays a quiet glyph; the muted Join from stays quiet */
.tt .cell .mini.join{--face:26px;min-height:26px;padding:0 12px 0 10px;gap:6px;margin-top:14px;background:var(--ink);color:#fff;font-weight:600;border-radius:var(--pill);box-shadow:var(--lift-bar)}
.tt .cell .mini.join::before,.ask .go[data-live]::before{content:'';flex:none;width:8px;height:8px;border-radius:50%;background:var(--sky);box-shadow:var(--glow)}
.tt .cell .mini.join svg{stroke:#fff}
.prose a.zoom .i{width:18px;height:18px;fill:none;stroke:currentColor;stroke-width:1.6;stroke-linecap:round;stroke-linejoin:round}
/* A FILE LINK IN A SENTENCE (the owner, 2026-09-29: "download the script here" drew a card in the sentence): the teacher's words keep the link's underline, the small file glyph stands before them in the line, the file's name quiet after a bare word; the outside mark follows as on any link that opens a new tab */
.prose a.file{text-decoration:none}
.prose a.file .i{display:inline-block;width:1em;height:1em;vertical-align:-.15em;margin-right:.2em;fill:none;stroke:currentColor;stroke-width:1.6;stroke-linecap:round;stroke-linejoin:round}
.prose a.file .w{text-decoration:underline;text-decoration-color:var(--faint);text-decoration-thickness:1px;text-underline-offset:3px}
.prose a.file:hover .w{text-decoration-color:currentColor}
.prose a.file .fname{color:var(--mute)}
.prose .gap{display:inline-block;min-width:6ch;border-bottom:1px solid var(--ink)}
/* pictures: the width attribute the cap, never a stretch; a strip of two or more; the tap */
/* THE PICTURE STANDS IN ITS AUTHOR'S LINE (the owner, 2026-10-01: centred in Canvas, left here): inline, so the block's text-align places it as Canvas does; never wider than the column */
.prose img{max-width:100%;height:auto;vertical-align:middle}
.prose img:not(.emoji){max-height:var(--fit);object-fit:contain}
/* an emoji picture (prose.js isEmoji) sits in its sentence at the text's size; its box is set here, so the line holds its place before the picture loads */
.prose img.emoji{display:inline-block;width:1.25em!important;height:1.25em!important;max-width:none;margin:0 .06em;vertical-align:-.26em;border-radius:0;object-fit:contain}
/* an equation (prose.js maths): the engine's picture in its sentence until the converter sets it in MathML; a display equation on its own line */
.prose img.equation{display:inline-block;width:auto;max-width:100%;max-height:none;margin:0 .15em;vertical-align:middle;border-radius:0}
.prose .tex[data-display]{display:block;margin:8px 0;white-space:pre-wrap}
.prose figcaption{margin-top:4px}
.prose a.pic{display:block;cursor:zoom-in}
/* tables: the author's (2026-10-01: its widths, heights, alignments and borders stand); the quiet list where the teacher drew no border, cell borders where the table's border attribute asks them; a run with no space breaks */
.prose .scroll{max-width:100%;scrollbar-width:thin;-webkit-overflow-scrolling:touch}
.prose .scroll :is(td,th){overflow-wrap:normal;white-space:normal}
.prose .scroll .run{overflow-wrap:anywhere}
/* code, quotes, maths */
.prose pre{font:var(--t2)/20px var(--mono);background:var(--tint);box-shadow:var(--tint-edge);border-radius:var(--r2);padding:12px 16px}
.prose code{font:.92em/1 var(--mono);background:var(--tint);padding:1px 5px;border-radius:var(--r1)}
.prose pre code{font:inherit;background:none;padding:0}
.prose blockquote{padding:0 0 0 15px;margin:0 0 20px;border-left:5px solid #e8eaec}
.prose math{font-size:inherit;max-width:100%;vertical-align:middle}
.prose math[display='block']{display:block;margin:8px 0;text-align:left}
/* the media card: a frame in its own ratio (the pass writes it), a native player at its own size, the off card with its sentence, the caption with the host and the title */
.prose .media .frame>iframe{position:absolute;inset:0;width:100%;height:100%;border:0;display:block}
/* a YouTube frame's poster until the tap (prose.js facade): the picture fills the frame's box, the play glyph over it; the poster is no body picture (no lightbox, no fit) */
.prose .media .frame>.poster{position:absolute;inset:0;width:100%;height:100%;margin:0;padding:0;border:0;border-radius:0;background:#000;color:#fff;cursor:pointer;display:block}
.prose .media .frame>.poster img{width:100%;height:100%;max-height:none;object-fit:cover;display:block;margin:0;border-radius:0}
.prose .media .frame>.poster .pglyph{position:absolute;left:50%;top:50%;width:64px;height:64px;margin:-32px 0 0 -32px;padding:14px;border-radius:50%;background:rgba(0,0,0,.55);fill:none;stroke:currentColor;stroke-width:1.6;stroke-linecap:round;stroke-linejoin:round;box-shadow:none}
.prose .media .frame>.poster:hover .pglyph,.prose .media .frame>.poster:focus-visible .pglyph{background:rgba(0,0,0,.75)}
/* the poster-less facade (prose.js facade, 2026-10-14): a Canvas clip, a body's own player or a framed video host with no poster stands as the dark frame with the play glyph until the tap, the ink fill the player's own box takes below; audio as the short raised bar the player takes, the glyph on its ink round */
.prose .media .frame>.poster.bare{background:var(--ink)}
.prose .media .frame>.poster.audio{position:relative;height:54px;background:transparent;color:var(--ink)}
.prose .media .frame>.poster.audio .pglyph{width:40px;height:40px;margin:-20px 0 0 -20px;padding:9px;background:var(--ink);color:#fff}
.prose .media .frame>.poster.audio:hover .pglyph,.prose .media .frame>.poster.audio:focus-visible .pglyph{background:var(--ink-soft)}
.prose .media .frame.paper{aspect-ratio:auto;width:100%;height:max(var(--paper-min),var(--fit))}
.prose .media .frame:has(>video),.prose .media .frame:has(>audio),.prose .media .frame:has(>.poster.audio){aspect-ratio:auto;width:min(100%,var(--w,100%));background:var(--ink)}
.prose .media .frame>video{display:block;width:100%;height:auto;max-height:var(--fit);aspect-ratio:auto 16/9}
.prose .media .frame:has(>audio),.prose .media .frame:has(>.poster.audio){background:var(--raised);box-shadow:var(--raised-edge);padding:12px}
.prose .media .frame>audio{display:block;width:100%}
.prose .media figcaption .host{color:var(--ink-soft);font-weight:500}
/* the file card and the link card: the word Open at the right is the design's; a card in a cell tighter, without its glyph */
.prose a.ccard .c-word .link{font:inherit;color:inherit;text-decoration:none}
.prose td a.ccard{min-height:44px;padding:8px 12px}
.prose td a.ccard .glyph{display:none}
/* a file on the page: the paper (a PDF without the browser's toolbar, filling the sheet), a picture, a video, a sentence for the rest */
.prose .paper{display:block;width:100%;border-radius:var(--r3)}
.prose object.paper,.prose iframe.paper{height:max(var(--paper-min),var(--fit));border:1px solid var(--line)}
.prose img.paper{max-width:100%;height:auto}
/* the lightbox: a native dialog, the ink behind, the picture at its own size and at most the viewport, its box sliding when wider than the screen, the alt under it, the close bubble at the top right */
.lightbox{border:0;padding:0;margin:0;width:100vw;height:100vh;height:100dvh;max-width:100vw;max-height:100vh;max-height:100dvh;background:transparent;color:#fff;display:grid;grid-template-rows:minmax(0,1fr) auto;place-items:center}
.lightbox:not([open]){display:none}
.lightbox::backdrop{background:rgba(35,36,103,.92)}
.lightbox .pane{max-width:100vw;max-height:100%;overflow:auto;touch-action:pan-x pan-y pinch-zoom;scrollbar-width:none}
.lightbox .pane img{display:block;max-height:calc(100vh - 64px);max-height:calc(100dvh - 64px)}
.lightbox .alt{margin:8px 16px 16px;font:400 var(--t2)/18px var(--sans);color:var(--night-mute);text-align:center;overflow-wrap:anywhere}
.lightbox .close{position:fixed;top:12px;right:12px;width:44px;height:44px;border:0;border-radius:var(--pill);background:rgba(255,255,255,.1);backdrop-filter:blur(14px);--rc:var(--isle-line);box-shadow:var(--ring);color:#fff;display:grid;place-items:center;cursor:pointer}
.lightbox .close .i{width:20px;height:20px;fill:none;stroke:currentColor;stroke-width:1.8;stroke-linecap:round}
/* ── TIMETABLE ── */
/* the head: the shown range with the term's week beside it, the week's arrows and This week at the row's end (dimmed while the shown week holds today); the columns follow the week (a weekend day only with a lesson) */
.gt.tthead{display:flex;align-items:center;justify-content:space-between;gap:8px 16px;flex-wrap:wrap}
.tthead small{font:400 var(--t1)/18px var(--sans);color:var(--faint);margin-left:8px}
.tthead .nav{display:inline-flex;align-items:center;gap:4px}
.tthead .arrow{--face:var(--chip-h-sm);--face-i:var(--face);width:var(--face);height:var(--face);border-radius:50%;display:inline-grid;place-items:center;color:var(--ink);box-shadow:var(--quiet-edge)}
.tthead .arrow:hover{background:var(--hover)}
.tthead .arrow svg{width:15px;height:15px;fill:none;stroke:currentColor;stroke-width:1.8;stroke-linecap:round;stroke-linejoin:round}
.tthead .word{--face:18px;font:500 var(--t2)/18px var(--sans);color:var(--ink);text-decoration:none;padding:0 8px}
.tthead .word.on{color:var(--faint);pointer-events:none}
/* ── TASKS ── */
/* a row that opens (a file, an entry) is the whole link; a written answer's box */
a.kv{text-decoration:none;color:inherit}
/* THE TASKS PAGE (tasks.js TasksView; the owner approved the Courses and Tasks proposal, 2026-09-27: "make sure everything is for a reason and works elegantly"; "think designer, and lux"): Next up a raised card on the sky's breath, its kind round in the sky, the facts as quiet chips (the reward on the spark's tint) and the ONE ink pill; the subject chips raised, the picked one in the ink; Catch up ONE waiting card (no red), the sky's when open; a row the kind round (no edge: the owner's QA, 2026-09-27) (warm within 48 hours, quiet when shut, the green tick when done), the title over its course, the day with its state in words, the quiet act on a desk alone (the row is the tap on a phone); a week's label over its run; Done a raised fold; All caught up Ottie's face with the tick. Motion: a fold's body rises 4 px in 180 ms, a pick fades the list in 150 ms, the chevron turns; reduced motion stills them (the rule at the foot) */
.who.work{container-type:inline-size;--up-kind:52px;--up-kind-sm:44px;--catch-h:56px;--row-h:64px}
.tup{margin-top:var(--head)}
.tupc{position:relative;display:grid;grid-template-columns:var(--up-kind-sm) minmax(0,1fr);gap:4px 14px;align-items:center;padding:18px;border-radius:var(--r4);background:radial-gradient(90% 140% at 100% 0%,rgba(var(--sky-rgb),.12),transparent 60%),var(--raised);box-shadow:var(--raised-edge),var(--top-light);color:inherit;text-decoration:none}
.tupc .pkind{width:var(--up-kind-sm);height:var(--up-kind-sm);border-radius:var(--r3)}.tupc .pkind svg{width:22px;height:22px}
.tupc .tb{min-width:0;display:grid}
.tupc .tkick{font:600 var(--t1)/16px var(--sans);letter-spacing:.08em;text-transform:uppercase;color:var(--sky-ink)}
.tupc .tuh{margin-top:2px;font:600 var(--t6)/24px var(--sans);letter-spacing:-.01em;color:var(--ink);overflow-wrap:anywhere}
.tupc .tb small{font:400 var(--t3)/20px var(--sans);color:var(--ink-soft)}
.tfacts{grid-column:1 / -1;display:flex;flex-wrap:wrap;gap:8px;margin-top:10px}
.tfact{display:inline-flex;align-items:center;gap:6px;height:var(--chip-h-sm);padding:0 11px;border-radius:var(--pill);background:#fff;box-shadow:var(--quiet-edge);font:500 var(--t2)/1 var(--sans);font-variant-numeric:var(--tab);color:var(--ink);white-space:nowrap}
.tfact .pglyph{width:14px;height:14px;color:var(--ink-soft)}
.tfact.k-spark{background:var(--tint);box-shadow:var(--tint-edge);color:var(--sky-ink)}
.tupc .pact.t-ink{grid-column:1 / -1;justify-content:space-between;height:var(--goal-act);margin-top:12px;font-size:var(--t4);padding:0 7px 0 22px;order:1}
.tfilters{display:flex;gap:8px;margin:18px calc(var(--gut) * -1) 0;padding:4px var(--gut);scrollbar-width:none}
.tfilters::-webkit-scrollbar{display:none}
.tfilter{--face:var(--chip-h);flex:none;display:inline-flex;align-items:center;gap:8px;height:var(--face);padding:0 14px;border-radius:var(--pill);background:var(--raised);box-shadow:var(--raised-edge);font:500 var(--t2)/1 var(--sans);color:var(--ink);white-space:nowrap;cursor:pointer;transition:background-color .15s,color .15s}
.tfilter:first-child{padding:0 18px}
.tfilter .tdot{flex:none;width:10px;height:10px;border-radius:50%;background:var(--h,var(--ink-soft))}
.tfilter em{font-style:normal;font-weight:400;font-variant-numeric:var(--tab);color:var(--mute)}
.tfilter:hover:not(.on){--rc:var(--sky);box-shadow:var(--ring-on)}
.tfilter.on{--g:var(--h,var(--ink));background:var(--tint);box-shadow:var(--tint-edge);color:var(--ink)}.tfilter.on em{color:var(--ink-soft)}.tfilter:first-child.on{--g:var(--ink)}
.tlist.fade{animation:t-in .15s ease-out both}
@keyframes t-in{from{opacity:0}}
.tcatch{--g:var(--ink);margin-top:18px;border-radius:var(--r4);background:var(--wait);box-shadow:var(--wait-edge)}
.tcatch.on{--g:var(--sky)}
.tch{display:grid;grid-template-columns:auto minmax(0,1fr) auto auto;align-items:center;gap:14px;width:100%;min-height:var(--catch-h);padding:12px 16px;border-radius:inherit;background:none;color:var(--ink);text-align:left;cursor:pointer}
.tico{display:grid;place-items:center;width:40px;height:40px;border-radius:50%;background:#fff;box-shadow:var(--quiet-edge);color:var(--ink-soft)}
.tw{min-width:0}.tw b{display:flex;align-items:baseline;font:600 var(--t5)/22px var(--sans)}.tw small{display:block;font:400 var(--t2)/18px var(--sans);color:var(--ink-soft)}
.tshow{display:none;font:500 var(--t2)/1 var(--sans);color:var(--ink-soft)}
.tchev{color:var(--mute);transform:rotate(90deg);transition:transform .3s}
[aria-expanded=true] > .tchev{transform:rotate(-90deg)}
.tcb{margin:0 16px;padding:2px 0 14px;border-top:1px solid var(--hairline);animation:mod-in .18s ease-out both}
.tfb{animation:mod-in .18s ease-out both}
.twk{display:flex;align-items:center;gap:10px;margin:16px 0 2px;font:500 var(--t1)/16px var(--sans);font-variant-numeric:var(--tab);color:var(--ink-soft)}
.tfold{--g:var(--ink);display:flex;align-items:center;gap:8px;width:100%;margin-top:14px;padding:0 14px;border-radius:var(--r3);background:var(--wait);box-shadow:var(--wait-edge);font:500 var(--t2)/18px var(--sans);color:var(--ink-soft);cursor:pointer}
.tfold > span{flex:1;text-align:left}.tfold .pglyph{width:14px;height:14px}
.tnote{margin:14px 0 0;font:400 var(--t2)/18px var(--sans);color:var(--mute)}
.tnone{display:flex;align-items:center;gap:8px;margin:4px 0 0;font:400 var(--t2)/18px var(--sans);color:var(--mute)}.tnone .pglyph{width:14px;height:14px;color:var(--ok)}
.trow{position:relative;display:grid;grid-template-columns:var(--kind-s) minmax(0,1fr) auto;gap:12px;align-items:center;min-height:var(--row-h);padding:10px 0;border-top:1px solid var(--hairline);color:inherit;text-decoration:none}
:is(.twk,.gt) + .trow{border-top:0}
.trow:hover{background:var(--hover)}
.trow > .ptick{justify-self:center}
.ttl{min-width:0;font:500 var(--t4)/20px var(--sans);color:var(--ink);overflow-wrap:anywhere}
.ttl small{display:block;margin-top:1px;font:400 var(--t2)/18px var(--sans);color:var(--mute);white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
.tdue{display:grid;justify-items:end;text-align:right;font:500 var(--t3)/18px var(--sans);font-variant-numeric:var(--tab);color:var(--ink);white-space:nowrap}
.tdue b{font-weight:500}.tdue b.ok{color:var(--ok);font-weight:600}
.tdue small{display:inline-flex;align-items:center;gap:4px;font:400 var(--t1)/16px var(--sans);color:var(--mute)}.tdue small .pglyph{width:12px;height:12px}
.trow.still .ttl,.trow.still .tdue b{color:var(--ink-soft)}
.tact{display:none}
.tdone{margin-top:var(--head);border-radius:var(--r3);background:var(--raised);box-shadow:var(--raised-edge)}
.tdone .tw b .n{margin-left:8px;font:400 var(--t2)/18px var(--sans);color:var(--mute)}
.tdone .ptick{width:28px;height:28px}
.tclear{margin-top:var(--head);padding:30px 22px 26px;border-radius:var(--r4);text-align:center;background:radial-gradient(100% 80% at 50% 0%,rgba(var(--sky-rgb),.14),transparent 70%),var(--raised);box-shadow:var(--raised-edge),var(--top-light)}
.tface{position:relative;display:inline-block;margin-bottom:12px}
.tface .ptick{position:absolute;right:-6px;bottom:-2px}
.tclear b{display:block;font:600 var(--t7)/28px var(--sans);color:var(--ink)}
.tclear p{margin:6px auto 0;max-width:300px;font:400 var(--t3)/20px var(--sans);color:var(--ink-soft);text-wrap:pretty}
@container (min-width:600px){
  .tupc{grid-template-columns:var(--up-kind) minmax(0,1fr) auto;gap:6px 18px;padding:22px 24px}
  .tupc .pkind{width:var(--up-kind);height:var(--up-kind);border-radius:16px}
  .tupc .tuh{font-size:var(--t7);line-height:28px}
  .tupc .tfacts{grid-column:2 / -1;margin-top:8px}
  .tupc .pact.t-ink{grid-column:3;grid-row:1;margin-top:0;justify-content:flex-start;order:0}
  .tfilters{flex-wrap:wrap;margin-inline:0;padding-inline:0;overflow:visible}
  .tshow{display:inline}
  .trow{grid-template-columns:var(--kind-s) minmax(0,1fr) 116px 112px;gap:14px}
  .tact{display:flex;justify-content:flex-end}
}
.fld textarea{border:0;border-radius:var(--r3);background:#fff;box-shadow:var(--ring);padding:11px 14px;font:400 var(--t5)/22px var(--sans);color:var(--ink);width:100%;resize:vertical;min-height:120px}
/* the hand-in well: the phone's code beside its words, the ways as quiet links on one row, a page with its thumb and its acts, a file with its progress, a finished hand-in's thumbs */
.kv.qr{grid-template-columns:auto minmax(0,1fr);align-items:start}
.kv.qr img{width:120px;height:120px;border-radius:var(--r2);background:#fff}
.kv.qr .lnk{display:block;margin-top:4px;font:400 var(--t2)/18px var(--mono);color:var(--ink);word-break:break-all}
.ways{display:flex;flex-wrap:wrap;gap:8px 22px;margin-top:8px}
/* THE PAGE ROW (2026-09-29): one compact row at the rounds' height; the thumb, Page n over the file's name on one line each, the three paper rounds 6 px apart at the end */
.kv.pg{grid-template-columns:auto minmax(0,1fr) auto;align-items:center;column-gap:12px}
.kv.pg .k,.kv.pg .k small:not(.need){white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
.kv.pg .v.acts{gap:6px 12px}
.kv.pg .thumb{width:36px;height:48px;object-fit:cover;border-radius:var(--r1);background:var(--raised);box-shadow:var(--raised-edge);display:block}
.kv.pg .thumb.bone{background:var(--wait);box-shadow:var(--wait-edge)}
.kv.pg .thumb.doc{display:grid;place-items:center;color:var(--mute)}.kv.pg .thumb.doc svg{width:22px;height:22px}
/* THE SLOT'S PAGES in the documents panel (documents.js Tray on parts.js's capture): the rows' quiet acts keep the quiet look under the panel's pills */
.doc .tray{margin-top:14px}.doc .tray .gt{margin-top:16px}
.doc .tray .kv .go.quiet{--face:1.2em;padding:0}.doc .tray .kv .go.quiet i{width:auto;height:auto}
.kv.up{row-gap:8px}
.kv .k .thumbs{display:flex;flex-wrap:wrap;gap:6px;margin-top:8px}
.kv .k .thumbs img{width:44px;height:58px;object-fit:cover;border-radius:var(--r1);background:var(--raised);box-shadow:var(--raised-edge)}
/* a question: the number, the words and the saved word with the points on one line, the control under; a gap in the text takes its input or select; a choice as the design's option */
.q .qh{display:grid;grid-template-columns:auto minmax(0,1fr) auto;gap:0 10px;align-items:baseline}
.q.info .qh{grid-template-columns:minmax(0,1fr)}
/* a question's number (the owner, 2026-09-27: "these quiz numbers in blue are not readable easily"; the How it works step's navy round shared its name, .qn, and dressed it navy under the faint ink): its own name alone, the ink on the sky's breath, a round of 28 px, the figures tabular */
.q .qn{display:inline-grid;place-items:center;flex:none;min-width:28px;height:28px;padding:0 6px;border-radius:var(--pill);background:var(--tint);box-shadow:var(--tint-edge);font:600 var(--t2)/1 var(--sans);font-variant-numeric:var(--tab);color:var(--ink)}
.q .qt.prose{margin-top:0;max-width:none;font:500 var(--t5)/22px var(--sans)}
.q .qs{display:grid;justify-items:end;min-inline-size:4.5em;font:400 var(--t1)/17px var(--sans);color:var(--mute);white-space:nowrap}
/* the kept line while a save waits wraps inside the slot, never widening the row past it */
.q .qs .st[data-saved=waiting]{white-space:normal;text-align:end;max-inline-size:8em}
.q .qs .st.ok{color:var(--ok);font-weight:500}
.q .qa{margin-top:8px}
/* the question a jump lands on, for a moment: its outline fades (paint alone, no layout) */
.q.flash{border-radius:var(--r2);animation:qflash 1.6s ease-out both}
@keyframes qflash{from{outline:var(--hair) solid var(--sky-ink);outline-offset:var(--r1)}to{outline:var(--hair) solid transparent;outline-offset:var(--r1)}}
.q .qa .fields{margin-top:6px}
.q .qa .kv{padding:6px 0}
.q .qa .kv select,.q .gap select,.kv.plan .v.acts select.pick{height:40px;border:0;border-radius:var(--r2);background:#fff;box-shadow:var(--ring);padding:0 32px 0 12px;font:400 var(--t4)/22px var(--sans);color:var(--ink);max-width:60vw;appearance:none;-webkit-appearance:none;background-image:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%236b6c93' stroke-width='1.8' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M6 9l6 6 6-6'/%3E%3C/svg%3E");background-repeat:no-repeat;background-position:right 10px center;background-size:16px}
.q .gap{display:inline-block;min-width:6ch;border-bottom:1.5px solid var(--line);padding:0 4px;margin:0 2px;font-weight:500}
.q .gap:has(input,select){border-bottom:0;padding:0}
.q .gap input{height:34px;border:0;border-radius:var(--r1);background:#fff;box-shadow:var(--ring);padding:0 8px;font:400 var(--t4)/22px var(--sans);color:var(--ink);width:12ch}
.q .gap select{height:34px;border-radius:var(--r1)}
.q .gap[data-tone=right]{color:var(--ok);border-color:var(--ok)}
.q .gap[data-tone=wrong]{color:var(--warm);border-color:var(--warm)}
.flow .opt .prose{margin:0;font:inherit;max-width:none}
.flow .opt.tick .ring{border-radius:var(--r1)}
/* the last-used row on a phone: its identity shrinks (the door card's .who sections are flex:none; this .who is the row's) */
.door .card .lastrow .who{flex:1 1 0%;min-width:0}
.bar .rule{background:rgba(20,21,58,.14)}
.bar .pip{background:var(--sky)}
/* THE PILL STANDS ON THE CARD (the owner, 2026-09-15: "keep the pill always in the middle of the card, not under Otty"): the bar lives inside the shell, floating at the card's foot on every size, so it is always over the white and one look does: frosted white glass, the ink ring, our icons in ink */
.app:not(.door):not(.enroldoor){--shell-foot:var(--band);--foot-r:var(--round);--card-r:var(--round) var(--round) var(--foot-r) var(--foot-r)}
/* THE PAGE SCROLLS AS ONE (the owner, on a short laptop screen: a video cut by a straight edge at two thirds of the card, 220 px of white under it, the card's own scrollbar): the stage is the one scroller on every size, the card grows with its words; THE BAR FLOATS OVER THE CARD (the owner, 2026-09-25: the Next step card cut by a straight edge, the bar and Back in an empty white band): no band and no edge above the bar, the words pass under the bar's and Back's own surfaces down to the card's foot; the one reserve is the room at the END of the card (a clear foot border of --bar-room: the bar, its edges and the safe area; a border, never a box, so a page's landing moves nothing) and the scroller's scroll-padding, so the last row scrolls clear of the bar and a focused field never stands under it; the shell fills the screen at least */
.app:not(.door):not(.enroldoor){--stage-h:calc(100vh - var(--top-h));--stage-h:calc(100dvh - var(--top-h))}
.app:not(.door):not(.enroldoor) .stage{padding-bottom:0;scroll-padding-bottom:calc(var(--bar-room) + var(--bar-gap))}
/* a place scrolled to (a module opened, a question, an anchor) lands a gap under the top rim; the phone's fill adds his head row */
.app:not(.door):not(.enroldoor) .card *{scroll-margin-top:var(--row)}
.app:not(.door):not(.enroldoor) .stage > .shell{position:relative;display:flex;flex-direction:column;padding-bottom:var(--shell-foot)}
.app:not(.door):not(.enroldoor) .stage > .shell > .card{position:relative;flex:1 0 auto}
/* BACK AND THE BAR NEVER COVER A ROW AT REST (the round 3b review: the last row stood against Back's round): the end reserve is the bar's room and one gap of air, on every page and size */
.app:not(.door):not(.enroldoor) .stage > .shell > .card{border-bottom:calc(var(--bar-room) + var(--bar-gap)) solid transparent;background-origin:border-box}
/* THE DIM UNDER A SHEET, ONE RULE FOR BOTH (the owner, 2026-09-16 00:1x: "make the chat page dim the rest under a bit so it shows nicer"; 02:1x: "the chatbot darkens the card behind it, but notifications darken everything: unify the experience"): a soft navy veil over the card's paper alone while the chat panel (html.chatopen, chat.js) or the account drawer (html.draweropen, shell.js) stands, under the pill, on every size */
.app:not(.door):not(.enroldoor) .stage > .shell::after{content:'';position:absolute;inset:var(--band) var(--band) var(--shell-foot);border-radius:var(--round) var(--round) var(--foot-r) var(--foot-r);background:rgba(20,21,58,.28);z-index:4;pointer-events:none;opacity:0;transition:opacity .35s ease}
html:is(.chatopen,.draweropen) .app:not(.door):not(.enroldoor) .stage > .shell::after{opacity:1}
/* the pill stands above the panel (the panel ends 8px over it, chat.js), so the launcher and its open state stay in reach */
html.chatopen .app:not(.door):not(.enroldoor) .shell > .barwrap{z-index:2147483001}
/* the pointer pill (shell.js MY.below): the act pill's own dress, arriving as the chat's X does */
.shell > .barwrap > .acts .go.below:not([hidden]){animation:bub-in .26s cubic-bezier(.2,.7,.2,1)}
/* the rounds stand at the row's edges; the row's side padding grows with the card, from the band and one gap (round 2 of the audit: at 8px the Start pill stood flush on the card's edge at 360) to the bar's own foot inset on a wide card, so the rounds sit the same distance from the side as from the bottom (the owner, 2026-09-15: "keep them this close if needed, but expand wide on larger screens, keeping an elegant padding from the corners"; 2026-09-16 20:0x: "on wider screens these round buttons on both sides should be closer to the corners, so they show a uniform padding on the bottom and the side") */
.app:not(.door):not(.enroldoor) .shell > .barwrap{position:sticky;left:auto;right:auto;top:auto;bottom:0;width:auto;height:auto;margin:calc(var(--bar-room) * -1) calc(var(--band) * -1) calc(var(--shell-foot) * -1);padding:var(--bar-edge) max(calc(var(--band) + var(--bar-gap)),min(var(--bar-edge),(100% - 457px) / 2 - 6px)) calc(var(--shell-foot) + var(--bar-edge) + env(safe-area-inset-bottom));display:grid;grid-template-columns:1fr auto 1fr;align-items:center;gap:6px;background:linear-gradient(0deg,var(--rim-foot) var(--shell-foot),transparent var(--shell-foot));pointer-events:auto;z-index:5}
.app:not(.door):not(.enroldoor) .shell > .barwrap > .bar{grid-column:2;margin:0;flex:none}
.app:not(.door):not(.enroldoor) .shell > .barwrap > .back{grid-column:1;justify-self:start}
.app:not(.door):not(.enroldoor) .shell > .barwrap > .acts{grid-column:3;justify-self:end}
.app:not(.door):not(.enroldoor) .shell > .barwrap{pointer-events:none}
.app:not(.door):not(.enroldoor) .shell > .barwrap > *{pointer-events:auto}
.bar .sg,.bar .sup{--lift-to:var(--edge),var(--lift-bar);transition:background .25s ease,color .2s ease;color:var(--ink)}
/* the initials chip is the phone's alone (the owner, 2026-09-15 21:4x: "this NM is not needed on desktop") */
#me .ini{display:none}
/* ── THE SIDES (the owner, 2026-09-15): a flow's Back and its action stand beside the bar on every size, three pieces of one height (58), one rhythm (8px) and one glass: the Back a round of the bar's glass, the action the navy pill with its arrow disc; the bar is never squeezed and never loses a word, the action keeps its word on every size ── */
/* AN EMPTY FOOT IS NOT DRAWN, one rule for every flow page (round 3 review: the empty sticky foot stood as a 24 px glass band over the words on phones): a foot none of whose children holds anything */
.flow .card > .foot:not(:has(> :not(:empty):not([hidden]))){display:none}
/* THE BACK ROUND, ONE DRESS (2026-10-06): the bar's Back on a flow page and a sheet's Back in its acts row (parts.js Back) are the same control; in a row it stands first, at the row's height, and pushes the act to the right */
:is(.shell > .barwrap, .acts) > .back{position:static;flex:none;display:grid;place-items:center;margin:0;border:0;border-radius:50%;cursor:pointer;pointer-events:auto;background:var(--bar-glass);-webkit-backdrop-filter:var(--glass-blur);backdrop-filter:var(--glass-blur);box-shadow:var(--edge),var(--lift-bar);color:var(--ink)}
.shell > .barwrap > .back{width:58px;height:58px}
.acts > .back{width:46px;height:46px;margin-right:auto}
.shell > .barwrap > .acts{position:static;flex:none;margin:0;pointer-events:auto;display:flex;align-items:center;gap:8px}
.shell > .barwrap > .acts .go{margin:0;height:58px;padding:0 7px 0 22px;gap:12px;font-size:var(--t4)}
.shell > .barwrap > .acts .go i{width:44px;height:44px}
.go i svg{width:20px;height:20px}
.shell > .barwrap > .acts [data-line]{display:none}
.shell > .barwrap > .acts .go.quiet{padding:0 12px}
.bar .sg:hover,.bar .sup:hover{background:var(--lens);-webkit-backdrop-filter:var(--lens-blur);backdrop-filter:var(--lens-blur)}
:is(.shell > .barwrap, .acts) > .back:hover{box-shadow:var(--ring-on),var(--lift-bar)}
/* the open place is glass on the glass (the owner, 2026-09-15: "the selected state nicer, glass effect"): a lighter frosted lozenge raised by a fine ring, a white top light and a soft cast shadow; the ink ring under the pointer */
.bar .sg.on,.bar .sup.open{color:var(--ink);background:rgba(255,255,255,.62)}
/* HIS QUESTIONS, ONE LOOK FOR BOTH LANES (moved from quest.css, 2026-09-27: the guardian's two questions wear them too, insight.js): the chip is the portal's chip on the night glass, a step quieter than his words (the muted night ink at the regular weight, white on hover), every item's face the hit itself (44 px at least), a chip that taps nothing never fades, one lap of light every 12 s (.shine.now); the question tapped stands small over his answer; the guardian's row size is his own (the learner's is quest.css .otty[data-quest]) */
.app:not(.learn) .otty{--o-row:var(--t3);--o-row-lh:20px}
.otty .say .bub .qq{width:fit-content;max-width:85%;margin:0 0 10px auto;padding:6px 12px;border-radius:var(--r3);background:rgba(255,255,255,.12);font:400 var(--o-row)/var(--o-row-lh) var(--sans);color:var(--night)}
.otty :is(.qask,.qway){--face:var(--hit);--face-i:var(--hit);min-height:var(--hit)}
.otty .qask{height:auto;padding:var(--it-y) 16px;border-radius:var(--pill);background:rgba(255,255,255,.1);--rc:rgba(255,255,255,.36);box-shadow:var(--ring);color:var(--night-mute);font:400 var(--o-row)/var(--o-row-lh) var(--sans);text-align:left;text-wrap:pretty;transition:color .2s}
.otty .qask:not(:disabled):hover{background:rgba(255,255,255,.16);color:#fff}
.otty .qask:disabled{opacity:1;cursor:default}.otty .qask[aria-busy]{cursor:progress}
/* ── THE LEFT SIDE'S PIECES (the owner, 2026-09-21: "the left is a mess now with these approvals under Otty; a duplication of the notifications"; "above Otty's head is a bit empty") ── */
/* the shell stands the slots: the learner's day over his words (.today, day.js) and the Points door under him (.under.door, rewards.js); the guardian's stats plate on the column's top (.term, pending.js; the one line under him, .needs, went on 2026-09-27: the bell holds those items) and his two questions (.asks, insight.js); ALL OF THEM ARE THE DESK'S ALONE: under 1000px none is drawn, so the phone's top band is him, his words and the capsule, never taller (the bell's counter and his words carry what waits) */
.otty > .under,.otty .under > .today,.otty > .term,.otty > .asks{display:none}
/* the Points door's slot stands its place from the first frame; empty (before its door mounts) it paints nothing */
.otty > .under.door:not([data-door]):not(:has(> .today > *)),.otty > .under.door:not([data-door]) > .yr{visibility:hidden}
/* ONE COMBINED MENU (the owner, 2026-09-22): the pane's sections (Today, the notifications, the account) stand in the paper's one scroll under small-caps titles; the Today rows: the date over the term's labels and rail on paper (the desk's Term, day.js, in the paper's ink), the lesson line with its way (the desk's own line) and the tasks due today leading to Tasks; Today stands only where the face does (under 1000 px: the desk keeps the day over Otty) */
.sheet .rows{min-height:0}
.sheet .h{font:500 var(--t1)/16px var(--sans);letter-spacing:.08em;text-transform:uppercase;color:var(--mute);padding:12px 14px 4px}
.sheet + .sheet{margin-top:6px;border-top:1px solid var(--hairline)}
.sheet[data-first]{margin-top:0;border-top:0}
/* THE DRAWER'S ONE SCROLL (the owner, 2026-09-24: the lines ran through the words): its sections keep their whole height in the paper's column and the paper scrolls; a section never shrinks under its rows, so a rule stands between sections, never through a row */
.drawer .paper > .sheet{flex:none}
#today .r.date{display:grid;gap:8px}
#today .r.date b{font:500 var(--t3)/20px var(--sans)}
#today .w{display:grid;gap:5px;max-width:240px}
#today .l{display:flex;justify-content:space-between;gap:8px;font:500 var(--t1)/16px var(--sans);letter-spacing:.06em;text-transform:uppercase;color:var(--mute)}
#today .l b{font-weight:500;font-variant-numeric:var(--tab);color:var(--ink)}
#today .wks{display:grid;grid-auto-flow:column;grid-auto-columns:1fr;gap:3px}
#today .wks i{height:4px;border-radius:var(--r1);background:var(--track-fill);box-shadow:var(--track-edge)}
#today .wks i.on{background:var(--sky)}
#today .wks i.dim{opacity:.45}
#today .r.nx{display:grid;grid-template-columns:8px minmax(0,1fr) auto;align-items:baseline;column-gap:8px}
#today .r.nx::before{content:'';align-self:center;width:8px;height:8px;border-radius:50%;--rc:var(--faint);box-shadow:var(--ring-on)}
#today .r.nx[data-now]::before{background:var(--sky);box-shadow:var(--glow)}
#today .r.nx .s{min-width:0;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
#today .r.nx .c{font:500 var(--t2)/18px var(--sans);font-variant-numeric:var(--tab);color:var(--sky-ink);white-space:nowrap}
#today .r.nx .c i,#today .r.due small i{font-style:normal;color:var(--mute);margin-left:4px}
#today .r.nx small{grid-column:2/-1;display:block;font:400 var(--t1)/17px var(--sans);color:var(--mute)}
#today .r.due{display:flex;justify-content:space-between;align-items:baseline;gap:12px}
#today .r.due small{font:500 var(--t2)/18px var(--sans);color:var(--mute);white-space:nowrap}
/* THE BUBBLE HUGS ITS WORDS, ONE RULE (the owner, 2026-09-29: "why are text boxes oversized compared to the text?"; a door's greeting stood in a box three times its line): the bubble is the .bub inside the slot on every page, the door too; its padding is the column's token (--col-pad, --col-pad-x, one place on .otty), its width its words up to 26 em and the slot's width, its height its words and nothing else: NO min-height on a bubble, ever. Room that must stand before a line lands is the SLOT's (.say: the phone's three-line slot, the desk's --say-min), which stays invisible, so the bubble never grows past its words */
.otty{--col-pad:12px;--col-pad-x:14px}
.otty .say .bub{position:relative;max-width:min(100%,26em);min-height:0;padding:var(--col-pad) var(--col-pad-x);transform-origin:left center}
/* THE ONE BUBBLE (the owner, 2026-09-27: "the bubble and arrow should be the same for both sides, without a line cutting the bubble from the arrow"): his words on every side (the learner's, the guardian's, the door's) are one shape in one fill. The surface is the bubble's ::before (--night-fill and the hairline --edge-night); the tail is its ::after (a 12 px square turned 45 degrees and clipped to its outer half, the same fill, the hairline on its two outer edges alone); the soft lift is the bubble's own shadow. THE BASE: the tail's base lies 1 px inside the bubble, and the surface's mask (--say-cut) takes that 1 px strip away under the base (its fill and its hairline), so the tail's fill stands there alone: no line across the base and no brighter seam, the outline runs round the tail as one. The tail points at him: left beside him under 1000 px, down over him from 1000 px (--tail-x, --tail-y, --tail-clip, --say-cut; the one place) */
.otty .say .bub{--tail:12px;--tail-base:16px;--tail-x:calc(1px - var(--tail) / 2);--tail-y:calc(50% - var(--tail) / 2);--tail-clip:polygon(0 0,0 100%,100% 100%);--say-cut:linear-gradient(#000 0 0) right / calc(100% - 1px) 100% no-repeat,linear-gradient(#000 calc(50% - var(--tail-base) / 2),#0000 0 calc(50% + var(--tail-base) / 2),#000 0) left / 1px 100% no-repeat;isolation:isolate;border-radius:var(--r4);background:none;-webkit-backdrop-filter:none;backdrop-filter:none;box-shadow:var(--night-lift)}
.otty .say .bub::before{content:'';display:block;position:absolute;inset:0;z-index:-1;border-radius:inherit;background:var(--night-fill);box-shadow:var(--edge-night);-webkit-mask:var(--say-cut);mask:var(--say-cut);pointer-events:none}
.otty .say .bub::after{content:'';position:absolute;left:var(--tail-x);top:var(--tail-y);right:auto;bottom:auto;z-index:-1;width:var(--tail);height:var(--tail);margin:0;border:0;border-radius:0;background:var(--night-fill);-webkit-backdrop-filter:none;backdrop-filter:none;box-shadow:var(--edge-night);clip-path:var(--tail-clip);transform:rotate(45deg);pointer-events:none}
@media (min-width:1000px){.otty .say .bub{--tail-x:calc(var(--tail-at,50%) - var(--tail) / 2);--tail-y:calc(100% - 1px - var(--tail) / 2);--tail-clip:polygon(0 100%,100% 0,100% 100%);--say-cut:linear-gradient(#000 0 0) top / 100% calc(100% - 1px) no-repeat,linear-gradient(90deg,#000 calc(var(--tail-at,50%) - var(--tail-base) / 2),#0000 0 calc(var(--tail-at,50%) + var(--tail-base) / 2),#000 0) bottom / 100% 1px no-repeat}}
.otty .say .bub.words:not(.grown){animation:bub-in .34s cubic-bezier(.2,.7,.2,1)}
.otty .say .bub.grown .said-words > p:not(.ghost){animation:bub-fade .4s ease-out .12s both}
@keyframes bub-fade{from{opacity:0}}
.otty .say .bub.think{padding:12px 16px;animation:bub-in .34s cubic-bezier(.2,.7,.2,1)}
/* THE ONE EMPTY-BUBBLE RULE: a bubble with no words is not drawn; no shape rule beats this */
.otty .say .bub:empty{display:none}
.otty .say .bub .ghost{display:block;visibility:hidden}
.otty .say .bub .said-words{position:relative}
.otty .say .bub .live{display:block;position:absolute;left:0;right:0;top:0;margin:0}
/* one plain paragraph (the owner, 2026-09-17 03:0x SAST: "totally remove the heading thing and generate normal text on all devices, and smaller font on mobile"): the type is the slot's, 13.5/19 under 1000px and 15/21 on the desk */
.otty .say .bub p{margin:0}
/* HIS BUBBLE'S ACTS, ONE RULE FOR BOTH LOOKS (2026-09-25: the guardian's Try again stood navy on his navy glass, 1.09:1, since these rules lived in the learner's quest.css alone): one row that never wraps (talk.js actsOf: Try again, a line's acts), the act white on his glass, a quiet one his light words, both 44 px; a link in his words his words' own light */
.otty .say .acts{display:flex;flex-wrap:nowrap;align-items:center;gap:8px;margin-top:12px;opacity:0;pointer-events:none;transition:opacity .3s}
.otty .say .acts.on{opacity:1;pointer-events:auto}
.otty .say .acts .go{min-height:44px;padding:0 18px;background:white;color:var(--ink);font-size:var(--t3);justify-self:auto}
.otty .say .acts .go.quiet{padding:0 8px;background:none;color:var(--night)}
.otty .say .acts .go.quiet:hover{color:white}
.otty .say .bub p a{color:var(--night);text-decoration:underline}
/* A LINE WITH A WAY IS A LINK (the owner, 2026-10-06, under a staff view: "why aren't these clickable?"): his words are one link to the line's way (talk.js way), in his words' own colour, a faint underline that fills on hover and focus */
.otty .say .bub a.into{color:inherit;text-decoration:underline;text-decoration-thickness:1px;text-underline-offset:3px;text-decoration-color:currentColor;text-decoration-color:color-mix(in srgb,currentColor 45%,transparent)}
.otty .say .bub a.into:hover,.otty .say .bub a.into:focus-visible{text-decoration-color:currentColor}
.otty .say .bub .dots{display:flex;gap:5px;align-items:center;height:14px}
.otty .say .bub .dots i{width:6px;height:6px;border-radius:50%;background:rgba(255,255,255,.75);animation:dot 1.1s ease-in-out infinite}
.otty .say .bub .dots i:nth-child(2){animation-delay:.18s}
.otty .say .bub .dots i:nth-child(3){animation-delay:.36s}
@keyframes dot{0%,80%,100%{opacity:.35;transform:translateY(0)}40%{opacity:1;transform:translateY(-2px)}}
@keyframes bub-in{from{opacity:0;transform:scale(.92)}60%{opacity:1}}
/* ── THE FLOATING BOXES, ONE RULING (the owner, 2026-09-15 17:0x: "where the chatbot box and the notification box dropdowns show, in accordance with the card; one unified ruling, now it is too unorganised") ── */
/* ── THE LEARNER'S SHEET (the owner, 2026-09-15: "this selector on mobile looks terrible and not aligned with theme; the pages that have different shapes and open for leaderboards etc. should be tabs of the same popup that doesn't change size", "light background aligned with theme, items glassy edges"; 18:1x: "they are squeezed in rounds and not looking good, and the word Chosen should go too; the other tabs of the leaderboards are just a data dump"): ONE POPUP for the points, the board, the houses and the buddy, the dropdown sheet's paper in its glass rim (the same rounds per size), one fixed size per device (the desk 560 wide and min(640, the screen less 80) tall; a phone and a tablet the screen less 16px gutters and less 96px tall), centred on the veil, the open tab's panel scrolling inside; the head's title in the ink and one glass round to close (the bar's glass, the lens on hover); the design's tabs with the ink slide alone marking the open one (no browser outline, no underline: "why this double line"); inside, one hero well, .kv rows, the reward rail, the board's discs, the house tiles with their share bars, the buddy's row of standing figures under the ink slide ── */
/* --rim-w is the rim's width (2026-09-21: named --rim, the film's colour token, it cost every popup its white film) */
.popup{--rim-w:var(--band);position:relative;isolation:isolate;display:flex;flex-direction:column;width:560px;max-width:calc(100vw - 32px - 2 * var(--rim-w));height:min(640px,calc(100vh - 80px));height:min(640px,calc(100dvh - 80px));color:var(--ink);border-radius:var(--round);outline:0}
.popup::before{content:'';position:absolute;inset:calc(var(--rim-w) * -1);border-radius:var(--rim-r);background:var(--rim);backdrop-filter:var(--rim-blur);-webkit-backdrop-filter:var(--rim-blur);box-shadow:var(--edge-night),var(--lift-sheet);z-index:-2}
.popup::after{content:'';position:absolute;inset:0;border-radius:var(--round);background:var(--sheet);box-shadow:var(--ring);z-index:-1}
/* THE YEAR CARD under Otty (desk.js Door; the owner, 2026-09-26: a small summary of Your year, quiet on the dark column): the plate's one link, its title in the kicker's caps with the way's mark, the facts one to a row as the HUD draws them (parts.js Stat: the glyph in its colour, the figure, one short word) */
.door .yc{display:block;margin:-12px -14px -14px;padding:12px 14px 12px;border-radius:inherit;color:#fff;text-decoration:none;transition:background .25s}
.door .yc:hover{background:rgba(255,255,255,.05)}
.door .yc>.k{display:flex;align-items:center;gap:2px}.door .yc>.k .way{--way-s:16px;margin-left:2px}.door .yc>.k .qdemo{margin-left:auto}
.door .f{display:grid;gap:2px;margin-top:8px}
.door .stat{display:grid;grid-template-columns:var(--icon-sm) auto minmax(0,1fr);align-items:center;column-gap:8px;min-height:28px}
.door .stat .num{font:500 var(--t4)/20px var(--sans);font-variant-numeric:var(--tab);white-space:nowrap}
.door .stat .sm{min-width:0;font:400 var(--t2)/18px var(--sans);color:var(--night);text-wrap:pretty;overflow-wrap:anywhere}
/* THE ROWS STAND FROM THE FIRST PAINT (the owner, 2026-10-19; stats.js): a waiting row keeps its words' room with the words unseen; the keeper is the row's plain fact laid over the words' columns and unseen, so a row with a figure is never shorter than its fact and the card's height is the same before and after the live read */
.door .stat.wait > :not(.keep){visibility:hidden}.door .stat .keep{grid-column:2/4;grid-row:1;visibility:hidden;pointer-events:none}.door .stat > :not(.keep){grid-row:1}
.door .stat:not(:has(.num)) .sm{grid-column:2/-1}
.door .stat.on .sm{color:rgb(var(--ice-rgb))}
/* ── THE GUARDIAN'S PAGES ── */
/* the change of grade, curriculum or format (the owner, 2026-09-16 17:4x SAST) and the notice of leaving (2026-09-25): the learner card's sheets stand a row under their fields */
:is([data-change-sheet],[data-leave-sheet]) .notice{margin:16px 0 0}
/* room under the sheets' buttons (the owner, 2026-10-02: "give more padding under the button") */
:is([data-change-sheet],[data-leave-sheet]) > .acts{padding-bottom:24px}
/* THE DATE CONTROL (the owner, 2026-10-08; kit.js DateField, supersedes the month-end picker of 2026-10-02): the masked field with the calendar button in its right end; the sheet under the field in the input's dress: the month arrows and the year select on the head row, the day grid, a day outside the window or off the served list muted; 360 px at most, so it fits a phone's column with the 16 px gutters */
.fld .date{position:relative}
.fld .date input{padding-right:50px}
.fld .date input[readonly]{cursor:pointer}
.cal-open{position:absolute;right:3px;top:3px;display:grid;place-items:center;width:40px;height:40px;border:0;border-radius:50%;background:transparent;color:var(--mute);cursor:pointer}
.cal-open svg{width:20px;height:20px;fill:none;stroke:currentColor;stroke-width:1.6;stroke-linecap:round;stroke-linejoin:round}
.cal-open:hover,.cal-open[aria-expanded=true]{color:var(--ink)}
.cal{border-radius:var(--r3);background:#fff;box-shadow:var(--ring);padding:12px;max-width:360px}
.cal-head{display:grid;grid-template-columns:40px minmax(0,1fr) auto 40px;align-items:center;gap:8px;margin-bottom:6px}
.cal-head b{font:500 var(--t4)/22px var(--sans);color:var(--ink);white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
.cal-nav{display:grid;place-items:center;width:40px;height:40px;border:0;border-radius:50%;background:transparent;box-shadow:var(--ring);color:var(--ink);cursor:pointer}
.cal-nav:disabled{opacity:.3;cursor:default}
.fld .cal-year{height:44px;width:auto;padding:0 32px 0 10px;border-radius:var(--r2);font:500 var(--t3)/22px var(--sans);background-position:right 12px center}
.cal-grid{display:grid;grid-template-columns:repeat(7,1fr);gap:2px;text-align:center}
.cal-wd{font:500 var(--t2)/32px var(--sans);color:var(--mute)}
.cal-day{height:40px;display:grid;place-items:center;font:400 var(--t3)/1 var(--sans);color:var(--ink);border:0;background:transparent;border-radius:50%;font-variant-numeric:var(--tab)}
.cal-day.off{color:var(--faint)}
button.cal-day{cursor:pointer;box-shadow:var(--ring-on);font-weight:500}
button.cal-day.on{background:var(--ink);color:#fff}
.cal>.meta{margin:8px 0 0}
/* one family (the owner, 2026-09-15 18:5x: "elegance and unity") on the three places and their three flows (the ones with a way back; the enrolment flow is not one): the head's name at 24/500 with its one line, the first row 12 px under it; the well with the figure first (.ask.sum: the mono figure, the words under it, the one-word act); the way to another learner is the tabs' plus alone (the owner, 2026-09-16 01:0x: "too many add-a-learner places") */
[data-side=guardian]:is(:not(.flow),[data-back]) .who .nm,.flow .step > .nm{font-weight:500;font-size:var(--t7);line-height:30px}
[data-side=guardian]:is(:not(.flow),[data-back]) .who > :is(.nm,.fx) + .kv{margin-top:calc(var(--head) - var(--row))}
[data-side=guardian].flow .who > :is(.nm,.fx) + .fields{margin-top:var(--head)}
.ask.sum .amt{margin-top:0}
.ask.sum .t small{margin-top:4px}
/* ── THE LEARNER'S BLOCK AS TWO CLEAN COLUMNS (the owner, 2026-09-16 01:0x, after the three wells: "not elegant or aligned with the theme; too many add-a-learner places; revert the tabbing and the plus; next to the plus just 'Add a learner' and the 5 % off label more elegantly, not in green; Now moves under Otty as we have it now; the other two can stay as columns but clean columns like the course side, not wells") ── */
/* the head's one plain line; School and Profile as two plain columns from 740 px (one on a phone), the kickers as the column heads, the rows as they are, no well; the tabs' plus says Add a learner with the sibling figure as quiet muted text beside it */
.who .cols{display:grid;gap:0 48px}
/* THE LEARNER'S TWO GROUPS AS TWO CARDS (the owner, 2026-09-27, on the bar and the fading line by ADMIN: "this line next to admin is not nice, redesign better, rethink"; his bar: "think designer, and lux"): School and Admin are each ONE RAISED card (THE SURFACES) with room inside; its head the group's glyph filled on a TINTED sky round and its title in the title type, no caps, no line; its rows at one height between hairlines (the row's lines centred in it, the act on the title's line and never more than half the row, so a title never breaks for it), the words left, the act right; the quiet Give notice of leaving is the Admin card's foot, muted, left, over a hairline; side by side from 768 px at their natural heights, one over the other on a phone */
.who > .cols.grps{gap:20px 24px;align-items:start;margin-top:var(--head)}
.who > .grp{margin-top:var(--head)}
.grp > .prose{margin:0 0 var(--row)}
.grp{--g:var(--sky);min-width:0;padding:20px 24px 6px;border-radius:var(--r4);background:var(--raised);box-shadow:var(--raised-edge),var(--top-light)}
.grp > .gh{display:flex;align-items:center;gap:12px;margin:0 0 6px;font:600 var(--t6)/24px var(--sans);letter-spacing:-.005em;color:var(--ink)}
/* the group's mark (the owner, 2026-09-27: "redesign these 2 logos more cleaner and minimal"): the glyph alone, a fine line in the soft ink at the title's size, no round, tint, ring or fill */
.grp > .gh .gtile{--g-tile:22px;background:none;box-shadow:none;color:var(--ink-soft)}.grp > .gh .gtile svg{width:22px;height:22px;fill:none;stroke-width:1.5}
.grp > .kv{min-height:68px;padding:13px 0}
.grp > .kv:not(.inv,.act){grid-template-columns:minmax(0,1fr) fit-content(50%);align-items:baseline;align-content:center}
.grp > :is(.kv,.receipt-row,.guide,[data-change-sheet],[data-leave-sheet]) ~ :is(.kv,.receipt-row,[data-change-sheet],[data-leave-sheet]){border-top:1px solid var(--hairline)}
.grp > .kv.due{align-items:center}
.grp > .kv.due .amt{margin-top:0}.grp > .kv.due .k small{margin-top:4px}
/* THE BALANCE CARD'S STATE WORD (payment.js BalanceCard, 2026-10-06): the word under the figure in its tone (ok for Paid up, In credit and an accepted proof; need for Overdue), Paid up with the one tick drawn in at the word's size */
.grp > .kv.due .k small{display:flex;align-items:center;gap:7px}.grp > .kv.due .k small.ok{color:var(--ok);font-weight:500}.grp > .kv.due .k small.need{color:var(--warm);font-weight:500}
.grp > .kv.due .k small .tick{flex:none;display:grid;place-items:center;width:18px;height:18px;border-radius:50%;background:var(--ok);animation:tick-pop .5s cubic-bezier(.2,.8,.2,1) both}.grp > .kv.due .k small .tick svg{width:11px;height:11px;fill:none;stroke:#fff;stroke-width:2.8;stroke-linecap:round;stroke-linejoin:round;stroke-dasharray:24;animation:tick-draw .45s .2s cubic-bezier(.2,.8,.2,1) both}
.grp > .kv.due .v.acts .go.quiet{white-space:nowrap}
.grp > .kv .v .go.quiet{flex-shrink:1;white-space:normal;text-align:right;line-height:20px}
.grp > .kv.foot{min-height:0;padding:12px 0 14px}
.grp > .kv.foot .go.quiet{font:400 var(--t3)/20px var(--sans);color:var(--mute)}
/* THE GUARDIAN'S ADDRESSES (/account/settings/ Contacts, 2026-09-30): the Main pill beside the main address, the last address's Remove drawn held (the go's own disabled look) */
.contacts .kv.alt .k .pill{margin-left:8px;vertical-align:middle}.contacts .go.quiet.off{opacity:var(--go-off,.4);cursor:default}
.tab.plus{flex:0 0 auto;display:inline-flex;align-items:center;justify-content:center;padding-left:14px;padding-right:14px;text-decoration:none;color:var(--mute);transition:color .2s;gap:8px}
/* the figure beside the plus's word is plain muted text (2026-09-21: it wore .chip and undid all of it) */
.tab.plus b{font:400 var(--t1)/1 var(--sans);color:var(--mute)}
/* THE PLUS DISABLED WHILE RE-ENROLMENT IS CLOSED (the owner, 2026-10-08): the same row item held, no link, the quiet word Opens soon in the figure's place, no pointer */
.tab.plus.off{cursor:default;opacity:var(--go-off,.55)}.tab.plus.off:hover{color:var(--mute)}
/* THE NEXT YEAR'S ROW (the owner, 2026-09-16 09:1x: re-enrol on every learner card, "the emphasis should be re-enrol and then a smaller, less emphasised 'XYZ is not coming back' button"): the Profile row's right side is the pill with the quiet link under it, the pill at the row pill's size; on a phone the pill takes the column's width and the link stands under it at the row's end */
.kv .v.acts.next{flex-direction:column;align-items:flex-end;gap:10px}
.kv .v.next .go:not(.quiet){--face:42px;padding:7px 7px 7px 18px;font-size:var(--t3)}
.kv .v.next .go:not(.quiet) i{width:28px;height:28px;font-size:var(--t4)}
.kv .v.next .go.quiet{font-size:var(--t2);color:var(--mute)}
.kv .v.next .go.quiet:hover{color:var(--ink)}
/* ── THE ENROLMENT'S FORM ── */
/* the owner, 2026-09-15 19:2x ("do better on the design and spacing here"): every step is one reading column, 720px at most, at the left of the white box; the step's title at the head's 24/500 with its one-line lead under it; the rows of a form 20px apart and a label 8px over its field; the names on one row (first, the middle when the family has one, surname) and the date of birth alone; a group's label 28px under what stands before it and 8px over its own controls; the chips a wrapped row of 44px pills, never stretched across the box; the helper small and quiet under them; the payment step keeps the pay page's own tree; the rail and the foot unchanged. The rows follow the column's own width (a container), so the door's card and the name sheet lay the names the same way */
.flow .step:not([data-step=fee]),.enroldoor form.who,.flow .who:has(.fields.name){container-type:inline-size}
.flow .step > .nm{text-wrap:pretty}
.flow .step > .fx{margin-top:4px;font:400 var(--t4)/22px var(--sans)}
.flow .step:not([data-step=fee]) .fields{grid-template-columns:1fr;gap:20px 16px;margin-top:24px}
.flow .step:not([data-step=fee]) .fields + .fields{margin-top:20px}
.flow .step:not([data-step=fee]) .fld{gap:8px}
.fields.name{grid-template-columns:1fr}
@container (min-width:560px){
  .flow .step:not([data-step=fee]) .fields{grid-template-columns:1fr 1fr}
  .fields.name,.flow .step:not([data-step=fee]) .fields.name{grid-template-columns:1fr 1fr 1fr}
  /* the three names share their rows (labels, fields, lines), so a label that wraps never pushes its field down */
  .fields.name{grid-template-rows:auto auto auto;grid-auto-flow:column;row-gap:8px;align-items:end}
  .fields.name > .fld{display:contents}
  .fields.name > .fld > span{align-self:end;grid-row:1}
  .fields.name > .fld > input{grid-row:2}
  .fields.name > .fld > .line{grid-row:3}
}
.enroldoor .fields.name + .fields{margin-top:14px}
.flow .step .gt{margin:28px 0 8px}
.flow .step > .head{margin:28px 0 8px}
.flow .step > :is(.nm,.fx) + .gt,.flow .step > :is(.nm,.fx,.meta) + :is(.opts,.head){margin-top:24px}
.flow .step .opts,.flow .step > .chips,.flow .step .check{margin-top:0}
.flow .step > .chips{display:flex;flex-wrap:wrap;gap:8px}
.flow .step > .chips .chip,.psubj .chip{--face:var(--hit);padding:6px 18px;font:500 var(--t4)/20px var(--sans)}
.flow .step > .chips.grid .chip{min-width:52px;padding:0 14px}
.flow .step > .meta{margin-top:10px;max-width:56ch;text-wrap:pretty}
.flow .step > .meta + .meta{margin-top:4px}
.flow .step > .go.quiet{margin-top:20px}
.flow .step > .fx + .doc{margin-top:10px}
/* a note with acts (a waiting device): its buttons in a row under its words */
.sheet .n .acts{display:flex;flex-wrap:wrap;align-items:center;gap:8px 14px;margin-top:8px}
/* a note's act is the portal's pill at its compact size (Pay, Upload, Approve: the owner, 2026-09-27: "designed like pay buttons, a navy button"); a way to look stays quiet */
.sheet .n .acts .go:not(.quiet){--face:36px;height:var(--face);padding:0 5px 0 16px;gap:10px;font-size:var(--t3)}
.sheet .n .acts .go:not(.quiet) i{width:26px;height:26px;font-size:var(--t4)}
/* THE POPUP ANNOUNCEMENT (popup.js): a native dialog as the veil (the veil's scrim on its backdrop, a tap on it does nothing), inside it the house popup's glass and paper, as tall as its words up to the room the screen has (a tall tablet shows a long message whole; the air round it grows with the screen) and never past the screen less the safe area; the head and the Close act stand, the body scrolls between them and fades at both ends; a long word breaks, a wide table slides in its own box, a picture never passes the column (the prose rules) */
.announce{box-sizing:border-box;border:0;margin:0;width:100vw;height:100vh;height:var(--vv-h,100dvh);max-width:100vw;max-height:100vh;max-height:var(--vv-h,100dvh);top:var(--vv-top,0px);bottom:auto;padding:calc(max(16px,4vh) + env(safe-area-inset-top)) calc(16px + env(safe-area-inset-right)) calc(max(16px,4vh) + env(safe-area-inset-bottom)) calc(16px + env(safe-area-inset-left));background:transparent;overflow:hidden;display:flex;align-items:center;justify-content:center}
.announce:not([open]){display:none}
.announce::backdrop{background:var(--dim)}
.announce .popup{width:min(560px,100% - 2 * var(--rim-w));max-width:none;height:auto;max-height:calc(100% - 2 * var(--rim-w))}
.announce .head{flex:none;padding:22px 24px 0}
.announce .head .gt{display:block;margin:0}
.announce .title{margin:6px 0 0;overflow-wrap:anywhere;text-wrap:balance}
.announce .prose{flex:1 1 auto;min-height:0;margin:0;padding:14px 24px 20px;overflow:auto;overscroll-behavior:contain;scrollbar-width:thin;scrollbar-color:var(--line) transparent;-webkit-mask-image:linear-gradient(transparent,#000 12px,#000 calc(100% - 18px),transparent);mask-image:linear-gradient(transparent,#000 12px,#000 calc(100% - 18px),transparent)}
/* the popup's own gutter on its foot, the popup's alone (2026-10-15: as `.announce .foot` it reached every confirm dialog, which wears `announce` too, and inset their row 18 to 24 px inside the sheet's padding) */
.announce .popup > .foot{flex:none;margin-top:0;padding:10px 24px 22px}
.announce .prose[hidden] + .foot{padding-top:20px}
.announce .prose:focus-visible{outline:2px solid var(--line);outline-offset:-2px}
/* THE FILES OF A NOTICE (2026-10-16, the lane group-files): the group message's attachments under the words of the sheet, one quiet way each (notices.js Files) */
.announce .files{display:flex;flex-direction:column;align-items:flex-start;gap:4px;margin:14px 0 0}.announce .files .go{margin:0;max-width:100%;overflow-wrap:anywhere}.announce .files .go small{color:var(--mute);font:400 var(--t1)/17px var(--sans)}
.announce .go{transition:transform .5s cubic-bezier(.32,.72,0,1),opacity .2s}
/* THE MESSAGE'S PAGE AND THE BELL'S QUIET COUNT (2026-09-21; message.js, shell.js row): a row that stands for more than one alike says how many after its title in the house's count dress (the mono, faint figure of .gt .n); a teacher's message as calm bubbles, the others' at the left on the well, the learner's own at the right on a breath of the sky, each under its small line (who, when), the words the pages' prose, the file rows inside their bubble */
.sheet .n b .times{font:400 var(--t1)/20px var(--sans);font-variant-numeric:var(--tab);color:var(--faint);margin-left:8px;white-space:nowrap}
.msgs{display:grid;gap:12px;margin-top:var(--head)}
.msg{justify-self:start;min-width:min(100%,220px);max-width:min(100%,620px);background:var(--raised);box-shadow:var(--raised-edge);border-radius:var(--r4) var(--r4) var(--r4) var(--r1);padding:12px 16px 14px}
.msg.mine{justify-self:end;background:var(--tint);box-shadow:var(--tint-edge);border-radius:var(--r4) var(--r4) var(--r1) var(--r4)}
.pbeside{justify-self:start;display:grid;grid-template-columns:var(--av-s) minmax(0,1fr);align-items:end;column-gap:var(--it-x);max-width:min(100%,calc(620px + var(--av-s) + var(--it-x)))}
.msg .by{display:block;font:500 var(--t1)/17px var(--sans);color:var(--mute)}
.msg .prose{margin-top:4px}
.msg .prose p+p{margin-top:10px}
.msg .meta{margin:8px 0 0}
.msg a.kv.file{padding:10px 0 0}
/* THE STAFF'S DECISION ON A DOCUMENT OR A RECEIPT (documents.js Review; the owner, 2026-09-21: "an approve or reject button with an elegant safety button, there on each doc"): drawn in a staff session alone, so a family's panel is not one pixel taller; at the head of the step before the cards (2026-09-22: under the cards' fold on every width under 1400 when it stood in the panel), so .docs and not .doc holds its rules; the theme's own pieces (the kicker, the pills, the field with its line, the quiet way) under one hairline, a step in the place of the control that opened it; what this page just decided with its clear Undo over the options, an older decision as one quiet line after them; the box short, its warm ring as an input's */
.docs .review{margin-top:var(--kick);border-top:1px solid var(--line);outline:0}
.docs .review .gt{margin-top:16px}
.docs .review .did,.docs .review .last{display:flex;flex-wrap:wrap;align-items:center;gap:6px 12px;font:400 var(--t2)/18px var(--sans);color:var(--mute)}
.docs .review .did{color:var(--ink);font-size:var(--t4);line-height:20px}
.docs .review :is(.did,.last) span{flex:1 1 220px;min-width:0;text-wrap:pretty;overflow-wrap:anywhere}
.docs .review :is(.did,.last) small{flex-basis:100%;font:400 var(--t2)/18px var(--sans);color:var(--mute);text-wrap:pretty}
.docs .review .go.quiet{height:auto;padding:0;line-height:20px}
.docs .review .go.quiet i{width:auto;height:auto;font-size:var(--t4)}
.docs .review .taps,.docs .review .step .line{margin-top:0}
.docs .review .taps{display:flex;flex-wrap:wrap;align-items:center;gap:10px 12px}.docs .review .taps .go{--face:42px;height:var(--face);padding:0 20px;font-size:var(--t3)}.docs .review .taps .go i{margin-right:-6px} /* the decision pills: their own row with a gap, symmetric padding without an icon (the owner, 2026-09-22 evening: "these buttons are not properly spaced") */
.docs .review :is(.did,.taps,.step,.last) + :is(.taps,.step,.last),.docs .review .step :is(.fld,.taps){margin-top:12px}
.docs .review .step .notice{margin:0 0 12px}
.docs .review .fld textarea{min-height:0}
.docs .review .fld.warn textarea,.docs .review .fld.warn textarea:focus{--rc:var(--warm);box-shadow:var(--ring-on)}
.docs .review .go:disabled{cursor:default}
/* THE INSTALL GUIDE, SHOWN (push.js Guide, 2026-09-21, the owner: "very texty and people will ignore"; one block, the sheet and Settings wear it alike): a step is one thin-ink glyph on a paper tile (the app's own icon on the last) and five words at most; the menu Share hides behind is a small paper chip in the words; the hint under a step is the small mute line with the switch drawn on; the pointer is the one moving thing: Share's glyph and a sky arrow, on the Not now line in the well's middle nodding down to the bottom bar, or in the well's top right nodding up to the top bar, six calm nods and rest; reduced motion leaves it still (the page's one rule above) */
/* THE INSTALL GUIDE IN THE APP'S OWN DRESS (2026-10-03, the owner: the sheet looked like a browser dialog, grey box and white squares): Otty's line beside his portrait at the sheet's head, the steps as the app's list rows with a hairline between them, each glyph on the app's round tile (parts.js gtile, the sky tint), the acts as the app's pill and quiet link; no box around the rows. THE ONE HEAD OF AN ASK (2026-10-06, push.js Ask): the notifications sheet's head is the bell on a 56 px tile beside the title, in the install sheet's grid, so every ask of both lanes opens the same way */
.offer .ohead{display:grid;grid-template-columns:56px minmax(0,1fr);gap:14px;align-items:center;margin:0 0 14px}
.offer .ohead .gtile{--g-tile:56px;--g:var(--sky)}
.offer .ohead img{width:56px;height:56px;object-fit:contain}
.offer .ohead p{margin:0;font:500 var(--t5)/22px var(--sans);color:var(--ink);text-wrap:pretty}
.guide{--g:var(--sky);--g-tile:40px;margin-top:4px}
.guide:focus{outline:0}
.guide ol{list-style:none;margin:0;padding:0;font:500 var(--t4)/20px var(--sans);color:var(--ink)}
.guide li{display:grid;grid-template-columns:var(--g-tile) minmax(0,1fr);gap:14px;align-items:center;padding:12px 0}
.guide li+li{border-top:1px solid var(--hairline)}
/* the tile's glyph: the thin ink centred by the tile's own grid, nothing else (2026-10-03: the inline-key rule below was `.guide li span svg`, which also matched `span.gtile > svg`, so the tile's glyph got the key's white box, padding and margin and sat up-left; the key is its own class now) */
:is(.guide,.offer .ohead) .gtile svg{width:22px;height:22px;fill:none;stroke:currentColor;stroke-width:1.6;stroke-linecap:round;stroke-linejoin:round}
.offer .ohead .gtile svg{width:26px;height:26px}
/* the inline key glyph in the words (push.js Ink inline: the menu Share hides behind), a small white key on the line */
.guide .key{box-sizing:border-box;width:26px;height:20px;padding:1px 4px;margin:0 1px 0 4px;vertical-align:-5px;border-radius:var(--r1);background:#fff;box-shadow:var(--ring);fill:none;stroke:currentColor;stroke-width:1.6;stroke-linecap:round;stroke-linejoin:round}
.guide svg.dots{stroke-width:2.8}
.guide li small{display:flex;align-items:center;gap:7px;font:400 var(--t1)/16px var(--sans);color:var(--mute)}
.guide small .on{flex:none;position:relative;width:24px;height:14px;border-radius:var(--pill);background:var(--ink)}
.guide small .on::after{content:'';position:absolute;top:2px;right:2px;width:10px;height:10px;border-radius:50%;background:#fff}
.guide small.addr{display:block;font-family:var(--mono);-webkit-user-select:all;user-select:all;overflow-wrap:anywhere}
.offer .guide .acts{margin-top:10px}
/* THE ACCOUNT MENU'S SETTING ROWS, one rule (the language flip, shell.js, the owner 2026-09-21: "a flip radio button, elegant and in theme"; the notifications switch, push.js, the owner 2026-09-26): the row is as tall as its siblings (4 + 36 + 4 = a row's 44 px), the label left, the control right; a small line (the switch's how to allow it, a refusal's sentence in the warm) takes its own line under them. The device rows' holder (push.js) is no box of its own. The flip is the island's own shape, a capsule of two parts with the chosen part lit, in the paper's light dress */
.sheet .dev{display:contents}
.sheet .r.set{display:flex;flex-wrap:wrap;align-items:center;justify-content:space-between;gap:0 12px;padding:4px 4px 4px 14px;cursor:default}
.sheet .r.set:hover{background:none}
.sheet .set .switch{margin:5px 6px 5px 0}
.sheet .lang .flip{display:inline-flex;gap:2px;padding:3px;border-radius:var(--pill);background:var(--track-fill);box-shadow:var(--track-edge)}
.sheet .lang .flip button{--face:30px;height:var(--face);padding:0 13px;border-radius:var(--pill);background:none;font:500 var(--t2)/1 var(--sans);color:var(--mute);cursor:pointer;white-space:nowrap;position:relative;--lift-to:var(--ring);transition:color .2s,background .2s}
.sheet .lang .flip button:hover{color:var(--ink)}
.sheet .zone select{height:30px;max-width:58%;border:0;border-radius:var(--pill);background:var(--track-fill);box-shadow:var(--track-edge);padding:0 12px;font:500 var(--t2)/1 var(--sans);color:var(--ink);cursor:pointer}
.sheet .zone .why{flex-basis:100%}
.sheet .lang .flip button[aria-checked="true"]{background:#fff;color:var(--ink);cursor:default}
.sheet .lang .flip button:focus{outline:none}
.sheet .lang .flip button:focus-visible{outline:2px solid var(--sky-ink);outline-offset:2px}
.sheet .lang[aria-busy] .flip button{pointer-events:none}
.sheet .set small{flex-basis:100%;padding:2px 10px 8px 0;font:400 var(--t1)/17px var(--sans);color:var(--mute);text-wrap:pretty}
.sheet .set .why{color:var(--warm)}
.sheet .hands .why{display:block;margin:2px 14px 8px;font:500 var(--t2)/18px var(--sans);color:var(--warm)}
.sheet .hands[aria-busy] .r{pointer-events:none;opacity:.6;cursor:progress}
/* THE HAND-IN'S PHONE PAGE AND VIEWER (2026-09-22, handin.html on the upload page's door look): the camera control is a button (the upload page's is a label round its input; here the controller's own input is clicked), the two other ways centred under it, the rows of the desk's piece on the card's white with their thumbs, the finish pill as the card's one wide act, the viewer's page thumbs in a row and its video player on the card's width */
.upl button.take{width:100%;border:0;-webkit-appearance:none;appearance:none;font-family:inherit}
.hin .who > .ways{justify-content:center;margin-top:14px}
.hin .who > .notice{margin-top:18px}
.upl .who > .gt{margin-top:22px}
:is(.upl,.tray) .kv .v.acts .go.quiet,.hin .who > .ways .go.quiet{font-size:var(--t3)}
.hin .who > .count{text-align:center}
.upl .go.wide{margin-top:22px}
.hin .who > .moment{margin-top:22px}
.hin .thumbs{display:flex;flex-wrap:wrap;gap:8px;margin-top:2px}
.hin .thumbs img{width:64px;height:84px;object-fit:cover;border-radius:var(--r1);background:var(--raised);box-shadow:var(--raised-edge);display:block}
.hin .play{display:block;width:100%;max-height:min(56vh,420px);border-radius:var(--r3);background:#000;margin:4px 0 6px}
/* ── THE PARTS (parts.js; the owner, 2026-09-24: "Design elements for reusability"): each part's one look on the site's tokens; no colour and no size is typed here (tools/floor.mjs holds this block to the tokens) ── */
.pstate{display:inline-flex;align-items:center;gap:4px;vertical-align:-.2em}
.pstate svg{width:var(--icon-sm);height:var(--icon-sm);stroke:currentColor;stroke-width:1.9;stroke-linecap:round;stroke-linejoin:round;overflow:visible}
.pstate .pdot{fill:var(--sky);stroke:none}
.pstate.t-warm{color:var(--warn)}.pstate.t-sky{color:var(--sky)}.pstate.t-ok{color:var(--ok)}
.pstate .pmark{font:500 var(--t2)/18px var(--sans);font-variant-numeric:var(--tab);color:var(--ink)}
.pstate .draw{stroke-dasharray:30;stroke-dashoffset:0;animation:pdraw .45s ease-out both}
.pstate .shackle{transform-box:fill-box;transform-origin:50% 100%;animation:pshut .5s cubic-bezier(.2,1.4,.4,1) both}
.pstate .hand{transform-box:view-box;transform-origin:12px 12px;animation:psweep .6s ease-out both}
.p-alert svg{animation:ppulse .6s ease-out both}
.p-next svg{animation:pnudge .45s ease-out both}
.p-glass svg{animation:pturn .6s ease-out both}
@keyframes pdraw{from{stroke-dashoffset:30}}@keyframes pdraw2{from{stroke-dashoffset:30}}
@keyframes pshut{from{transform:translateY(-22%)}}@keyframes pshut2{from{transform:translateY(-22%)}}
@keyframes psweep{from{transform:rotate(-90deg)}}@keyframes psweep2{from{transform:rotate(-90deg)}}
@keyframes ppulse{40%{transform:scale(1.18);opacity:.7}}
@keyframes pnudge{50%{transform:translateX(3px)}}@keyframes pnudge2{50%{transform:translateX(3px)}}
@keyframes pturn{from{transform:rotate(-180deg)}}
.ptip{position:fixed;z-index:60;max-width:min(280px,80vw);padding:6px 10px;white-space:pre-line;border-radius:var(--r2);background:var(--ink);color:white;font:500 var(--t1)/16px var(--sans);pointer-events:none;animation:pfade .15s ease-out}
@keyframes pfade{from{opacity:0}}
.ptokens{display:flex;align-items:center;gap:12px}
.ptokens .row{display:flex;gap:6px}
.ptok{display:grid;place-items:center;width:var(--tok-s);height:var(--tok-s);border-radius:50%;box-shadow:var(--ring);position:relative;--lift-to:var(--ring-on),var(--glow);transform:scale(.92);transition:background .4s,transform .4s cubic-bezier(.2,1.4,.4,1)}
.ptok svg{width:var(--icon-sm);height:var(--icon-sm);fill:none;stroke:var(--faint);stroke-width:1.7;stroke-linecap:round;stroke-linejoin:round;transition:fill .4s,stroke .4s}
.ptok.lit{transform:none;background:var(--sky-soft);background:color-mix(in srgb,var(--g) 13%,white);--rc:var(--g)}
.ptok.lit svg{fill:var(--g);stroke:var(--g)}
.ptokens .w{display:grid}.ptokens .w b{font:500 var(--t3)/20px var(--sans);color:var(--ink)}.ptokens .w small{font:400 var(--t2)/18px var(--sans);color:var(--ink-soft)}
.ptokens.on-night .ptok{--rc:var(--isle-line);box-shadow:var(--ring-on)}.ptokens.on-night .ptok svg{stroke:var(--night-faint)}
.ptokens.on-night .w b{color:white}.ptokens.on-night .w small{color:var(--night-mute)}
/* THE AVATAR (parts.js Avatar), a person's one face: one soft round, one hairline inside its own edge (so the same contrast on navy and on white), the face's one crop filling it; `on` the one accent ring; the name beside or under it in one type style */
.pav{--av:var(--av-m);position:relative;display:inline-grid;place-items:center;flex:none;width:var(--av);height:var(--av);border-radius:50%;background:var(--av-ground);color:var(--ink-soft);font:600 calc(var(--av) * .34)/1 var(--sans);letter-spacing:.02em}
.pav.s-s{--av:var(--av-s)}.pav.s-l{--av:var(--av-l)}
.pav > img{display:block;width:100%;height:100%;border-radius:50%;object-fit:cover}.pav > .pemb.s-fill{width:70%;height:70%}
.pav::after{content:'';position:absolute;inset:0;border-radius:50%;--rc:var(--av-rim);box-shadow:var(--ring);pointer-events:none}
/* the accent ring: him among others, the one picked, and the head's round while its pane stands (shell.js) */
.pav.on::after,#me[aria-expanded=true] .pav::after{--rc:var(--sky);box-shadow:var(--ring-hair)}
.pav .ppen{position:absolute;z-index:1;right:-3px;bottom:-3px;display:grid;place-items:center;width:var(--icon-sm);height:var(--icon-sm);border-radius:50%;background:var(--sky);border:var(--hair) solid var(--paper)}
.pav .ppen svg{width:60%;height:60%;stroke:white;stroke-width:2.4;stroke-linecap:round;stroke-linejoin:round}
.pwho{display:inline-flex;align-items:center;gap:10px;min-width:0}.pwho.under{flex-direction:column;gap:6px;text-align:center}
.pwho .nm{display:grid;min-width:0;font:500 var(--t2)/18px var(--sans);color:inherit}.pwho.under .nm{justify-items:center}
.pround{--face:var(--round-h);--face-i:var(--face);display:grid;place-items:center;width:var(--face);height:var(--face);padding:0;border-radius:50%;background:none;box-shadow:var(--quiet-edge);color:var(--ink);cursor:pointer}
.pround:not(:disabled):not(.on-night):hover{background:var(--hover)}
.pround:disabled{opacity:.4;cursor:default}
.pround svg{width:var(--icon-s);height:var(--icon-s);stroke:currentColor;stroke-width:1.8;stroke-linecap:round;stroke-linejoin:round}
.pround.on-night{background:var(--isle);box-shadow:var(--edge-night),var(--lift-bar);color:white}
.pround.s-sm{--face:var(--chip-h-sm)}.pround.s-sm svg{width:var(--icon-sm);height:var(--icon-sm)}
.prow{border-radius:var(--r3)}
.prow .line{display:grid;grid-template-columns:auto minmax(0,1fr) auto auto;align-items:center;column-gap:12px;min-height:calc(var(--av-m) + 16px);padding:8px 12px}
.prow .w{min-width:0}.prow .w b{display:-webkit-box;-webkit-box-orient:vertical;-webkit-line-clamp:2;font:500 var(--t4)/20px var(--sans);color:var(--ink);overflow:hidden;overflow-wrap:anywhere}.prow .w small{display:block;font:400 var(--t2)/18px var(--sans);color:var(--ink-soft)}
.prow .fig{font:500 var(--t4)/20px var(--sans);font-variant-numeric:var(--tab);color:var(--ink)}
.prow.me{background:var(--sky-soft)}
.prow.open{background:white;box-shadow:var(--ring);padding-bottom:12px}
.pmeter .hd{display:flex;justify-content:space-between;gap:12px;margin-bottom:8px;font:500 var(--t2)/18px var(--sans);color:var(--ink-soft)}.pmeter .hd b{font-weight:500;color:var(--ink);font-variant-numeric:var(--tab)}
.pmeter .pbar{position:relative;display:block;height:var(--meter-h);border-radius:var(--pill);background:var(--track-fill);box-shadow:var(--track-edge);overflow:visible}
.pmeter .pbar > b{display:block;height:100%;border-radius:var(--pill);background:var(--sky)}
.pmeter .mk{position:absolute;top:-2px;bottom:-2px;width:var(--hair);margin-left:-1px;border-radius:var(--r1);background:var(--line)}
/* the step badge: the number in a round of --round-h, the progress a ring from the top, a tick once done, a lock while locked; a disclosure's open step (the open module) in the accent */
.pstep{position:relative;flex:none;display:grid;place-items:center;width:var(--round-h);height:var(--round-h);color:var(--ink-soft)}
.pstep > .pring{position:absolute;inset:0;width:100%;height:100%;overflow:visible}
.pstep .pring .rt{stroke:var(--track-line)}.pstep .pring .rn{stroke:var(--sky)}
.pstep > :is(b,.pglyph){position:relative}.pstep b{font:500 var(--t3)/1 var(--sans);font-variant-numeric:var(--tab)}
.pstep.s-done{color:var(--ok)}.pstep.s-done .pring .rn{stroke:var(--ok)}
.pstep.s-locked{color:var(--mute)}.pstep.s-locked .pring .rt{stroke-dasharray:var(--dash)}
/* DONE IS ALWAYS GREEN (round 2 of the audit: the open module's accent turned a done tick sky): the open step takes the accent unless it is done */
[open] > summary .pstep:not(.s-done){color:var(--sky-ink)}[open] > summary .pstep:not(.s-done) .pring .rt{fill:var(--sky-soft);stroke:var(--sky-soft)}
.pmeter.on-night .pbar{background:var(--isle-line);box-shadow:none}.pmeter.on-night .hd{color:var(--night-mute)}.pmeter.on-night .hd b{color:white}
.pbadge{position:relative;display:grid;justify-items:center;align-content:start;gap:6px;padding:14px 10px;border:0;border-radius:var(--r4);background:var(--raised);box-shadow:var(--raised-edge);color:var(--ink);text-align:center;font:500 var(--t2)/18px var(--sans)}
.pbadge .pmeter{width:100%}.pbadge small{font:400 var(--t1)/16px var(--sans);color:var(--ink-soft)}.pbadge .rw{font:500 var(--t1)/16px var(--sans);color:var(--sky-ink)}
.pbadge.close{--rc:var(--sky);box-shadow:var(--ring-on),var(--lift-raised)}
.pinfo{position:absolute;top:0;right:0;display:grid;place-items:center;width:var(--hit);height:var(--hit);border-radius:var(--r4);color:var(--mute);cursor:help}
.pinfo::before{content:'';width:var(--icon-sm);height:var(--icon-sm);background:currentColor;-webkit-mask:var(--glyph-info) center/var(--icon-sm) no-repeat;mask:var(--glyph-info) center/var(--icon-sm) no-repeat}
.pinfo:hover,.pinfo:focus-visible{color:var(--sky-ink)}
.pbadge .bx{display:block;width:var(--badge-art);height:var(--badge-art);overflow:visible;margin:2px 0}
.bx .shr{transform:translateX(0)}
.bx.in .shr{animation:pbsh 1.3s cubic-bezier(.3,.7,.2,1) .15s 1 both}
@keyframes pbsh{to{transform:translateX(210px)}}
.pempty{margin:0;padding:16px 0;font:400 var(--t3)/20px var(--sans);color:var(--ink-soft)}
.pglyph{width:var(--icon-s);height:var(--icon-s);flex:none;stroke:currentColor;stroke-width:1.7;stroke-linecap:round;stroke-linejoin:round}
/* THE GAME'S ICONS (parts.js ICON and Icon; the owner, 2026-09-27: "all these icons designs should have a meaning and how they relate to each other or where they are displayed"): a concept's tone sets its two colours (--g on paper, --g-lt on the night), the one place a game colour is named; inline the 16 px line glyph in --g (the night's --g-lt), tile the glyph filled on its tinted round (on: the gold round and its dot, sparks wait; off: THE SURFACES' waiting, none yet), the level's tile the night lens ringed in its metal */
.k-spark{--g:var(--g-spark);--g-lt:var(--g-spark-lt)}.k-sparkle{--g:var(--g-sparkle);--g-lt:var(--g-sparkle-lt)}.k-thanks{--g:var(--g-thanks);--g-lt:var(--g-thanks-lt)}.k-flame{--g:var(--g-flame);--g-lt:var(--g-flame-lt)}.k-chest{--g:var(--g-chest);--g-lt:var(--g-chest-lt)}.k-mission{--g:var(--g-mission);--g-lt:var(--g-mission-lt)}.k-rank,.k-ink{--g:var(--g-rank);--g-lt:var(--g-rank-lt)}
.k-level.t0{--g:var(--ink-soft);--g-lt:var(--night-mute)}.k-level.t1{--g:var(--g-level-1);--g-lt:var(--g-level-1-lt)}.k-level.t2{--g:var(--g-level-2);--g-lt:var(--g-level-2-lt)}.k-level.t3{--g:var(--g-level-3);--g-lt:var(--g-level-3-lt)}
.gi{width:var(--icon-sm);height:var(--icon-sm);flex:none;fill:none;stroke:currentColor;stroke-width:1.8;stroke-linecap:round;stroke-linejoin:round;color:var(--g)}
:where(.pgoal,.door,.qcer,.qrow.me,.on-night) .gi{color:var(--g-lt)}
/* THE BANK ACT, ONE (stages.js Bank; the owner, 2026-09-29: "can this Bank button be designed a bit more elegantly and aligned with the chests etc, but also with the website design ... it should always stand out"): the chest's own act in the chest's tone family, never a gold slab: the word and its figure (the spark in its own sky) in the ink on TINTED at the chest's gold, the hairline the gold; a chest always stands beside it (the row's glyph, the tile's or the goal's art), so it draws none; on the night the night's fill, the hairline and the word in the chest's light; the hero on the goal panel white as the goal's own act, ringed in the gold; while sparks wait one gold light crosses it as it appears and then it rests (none with reduced motion); nothing to bank: WAITING in the gold, the words muted, no act, no motion; its hit layer is its ::before (THE HIT) */
.pbank{--g:var(--g-chest);--g-lt:var(--g-chest-lt);--gleam:rgba(246,195,80,.55);--gleam:color-mix(in srgb,var(--g-chest-lt) 55%,transparent);--face:32px;position:relative;display:inline-flex;align-items:center;justify-content:center;gap:6px;height:var(--face);padding:0 14px;border:0;border-radius:var(--pill);background:var(--tint);box-shadow:var(--tint-edge);color:var(--ink);font:500 var(--t3)/1 var(--sans);font-variant-numeric:var(--tab);white-space:nowrap;cursor:pointer;transition:background-color .15s,box-shadow .15s}
.pbank > span{position:relative;z-index:1}
.pbank:not(.off):hover{--tint-hi:rgba(255,255,255,.86);--tint-lo:rgba(255,255,255,.78)}
.pbank:not(.off)::after{content:'';position:absolute;inset:0;border-radius:inherit;pointer-events:none;background:linear-gradient(115deg,transparent 35%,var(--gleam) 50%,transparent 65%) no-repeat;background-size:260% 100%;background-position:-160% 0;animation:pbank-gleam 1.6s ease-out .6s 1 both}
@keyframes pbank-gleam{from{background-position:-160% 0}to{background-position:160% 0}}
@media (prefers-reduced-motion:reduce){.pbank::after{content:none}}
.pbank.hero{--face:var(--goal-act);padding:0 20px;font-size:var(--t4)}
.pbank.off{background:var(--wait);box-shadow:var(--wait-edge);color:var(--mute);cursor:default}
:where(.pgoal,.door,.on-night) .pbank{background:var(--night-fill);--rc:rgba(246,195,80,.45);--rc:color-mix(in srgb,var(--g-chest-lt) 45%,transparent);box-shadow:var(--ring);color:var(--night);--gleam:rgba(246,195,80,.35);--gleam:color-mix(in srgb,var(--g-chest-lt) 35%,transparent)}
:where(.pgoal,.door,.on-night) .pbank:not(.off):hover{background:rgba(255,255,255,.1)}
:where(.pgoal,.door,.on-night) .pbank.off{background:none;--rc:rgba(246,195,80,.3);--rc:color-mix(in srgb,var(--g-chest-lt) 30%,transparent);color:var(--night-mute)}
.pgoal .pbank.hero{background:white;color:var(--ink);--rc:rgba(189,127,10,.55);--rc:color-mix(in srgb,var(--g-chest) 55%,transparent);box-shadow:var(--ring-on)}.pgoal .pbank.hero:hover{background:white}.pgoal .pbank.hero .gi{color:var(--g)}
.door .stat .pbank{--face:24px;justify-self:start;padding:0 10px;font-size:var(--t2)}
.gtile{position:relative;display:grid;place-items:center;flex:none;width:var(--g-tile);height:var(--g-tile);border-radius:50%;background:var(--tint);box-shadow:var(--tint-edge);color:var(--g)}
.gtile svg{width:calc(var(--g-tile) * .62);height:calc(var(--g-tile) * .62);fill:currentColor;fill-opacity:.3;stroke:currentColor;stroke-width:1.7;stroke-linecap:round;stroke-linejoin:round}
.gtile.off{background:var(--wait);box-shadow:var(--wait-edge)}.gtile.off svg{fill:none;opacity:.6}
.gtile.on{background:var(--gold);color:var(--g-on-gold);box-shadow:var(--gold-lift)}
.gtile.on::after{content:'';position:absolute;right:-2px;top:-2px;width:var(--g-dot);height:var(--g-dot);border-radius:50%;background:var(--g-dot-lit);box-shadow:var(--gold-glow);animation:pdot 1.8s ease-in-out infinite}
@keyframes pdot{50%{opacity:.45}}
.gtile.k-level{background:var(--field);color:var(--g-lt);--rc:var(--g-lt);box-shadow:var(--ring-hair)}.gtile.k-level svg{fill-opacity:1}.gtile.k-level.t0 svg{fill:none}
@media (prefers-reduced-motion:reduce){.gtile.on::after{animation:none}}
.pbuddy{display:grid;gap:12px;justify-items:center}
.pbuddy .look{display:grid;grid-template-columns:auto minmax(0,1fr) auto;align-items:center;gap:8px;width:100%}
.pbuddy .look img{justify-self:center;width:min(60vw,calc(var(--face-pod) * 4));aspect-ratio:1;object-fit:contain}
.pbuddy .nm{margin:0;font:500 var(--t5)/22px var(--sans);color:var(--ink)}
.pbuddy .row{display:flex;gap:8px;width:100%;scroll-snap-type:x proximity;scrollbar-width:none;padding:4px 2px 8px}
.pbuddy .row::-webkit-scrollbar{display:none}
.pbuddy .row button{flex:none;display:grid;place-items:center;width:var(--face-pod);height:var(--face-pod);padding:0;border:0;border-radius:var(--r4);background:var(--raised);box-shadow:var(--raised-edge);scroll-snap-align:center;cursor:pointer}
.pbuddy .row button img{width:100%;height:100%;object-fit:contain}
.pbuddy .row button[aria-pressed=true]{background:var(--sky-soft);--rc:var(--sky);box-shadow:var(--ring-on)}
.pbuddy .foot{display:flex;justify-content:flex-end;width:100%;min-height:var(--round-h);align-items:center}
.pgoal{display:grid;gap:14px;padding:20px;border-radius:var(--r4);background:radial-gradient(120% 90% at 85% 0%,rgba(var(--sky-rgb),.3),rgba(var(--sky-rgb),0) 60%),var(--field);color:white}
.pgoal .k{font:500 var(--t1)/16px var(--sans);letter-spacing:.08em;text-transform:uppercase;color:rgb(var(--ice-rgb))}
.pgoal h2{margin:4px 0 14px;font:500 var(--t7)/28px var(--sans);letter-spacing:-.01em;color:white}
.pgoal .gw > :last-child{margin-bottom:0}
.pgoal .pmeter .pbar{height:var(--goal-bar)}.pgoal .pmeter .pbar > b{background:rgb(var(--ice-rgb))}
.pgoal .go.pwhite{justify-self:start;display:inline-flex;align-items:center;gap:10px;height:var(--goal-act);padding:0 6px 0 18px;border-radius:var(--pill);background:white;color:var(--ink);box-shadow:none;font:500 var(--t3)/1 var(--sans);text-decoration:none}
.pgoal .go.pwhite i{display:grid;place-items:center;width:var(--way-r);height:var(--way-r);border-radius:50%;font-style:normal}
.ptiles{display:grid;grid-template-columns:repeat(var(--n,3),minmax(0,1fr));gap:var(--y-gap,12px)}
.ptile{display:grid;grid-template-columns:auto minmax(0,1fr);grid-template-areas:'ld ch' 'v v' 's s' 'ft ft';align-content:start;align-items:center;column-gap:8px;min-width:0;padding:14px 16px;border-radius:var(--r4);background:var(--raised);box-shadow:var(--raised-edge)}
/* THE ME TILES' ART (the coordinator's review of the surfaces, 2026-09-27: "think designer, and lux"): the chest, the flame and the level each lead their tile with their own art piece at --tile-art (the chest in its state, the flame's badge in its waiting outline before a flame, the level's medal), one row of art, never art beside a line glyph; a tile with no figure says its words as its title; the chest's card wears the gold's faint wash with its top light and a gold hairline */
.ptile.t-chest{--g:var(--g-chest);background:var(--wait);box-shadow:var(--tint-edge),var(--lift-raised)}.ptile.t-flame{--g:var(--g-flame)}
.ptile > .ld:has(> :is(.pchestart,.qmedal,.bx,.s-art)){min-height:var(--tile-art);margin-bottom:var(--it-y)}
.gtile.s-art{width:var(--tile-art);height:var(--tile-art)}.gtile.s-art svg{width:calc(var(--tile-art) * .5);height:calc(var(--tile-art) * .5)}.gtile.s-art.off svg{fill:currentColor;fill-opacity:.22;opacity:.85}
.ptile > .ld > .pchestart{width:calc(var(--tile-art) * 1.18);height:var(--tile-art)}.ptile > .ld > .qmedal,.ptile > .ld > .bx{width:var(--tile-art);height:var(--tile-art);margin:0}
.ptile:not(:has(> .v)) > small{font:600 var(--t6)/24px var(--sans);letter-spacing:-.01em;color:var(--ink)}
.ptile > .ld{grid-area:ld;display:flex;align-items:center;min-height:var(--chip-h);margin-bottom:6px}.ptile > .ch{grid-area:ch;justify-self:end;align-self:start}
.ptile > .v{grid-area:v;font:600 var(--t7)/28px var(--sans);font-variant-numeric:var(--tab);color:var(--ink);overflow-wrap:anywhere}
.ptile > small{grid-area:s;font:400 var(--t2)/18px var(--sans);color:var(--ink-soft)}
.ptile > .ft{grid-area:ft;margin-top:8px}
.ptile .ptokens .w{display:none}
/* on a phone the tiles stand one under another, each one line: the lead, the figure over its words, the chip; the foot under them */
@media (max-width:599px){:root{--tile-art:48px}.ptile:not(:has(> .v)) > small{grid-row:1 / span 2;align-self:center}.ptile > .ld:has(> :is(.pchestart,.qmedal,.bx,.s-art)){margin:0}.ptiles{grid-template-columns:minmax(0,1fr)}.ptile{grid-template-columns:auto minmax(0,1fr) auto;grid-template-areas:'ld v ch' 'ld s ch' 'ft ft ft';column-gap:12px;padding:12px 14px}.ptile > .ld{margin:0}.ptile > .ch{align-self:center}.ptile > .v{font-size:var(--t6);line-height:24px}}
.pring{width:var(--goal-act);height:var(--goal-act);flex:none;transform:rotate(-90deg)}
.pring circle{fill:none}.pring .rt{stroke:white}.pring .rl{stroke:var(--line);stroke-width:2;stroke-linecap:round}.pring .rn{stroke:var(--sky);stroke-linecap:round}
/* THE TIMER CHIP (parts.js Timer): a clock and the time left, on the paper's well */
.ptimer{display:inline-flex;align-items:center;gap:6px;font:400 var(--t3)/20px var(--sans);font-variant-numeric:var(--tab);color:var(--mute);white-space:nowrap}.ptimer .pglyph{width:var(--icon-sm);height:var(--icon-sm)}
/* a sentence for a screen reader alone, beside its short form on screen */
.pvh{position:absolute;overflow:hidden;clip-path:inset(50%);white-space:nowrap}
/* a tile or a row that is a tap (parts.js Tiles, PersonRow tap): the same look, the whole of it the button */
.ptile.tap,.prow.tap{width:100%;border:0;font:inherit;color:inherit;text-align:left;cursor:pointer}.ptile.tap:hover,.prow.tap:hover{--rc:var(--sky-glow);box-shadow:var(--ring-on)}
/* the house emblem (parts.js Emblem): a row, a tile and a card size; its glow a pre-drawn halo whose opacity moves once (never a filter: WebKit crashed on an animated one) */
.pemb{display:block;flex:none;width:var(--crest-row);height:var(--crest-row);overflow:visible}
.pemb.s-inline{width:var(--crest-inline);height:var(--crest-inline)}.pemb.s-card{width:var(--crest-card);height:var(--crest-card)}.pemb.s-hero{width:min(var(--crest-hero),100%);height:auto;aspect-ratio:1}.pemb.s-fill{width:100%;height:100%}
.pemb .eh{opacity:0}.pemb.glow .eh{animation:pemglow 1.8s ease-out .2s 1 both}
/* THE CHEST ART (parts.js ChestArt): the drawn chest at a row's lead; ready, a soft glow breathes behind it (still with reduced motion); lost, faded to grey */
/* THE SPARK ART (parts.js sparkSvg, SparkArt; 2026-09-27, the owner: "a bit dull compared, even maybe with a shine that appears from time to time"): the art size of the spark beside the chest and the medal; its tip's twinkle glints once every 9 s and rests between; none with reduced motion */
.psparkart{display:grid;place-items:center;flex:none;width:var(--av-m);height:var(--av-m)}
.pspark{width:100%;height:100%;overflow:visible}
.pspark .tw{transform-box:fill-box;transform-origin:center;opacity:0;animation:pspark-tw 9s ease-in-out 1.5s infinite}
@keyframes pspark-tw{0%,86%,100%{opacity:0;transform:scale(.3) rotate(0)}91%{opacity:1;transform:scale(1.15) rotate(45deg)}95%{opacity:.8;transform:scale(.9) rotate(70deg)}}
@media (prefers-reduced-motion:reduce){.pspark .tw{animation:none;opacity:.8;transform:none}}
.pchestart{position:relative;display:grid;place-items:center;flex:none;width:calc(var(--av-m) + 8px);height:var(--av-m)}
.pchestart .pchest{position:relative;width:100%;height:100%;overflow:visible}
.pchestart.s-ready::before{content:'';position:absolute;inset:-12px;border-radius:50%;background:radial-gradient(circle,rgba(255,226,154,.5),rgba(var(--glow-rgb),.3) 40%,rgba(var(--glow-rgb),0) 68%);animation:pcglow 2.4s ease-in-out infinite alternate}
@keyframes pcglow{from{opacity:.5;transform:scale(.9)}to{opacity:1;transform:scale(1.06)}}
@media (prefers-reduced-motion:reduce){.pchestart.s-ready::before{animation:none}}
.pchestart.s-lost{opacity:.45;filter:grayscale(.7)}
@keyframes pemglow{0%{opacity:0}35%{opacity:.9}100%{opacity:0}}
@media (prefers-reduced-motion:reduce){.pemb.glow .eh{animation:none}}
.pgoal .ga{display:flex;align-items:center;gap:8px}
.pchoices{display:grid;gap:8px;margin-top:8px}
.pchoice{display:flex;align-items:center;gap:12px;width:100%;min-height:var(--hit);padding:0 14px;border:0;background:var(--raised);box-shadow:var(--raised-edge);border-radius:var(--r3);color:var(--ink);text-align:left;font:500 var(--t4)/22px var(--sans);cursor:pointer}
.pchoice span{flex:1;min-width:0}
.pchoice i{font-style:normal;color:var(--mute)}
.paybox .pchoice{background:white;box-shadow:var(--ring)}
/* THE COURSE PIECES (parts.js Crest, KindRound, RowAct, Tick, Segments, DoneRing, the Step's five states; the Courses and Tasks proposal the owner approved, 2026-09-27: "make sure everything is for a reason and works elegantly"): a subject's hue (--h, THE SUBJECT FAMILIES above, by the course's subject since 2026-10-07; ~~six hues by the course id~~) on its crest, its bar and its wash; the kind's glyph in a quiet round, tinted by its state; the row's one act a pill of the verb; done a green round whose tick draws once; the bars and rings fill once, a kept value paints still */
/* THE SUBJECT FAMILIES (the owner, 2026-10-07, after the teachers' review: "give each subject's whole block a colour, the same colour for that subject in every grade"; learn.js subjectHueOf the one source, my-api's rules.lms.subjectFamilies the one map): seven families, each ONE hue across every grade: languages blue, maths violet, sciences olive, social and commerce rust, arts and technology rose, life (LO, coaching) teal, other slate; the class sets --h, the wash (the light ground of a tile, a row and a lesson card: 9 % of the hue over white) and the edge (18 %); the crest wears the full hue with a white letter, the bar fills in the hue. NO COLOURED BAND AND NO COLOURED SIDE (the owner, 2026-10-07: "I don't like the top border being coloured only, or any side alone"): the colour is the whole ground, never one edge. AA: the ink on every wash 12.1 to 12.4, the mute text 4.7 to 4.8, the white letter on the full hue 4.5 to 6.9 (18 px bold) */
.hue-languages{--h:var(--h-1)}.hue-life{--h:var(--h-2)}.hue-arts{--h:var(--h-3)}.hue-sciences{--h:var(--h-4)}.hue-social{--h:var(--h-5)}.hue-other{--h:var(--h-6)}.hue-maths{--h:var(--h-v)}
.hue-languages,.hue-life,.hue-arts,.hue-sciences,.hue-social,.hue-other,.hue-maths{--wash:var(--raised);--h-edge:var(--hairline);--wash:linear-gradient(180deg,color-mix(in srgb,var(--h) 9%,white),color-mix(in srgb,var(--h) 7%,white));--h-edge:color-mix(in srgb,var(--h) 18%,transparent);--hue-edge:inset 0 0 0 1px var(--h-edge),var(--lift-raised)}
.pcrest{position:relative;flex:none;display:grid;place-items:center;width:var(--crest-m);height:var(--crest-m);border-radius:var(--r3);background:var(--h,var(--ink-soft));color:white;font:650 var(--t6)/1 var(--sans);letter-spacing:-.02em;padding-bottom:var(--r1)}
.pcrest small{position:absolute;inset:auto 0 5px;text-align:center;font:600 var(--t1)/1 var(--sans);font-size:8px;letter-spacing:.06em;opacity:.85}
.pcrest.s-s{width:var(--crest-s);height:var(--crest-s);border-radius:var(--r2);font-size:var(--t3);padding-bottom:0}
.pcrest.s-l{width:var(--crest-l);height:var(--crest-l);border-radius:var(--r4);font-size:var(--t7);padding-bottom:var(--r2)}.pcrest.s-l small{font-size:10px;bottom:8px}
.pkind{display:grid;place-items:center;flex:none;width:var(--kind-s);height:var(--kind-s);border-radius:var(--r2);background:white;box-shadow:var(--quiet-edge);color:var(--ink-soft)}
.pkind svg{width:var(--icon-s);height:var(--icon-s);fill:none;stroke:currentColor;stroke-width:1.7;stroke-linecap:round;stroke-linejoin:round}
.pkind.s-now{--g:var(--sky);background:var(--tint);box-shadow:var(--tint-edge);color:var(--sky-ink)}
.pkind.s-done{--g:var(--ok);background:var(--tint);box-shadow:var(--tint-edge);color:var(--ok)}
.pkind.s-soon{--g:var(--g-flame);background:var(--tint);box-shadow:var(--flame-edge);color:var(--g-flame)}
.pkind.s-lock{background:none;box-shadow:var(--quiet-edge);color:var(--faint)}
.pkind.s-past{background:none;box-shadow:none;color:var(--faint)}
.pact{--face:var(--act-h);display:inline-flex;align-items:center;gap:8px;height:var(--act-h);padding:0 16px;border-radius:var(--pill);background:white;box-shadow:var(--quiet-edge);color:var(--ink);font:500 var(--t2)/1 var(--sans);white-space:nowrap;transition:transform .2s ease}
.pact.t-sky{--g:var(--sky);background:var(--tint);box-shadow:var(--sky-edge);color:var(--sky-ink)}
.pact.t-ink{background:var(--ink);color:white;box-shadow:var(--lift-bar);padding:0 5px 0 16px}
.pact.t-ink .way{width:var(--act-way);height:var(--act-way);border-radius:50%;background:rgba(255,255,255,.28)}
a:hover > * > .pact,a:hover > .pact{transform:translateX(2px)}
.ptick{display:grid;place-items:center;flex:none;width:var(--tick-s);height:var(--tick-s);border-radius:50%;background:var(--ok);background:var(--ok-disc);box-shadow:var(--lift-ok);color:white}
.ptick svg{width:var(--icon-sm);height:var(--icon-sm);fill:none;stroke:currentColor;stroke-width:2.6;stroke-linecap:round;stroke-linejoin:round}
.ptick path{stroke-dasharray:24;animation:tick-draw .45s .15s ease-out both}
.pseg{display:flex;gap:3px;height:var(--seg-h);flex:1 1 auto;min-width:0}
.pseg > i{flex:1;border-radius:var(--seg-h);background:var(--track-fill);box-shadow:var(--track-edge)}
.pseg > i.d{background:var(--h,var(--sky));box-shadow:none;transform-origin:left center}
.pseg.grow > i.d{animation:fill .6s cubic-bezier(.2,.8,.2,1) calc(var(--i) * 40ms) both}
.pseg > i.bar{position:relative;overflow:hidden}.pseg > i.bar > b{position:absolute;inset:0 auto 0 0;border-radius:inherit;background:var(--h,var(--sky));transform-origin:left center}
.pseg.grow > i.bar > b{animation:fill .6s cubic-bezier(.2,.8,.2,1) both}
.pstep.s-now{border-radius:50%;box-shadow:var(--halo);color:var(--sky-ink)}.pstep.s-now .pring .rn{stroke:var(--sky)}
.pstep.s-done{border-radius:50%;background:var(--ok);background:var(--ok-disc);box-shadow:var(--lift-ok);color:white}.pstep.s-done .pring{display:none}.pstep.s-done .pglyph{stroke-width:2.4}
.pstep.s-past{color:var(--faint)}.pstep.s-past .pring .rt{stroke:var(--hairline)}
.pstep .pdotstep{width:var(--dot-s);height:var(--dot-s);border-radius:50%;background:white;box-shadow:var(--dot-edge)}
/* ── END OF THE PARTS ── */
@media (max-width:1279px){
  /* up to 1280px the week has no room beside the box: the statement, the box across, the week under it across */
  .app.enroldoor .stage{grid-template-columns:1fr;grid-template-rows:auto auto}
  .app.enroldoor .statement{grid-column:1;grid-row:1}
  .app.enroldoor .shell{grid-column:1;grid-row:2;width:min(100%,760px)}
}
@media (max-width:999px){
  /* THE PARENT'S HEAD ONE HEIGHT ON A PHONE AND A TABLET (2026-09-27, the tap lane: a long line on Marks moved the card 19 px): his words' box holds three lines and a Show me row from the first paint, the learner's rule (quest.css HIS HEAD ONE HEIGHT: three lines, the words scrolling inside past them), so a line or its act landing moves nothing under the band */
  .app:not(.learn):not(.door):not(.enroldoor) .otty > .say{min-height:calc(22px + 3 * 19px + 12px + 36px)}
  .app:not(.learn):not(.door):not(.enroldoor) .otty .say .bub .said-words{max-height:57px;overflow-y:auto;overscroll-behavior:contain;scrollbar-width:none}
  .app:not(.learn):not(.door):not(.enroldoor) .otty .say .bub .acts .go{--face:36px;height:36px;min-height:36px;padding:0 14px;font-size:var(--t2)}
}
@media (max-width:999px){
  /* the phone and the tablet: one frame (the band, the rounds) for every page, sheet and popup */
  :root{--band:4px;--round:24px;--rim-r:28px;--top-h:56px;--corner-x:10px;--corner-y:calc(10px + 2 * var(--hit) + 8px);--sheet-top:calc(var(--corner-y) + var(--bar-gap) + env(safe-area-inset-top));--sheet-x:var(--corner-x);--sheet-bottom:calc(var(--bar-room) + var(--bar-gap))}
  .flow .card{overflow:clip}
  /* a flow's foot sticks over the bar (the card's own foot alone: as .flow .foot it reached every foot in a flow page, the hand-in confirm's too, and stood that sheet's acts over its words on a phone) */
  .flow .card > .foot{position:sticky;bottom:var(--bar-room);background:var(--glass);-webkit-backdrop-filter:var(--glass-blur);backdrop-filter:var(--glass-blur)}
  /* the enrol door on a phone or tablet: the words, then the form */
  .enroldoor .statement,.enroldoor .shell{grid-column:1}
  .enroldoor .statement{grid-row:1;padding:8px 0 12px}
  .enroldoor .statement h1{font-size:var(--t8);margin:8px 0 6px}
  .enroldoor .statement p{font-size:var(--t3);line-height:20px}
  .enroldoor .statement p br{display:none}
  .enroldoor .shell{grid-row:2}
  .isl.two .seg{padding:0 10px;font-size:var(--t2)}
  /* on a phone every control is at least 16px, so iOS never zooms into a field and the person never has to zoom back out */
  input,select,textarea{font-size:max(1em,16px)}
  .enroldoor .glance{gap:8px 20px;margin-top:12px}
  /* THE STATEMENT'S HEIGHT FROM THE FIRST PAINT (the morning round, 2026-09-28: a signed-in guardian's statement and facts are drawn once the family's country is known, about 2 s in, and pushed the form 159 px down, a shift of 0.133 at 393): each empty line holds one line of its own font, the facts their rows as they wrap at each width (a row: a label of 12 px, 3 px, a value of 20 px; 8 px between rows; one row from 600 px, two from 381 px, three under it, measured on 360, 393, 430, 700 and 820) */
  .enroldoor .statement :is(.eyebrow,h1,p):empty::before{content:'\00a0'}
  .enroldoor .glance:empty{min-height:35px}
  /* ── FILL (the owner, 2026-09-15, a trial on the Learners page for his eye): on a phone and a tablet the white box fills the screen edge to edge and down to the bottom; only its top curve with the glass ring around it stays; the bar floats over the white as light glass with our icons in ink ── */
  html[data-fill=on] .app:not(.door):not(.enroldoor){--shell-foot:0px;--foot-r:0px;--round-shell:var(--rim-r) var(--rim-r) 0 0}
  html[data-fill=on] .app:not(.door):not(.enroldoor){--stage-h:100vh;--stage-h:100dvh}
  /* a place scrolled to (a module opened, a question, an anchor) lands under his head row, never behind it */
  html[data-fill=on] .app:not(.door):not(.enroldoor) .card *{scroll-margin-top:calc(var(--otty-row) + env(safe-area-inset-top) + var(--row))}
  html[data-fill=on] .app:not(.door):not(.enroldoor) .stage{display:flex;flex-direction:column;padding:0;overflow:hidden auto;max-width:none}
  /* THE GUARDIAN'S HEAD ONE HEIGHT ON A PHONE (2026-09-28, the skeleton's capture: his band stood 149 then 138 px as his words landed, so the page under it moved 0.011 on the Learners page, Billing and Settings): his column holds the learner's rule, its row's height named, 149 px, the height it takes with his three-line box */
  html[data-fill=on] .app:not(.learn):not(.door):not(.enroldoor) .otty{--otty-row:149px}
  html[data-fill=on] .app:not(.door):not(.enroldoor) .otty{margin:0;--osz:104px;min-height:calc(max(var(--otty-row), var(--corner-y) + 12px) + env(safe-area-inset-top));padding:calc(10px + env(safe-area-inset-top)) 14px 12px;column-gap:14px}
  html[data-fill=on] .app:not(.door):not(.enroldoor) .otty > :is(.say,.under){margin-right:calc(var(--corner) - 14px)}
  html[data-fill=on] .app:not(.door):not(.enroldoor) .stage > .shell{flex:1 0 auto;width:100%;margin:0;padding:var(--band) var(--band) 0;border-radius:var(--round-shell,var(--rim-r));display:flex;flex-direction:column;box-shadow:var(--edge-night)}
  html[data-fill=on] .app:not(.door):not(.enroldoor) .stage > .shell > .card{flex:1 0 auto;border-radius:var(--card-r)}
  /* THE TOP ON A PHONE AND A TABLET, a trial (the owner, 2026-09-15 12:3x UTC: "move Otty to the top, no need for the menu bar and the T, fit his message nicely there, so there is more space for the white and Otty can be slightly larger"): the top bar folds to the bell alone at the top right; Otty and his words take the top row, he a little larger; the white box starts higher */
  /* THE CORNER'S ROOM, ONE RULE (round 3b review: at 852x393 the initials' round covered a module's fold arrow): the round stands at the top right over the page, --corner-x in from the edge; what scrolls keeps --corner clear of it: his words and his row beside it always (his .say and .under, one rule since 2026-09-26), and the whole page wherever no head row stands over the corner (--under: a page with no head row, a short screen whose head row goes up with the page) */
  /* THE BELL UNDER THE ROUND (the owner, 2026-09-25: "mobile needs the notification bell too. It was there before, vertically, with its counter"): the corner holds one column of two rounds, the round over the bell; --corner-y is the column's foot, and his head row is never shorter than it with its 12 px of air */
  html[data-fill=on] .app:not(.door):not(.enroldoor){--corner:calc(var(--corner-x) + var(--hit) + 14px)}
  html[data-fill=on] .app:not(.door):not(.enroldoor) .stage:not(:has(> .otty)){--under:1}
  html[data-fill=on] .app:not(.door):not(.enroldoor) .stage{padding-right:calc(var(--corner) * var(--under,0))}
  html[data-fill=on] .app:not(.door):not(.enroldoor) .stage > .otty{margin-right:calc(var(--corner) * var(--under,0) * -1)}
  /* THE INSTALLED APP'S STRIP KEEPS THE CORNER'S ROOM (2026-10-17, the lane web-owed-4; wave 13 by eye at 393: the strip's close round ran under the corner's column): push.js mounts the strip after the top bar, so on a phone and a tablet it is the first band of the page where the bar floats at the corner; its band ends --corner short of the edge, as his words do (the one rule above); wide, the bar stands in the flow over it and nothing meets it */
  html[data-fill=on] .app:not(.door):not(.enroldoor) > .stripper{padding-right:var(--corner)}
  html[data-fill=on] .top{position:absolute;right:var(--corner-x);left:auto;width:auto;height:auto;padding:0;z-index:7;top:calc(10px + env(safe-area-inset-top))}
  html[data-fill=on] .top .logo{display:none}
  /* THE ONE WIDGET (the owner, 2026-09-21: "combine the two widgets that open and don't open but just the previous buttons open"; 2026-09-22: "I expected a better combined menu and more useful"): the island stands upright at the top right as one glass of 96 px with the pill's 25 px corner: the now button at the left where the rules below draw it (its face day.js compact, seated in it by shell.js; it opens the pane at Today), a hairline, then the bell over the initials, both round; with no face the two rounds alone, the pill as it was */
  #me .ini{display:none}
  html[data-fill=on] .top .isl{display:grid;grid-template-columns:auto auto auto;grid-template-rows:44px 44px;grid-template-areas:'now rule bell' 'now rule me';gap:2px 0;padding:3px;border-radius:var(--round)}
  html[data-fill=on] .top #bell{grid-area:bell}
  html[data-fill=on] .top #me{grid-area:me}
  html[data-fill=on] .top .rule{display:none}
  html[data-fill=on] .top #me .nm,html[data-fill=on] .top #me .chev{display:none}
  html[data-fill=on] .top #me .ini{display:grid}
  html[data-fill=on] .top #bell,html[data-fill=on] .top #me{--face:var(--hit);--face-i:var(--hit);width:var(--hit);height:var(--hit);padding:0;justify-content:center;border-radius:50%}
  /* the drawer on a phone and a tablet: the upright capsule stays as it is, the two as stacked tabs, the pane under it as wide as the screen less the gutters */
  html[data-fill=on] .me.open .isl{border-radius:var(--round) var(--round) 0 0}
  /* THE FACE OF THE ONE WIDGET (the owner, 2026-09-22: "just date is not useful"; day.js compact): three small lines, the first cut with an ellipsis where a subject's word is long (Afrikaans FAL), the middle light and tabular, the last lit in the sky while a lesson runs and can be joined; the lesson's dot on the widget's top left rim: a ring within the hour, full and lit while the lesson runs (the ring filled with the glass's own tone so the rim does not cross it); the face is drawn from the width where every built line of his stands whole beside it, measured in both languages (the block after this one), and the shell takes it away where his words would stand taller than he does (shell.js fit, none), so the head never grows; his words keep 8 px clear of the widget (--now, the face's width, and the hairline's 7 px with its air) from the first paint; the margin's change takes no time, so the shell's measure reads the head as it will stand (reduced motion gives every change its hundredth of a millisecond) */
  html[data-fill=on] .app.learn .otty .say{transition:none}
  .top #now{position:relative}
  .top #now .today{display:block;width:100%;height:100%}
  .top #now .g{display:grid;align-content:center;justify-items:center;height:100%}
  .top #now .d{display:grid;justify-items:center;width:100%;min-width:0}
  .top #now .k{max-width:100%;font-size:var(--t1);line-height:12px;letter-spacing:.04em;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
  .top #now .k.lit{color:rgb(var(--glow-rgb))}
  .top #now .d b{font:300 var(--t6)/22px var(--sans);font-variant-numeric:var(--tab);letter-spacing:-.01em}
  .top #now .g[data-soon]::after,.top #now .g[data-now]::after{content:'';position:absolute;top:1px;left:1px;width:6px;height:6px;border-radius:50%;background:#1e2050;--rc:rgba(255,255,255,.5);box-shadow:var(--ring-on)}
  .top #now .g[data-now]::after{background:rgb(var(--glow-rgb));box-shadow:var(--glow)}
  /* the door's bubble follows the slot's phone type (the owner, 2026-09-17: smaller font on mobile) */
  .popup{width:calc(100vw - 32px - 2 * var(--rim-w));height:calc(100vh - 96px);height:calc(100dvh - 96px)}
  /* the round and the bell under it are the island's two children, one column: every other child goes, with an ID's weight so no wider rule (the day tile's rule, #now ~ .rule) can bring one back and grow the capsule (2026-09-24 at 430 px: an 87 px capsule) */
  html[data-fill=on] .app:not(.door):not(.enroldoor) .top .isl{grid-template-columns:auto;grid-template-rows:var(--hit) var(--hit);grid-template-areas:'me' 'bell';padding:3px;border-radius:var(--pill);align-self:start;height:auto}
  html[data-fill=on] .app:not(.door):not(.enroldoor) .top .isl > :not(#me):not(#bell){display:none}
  html[data-fill=on] .app.learn:has(.top #now > .today[data-fit=tile]) .otty .say{margin-right:calc(var(--corner) - 14px)}
  /* open, the round stays a round with its pressed look, and the drawer stands clearly under it (2026-09-24, the owner: "why, under the circle, when this menu opens, is there half a horizontal line?": the round had turned into a tab whose outline ran past it) */
  html[data-fill=on] .app:not(.door):not(.enroldoor) .me.open .isl{border-radius:var(--pill);box-shadow:var(--edge-night),var(--lift-bar)}
  html[data-fill=on] .app:not(.door):not(.enroldoor) .me.open .isl::after{display:none}
  html[data-fill=on] .app:not(.door):not(.enroldoor) .me.open .isl{z-index:8}
  /* while a sheet stands his bubble steps back, so a three-line bubble is never cut by the sheet's edge (2026-09-24, the size sweep at 393 px on the guardian's page; the chat panel too since 2026-09-26, the one Sheet) */
  html:is(.draweropen,.chatopen)[data-fill=on] .app:not(.door):not(.enroldoor) .otty .say{opacity:0;transition:opacity .2s}
  /* ONE PHONE AND TABLET SCALE (the learner audit, 2026-09-25: the phone rules stopped at 599 px, so a tablet wore the desk's split door with its top 40 % empty and the desk's timetable): under 1000 px every page takes the phone's layout, and the fluid tokens (the gutter, the card's padding) size it up on a tablet; the narrow things alone (the phone's table words, the wells, the rhythm) stay in the 599 px block */
  .app{--gut:clamp(14px,3vw,28px)}
  .card{padding:clamp(16px,3vw,28px) var(--gut) clamp(18px,3vw,28px)}
  .ttwrap{padding:0}
  .tt{--col-quiet:minmax(0,.6fr);gap:3px}
  .tt .tm{padding:6px 4px 0 0}
  .tt .cell{padding:6px 6px 7px;min-height:var(--hit);border-radius:var(--r1)}
  .tt .cell.rec{padding:4.5px 4.5px 5.5px}
  .tt .cell b{overflow-wrap:normal;hyphens:manual}
  .app:not(.door):not(.enroldoor) .stage{display:flex;flex-direction:column;padding-bottom:0}
  .app:not(.door):not(.enroldoor) .stage > .shell{flex:1 0 auto;display:flex;flex-direction:column}
  .app:not(.door):not(.enroldoor) .stage > .shell > .card{flex:1 1 auto}
  /* THE DOOR UNDER 1000 PX, one layout for a phone and a tablet (the learner audit, 2026-09-25: at 700 and 820 he stood off the column and the foot stood empty): the words, his row and the card in the card's one column, the three one group centred in the height (the free room shared over and under it) */
  .door .stage{grid-template-rows:minmax(0,1fr) auto var(--his-row) auto minmax(0,1fr);align-content:stretch;padding-bottom:24px}
  .door .statement{display:block;grid-row:2;width:min(100%,540px);margin:0 auto;padding:12px 0 6px}
  .door .otty{grid-row:3;width:min(100%,540px);justify-self:center;min-height:calc(var(--fig) + 6px);margin:4px 0 8px}
  .door .shell{grid-row:4}
  .door .statement h1{font-size:var(--t8);margin:10px 0 8px;min-height:1.12em}
  /* A DOOR FIELD'S LINE HAS ITS BOX FROM THE FIRST PAINT (2026-10-16, the night lane web-door-2; door.html Idle passes an empty line, kit's Field draws the box): a refusal fills a box that stands, so the card grows by nothing but a second line; each way's box is the run web-door-2's settled card plus the line's 24 px (the line's 18 and the field's 6 px gap), to be read again off the next run's card log; the sign-in door's three ways alone: the phone's upload pages wear .door and data-on=email too (.upl) and keep their own height */
  .door:not(.auth):not(.upl)[data-ctx=door][data-on=learner] .card{min-height:357px}.door:not(.auth):not(.upl)[data-ctx=door][data-on=email] .card{min-height:415px}.door:not(.auth):not(.upl)[data-ctx=door][data-on=sms] .card{min-height:349px}
  .door .card .who{flex:none}
  .door .tabs.ways{margin:-2px 0 16px}
  .door .statement h1 br{display:none}
  .door .statement p{font-size:var(--t4);line-height:22px;max-width:none;min-height:22px}
  /* the chat disc's clear band at the foot of a phone's enrol door (the sweep, 2026-09-15): the terms line's last link never under it */
  .enroldoor .stage{grid-template-columns:1fr;grid-template-rows:auto auto;padding-top:8px;padding-bottom:96px}
  .upl .stage{padding-bottom:96px}
  /* a phone: his row between the words and the card, the box at the left with most of its empty margin off the screen's edge (0.12 of the box, not the 0.18 the base names: at 0.18 his waving hand touched the edge, the learner test lane's F12; at 0.12 it clears it by the page's gutter and his body stands a little right of the words' edge), his words at his right at the row's middle with the tail toward him, capped so they never leave the row */
  .door .otty .mount{left:calc(var(--fig) * -0.12)}
  .door .otty .say{font-size:var(--t2);line-height:19px;left:calc(84% + 8px);right:auto;bottom:auto;top:36%;transform:translateY(-50%);max-width:calc(var(--room,100vw) - var(--fig) * 0.68 - 8px)}
  .pgoal .ga:has(> :nth-child(2)){display:grid;grid-template-columns:1fr 1fr;gap:8px}.pgoal .ga:has(> :nth-child(2)) .go.pwhite{justify-content:center;min-width:0;padding:0 12px}.pgoal .ga:has(> :nth-child(2)) .go.pwhite:not(.rim){justify-content:space-between;padding:0 6px 0 14px}
}
@media (max-width:599px){.enroldoor .glance:empty{min-height:78px}}
@media (max-width:599px){.door:not(.auth):not(.upl)[data-ctx=door][data-on=learner] .card{min-height:376px}.door:not(.auth):not(.upl)[data-ctx=door][data-on=email] .card{min-height:434px}.door:not(.auth):not(.upl)[data-ctx=door][data-on=sms] .card{min-height:431px}}
@media (max-width:380px){.enroldoor .glance:empty{min-height:121px}}
@media (max-width:767px){
  /* THE ACT ROW, one step (--act-row, 768 px; shell.js reads the token): under it the Back and the act share ONE row over the pill, and the shell's foot holds both rows from the first paint, so neither ever stands over the card's words */
  .app.flow:not(.door):not(.enroldoor) .stage{--bar-room:calc(2 * var(--bar-h) + var(--bar-gap) + 2 * var(--bar-edge) + env(safe-area-inset-bottom))}
  .app.flow:not(.door):not(.enroldoor) .shell > .barwrap{grid-template-columns:auto minmax(0,1fr);grid-template-rows:auto auto;row-gap:var(--bar-gap)}
  .app.flow:not(.door):not(.enroldoor) .shell > .barwrap > .back{grid-row:1;grid-column:1}
  .app.flow:not(.door):not(.enroldoor) .shell > .barwrap > .acts{grid-row:1;grid-column:2;justify-self:end}
  .app.flow:not(.door):not(.enroldoor) .shell > .barwrap > .bar{grid-row:2;grid-column:1/-1;justify-self:center}
}
@media (max-width:666px){
  /* on a phone the same paper card in the same band (the owner, 2026-09-15 17:5x: "the mobile full screen version needs to match that too"): the vendor's full screen stands 8px in from the screen's edges with the 7px band around the paper */
  html .woot-widget-holder{top:calc(8px + env(safe-area-inset-top))!important;left:8px!important;right:8px!important;bottom:8px!important;width:auto!important;height:auto!important;max-height:none!important;background:var(--rim);-webkit-backdrop-filter:var(--rim-blur);backdrop-filter:var(--rim-blur);box-shadow:var(--edge-night)}
}
@media (max-width:599px){
  /* a phone's tab row: the plus keeps its word and drops the figure beside two names, and stands alone beside three or more */
  .tabs:not(.one) .tab.plus b,.tabs:has(button.tab:nth-of-type(3)) .tab.plus span{display:none}
  .kv .v.acts.next{justify-self:stretch;align-items:stretch;padding-top:4px}.kv .v.next .go:not(.quiet){position:relative;justify-content:center;padding-inline:44px}.kv .v.next .go:not(.quiet) > i{position:absolute;right:6px;top:50%;transform:translateY(-50%)}.kv .v.next .go.quiet{align-self:flex-end}
  /* THE PHONE'S NARROW THINGS, up to 599 px (the layout is the 999 px block's): the table's words small and short, the teacher left out */
  .tt .cell{padding:6px 5px 7px}
  .tt .cell.live{padding:4.5px 3.5px 5.5px}
  .tt .cell b{font-size:var(--t1);line-height:15px}
  .tt .cell small{display:none}
  .tt .cell .mini{font-size:var(--t1);margin-top:18px}
  .tt .cell .long{display:none}
  .tt .tchip .t{display:none}
  /* A CELL'S NAME STAYS IN ITS CELL (round 2 of the audit: "Doematics" ran past the border at 360): the cell's one column and its title shrink, the short name cut with an ellipsis */
  .tt .cell{grid-template-columns:minmax(0,1fr)}.tt .cell b{min-width:0}
  /* a day's head stays one line in its column (a quiet weekend column included): the words never wrap, a narrow column cuts them */
  .tt .th{min-width:0;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
  .tt .cell .short{display:block;min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
  /* THE DAY STRIP in the week grid's place (timetable.js Day): the seven day chips across the card inside its margin, as every row stands (round 3b review: they ran into the gutter; never under the hit, a narrower card scrolls them), the chosen day's lessons and tasks under them at their full names, a swipe across the rows moves the day */
  .ttwrap{display:none}
  .ttday{display:grid;gap:var(--row);margin-top:var(--it-y)}
  .ttday .days{display:grid;grid-template-columns:repeat(7,minmax(var(--hit),1fr));gap:var(--hair);scrollbar-width:none}
  .ttday .days button{--face:calc(var(--hit) + var(--row));display:grid;place-items:center;align-content:center;gap:0;min-height:var(--face);padding:var(--r1) 0;border-radius:var(--r2);background:none;box-shadow:var(--quiet-edge);color:var(--ink);font:500 var(--t4)/20px var(--sans);font-variant-numeric:var(--tab);cursor:pointer}
  .ttday .days button small{font:400 var(--t1)/15px var(--sans);color:var(--mute)}
  .ttday .days button[data-quiet]{color:var(--faint)}
  .ttday .days button b{display:inline-grid;place-items:center;min-width:24px;height:24px;padding:0 5px;border-radius:var(--pill)}
  .ttday .days button[aria-current=date] b{background:var(--ink);color:#fff}
  .ttday .days button[aria-pressed=true][aria-current=date] b{background:#fff;color:var(--ink)}
  .ttday .days button[aria-pressed=true]{background:var(--ink);color:white}
  .ttday .days button[aria-pressed=true] small{color:var(--night-mute)}
  .ttday .tt{grid-template-columns:minmax(0,1fr);gap:calc(var(--hair) * 3);min-width:0;touch-action:pan-y}
  .ttday .tt > .gt{margin:0 0 var(--hair)}
  .ttday .tt .cell{padding:var(--it-y) var(--it-x);border-radius:var(--r2)}
  .ttday .tt .cell.rec{padding:calc(var(--it-y) - 1.5px) calc(var(--it-x) - 1.5px)}
  .ttday .tt .cell b{font-size:var(--t3);line-height:20px}
  .ttday .tt .cell small{display:block}
  .ttday .tt .cell .hd{display:inline-flex;margin-bottom:3px}
  .ttday .tt .cell .long{display:inline}
  /* the phone's day keeps the same shapes (2026-09-29): the Join pill across the card, the now line one row with its time; today is the date's round in the strip alone (never a class .today on the rows: the Today pane's .today .k rule would reach the chips) */
  .ttday .tt .cell .mini.join{justify-self:stretch;justify-content:center}
  .ttday .tt .nowtm{display:flex;align-items:center;gap:8px;padding:2px 0;line-height:14px}
  .ttday .tt .nowtm::after{content:'';flex:1;height:1px;background:var(--now-line)}
  .ttday .tt .nowline{display:none}
  .ttday .tt .cell .short{display:none}
  .ttday .tt .tchip .t{display:-webkit-box}
  .ttday .tt [data-none]{margin:0}
  /* the phone, tighter still: the gutter, the shell, the box, the rows and the labels */
  .stage{overflow-x:hidden;padding-left:8px;padding-right:8px}
  .otty{padding:4px 8px 10px;margin-left:-8px;margin-right:-8px}
  .fields{gap:12px 12px}
  /* a phone's tighter wells (2026-09-21: the list named .well, which nothing wears, and .got, no well: it padded the phone sheet's Received line) */
  .paybox,.ask,.notice{padding:12px 14px}
  .fld input,.fld select{padding-left:12px;padding-right:12px}
  .fld select{padding-right:36px}
  :root{--row:10px;--kick:18px;--head:20px}
  .gt{margin-bottom:6px}
  .tabs{margin-bottom:12px}
  .who .fx{margin-top:2px}
  /* on a phone every billing row's figures end at the same right edge: the chevron column goes (the whole row is the link) */
  .kv.inv{grid-template-columns:minmax(0,1fr) auto}
  .kv.inv .a{display:none}
  .ask{grid-template-columns:1fr}
  .ask .go{justify-self:stretch;position:relative;justify-content:center;padding-inline:44px}.ask .go > i{position:absolute;right:6px;top:50%;transform:translateY(-50%)} /* THE WIDE ACT's declarations again: a member of a phone block cannot join the global list */
  /* on a phone the two acts stand on their own line under the words, at the row's end; the page row keeps its one line (its acts are rounds) */
  .kv:not(.pg):has(> .v.acts){grid-template-columns:minmax(0,1fr)}
  .grp{padding:18px 18px 4px}
  .kv:not(.pg) .v.acts{justify-self:end;padding-top:8px}
  .paycard{padding-top:10px}
  .paywait{padding:18px 16px 16px}
  .paycard .payframe{margin:0 -10px}
  /* THE SHEET'S ACTION ROW on a phone: the acts stacked, each the row's width, the filled act on top */
  :is(.offer .foot,.announce .foot,.offer .guide .acts){flex-direction:column-reverse;align-items:stretch;gap:12px}
  :is(.offer .foot,.announce .foot,.offer .guide .acts) > .go{flex:none}
  :is(.offer .foot,.announce .foot,.offer .guide .acts) > .go.quiet{justify-content:center} /* stacked under the pill, the quiet act is centred */
  .kv.qr{grid-template-columns:minmax(0,1fr)}
  .announce .head{padding:18px 18px 0}.announce .prose{padding:12px 18px 18px}.announce .popup > .foot{padding:8px 18px 18px}
}
@media (max-width:479px){
}
@media (max-width:379px){
  /* a 360 phone holds the whole pill to the pixel on every page: the places narrow to 70 with a 10 px word padding (the words never go) */
  .app:not(.door):not(.enroldoor) .shell > .barwrap .sg{min-width:70px;padding:0 10px}
}
@media (min-width:600px){
  .fields{grid-template-columns:1fr 1fr}
  /* the chat panel from 600px slides in from the right (chat.js places it as the card's box, the rule at its place() ; a door keeps its panel on the rim, the 667px rule) */
  .woot-widget-holder.woot--hide{transform:translateX(48px)}
  .fields .wide{grid-column:1/-1}
}
@media (min-width:667px){
  /* the chat panel wears the dropdown sheet's dress exactly (the owner, 2026-09-15 17:1x and 17:4x: "make the surrounding box of the chat box the same as the notification dropdown"; "still has three borders"): the holder itself is the frosted band (the sheet's ::before: the 6 percent white, the blur, the white ring) around the paper frame at the 25px round; no outer shadow lines; the panel's band lies on the card's own rim exactly as the dropdown's does (the paper's right edge 23px from the screen's, the band over the shell's 7px), so the blur mixes the navy and the white along it (the owner, 17:4x: "the notifications fall on the border of the card under it, that's why it shows a nice mixed colour on its border; do the same for the chat"); the widget's own sheet draws no rim (desk-web) */
  html .woot-widget-holder.woot-elements--right{right:max(16px,calc((100vw - 1760px) / 2 + 16px));bottom:95px;height:min(var(--chat-h),calc(100vh - 102px - 88px))!important;height:min(var(--chat-h),calc(100dvh - 102px - 88px))!important;max-height:none!important;background:var(--rim);-webkit-backdrop-filter:var(--rim-blur);backdrop-filter:var(--rim-blur);box-shadow:var(--lift-sheet)}
}
@media (min-width:768px){
  .who .cols{grid-template-columns:minmax(0,1fr) minmax(0,1fr)}
  .flow .opts.two{grid-template-columns:1fr 1fr;gap:12px}
  .veil.offer{align-items:center;padding:24px}
}
@media (min-width:1000px){
  :root{--way-s:18px}
  #today{display:none}
  /* THE DOOR'S WIDE STAGE, ONE RULE FOR BOTH WIDE LAYOUTS (the owner, 2026-10-01, the guardian door's code step at a desk: "Check your inbox." stood left of the card, cut at the screen's edge, the card centred; the doors flow could not draw it at 1440, 1440x800 or 1920 in either engine, so this rule closes every way a column can leave the view): (1) the stage is clipped, never hidden: a hidden box still scrolls under a focus, a scrollIntoView or a vendor's script, and a centred grid scrolled sideways cuts its first column at the screen's edge; a clipped box cannot scroll at all (Safari 15 keeps hidden); (2) every column's floor is the stage's own width at most (min(…,100%)) and the floors' sum with the gaps and the gutters stands under each layout's first width (720 from 1000, 920 from 1280), so the grid is never wider than the stage and nothing is centred past its edges; (3) the free height is two minmax(0,1fr) rows, never align-content:center, so a card taller than the window overflows at the foot alone and the headline's top never leaves the view; (4) the headline's column is min-width 0 and its words wrap, a long word broken; (5) up to 1279 the headline stands above the card and he spans both rows, from 1280 the three stand in a row (below); the gutters 4vw, never under 16; proven by the doors flow's check door-headline-in-view (tools/shots.mjs) */
  .door .stage{grid-template-columns:minmax(min(440px,100%),560px) minmax(min(280px,100%),420px);grid-template-rows:minmax(0,1fr) auto auto minmax(0,1fr);gap:8px clamp(24px,3.5vw,64px);align-items:center;align-content:stretch;justify-content:center;overflow:hidden;overflow:clip;padding-left:clamp(16px,4vw,80px);padding-right:clamp(16px,4vw,80px)}
  .door .statement{grid-column:1;grid-row:2;align-self:end;padding-bottom:28px;min-width:0;overflow-wrap:anywhere}
  .door .statement h1{font-size:var(--t8);margin:12px 0 10px}
  .door .shell{grid-column:1;grid-row:3;display:block}
  /* beside the card: his box hung from the cell's foot at the column's middle (--otty-x), the bubble's foot 8px over 0.09 of the box, so the tail hangs over his drawn head */
  .door .otty{grid-column:2;grid-row:2/4;--band:right;min-height:0}
  .door .card{flex:none;overflow:visible}
  .door .otty .mount{left:var(--otty-x,50%);bottom:0;margin:0 0 calc(var(--fig) * var(--feet) * -1);transform:translateX(-50%)}
  .door .otty .say{bottom:calc(100% - var(--fig) * 0.09 + 40px)}
  /* the top bar keeps to the same 1760px box as the stage: logo over the left edge, island over the right */
  .top{padding:0 max(clamp(12px,2vw,24px), calc((100% - 1760px) / 2 + clamp(12px,2vw,24px)))}
  .logo{--face:28px;--face-i:var(--hit)}
  .logo img{display:block}
  .logo .mark{display:none}
  .stage{grid-template-columns:minmax(250px,.72fr) minmax(0,2.8fr);grid-template-rows:minmax(0,1fr);gap:clamp(16px,3vw,40px);align-items:stretch;align-content:stretch;overflow:hidden;padding-top:0;padding-left:clamp(12px,2vw,24px);padding-right:clamp(12px,2vw,24px)}
  /* his box is a true square: as tall as the column leaves him, never wider than the column */
  .otty{position:relative;z-index:auto;height:auto;min-height:0;container-type:inline-size;display:grid;grid-template-columns:1fr auto 1fr;grid-template-rows:auto auto;align-content:center;grid-template-areas:'. say .' 'mount mount mount' '. bar .';margin:0;background:none;padding:28px 0 0;gap:0;--say-h:64px;--room:calc(var(--stage-h,100vh) - 28px - var(--say-h) - 66px);--osz:min(100cqw,var(--room),560px)}
  /* the box (--say-h) holds two lines of the sentence at the desk's 15/21 with the bubble's 22px of padding (64px; three lines where the column is narrower, the band rule below; the owner, 2026-09-15: "more gap for the text above Otty, so when it types three lines Otty doesn't have to move or the text go up"; 2026-09-18: "Otty's bubble line count: just set the words it says to 85, or 75 if it is better", the line one sentence of at most 75 characters): the words start at its top, he stands under it, neither moves */
  /* HIS WORDS NEVER UNDER HIM, ONE RULE FOR BOTH LANES (2026-09-27, the owner: "why is the text behind the head, bring Ottie down a bit"): the bubble paints over his box (a move that lifts him never hides a word) and its foot, the tail, stands --head-gap over his box */
  .otty{--head-gap:16px;--say-min:calc(22px + 3 * 21px + 56px + var(--head-gap));--q-lines:3;--q-two:calc(2 * (16px + var(--q-lines) * var(--o-row-lh)) + 8px)}
  .otty .say{position:relative;z-index:4;padding:0;margin:12px 0 var(--head-gap);width:min(460px,100cqw);max-width:100%;min-height:var(--say-h);overflow:visible;color:var(--night);justify-content:center;font-size:var(--t4);line-height:21px}
  /* THE GUARDIAN'S COLUMN MIRRORS THE LEARNER'S (2026-09-27, the owner: "Ottie had better go at the bottom, just like the learner side, and the stats nicely designed on top"; "he should have been the same size, moved lower"; "because the white card on the parent side is smaller, Ottie should normally be larger"): from the top the stats plate (THIS TERM or NEXT TERM, the learner's Your year surface), the one line, then the free room where his words stand on their foot and grow up (the learner's words rule), him near the foot, his row's slot under him; his box is the column's width, as the learner's, capped only by the height the plate, the line, his words' worst case (--say-min) and his row's slot (--q-two) leave (shell.js measures it; --room is the first paint's same sum with the plate's usual 190 px), so a wider or taller column is a larger Ottie and he never moves */
  .app:not(.learn) .otty{grid-template-areas:'. term .' '. say .' 'mount mount mount' '. asks .';grid-template-rows:auto minmax(max-content,1fr) auto auto;align-content:stretch;padding:0 0 7px;--term-h:212px;--q-lines:2;--col-fill:var(--night-fill);--col-edge:var(--edge-night),var(--night-lift);--room:calc(var(--stage-h) - var(--term-h) - var(--say-min) - var(--q-two) - 31px)}
  .app:not(.learn) .otty > .term{align-self:start}
  .app:not(.learn) .otty > .term.door{padding:var(--col-pad) var(--col-pad-x);border-radius:var(--r4);background:var(--col-fill);-webkit-backdrop-filter:none;backdrop-filter:none;box-shadow:var(--col-edge)}
  .app:not(.learn) .otty > .mount{width:var(--osz);height:var(--osz);max-width:100%;align-self:end;justify-self:center;margin:0}
  /* HIS BUBBLE IS CENTRED, ONE RULE FOR BOTH LANES (2026-09-27, the owner: "just make the speech bubble centred; the text and button inside it should be left-aligned"): on a desk the bubble fits its words and stands centred over him, its tail at its own middle straight down at his head; inside it the words, the tapped question and the acts stay on its left edge; his row's chips centred and wrapping centred (below); on a phone the bubble stands beside him as before */
  .otty .say{justify-content:center}
  /* the one difference between the lanes (the owner, 2026-09-15): the learner's white box is wider, so his column is narrower and he is half the guardian's size; every other element is the same one */
  .learn .stage{grid-template-columns:minmax(220px,.42fr) minmax(0,3.2fr)}
  .learn .otty{--osz:min(100cqw,var(--room),420px)}
  .otty .mount{width:var(--osz);height:auto;aspect-ratio:1;align-self:start;justify-self:center;margin:0}
  .otty .barwrap{margin-top:0;justify-self:stretch}
  .shell{margin:0;width:100%;display:flex;flex-direction:column;min-height:0}
  /* the shell stands in its own column from the first paint, so Otty's column lands beside it and nothing moves */
  .app:not(.door):not(.enroldoor) .stage > .shell{grid-column:-2}
  /* a frame is whole over the bar on the page's first screen too (round 3 review: at 1280x720 the drama's video stood under its title and its controls under the bar): the room of the page's head over its first frame, --page-head, comes off its height */
  .prose{--frame-h:calc(var(--top-h) + var(--page-head) + var(--bar-room) + 2 * var(--band) + var(--row))}
  .card{flex:1 0 auto}
  /* the desk's one scroller: the stage at the window's full width (its 1760 px box its padding), his column standing still beside the card as the page moves */
  .app:not(.door):not(.enroldoor) .stage{grid-template-rows:auto;align-items:start;align-content:start;overflow:hidden auto;max-width:none;padding-inline:max(clamp(12px,2vw,24px),calc((100% - 1760px) / 2 + clamp(12px,2vw,24px)))}
  .app:not(.door):not(.enroldoor) .stage > .otty{position:sticky;top:min(0px,calc(var(--stage-h) - var(--col-h,0px)));min-height:var(--stage-h);height:auto}
  /* HIS BOX NEVER COVERS THE CARD (2026-09-27, the coordinator: "nothing may block clicks on the card at 1000-1279 px"): his box may be wider than the column by its empty sides (shell.js fitOtty), so the column clips what stands past its edges, 16 px of air kept for the shadows; the stage's gap is 16 px at least, so a clipped box never reaches the card, and a tap past it lands on the card */
  .app:not(.door):not(.enroldoor) .stage > .otty{overflow-x:clip;overflow-clip-margin:16px}
  .app:not(.door):not(.enroldoor) .stage > .shell{min-height:var(--stage-h)}
  /* under him the bar is a plain box (no container: its own width sizes the column's track, so the greeting is as wide as the bar); the icon fold for the narrowest column is the media rule below */
  .barwrap{position:static;width:100%;height:auto;justify-content:center;padding:0;container-type:normal}
  .flow .weeks{grid-template-columns:1fr 1fr;gap:16px}
  .flow .weeks.multi{grid-template-columns:minmax(0,1fr)} /* a matrix of several boards keeps one card a row: its cells need the width (2026-10-16) */
  .chatbubble{right:28px;bottom:28px}
  .app > .peek{right:28px;bottom:96px}
  .otty > .under{grid-area:under;display:block;width:min(460px,100cqw);max-width:100%;margin:12px 0 0;justify-self:center}
  /* ── THE ONE LINE AND THE TERM PLATE (pending.js) ── */
  /* the one line under him (the owner, 2026-09-21: "3 things need you ›"): the island's glass as one pill, the bell's own dot, the count, the chevron; its slot keeps its 36px whether the pill stands or not */
  /* THE GUARDIAN'S TWO QUESTIONS (insight.js, 2026-09-27): the slot under him keeps the worst case (--q-two: both chips stacked, --q-lines each: two here, the catalogue's questions are short) from the first paint, empty, loading or full, so he never moves; the chips are the learner's (ui.css HIS QUESTIONS) */
  .app:not(.learn) .otty > .asks{grid-area:asks;display:block;width:min(460px,100cqw);max-width:100%;height:var(--q-two);margin:10px 0 0;justify-self:center}
  /* HIS QUESTIONS WRAP ONLY WHEN NEEDED, ONE RULE FOR BOTH LANES (2026-09-27, the owner: "these buttons don't wrap when needed only?"): side by side while they fit, the next line only for a chip that does not; a chip's words wrap inside it only when the chip alone is wider than the row; the slot's height is the worst case, --q-two: both stacked at --q-lines each (the learner's three, the guardian's two) */
  .otty > .asks .qs{display:flex;flex-wrap:wrap;align-items:flex-start;justify-content:center;gap:8px}
  .otty > .asks .qs .qask{text-align:center}
  .otty > .asks .qs .qask{flex:0 1 auto;max-width:100%}
  /* THE CHIPS SAY THEY ACT (the owner, 2026-10-06): ONE hover for his question chips on both looks; a surface sets the chip's fill by the token --chip-fill and its hover tint by --qask-hover (the dark desk column, quest.css), never by a rule of its own, so this one rule always wins; the pointer is .chip's, and .chip:disabled takes both away while a tap runs */
  .otty > .asks .qask:not(:disabled):hover{background:var(--qask-hover,var(--hover))}
  /* the term plate: the learner's Leaderboards plate (.otty .door, its kicker .door>.k) on the card's foot line: the term's name and its week, the term as a rail of weeks (done in the mini graph's white, this week in the sky, ahead dim), then the family's next dates, the day in the tabular figures and the chevron only on the line that opens a page */
  .otty > .term{grid-area:term;display:block;align-self:end;justify-self:center;width:min(460px,100cqw);max-width:100%}
  .otty > .term.door{padding:12px 14px 8px}
  .term .tl{display:flex;align-items:baseline;justify-content:space-between;gap:12px;margin-top:9px;font:400 var(--t4)/21px var(--sans);color:var(--night)}
  .term .tl small{font:400 var(--t2)/18px var(--sans);color:var(--night-faint);white-space:nowrap}
  /* the plate's week is the pager's own slim track (.pbar, 2026-09-29: eleven segments the plate's width read as a heavy bar), in the night surface's inks: ahead faint, done in the sky, this week with the halo */
  .term .pbar{margin:9px 0 2px}
  .term .pbar i{background:rgba(255,255,255,.12);box-shadow:none}
  .term .pbar i.on{background:var(--sky)}
  .term .pbar i.now{box-shadow:var(--halo)}
  /* THE LEARNERS' SLIDES on the plate (stats.js Slides, 2026-09-27): the plate's surface and type (the title t3/20 with the first name in white, the line and the counts' line t2/18, the dials' words the kicker's t1/16), one slide in view sliding in 0.45 s (none with reduced motion), the dots under them, the active one a short pill */
  /* THE GUARDIAN'S CARD IN THE LEARNER CARD'S LANGUAGE (the owner, 2026-09-27: "use this as inspiration for the parent side"; "REUSE the learner card's existing elements exactly"): a slide is the learner's head row (.today: the date mark and the term path, daycard.js), a hairline, the child's title and rows (.yc, .f, .stat as the learner's Your year card, the same sizes), then its foot row in the learner's lesson row (.today .nx); the plate's dated rows the same rows under one hairline; the same paddings as the learner's card (12 px by 14 px) */
  .app:not(.learn) .otty > .term.door{padding:0}
  .term .today{padding:12px 14px}
  .term .yc{display:block;margin:0;padding:12px 14px;border-radius:0;box-shadow:var(--hair-night);color:#fff;text-decoration:none}
  .term .yc:hover{background:rgba(255,255,255,.05)}
  .term .f{gap:4px;margin-top:8px}
  .term .stat{min-height:var(--q-row-sm)}
  .term .stat :is(.num,.sm){font-size:var(--t3);line-height:20px}
  .term .yc + .today,.term .slides + .today,.term .today + .today{box-shadow:var(--hair-night)}
  .term .today .nx{margin-top:0;min-height:32px;align-items:center}
  .term .today > .k + .nx{margin-top:8px}
  .term .today .nx + .nx{margin-top:2px}
  .today .nx[data-warm]::before{background:#d98b6c;box-shadow:none}
  .term .slides{overflow:hidden;touch-action:pan-y}
  .term .track{display:flex;transition:transform .45s cubic-bezier(.2,.8,.2,1)}
  .term .sl{flex:0 0 100%;min-width:0}
  /* ONE RHYTHM (the owner, 2026-09-27: "the gaps generally, the dashes all"): kicker 4 px, name, term line 2 px, the week track 10 px, the rings 12 px, the footer 12 px over its hairline; the week track on the night takes the card's width and the night's colours */
  .term .dots{display:flex;justify-content:center;gap:2px;margin:0 0 8px}
  .term .dots button{position:relative;width:14px;height:14px;padding:0;border:0;background:none;cursor:pointer}
  .term .dots button::before{content:'';position:absolute;left:4px;top:4px;width:6px;height:6px;border-radius:var(--pill);background:rgba(255,255,255,.3);transition:background .3s,left .3s,width .3s}
  .term .dots button.on::before{left:1px;width:12px;background:#fff}
  /* THE CARD'S FOOT ROWS, ONE (parts.js FootRow; the owner, 2026-09-27: "these arrows and colours don't match"): every row the same: 38 px, a hairline over it, the left column of 58 px (the day, or the small warm dot of what is urgent), the words in the night's ink, the one quiet chevron; the first row 12 px under the rings */
  /* ── THE DAY (day.js) ── */
  /* the learner's day on the card's top line, in the free room over his words (the owner, 2026-09-21, option A; the same day: less text, more graphic, in the theme): one graphic (.g) of the date (.d: the calendar leaf in the bar's ink with the day's number in it, the weekday and the month in the kicker's caps) and the term (.w: its number and its week, the week's figure in the live blue, over the rail of its weeks, which is the guardian's term plate's rail: done in the mini graph's white, this week in the sky, ahead dim; outside a term the term's name alone, wrapped, never cut), then the next lesson with its countdown behind its dot (a ring before the lesson, full and lit while it runs); NO WORD IS CUT (2026-09-27, the owner: "the text is not shown fully"): the term's two labels wrap to two lines where they do not fit beside the date, the lesson's name and line wrap; it is the head row of the ONE Your year card at the column's top (2026-09-27, quest.css THE ONE CARD) and takes its room from the free room over his words, so he stands where he stood; the shell measures the air between the card and the bubble (shell.js fit) and marks it short (the lesson line gone) or none (gone, its room back to the words) */
  .learn .otty .under > .today{display:block;min-width:0;color:#fff}
  .today[data-fit=none]{visibility:hidden}
  .learn .otty .under > .today[data-fit=none]{display:none}
  .today[data-fit=short] .nx{display:none}
  .today .g{display:grid;grid-template-columns:auto minmax(0,1fr);align-items:center;column-gap:16px}
  .today .d{display:grid;grid-template-columns:32px auto;column-gap:8px}
  .today .leaf{grid-row:1/3;position:relative;width:32px;height:32px;color:var(--night-faint)}
  .today .leaf svg{display:block;width:32px;height:32px;fill:none;stroke:currentColor;stroke-width:1.5;stroke-linecap:round;stroke-linejoin:round}
  .today .leaf b{position:absolute;inset:12px 0 1px;display:grid;place-items:center;font:500 var(--t3)/18px var(--sans);font-variant-numeric:var(--tab);color:#fff}
  .today .k{white-space:nowrap}
  .today .k b{font-weight:500;font-variant-numeric:var(--tab);color:var(--night)}
  .today .w{display:grid;grid-template-rows:minmax(16px,auto) minmax(16px,auto);align-items:center;justify-self:end;width:min(100%,176px);min-width:0}
  .today .w>.k{grid-row:1/3;align-self:start;white-space:normal;text-align:right}
  .today .l{display:flex;flex-wrap:wrap;justify-content:space-between;column-gap:8px}
  .today .l .k+.k{margin-left:auto}
  .today .l .k+.k b{color:rgb(var(--ice-rgb))}
  .today .wks{display:grid;grid-auto-flow:column;grid-auto-columns:1fr;gap:3px}
  .today .wks i{height:4px;border-radius:var(--r1);background:rgba(255,255,255,.35)}
  .today .wks i.on{background:var(--sky)}
  .today .wks i.dim{background:rgba(255,255,255,.12)}
  .today .nx{--face:36px;display:grid;grid-template-columns:6px minmax(0,1fr) auto;align-items:baseline;column-gap:8px;margin-top:20px;color:#fff;text-decoration:none;font:400 var(--t3)/18px var(--sans)}
  .today .nx::before{content:'';align-self:center;width:6px;height:6px;border-radius:50%;--rc:rgba(255,255,255,.5);box-shadow:var(--ring-on)}
  .today .nx[data-now]::before{background:rgb(var(--glow-rgb));box-shadow:var(--glow)}
  .today .nx .s{min-width:0;overflow-wrap:anywhere;color:var(--night)}
  .today .nx .c{font:500 var(--t2)/18px var(--sans);font-variant-numeric:var(--tab);color:rgb(var(--ice-rgb));white-space:nowrap}
  .today .nx .c i{font-style:normal;margin-left:4px}
  .today .nx small{grid-column:2/-1;margin-top:1px;font:400 var(--t2)/17px var(--sans);color:var(--night-faint);overflow-wrap:anywhere}
  .otty .say .bub{transform-origin:center bottom}
  html .woot-widget-holder.woot-elements--right{bottom:97px}
  /* he is placed by the door alone (the owner, 2026-09-15 22:3x: "here Otty moves based on the speech bubble, how can that be avoided"): the greeting's track is the flexible one, he and the words sit on its foot, so a longer line grows upward into the free room and never moves him */
  /* THE YOUR YEAR CARD ON TOP (the owner, 2026-09-26: under the date, over Ottie; 2026-09-27 the date is the card's head row, quest.css THE ONE CARD): the card, then his words, him, his row and the bar as before */
  .learn .otty:has(> .under.door){padding-top:0;grid-template-areas:'. under .' '. say .' 'mount mount mount' '. asks .' '. bar .';align-content:stretch;grid-template-rows:auto minmax(max-content,1fr) auto auto auto}
  /* THE BUBBLE'S BAND, ONE RULE FOR BOTH LANES (the owner, 2026-10-16 08:1x UTC, the guardian home on a desk with the EFT line and its Show me over the card's last row: "sometimes we get an overlap here, can it be managed without losing beauty or functionality"): his words live in the band between the card's foot and his head; the band keeps its worst case (--say-min) from the first paint, and the words' row is at least its words (minmax(max-content,1fr) above: an auto minimum read the slot's min-height alone, so longer words overflowed the row upward over the card); where the words need more, the column grows by the difference under the stage's height floor (min-height, not height, and no size containment, so the column's height may follow its content) and the stage scrolls, him and his row moved down together; the column stays pinned, its top offset by the excess (--col-h, shell.js fitOtty), so its foot stands on the viewport's foot once the page is scrolled; never a clipped line, never a scroll inside the bubble, the tail on his head as before; his box keeps the size the stage's height gives (fitOtty reads the stage, never the grown column) */
  :is(.learn .otty:has(> .under.door),.app:not(.learn):not(.door) .otty) .say{margin:7px 0 0;padding-bottom:var(--head-gap);min-height:var(--say-min);align-items:flex-end;align-self:end}
  /* HIS WORDS STAND ON HIS HEAD (the owner, 2026-09-26: "why the balloon points to other balloon"): the bubble's tail points down at him, so nothing stands between them; his row (.asks) stands under him, over the bar */
  /* he stands on the door, never moving when the bubble grows (the owner, 19:0x: "Otty moves up and down, unify, clean up"; 21:5x: "and the gap" put the free room between him and the door, the 22:3x ruling above puts it over the words): him right under the words with the bubble's tail on his head */
  /* his feet 24 px over his row (2026-09-27: 44 px until his row's slot took a third line for the question, so the line came out of this gap and he stands where he stood) */
  .learn .otty:has(> .under.door) .mount{width:var(--osz);height:var(--osz);max-width:100%;align-self:end;justify-self:center;margin:0 0 calc(44px - var(--o-row-lh))}
  .learn .otty:has(> .under.door) > .under.door{margin:0}
  .pgoal{grid-template-columns:minmax(0,1fr) auto;align-items:center;column-gap:40px;padding:28px 32px}.pgoal h2{font-size:var(--t8);line-height:32px}.pgoal:has(> .gside){grid-template-columns:minmax(0,1fr) auto auto}.pgoal .ga{justify-self:end}
}
@media (min-width:1000px) and (max-width:1279px){
  /* the column is 250px wide up to 1279px (about 30 characters a line at 15px), so the cap's sentence of 75 characters runs to three lines: the box holds them (85px) and he still never moves */
  .otty{--say-h:85px}
  /* the door's statement stands above the box: one line each, room under it, no forced breaks */
  .door .statement h1 br{display:none}
  .door .statement p{max-width:none}
}
@media (min-width:1000px) and (max-height:735px){
  /* A SHORT DESK WINDOW, ONE ANSWER FOR BOTH COLUMNS (2026-09-27, the coordinator: the learner's Ottie stood at 70 px at 1280x720): the learner's Your year card keeps its day row and its facts as one flowing line (the glyph and the figure; the word only where no figure stands, a pill as it is; a fact that does not fit takes the next line), its title gone, so he keeps a good size; the phone's sheet holds the whole card; html over quest.css's own rules, which load after this sheet */
  html .learn .otty .under .yc > .k{display:none}
  html .learn .otty:has(> .under.door) .door .f{display:flex;flex-wrap:wrap;align-items:center;gap:2px 14px;margin-top:0}
  html .learn .otty:has(> .under.door) .door .stat{display:inline-flex;align-items:center;gap:6px;min-height:24px;white-space:nowrap}
  html .learn .otty:has(> .under.door) .door .stat .sm{white-space:nowrap}html .learn .otty:has(> .under.door) .door .stat .keep{display:none}
  /* and his row keeps its two questions: the Support line gives its room to him there, as on a phone (the bar's Support stands) */
  html .learn .otty > .asks{grid-template-rows:minmax(var(--q-two),auto);min-height:var(--q-two)}
  html .learn .otty > .asks .qsup{display:none}
  html .learn .otty:has(> .under.door) .door .stat:has(.num) .sm{display:none}
  /* a short desk window: the term plate gives its room to him (under 736px of height, the plate's 296px since its slides of 2026-09-27, his box would fall under 160px beside it); the one line stays */
  .app:not(.learn) .otty{--term-h:0px}
  .otty > .term{display:none}
}
@media (min-width:1400px){
  /* THE PAY STEP AS TWO COLUMNS ON A WIDE CARD (the owner, 2026-09-16 11:2x SAST, at a wide desk with Card details under the fold: "this pay page hides the card form under; how can it be improved so it is side by side?"): the summary at the left (the head and its line, the rows, Subtotal, the discount, Due now, Add a coupon, the Billing address row, the word and the note) and the pay box at the right from the head's top (the tabs' blocks: the address gate, the processor's frame, the bank slip), the box at its content's height (400 to 480 wide, the summary never under 320; the step's 720px reading cap lifted here alone); the same nodes as the one column, placed by the grid alone (the host's own box dissolved, the box across every row, the last row the flexible one so the rows keep the summary's rhythm), so the processor's frame never remounts across a resize; one column again once the box is gone (paid, closed, the document) */
  .flow .who:has(> .payhost > [data-pay]:not([hidden])){display:grid;grid-template-columns:minmax(320px,1fr) minmax(400px,480px);grid-template-rows:repeat(11,auto) minmax(0,1fr);column-gap:clamp(32px,5%,56px);align-items:start;max-width:none}
  .flow .who:has(> .payhost > [data-pay]:not([hidden])) > .payhost{display:contents}
  .flow .who:has(> .payhost > [data-pay]:not([hidden])) > :not(.payhost),.flow .who:has(> .payhost > [data-pay]:not([hidden])) > .payhost > :not([data-pay]){grid-column:1}
  .flow .who:has(> .payhost > [data-pay]:not([hidden])) > .payhost > [data-pay]{grid-column:2;grid-row:1/-1;margin-top:0}
  /* the box's kicker (Card details) on the head's line: a grid item never folds its child's margin into its own, so the step's 28 px kicker margin is set to the head's cap here alone */
  .flow .who:has(> .payhost > [data-pay]:not([hidden])) > .payhost > [data-pay] [data-block]>.gt{margin-top:5px}
  /* THE DOCUMENTS STEP AS TWO COLUMNS ON A WIDE CARD (the owner, 2026-09-16 18:4x SAST, at a wide desk with the three cards in a row and the panel under them in the 720px column: "I do not understand why half of the screen is not used for this; this needs a better UX"): the same grid as the pay step's: the head and its line, the slots as a column of picture cards (300 to 340 wide, each the picture beside its name and word) and Do this later on the left; the picked slot's panel on the right from the head's top, its read rows open and the fold's tap a kicker (the room is there); the same nodes as the one-column tree placed by the grid alone (the host `.docs` dissolves), so a resize keeps the picker's input and the read's state; the step's 720px reading cap lifted here alone */
  .flow .who:has(> .docs > .dpanel){display:grid;grid-template-columns:minmax(300px,340px) minmax(0,1fr);grid-template-rows:repeat(5,auto) minmax(0,1fr);column-gap:clamp(32px,5%,56px);align-items:start;max-width:none}
  .flow .who:has(> .docs > .dpanel) > .docs{display:contents}
  .flow .who:has(> .docs > .dpanel) > :not(.docs),.flow .who:has(> .docs > .dpanel) > .docs > :not(.dpanel){grid-column:1}
  .flow .who:has(> .docs > .dpanel) > .docs > .dpanel{grid-column:2;grid-row:1/-1;min-width:0}
  .flow .who:has(> .docs > .dpanel) .dcards{grid-template-columns:minmax(0,1fr);gap:8px;margin:var(--head) 0 0}
  .flow .who:has(> .docs > .dpanel) .dcard{grid-template-columns:88px minmax(0,1fr);grid-template-rows:auto auto;column-gap:14px;row-gap:1px;align-content:center;padding:10px 14px 10px 10px;border-radius:var(--r3)}
  .flow .who:has(> .docs > .dpanel) .dcard .pic{grid-row:1/3;width:88px}
  .flow .who:has(> .docs > .dpanel) .dcard .pic img.sample{width:48px}
  .flow .who:has(> .docs > .dpanel) .dcard .t{align-self:end;font-size:var(--t4);line-height:20px}
  .flow .who:has(> .docs > .dpanel) .dcard .s{align-self:start}
  .flow .who:has(> .docs > .dpanel) .doc{padding-top:0}
  .flow .who:has(> .docs > .dpanel) .doc .dh .k{font-size:var(--t7);line-height:30px}
  .flow .who:has(> .docs > .dpanel) .doc .read[data-open] .rows{display:block}
  .flow .who:has(> .docs > .dpanel) .doc .read .fold{pointer-events:none}
  .flow .who:has(> .docs > .dpanel) .doc .read .fold i{display:none}
  .flow .who:has(> .docs > .dpanel) .later{margin-top:16px}
}
@media (min-width:1280px){
  /* THE DESK'S ONE TYPE STEP (round 2 of the audit: the module list a thin column of small text on a wide card): every step from the line under a title up one to two pixels, through the tokens alone */
  :root{--t2:14px;--t3:15px;--t4:16px;--t5:17px;--t6:19px;--t7:24px;--t8:30px}
  /* the guardian's white box a bit wider and his column a bit narrower, still wider than the learner's .42fr (the owner, 2026-09-16 21:1x: "on the guardian portal make the main card a bit wider and Otty's side a bit smaller, but not as much as the learner side") */
  .stage{grid-template-columns:minmax(340px,.85fr) minmax(0,2.15fr)}
  /* the door's three columns in a row (THE DOOR'S WIDE STAGE, the 1000 block: the floors, the clip and the free rows are its) */
  .door .stage{grid-template-columns:minmax(min(200px,100%),320px) minmax(min(440px,100%),540px) minmax(min(280px,100%),420px);grid-template-rows:minmax(0,1fr) auto minmax(0,1fr)}
  .door .statement{grid-column:1;grid-row:2;align-self:center;padding-bottom:0}
  .door .statement h1{font-size:var(--t-display)}
  .door .shell{grid-column:2;grid-row:2}
  .door .otty{grid-column:3;grid-row:2}
}
@media (display-mode:standalone){
  /* ── APP: install and notifications ── */
  /* installed on a phone the status bar lies over the page: the app steps down by the top inset, the field's seam with it */
  .app{padding-top:env(safe-area-inset-top)}
  .otty{background-position:0 calc(-56px - env(safe-area-inset-top))}
}
@media (max-height:820px){
  /* A SHORT SCREEN'S FRAME COMPACTS: the top bar and the bar's edges one step tighter, so the words keep the room */
  :root{--top-h:52px;--bar-edge:8px}
}
@media (max-height:560px){
  /* a short screen: his head row goes up with the page, so the words have the screen, and the page keeps the corner's room (--under, the corner's one rule) */
  html[data-fill=on] .app:not(.door):not(.enroldoor) .otty{position:relative}
  html[data-fill=on] .app:not(.door):not(.enroldoor) .stage{--under:1}
  html[data-fill=on] .app:not(.door):not(.enroldoor) .card *{scroll-margin-top:var(--row)}
  html[data-fill=on] .prose{--frame-h:calc(var(--bar-room) + 2 * var(--band) + var(--row) + env(safe-area-inset-top))}
  /* a short screen (under 560px tall): his column goes, as at the live door, and his row with it; the statement's air goes too (2026-10-16, the lane web-door-2: at 852x393 the learner field's foot stood 12 px under the shell's keyboard band (shell.js intoView), so its focus centred it with a 136 px scroll and the headline left the view) */
  .door{--his-row:0px}
  .door .otty{display:none}
  .door .statement{padding:4px 0 0}
  .door .statement h1{margin:6px 0 4px}
}
@media (hover:hover){
  .pstate:hover .draw,.pstate:hover .shackle,.pstate:hover .hand,.pstate.p-alert:hover svg,.pstate.p-next:hover svg,.pstate.p-glass:hover svg{animation-name:none}.pstate:hover .draw{animation:pdraw2 .45s ease-out both}.pstate:hover .shackle{animation:pshut2 .5s cubic-bezier(.2,1.4,.4,1) both}.pstate:hover .hand{animation:psweep2 .6s ease-out both}.pstate.p-next:hover svg{animation:pnudge2 .45s ease-out both}
}
@media (prefers-reduced-motion:reduce){
  .term .track,.term .dots button::before{transition:none}

  ::view-transition-group(*),::view-transition-old(*),::view-transition-new(*){animation:none!important}
  .scanbar i{animation:none;left:0;width:100%;opacity:.35}
  .otty .say .bub{animation:none}
  /* the pane too (2026-09-22): a hundredth of a millisecond still paints the way in's first frame (the pane scaled to .96), and a probe measured the menu's flip 29 px in that frame; with no animation the pane stands at once and shell.js hides it at once */
  .drawer,.drawer.away{animation:none}
  /* reduced motion: no page change animation, no breathing, no film; the pages stand still */
  *,*::before,*::after{animation-duration:.01ms!important;animation-iteration-count:1!important;transition-duration:.01ms!important}
  .pstate *,.pstate svg,.ptok,.ptok svg,.pmeter .pbar > b,.ptip,.bx .shr,details.mod[open] > .its{animation:none!important;transition:none!important}
}

/* ── THE WORK (2026-09-25): the one hand-in confirm and the one discussion thread; every value a token ── */
/* THE ONE CONFIRM (parts.js Confirm: the hand-in's and the notice of leaving's): the popup's native dialog (.announce) wearing the offer's sheet (.offer .sheet), risen from the foot on a phone and centred from 768 px; it rises by transform and opacity alone; the numbers of the questions with no answer are words of the sentence (copy.js listed: "2, 5 and 9", each comma against its number), underlined in the link's ink, each 44 px high to the touch */
dialog.confirm{align-items:flex-end;padding:0 var(--row) calc(var(--row) + env(safe-area-inset-bottom))}
dialog.confirm[open] .sheet{animation:var(--sheet-in)}
.confirm .nm{margin:0}
.confirm .fx{color:var(--ink-soft);margin-top:var(--it-y)}
.confirm.ask .fld{margin-top:var(--it-y)}.confirm.ask .fld textarea{min-height:88px}
/* a number in running words is a word: no tap box of its own in the line (its hit is the ::after below), so no gap before its comma */
.confirm .nobr{white-space:nowrap}
.confirm .gapn{min-inline-size:0;min-block-size:0;margin:0;position:relative;display:inline;padding:0;border:0;background:none;color:var(--sky-ink);font:inherit;font-weight:600;font-variant-numeric:var(--tab);text-decoration:underline;text-decoration-thickness:var(--hair);text-underline-offset:var(--hair);cursor:pointer}
/* THE CAMERA SHEET (parts.js Camera, 2026-09-29: a desk's browser ignores an input's capture): the one confirm's dialog, wider for the picture; the preview on the video's own black at 4:3, the recording's word and clock on the night glass at its corner with the warm dot; the shots as the hand-in's thumbs, each with its paper round to remove at its corner; the clip as a file row */
dialog.cam > .sheet{width:min(100%,640px);max-height:100%}
.cam .view{position:relative;margin-top:var(--row);border-radius:var(--r3);overflow:hidden;background:#000;aspect-ratio:4/3;max-height:min(52vh,480px)}
.cam .view video{display:block;width:100%;height:100%;object-fit:cover}
.cam .view .rec{position:absolute;top:10px;left:12px;display:flex;align-items:center;gap:8px;padding:4px 12px;border-radius:var(--pill);background:var(--isle);-webkit-backdrop-filter:var(--isle-blur);backdrop-filter:var(--isle-blur);box-shadow:var(--edge-night);color:white;font:500 var(--t2)/18px var(--sans);font-variant-numeric:var(--tab)}
.cam .view .rec::before{content:'';width:8px;height:8px;border-radius:50%;background:var(--warm);animation:cam-blink .8s ease-in-out infinite alternate}
@keyframes cam-blink{to{opacity:.25}}
.cam .dot::before{content:'·';margin:0 6px}
.cam .stack{display:flex;flex-wrap:wrap;gap:12px;margin-top:var(--row)}
.cam .stack .shot{position:relative}
.cam .stack img{display:block;width:64px;height:84px;object-fit:cover;border-radius:var(--r1);background:var(--raised);box-shadow:var(--raised-edge)}
.cam .stack .pround{position:absolute;top:-10px;right:-10px;background:var(--sheet-foot)}
.cam .kv.up{margin-top:var(--row)}
@media (prefers-reduced-motion:reduce){.cam .view .rec::before{animation:none}}
.confirm .gapn::after{content:'';position:absolute;left:50%;top:50%;width:max(100%,var(--hit));height:var(--hit);transform:translate(-50%,-50%)}
.confirm .gapn:hover{color:var(--ink)}
.confirm .gapn:focus-visible{outline:var(--hair) solid var(--sky-ink);outline-offset:var(--hair)}
@media (min-width:768px){dialog.confirm{align-items:center;padding:var(--head)}}
/* THE ACCESS SHEET (parts.js Sheet; the owner, 2026-09-27, on the device rows: "something to click on, to check what accesses are given"): the one confirm's dialog, its rows the cards' rows (one height, hairlines between, the act on the title's line), the list scrolling under its head */
dialog.list > .sheet{width:min(100%,520px);max-height:100%}
dialog.list .rows{flex:1 1 auto;min-height:0;overflow:auto;overscroll-behavior:contain;margin-top:var(--row)}
dialog.list .rows > .kv{min-height:64px;padding:12px 0;grid-template-columns:minmax(0,1fr) fit-content(50%);align-items:baseline;align-content:center}
dialog.list .rows > .kv ~ .kv{border-top:1px solid var(--hairline)}
/* THE THREAD (tasks.js DiscussionView): the count once as its head; a post is the author's face beside the name and time over the words, Reply right under them; a reply one step in (the face and its gap); no lines, the rhythm's steps alone; the reply just posted lit softly, the light fading by opacity */
.thread{margin-top:var(--head)}
.thread > .gt{margin-top:0}
.thread .branch{padding:var(--it-y) 0}
.thread .in{padding-left:calc(var(--av-m) + var(--row))}
.post{position:relative;isolation:isolate;display:grid;grid-template-columns:var(--av-m) minmax(0,1fr);column-gap:var(--row);padding:var(--it-y) 0}
.post::before{content:'';position:absolute;inset:0 calc(var(--it-x) * -1);border-radius:var(--r3);background:var(--sky-soft);opacity:0;pointer-events:none;z-index:-1}
.post.lit::before{animation:work-lit 2.4s ease-out}
@keyframes work-lit{from{opacity:1}to{opacity:0}}
.post .pb{min-width:0;display:grid;justify-items:start}
.post .by{display:flex;flex-wrap:wrap;align-items:baseline;column-gap:var(--it-x)}
.post .by b{font:500 var(--t4)/22px var(--sans);color:var(--ink)}
.post .by small{font:400 var(--t2)/18px var(--sans);color:var(--mute)}
.post .pb > .prose{justify-self:stretch;margin-top:0}
.post .pb > .meta{margin:0}
.post [data-reply]{justify-self:start;margin-top:var(--r1);font-size:var(--t3);color:var(--mute)}
.post [data-reply]:hover{color:var(--ink)}
.thread > .said{margin-top:0}
.thread > [data-none]{margin:0 0 var(--it-y)}
/* THE BOX at the thread's foot, docked: one ringed round with the words and Post inside it on the right, growing with the words to six lines (tasks.js grow sets the rows); replying to someone is one small line over it with its × */
/* THE BOX IS DOCKED (round 3 review: with eight replies it stood a long scroll away): it sticks at the view's foot while its thread is in view, on its own glass, so the thread passes under it and it is always in reach; ITS FOOT IS ONE GAP OVER THE BACK'S TOP (round 3b: the Back stood over the box): the bar's room and the shell's foot, less the bar's own top edge, plus one gap, on every size (one row or two, a short screen's tighter edge, the phone's fill) */
.compose{position:sticky;bottom:calc(var(--bar-room) + var(--shell-foot,0px) - var(--bar-edge) + var(--bar-gap));z-index:3;order:2;padding-block:var(--r1);border-radius:var(--r4);background:var(--bar-glass);-webkit-backdrop-filter:var(--glass-blur);backdrop-filter:var(--glass-blur)}
/* A SHORT THREAD'S BOX STANDS AT THE FOOT TOO (round 3b: a new conversation's box stood under its first line): every box from the page's body down to the box's holder is a column that fills the room, and the holder's one spacer (one row at least) stands the box at the foot */
.flow .body:has(.compose),.flow .body :has(.compose){display:flex;flex-direction:column}
.flow .body :has(.compose){flex:1 0 auto}
.flow .body :has(> .compose)::after{content:'';order:1;flex:1 0 var(--row)}
.compose > .notice{margin:0 0 var(--r1)}
.compose > [data-again]{margin:0 0 var(--r1);padding-inline:var(--row)}
.compose .to{display:flex;align-items:center;justify-content:space-between;gap:var(--it-x);font:400 var(--t2)/18px var(--sans);color:var(--mute);margin-bottom:var(--r1)}
.compose .to .x,.compose .att .x{--face:var(--chip-h-sm);--face-i:var(--face);display:grid;place-items:center;padding:0;background:none;border-radius:50%;color:var(--mute);cursor:pointer}
.compose .to .x:hover,.compose .att .x:hover{color:var(--ink);background:var(--hover)}
.compose .box{display:flex;align-items:flex-end;gap:var(--it-x);border-radius:var(--r4);box-shadow:var(--ring);padding:var(--r1) var(--r1) var(--r1) var(--row)}
.compose .box:focus-within{box-shadow:var(--ring-on)}
.compose textarea{flex:1;min-width:0;min-block-size:var(--hit);border:0;background:none;resize:none;padding:calc((var(--hit) - 22px) / 2) 0;font:400 var(--t5)/22px var(--sans);color:var(--ink);outline:0;overflow-y:auto}
.compose textarea::placeholder{color:var(--faint)}
.compose [data-post]{flex:none}
/* THE REPLY'S FILE (2026-10-14, tasks.js attaching): the clip at the box's left, the chip under the box (the thumbnail or the file glyph, the name over the label, Remove), a picture attached to a post inline at the post's width */
.compose .box .clip{flex:none;display:grid;place-items:center;width:var(--hit);height:var(--hit);padding:0;margin-left:calc(-1 * var(--row) + var(--r1));border:0;border-radius:50%;background:none;color:var(--mute);cursor:pointer}
.compose .box .clip:hover{color:var(--ink);background:var(--hover)}
.compose .att{display:flex;align-items:center;gap:var(--it-x);margin-top:var(--r1);padding:var(--r1) var(--r1) var(--r1) var(--row);border-radius:var(--r4);box-shadow:var(--ring)}
.compose .att img,.compose .att i{flex:none;width:48px;height:48px;border-radius:var(--r2);object-fit:cover}
.compose .att i{display:grid;place-items:center;color:var(--mute)}
.compose .att .k{flex:1;min-width:0;display:grid;font:500 var(--t3)/20px var(--sans);color:var(--ink)}
.compose .att .k,.compose .att .k small{white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
.compose .att .k small{font:400 var(--t2)/18px var(--sans);color:var(--mute)}
.post .pb > .pic{display:block;margin-top:var(--r1);max-width:min(100%,420px)}
.post .pb > .pic img{display:block;max-width:100%;max-height:360px;border-radius:var(--r3);box-shadow:var(--ring)}
/* THE SUBJECT PICKER (parts.js SubjectPicker; the wizard's subjects step and the Change of subjects sheet; 2026-09-27, the owner: "the spacing etc on this page is not nice"): ONE RHYTHM, --head between sections, --it-y inside one; a section is a small heading (the word in ink, its count muted after a middle dot) and its body; Compulsory a calm locked list on the well, one column on a phone, two or more where it fits, no numbers; the chips the flow's */
.psubj{display:flex;flex-direction:column;gap:var(--head);min-width:0}
.psubj .pgroups{display:contents}
.psec{display:flex;flex-direction:column;gap:var(--it-y);min-width:0}
.psec .ph{margin:0;font:600 var(--t3)/20px var(--sans);color:var(--ink)}
.psec .ph small{font:400 var(--t3)/20px var(--sans);color:var(--mute)}
.psec .chips{margin:0}
.plock{display:grid;grid-template-columns:repeat(auto-fill,minmax(min(100%,180px),1fr));gap:0 var(--row);margin:0;padding:var(--it-y) var(--row);list-style:none;border-radius:var(--r2);background:var(--wait);box-shadow:var(--wait-edge)}
.plock li{padding:4px 0;font:400 var(--t3)/20px var(--sans);color:var(--ink-soft);min-width:0;overflow-wrap:anywhere}
.flow .step > :is(.nm,.fx) + .psubj{margin-top:var(--head)}
/* THE SUBJECT CHANGE (subjects.js, 2026-09-26): the Subjects row's status chip beside its word; the sheet is the one confirm's dialog, wide enough for the picker: the title, its short line (--it-y under it), the body scrolling under them on the picker's rhythm (the verdict and the price one set in it), the foot on a hairline at --head; a locked chip muted with its lock; the waiting request's swap, the dropped struck and the added bold */
.kv.subj .k .ptimer{margin-left:8px;vertical-align:1px}
dialog.subjects > .sheet{width:min(100%,560px);max-height:100%}
dialog.subjects [data-sub]{color:var(--mute)}
dialog.subjects .sbody{flex:1 1 auto;min-height:0;overflow:auto;overscroll-behavior:contain;display:flex;flex-direction:column;gap:var(--head);margin-top:var(--head)}
dialog.subjects .sbody :is(.notice,.lines,.fx,.swap){margin:0}
dialog.subjects .pset{display:flex;flex-direction:column;gap:var(--it-y)}
dialog.subjects .foot{margin-top:var(--head);padding-top:var(--row);border-top:1px solid var(--hairline)}
.chip.locked{opacity:.45;cursor:default}
.chip .pglyph{width:var(--icon-sm);height:var(--icon-sm);margin-right:4px;vertical-align:-2px}
.swap{display:flex;flex-wrap:wrap;align-items:center;gap:6px 8px;margin-top:var(--it-y)}
.swap s{color:var(--mute)}
.swap .pglyph{color:var(--mute)}
/* ── THE SHEET (2026-09-26, the owner on his phone: "a lack of uniformity between the notification dropdown and the other popups like the chatbot"; "the shadows around the popups are not uniform"): under 1000 px every popup is ONE overlay: the notifications and menu sheet (#drawer), the Support chat's frame (chat.js puts .sheet-o on the vendor's holder), the buddy picker and every sheet on the veil (the offers, the moments, the upload link), the confirm and the announcement on the native dialog. ONE SHAPE: the rim's radius (--rim-r) on all four corners, the rim a --band border of the one solid ground (--rim-foot) with its one hairline (--isle-line), the paper (--sheet) inside it at the concentric --round; ONE FRAME: its top one gap under the corner's column (--sheet-top), its sides the corner's inset (--sheet-x), its foot one gap over the floating bar (--sheet-bottom: --bar-room plus --bar-gap); a tall sheet (the menu, the chat) fills the frame, a short one stands at its foot and never passes its top; ONE ELEVATION (--lift-sheet); its content scrolls inside it and the page behind holds still; ONE MOTION: it rises from its foot (--sheet-in, --sheet-out), none under reduced motion; ONE HEAD: ui.css .sheet > .hd. ONE GROUND (2026-09-26, the live review: the bell's sheet and the picker stood see-through with stripes in WebKit): the paper token --sheet carries its solid colour (--sheet-foot) under its gradient, so every paper is opaque even where the gradient fails, and no glass stands behind or inside a sheet (the dims are one solid --dim, and the page's glass goes while a sheet stands); ONE CLOSE: the paper round (parts.js Round, .pround.s-sm.on-paper) on every sheet, a night round turned to paper; THE OPENER'S SECTION FIRST: the menu sheet stands the section its button opened on top and says its heading in the head (shell.js firstShown); THE CHAT'S HEAD IS THE VENDOR'S (the one exception, the owner 2026-09-26): no title band; its navy head (the logo, the name, the reply time) is the sheet's top under the sheet's corners and shadow, with ONE white X over it at the top right (chat.js headOf), and the chat stands at the foot, --chat-h tall at most ── */
@media (max-width:999px){
  :is(html[data-fill=on] .drawer,html .woot-widget-holder.sheet-o){position:fixed!important;inset:var(--sheet-top) var(--sheet-x) var(--sheet-bottom)!important;width:auto!important;min-width:0;height:auto!important;max-height:none!important;margin:0;padding:0!important}
  :is(.veil.offer,dialog.announce){padding:var(--sheet-top) var(--sheet-x) var(--sheet-bottom);align-items:flex-end;justify-content:center}
  :is(.veil.offer,dialog.announce) > :is(.sheet,.popup){width:100%;max-width:none;height:auto;max-height:100%}
  :is(html[data-fill=on] .drawer,html .woot-widget-holder.sheet-o,.veil.offer > .sheet,dialog.announce > :is(.sheet,.popup)){border:var(--band) solid var(--rim-foot);border-radius:var(--rim-r)!important;background:var(--sheet) padding-box;outline:1px solid var(--isle-line);outline-offset:-1px;box-shadow:var(--lift-sheet)!important;-webkit-backdrop-filter:none;backdrop-filter:none;overflow:auto;overscroll-behavior:contain;animation:var(--sheet-in)}
  :is(html[data-fill=on] .drawer,html .woot-widget-holder.sheet-o,.veil.offer > .sheet,dialog.announce > :is(.sheet,.popup))::before,:is(html[data-fill=on] .drawer,html .woot-widget-holder.sheet-o,.veil.offer > .sheet,dialog.announce > :is(.sheet,.popup))::after{display:none}
  /* no glass behind a sheet or inside it: while one stands the page's glass goes (WebKit painted the paper see-through over a blur) */
  html:is(.draweropen,.chatopen,:has(.veil:not([hidden]),dialog[open])) :is(.stage,.stage *){-webkit-backdrop-filter:none!important;backdrop-filter:none!important}
  /* the one close: a night round in a sheet is the paper round */
  :is(html[data-fill=on] .drawer,html .woot-widget-holder.sheet-o,.veil.offer > .sheet,dialog.announce > :is(.sheet,.popup)) .pround.on-night{background:none;box-shadow:var(--quiet-edge);color:var(--ink)}
  /* the opener's section stands first; a divider stands between two shown sections in the order they stand */
  html[data-fill=on] .drawer .paper > .sheet[data-lead]{order:-1}
  html[data-fill=on] .drawer .paper > .sheet{margin-top:6px;border-top:1px solid var(--hairline)}
  html[data-fill=on] .drawer .paper > .sheet[data-first]{margin-top:0;border-top:0}
  /* the chat: the vendor's frame the whole sheet (its navy head the top), the whole sheet at the frame's foot, --chat-h tall at most; its one white X (THE CHAT'S ONE CLOSE above) is the holder's on every page at every size */
  html .woot-widget-holder.sheet-o{top:max(var(--sheet-top),calc(100vh - var(--sheet-bottom) - var(--chat-h)))!important;top:max(var(--sheet-top),calc(100dvh - var(--sheet-bottom) - var(--chat-h)))!important;overflow:hidden}
  html[data-fill=on] .drawer.away{animation:var(--sheet-out)}
  html[data-fill=on] .drawer .paper{flex:1 1 auto;padding:0 8px 8px;border-radius:0;background:none;box-shadow:none}
  /* one title: the sheet's head says the first section's heading, so that section's own heading goes (shell.js firstShown); a section under it keeps its heading */
  html[data-fill=on] .drawer .paper > .sheet[data-first] > .h{display:none}
  /* the vendor's holder: its paper under the frame, its open and close the same rise */
  html .woot-widget-holder.sheet-o iframe{border-radius:var(--round)!important}
  html .woot-widget-holder.sheet-o{transition:opacity .35s cubic-bezier(.2,.8,.2,1),transform .35s cubic-bezier(.2,.8,.2,1)!important}
  html .woot-widget-holder.sheet-o.woot--hide{transform:translateY(var(--row))}
  html:is(.draweropen,.chatopen)[data-fill=on] .app:not(.door):not(.enroldoor) .stage{overflow:hidden;overscroll-behavior:none;overflow-anchor:none}
}
/* THE SHEET'S FIRST GROUP, ONE RULE FOR BOTH SIDES (2026-09-27; the owner on the learner's phone: "can even be in the box of notifications as a group when opening?"; on the guardian's: "just like we did for the learner, you can on mobile put these into the notification dropdown of parents, the same way we did for the learner"): under 1000 px a .sgroup (the learner's Your year, desk.js YearGroup; the guardian's learners, pending.js KidGroup) stands first in the sheet the bell or the round opens, over the opener's section, with no divider; while it holds its content the head leaves the flow, its title goes and its Close stands at the right of the group's label row (--g-mid, the Close's middle), and the section after the group keeps its divider and its own heading; an empty group is not drawn */
.sgroup:empty{display:none}
@media (max-width:999px){
  .drawer{--g-mid:calc(var(--sheet-pad) + var(--chip-h-sm) / 2)}
  .drawer .paper > .sgroup{order:-2}
  html[data-fill=on] .drawer .paper > .sheet.sgroup{margin-top:0;border-top:0}
  html[data-fill=on] .drawer .paper > .sgroup:has(.ytiles,.slides) ~ .sheet[data-first]{margin-top:6px;border-top:1px solid var(--hairline)}
  html[data-fill=on] .drawer .paper > .sgroup:has(.ytiles,.slides) ~ .sheet[data-first] > .h{display:block}
  html[data-fill=on] .drawer:has(.sgroup .ytiles,.sgroup .slides) #dtitle{display:none}
  html[data-fill=on] .drawer:has(.sgroup .ytiles,.sgroup .slides) .dhead{position:absolute;top:0;right:0;z-index:2;width:auto;padding:var(--sheet-pad) var(--sheet-pad) 0}
  /* the guardian's group: the plate's slides (stats.js Slides) on the paper: the name and grade the label row, the term line, the track, the rings (each a link to the learner's Progress), the footer, the dots */
  .kidstats .slides{overflow:hidden;touch-action:pan-y;padding:calc(var(--g-mid) - 16px) 14px 12px}
  /* the slide's first row (the date and the term, two 16 px lines) is the group's label row: centred on the Close (--g-mid) and clear of it at the right */
  .kidstats .sl > .today:first-child > .g{padding-right:calc(var(--chip-h-sm) + 12px)}
  .kidstats .slides > .track{display:flex;transition:transform .45s cubic-bezier(.2,.8,.2,1)}
  .kidstats .track > .sl{flex:0 0 100%;min-width:0}
  .kidstats .track > .sl > .k{display:none}
  /* the same pieces in the sheet's paper (the learner card's shapes in the paper's inks) */
  .kidstats .g{display:grid;grid-template-columns:auto minmax(0,1fr);align-items:center;column-gap:16px}
  .kidstats .d{display:grid;grid-template-columns:32px auto;column-gap:8px}
  .kidstats .leaf{grid-row:1/3;position:relative;width:32px;height:32px;color:var(--faint)}
  .kidstats .leaf svg{display:block;width:32px;height:32px;fill:none;stroke:currentColor;stroke-width:1.5;stroke-linecap:round;stroke-linejoin:round}
  .kidstats .leaf b{position:absolute;inset:12px 0 1px;display:grid;place-items:center;font:500 var(--t3)/18px var(--sans);font-variant-numeric:var(--tab);color:var(--ink)}
  .kidstats .k{display:block;font:500 var(--t1)/16px var(--sans);letter-spacing:.08em;text-transform:uppercase;color:var(--mute);white-space:nowrap}
  .kidstats .k b{font-weight:500;font-variant-numeric:var(--tab);color:var(--ink)}
  .kidstats .w{justify-self:end;width:min(100%,176px);min-width:0}
  .kidstats .w>.k{white-space:normal;text-align:right}
  .kidstats .l{display:flex;justify-content:space-between;column-gap:8px}
  .kidstats .qmap .road{stroke:var(--track-line)}.kidstats .qmap circle{fill:var(--track-line)}.kidstats .qmap circle.on{fill:var(--sky)}.kidstats .qmap circle.now{fill:#fff}
  .kidstats .yc{display:block;margin-top:12px;padding-top:12px;border-top:1px solid var(--hairline);color:var(--ink);text-decoration:none}
  .kidstats .yc>.k{display:flex;align-items:center;gap:2px}
  .kidstats .f{display:grid;gap:4px;margin-top:8px}
  .kidstats .stat{display:grid;grid-template-columns:var(--icon-sm) auto minmax(0,1fr);align-items:center;column-gap:8px;min-height:var(--q-row-sm)}
  .kidstats .stat .num{font:500 var(--t3)/20px var(--sans);font-variant-numeric:var(--tab);color:var(--ink)}
  .kidstats .stat .sm{min-width:0;font:400 var(--t3)/20px var(--sans);color:var(--ink-soft)}
  .kidstats .yc + .today{margin-top:8px;padding-top:6px;border-top:1px solid var(--hairline)}
  .kidstats .nx{display:grid;grid-template-columns:6px minmax(0,1fr) auto;align-items:center;column-gap:8px;min-height:32px;color:var(--ink);text-decoration:none;font:400 var(--t3)/18px var(--sans)}
  .kidstats .nx::before{content:'';width:6px;height:6px;border-radius:50%;--rc:var(--faint);box-shadow:var(--ring-on)}
  .kidstats .nx[data-warm]::before{background:var(--warm);box-shadow:none}
  .kidstats .nx .s{min-width:0;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
  .kidstats .nx .c{font:500 var(--t2)/18px var(--sans);font-variant-numeric:var(--tab);color:var(--sky-ink);white-space:nowrap}
  .kidstats .dots{display:flex;justify-content:center;gap:2px;margin:8px 0 0}
  .kidstats .dots button{--face:14px;--face-i:14px;width:14px;height:14px;padding:0;background:none;cursor:pointer}
  .kidstats .dots button::before{content:'';position:absolute;left:4px;top:4px;width:6px;height:6px;border-radius:var(--pill);background:var(--track-line);transition:background .3s,left .3s,width .3s}
  .kidstats .dots button.on::before{left:1px;width:12px;background:var(--sky)}
}
@media (prefers-reduced-motion:reduce){.kidstats .slides > .track,.kidstats .dots button::before{transition:none}}
@keyframes sheet-in{from{opacity:0;transform:translateY(var(--row))}}
/* ── THE PHONE CHAT (the owner's go, 2026-10-14 15:5x SAST, on the analysis of that day: with the keyboard open the sheet frame kept 348 px of a 544 px viewport at 390 x 844 (the corner's column above it, the bar's room under it) and 304 of 500 at 360 x 800, the thread 0 px under the vendor's ten-line composer; on iOS the frame scrolled under the keyboard; README 3 THE PHONE CHAT): ON A PHONE (666 px and under; a tablet keeps the sheet frame above) THE OPEN CHAT IS THE WHOLE VISUAL VIEWPORT, one rule for a signed-in page's holder and a door's alike (chat.js place() puts .sheet-o on both): fixed at the visual viewport's top and height (--vv-top, --vv-h, written by shell.js; 100vh the fallback of a lost script, never 100dvh), the sides, the rim and the corners gone, the safe areas as its padding (the head's navy under the top inset, white under the bottom), the frame the whole paper; the bar, the banners and the corner's buttons stand under it (the bar's lift over the chat is the tablet's and the desk's); the ask dialog and the announcement take the same viewport (.announce above). The inside (the one-line head, the four-line composer, the thread's padding, Enter as a newline) is the Desk's one-light.css and phone.js. ── */
@media (max-width:666px),(max-height:500px){
  /* A PHONE IS 666 PX WIDE OR 500 PX TALL AND UNDER (the deep test of 2026-10-14 evening: sideways with the keyboard up a phone is 844 x 180, which read as a desk; chat.js place() the same pair, the Desk's MOBILE_SHIM the vendor's flag); the vendor's position class rides the selector for its weight alone: THE SHEET's rim rule above is :is() with a three-class argument (0,3,0), so a (0,2,1) rule here lost its border and corners to it (the first probe run: the frame 4 px inside the holder, the corners round) */
  html .woot-widget-holder.sheet-o.woot-elements--right{top:var(--vv-top,0px)!important;left:0!important;right:0!important;bottom:auto!important;height:100vh;height:var(--vv-h,100dvh)!important;min-height:0!important;max-height:none!important;width:auto!important;min-width:0!important;border:0!important;border-radius:0!important;outline:0!important;padding:env(safe-area-inset-top) 0 env(safe-area-inset-bottom)!important;box-sizing:border-box;background:linear-gradient(var(--ink),var(--ink)) top/100% env(safe-area-inset-top) no-repeat #fff!important;box-shadow:none!important}
  html .woot-widget-holder.sheet-o iframe{border-radius:0!important}
  html.chatopen .app:not(.door):not(.enroldoor) .shell > .barwrap{z-index:5}
}
@keyframes sheet-out{to{opacity:0;transform:translateY(var(--row))}}
@media (prefers-reduced-motion:reduce){:root{--sheet-in:none;--sheet-out:none}}
/* THE LIGHT TIER DRAWS NO BLUR AND RUNS NO ANIMATION BUT THE BUSY BAR (the owner, 2026-10-14: "some learners have good computers, some have very weak ones from 2012"; the morning rush, "the computers were frozen yesterday from everything loading"; a backdrop blur is re-rendered by the GPU on every frame under it, and an integrated or software renderer pays it in whole frames): tier.js, THE DEVICE TIER, one judgement kept on the device, and shell.js marks the root data-tier light or full before any paint; under the light tier the four blur tokens are none, so the 17 rules that name a token draw no blur (5 more were none already) and the one literal blur (.lightbox .close) follows, while every glass fill keeps its own token (the same hue and alpha, composited flat), and every animation stops but the busy bar's scan (not under reduced motion, whose own rule stills it); the full tier changes nothing */
html[data-tier="light"]{--isle-blur:none;--glass-blur:none;--lens-blur:none;--rim-blur:none}
html[data-tier="light"] .lightbox .close{-webkit-backdrop-filter:none;backdrop-filter:none}
html[data-tier="light"] *,html[data-tier="light"] *::before,html[data-tier="light"] *::after{animation:none!important}
@media (prefers-reduced-motion:no-preference){html[data-tier="light"] .scanbar i{animation:scan 1.6s cubic-bezier(.4,0,.6,1) infinite alternate both!important}}
