/* ─── Typefaces ───
   Served from the hub rather than Google. They used to be fetched with a stylesheet marked media="print" and
   flipped to "all" by an onload handler, which is a trick to stop it blocking the first paint — but when the flip
   didn't happen the page simply kept the fallback font until you reloaded. These also work with no internet,
   which her portal needs, and nobody outside the house is told when a page is opened.

   Rajdhani, Orbitron and Share Tech Mono are all under the SIL Open Font License, latin subset only. */
@font-face {
    font-family: 'Rajdhani'; font-style: normal; font-weight: 500; font-display: swap;
    src: url('fonts/Rajdhani-500.woff2') format('woff2');
}
@font-face {
    font-family: 'Rajdhani'; font-style: normal; font-weight: 600; font-display: swap;
    src: url('fonts/Rajdhani-600.woff2') format('woff2');
}
@font-face {
    font-family: 'Rajdhani'; font-style: normal; font-weight: 700; font-display: swap;
    src: url('fonts/Rajdhani-700.woff2') format('woff2');
}
@font-face {
    font-family: 'Orbitron'; font-style: normal; font-weight: 600; font-display: swap;
    src: url('fonts/Orbitron-600.woff2') format('woff2');
}
@font-face {
    font-family: 'Orbitron'; font-style: normal; font-weight: 700; font-display: swap;
    src: url('fonts/Orbitron-700.woff2') format('woff2');
}
@font-face {
    font-family: 'Share Tech Mono'; font-style: normal; font-weight: 400; font-display: swap;
    src: url('fonts/ShareTechMono-400.woff2') format('woff2');
}

/* ─── Ornge UI · shared by the parent dashboard and the student portal ─────
   Styled after OrngeChat. Each theme sets a handful of base tokens on
   <html data-theme="…">; everything else is derived from them below.
   ─────────────────────────────────────────────────────────────────── */

/* ─── Themes (same palette as OrngeChat) ─── */

:root,
[data-theme="orange"] {
    --accent: #ff6b00;
    --accent-rgb: 255, 107, 0;
    --accent-hover: #ff8533;
    --on-accent: #000;
    --bg-base: #050510;
    --bg-primary: #0a0a1e;
    --bg-secondary: #12121f;
    --bg-tertiary: #1a1a2e;
    --text-primary: #ffffff;
    --text-secondary: rgba(255, 255, 255, .72);
    --text-muted: rgba(255, 255, 255, .5);
    --card-border: rgba(255, 255, 255, .08);
    --ok: #23a55a;
    --warn: #faa61a;
    --danger: #f23f43;
    --glow: 1;
    color-scheme: dark;
}

[data-theme="cyan"] { --accent: #00f0ff; --accent-rgb: 0, 240, 255; --accent-hover: #33f5ff; }
[data-theme="magenta"] { --accent: #ff00aa; --accent-rgb: 255, 0, 170; --accent-hover: #ff33bb; }
[data-theme="green"] { --accent: #05ffa1; --accent-rgb: 5, 255, 161; --accent-hover: #33ffb5; }
[data-theme="purple"] { --accent: #b829dd; --accent-rgb: 184, 41, 221; --accent-hover: #c54fe5; --on-accent: #fff; }
[data-theme="gold"] { --accent: #fcee0a; --accent-rgb: 252, 238, 10; --accent-hover: #fdf23b; }

[data-theme="slate"] {
    --accent: #5865f2;
    --accent-rgb: 88, 101, 242;
    --accent-hover: #4752c4;
    --on-accent: #fff;
    --bg-base: #e3e5e8;
    --bg-primary: #f2f3f5;
    --bg-secondary: #ffffff;
    --bg-tertiary: #ebedef;
    --text-primary: #060607;
    --text-secondary: rgba(6, 6, 7, .72);
    --text-muted: rgba(6, 6, 7, .55);
    --card-border: rgba(0, 0, 0, .1);
    --ok: #15803d;
    --warn: #a16207;
    --danger: #c62828;
    --glow: 0;
    color-scheme: light;
}

[data-theme="light"] {
    --accent: #0088cc;
    --accent-rgb: 0, 136, 204;
    --accent-hover: #0072ab;
    --on-accent: #fff;
    --bg-base: #f0f2f5;
    --bg-primary: #f8f9fa;
    --bg-secondary: #ffffff;
    --bg-tertiary: #f0f2f5;
    --text-primary: #1a1a1a;
    --text-secondary: rgba(26, 26, 26, .72);
    --text-muted: rgba(26, 26, 26, .55);
    --card-border: rgba(0, 0, 0, .1);
    --ok: #15803d;
    --warn: #a16207;
    --danger: #c62828;
    --glow: 0;
    color-scheme: light;
}

[data-theme="fox"] {
    --accent: #c25100;
    --accent-rgb: 194, 81, 0;
    --accent-hover: #a34400;
    --on-accent: #fff;
    --bg-base: #a1a1a1;
    --bg-primary: #bababa;
    --bg-secondary: #cbcbcb;
    --bg-tertiary: #c1c1c1;
    --text-primary: #121212;
    --text-secondary: rgba(18, 18, 18, .78);
    --text-muted: rgba(18, 18, 18, .62);
    --card-border: rgba(0, 0, 0, .14);
    --ok: #14622f;
    --warn: #7a4a00;
    --danger: #a11b1b;
    --glow: 0;
    color-scheme: light;
}

/* ─── Derived tokens (used by the components below) ─── */

:root {
    --brand: var(--accent);
    --brand-strong: var(--accent-hover);
    --brand-soft: rgba(var(--accent-rgb), .14);
    --bg: var(--bg-primary);
    --surface: var(--bg-secondary);
    --surface-2: var(--bg-tertiary);
    --border: var(--card-border);
    --glass-border: rgba(var(--accent-rgb), .18);
    --glass-border-hover: rgba(var(--accent-rgb), .4);
    --text: var(--text-primary);
    --ok-soft: color-mix(in srgb, var(--ok) 16%, transparent);
    --warn-soft: color-mix(in srgb, var(--warn) 16%, transparent);
    --danger-soft: color-mix(in srgb, var(--danger) 14%, transparent);
    --radius: 16px;
    --radius-sm: 10px;
    --shadow: 0 8px 32px rgba(0, 0, 0, calc(.12 + .28 * var(--glow)));
    --shadow-glow: 0 0 20px rgba(var(--accent-rgb), calc(.3 * var(--glow))), 0 0 40px rgba(var(--accent-rgb), calc(.1 * var(--glow)));
    --nav-w: 236px;
    --font-sans: "Rajdhani", "Segoe UI", system-ui, sans-serif;
    --font-display: "Orbitron", "Segoe UI", system-ui, sans-serif;
}

* { box-sizing: border-box; }

html, body {
    margin: 0;
    color: var(--text);
    font-family: var(--font-sans);
    font-size: 16.5px;
    font-weight: 500;
    line-height: 1.5;
    -webkit-font-smoothing: antialiased;
}

body {
    min-height: 100vh;
    background: var(--bg-primary);
}

/* The glow sits on its own fixed layer. (background-attachment: fixed made the browser repaint the whole
   screen on every scroll and update, which felt sluggish, especially on PCs without graphics acceleration.) */
body::before {
    content: "";
    position: fixed;
    inset: 0;
    z-index: -1;
    pointer-events: none;
    background:
        radial-gradient(1200px 600px at 100% -10%, rgba(var(--accent-rgb), calc(.03 + .07 * var(--glow))), transparent 60%),
        radial-gradient(900px 500px at -10% 110%, rgba(var(--accent-rgb), calc(.02 + .04 * var(--glow))), transparent 60%);
}

h1, h2, h3 { line-height: 1.25; margin: 0 0 .5rem; }
h1 { font-family: var(--font-display); font-size: 1.45rem; font-weight: 700; letter-spacing: .02em; }
h2 { font-size: 1.2rem; font-weight: 700; letter-spacing: .01em; }
h3 { font-size: 1.05rem; font-weight: 700; }
h1:focus { outline: none; }
p { margin: 0 0 .75rem; }
a { color: var(--accent); }
a:hover { color: var(--accent-hover); }
.muted { color: var(--text-muted); }
.small { font-size: .88rem; }
::selection { background: rgba(var(--accent-rgb), .35); }

/* ─── Shell ─── */

.shell { display: flex; min-height: 100vh; }

.sidenav {
    width: var(--nav-w);
    flex-shrink: 0;
    background: var(--bg-base);
    border-right: 1px solid var(--glass-border);
    padding: 1.25rem .75rem;
    display: flex;
    flex-direction: column;
    gap: .2rem;
    position: sticky;
    top: 0;
    height: 100vh;
}

.brand {
    display: flex;
    align-items: center;
    gap: .65rem;
    font-family: var(--font-display);
    font-weight: 700;
    font-size: .95rem;
    letter-spacing: .03em;
    padding: .25rem .6rem 1.1rem;
    color: var(--text);
    text-decoration: none;
}
.brand-mark {
    width: 34px; height: 34px;
    border-radius: 10px;
    background: var(--accent);
    color: var(--on-accent);
    display: grid; place-items: center;
    font-size: 1rem;
    box-shadow: var(--shadow-glow);
    flex-shrink: 0;
}
.brand small { display: block; font-family: var(--font-sans); font-weight: 600; font-size: .75rem; letter-spacing: .08em; text-transform: uppercase; color: var(--text-muted); }

.navlink {
    display: flex;
    align-items: center;
    gap: .7rem;
    padding: .55rem .75rem;
    border-radius: var(--radius-sm);
    color: var(--text-secondary);
    text-decoration: none;
    font-weight: 600;
    font-size: 1.02rem;
    border-left: 3px solid transparent;
    transition: background .15s, color .15s;
}
.navlink:hover { background: rgba(var(--accent-rgb), .07); color: var(--text); }
.navlink.active { background: var(--brand-soft); color: var(--accent); border-left-color: var(--accent); }
.navlink .ico { width: 1.3rem; text-align: center; }
.nav-spacer { flex: 1; }

.main { flex: 1; min-width: 0; padding: 2rem clamp(1rem, 3vw, 2.5rem) 4rem; max-width: 1200px; }

.page-head { display: flex; align-items: flex-end; justify-content: space-between; gap: 1rem; flex-wrap: wrap; margin-bottom: 1.5rem; }
.page-head p { margin: 0; }

@media (max-width: 820px) {
    .shell { flex-direction: column; }
    .sidenav {
        position: fixed; bottom: 0; top: auto; left: 0; right: 0;
        width: 100%; height: auto;
        flex-direction: row; justify-content: space-around;
        padding: .35rem .25rem calc(.35rem + env(safe-area-inset-bottom));
        border-right: 0; border-top: 1px solid var(--glass-border);
        z-index: 50;
    }
    .brand, .nav-spacer, .nav-secondary { display: none; }
    .navlink { flex-direction: column; gap: 0; font-size: .72rem; padding: .35rem .4rem; border-left: 0; border-top: 2px solid transparent; }
    .navlink.active { border-top-color: var(--accent); }
    .navlink .ico { font-size: 1.2rem; }
    .main { padding: 1.25rem 1rem 6rem; }
    .sidenav .nav-more { display: block; }
}

/* "More" in the phone bottom bar: the pages that don't fit, in a panel above the bar. */
.nav-more { display: none; }
.nav-more > summary { list-style: none; cursor: pointer; }
.nav-more > summary::-webkit-details-marker { display: none; }
.nav-more[open] > summary { color: var(--accent); border-top-color: var(--accent); }
.nav-more-panel {
    position: fixed; left: .5rem; right: .5rem; bottom: calc(4.3rem + env(safe-area-inset-bottom)); z-index: 60;
    display: grid; grid-template-columns: 1fr 1fr; gap: .2rem; padding: .5rem;
    background: var(--bg-base); border: 1px solid var(--glass-border); border-radius: 14px; box-shadow: 0 -10px 30px rgba(0, 0, 0, .45);
}
.nav-more-panel .navlink { flex-direction: row; gap: .55rem; font-size: .95rem; padding: .6rem .7rem; border-top: 0; }

/* ─── Building blocks ─── */

.card {
    background: var(--surface);
    border: 1px solid var(--glass-border);
    border-radius: var(--radius);
    padding: 1.25rem;
    box-shadow: var(--shadow);
    transition: border-color .2s, box-shadow .2s;
}
.card + .card { margin-top: 1rem; }
.grid > .card, .stats > .card, .grid > * > .card:first-child { margin-top: 0; }
.card-head { display: flex; justify-content: space-between; align-items: center; gap: .75rem; margin-bottom: .75rem; }
.card-head h2 { margin: 0; }

.grid { display: grid; gap: 1rem; }
.grid > * { min-width: 0; }
.grid-2 { grid-template-columns: repeat(auto-fit, minmax(300px, 1fr)); }
.grid-3 { grid-template-columns: repeat(auto-fit, minmax(220px, 1fr)); }
/* Stat tiles: always one row on desktop, stacked on phones. */
.stats { display: grid; gap: 1rem; grid-template-columns: repeat(3, minmax(0, 1fr)); margin-bottom: 1rem; }
@media (max-width: 720px) { .stats { grid-template-columns: 1fr; } }

.stat { display: flex; flex-direction: column; gap: .2rem; }
.stat .label { color: var(--text-muted); font-size: .8rem; font-weight: 700; letter-spacing: .08em; text-transform: uppercase; }
.stat .value { font-family: var(--font-display); font-size: 1.15rem; font-weight: 700; margin: .15rem 0; }
.stat .value.big { font-family: var(--font-sans); font-size: 2.3rem; line-height: 1.1; color: var(--accent); }
.stat .detail { color: var(--text-secondary); font-size: .92rem; }

.btn {
    display: inline-flex; align-items: center; justify-content: center; gap: .4rem;
    font: inherit; font-weight: 700; letter-spacing: .02em;
    padding: .5rem 1rem;
    border-radius: var(--radius-sm);
    border: 1px solid var(--card-border);
    background: var(--surface-2);
    color: var(--text);
    cursor: pointer;
    text-decoration: none;
    white-space: nowrap;
    transition: background .15s, border-color .15s, box-shadow .15s, color .15s;
}
.btn:hover { border-color: var(--glass-border-hover); color: var(--text); }
.btn:disabled { opacity: .55; cursor: not-allowed; }
.btn-primary { background: var(--accent); border-color: var(--accent); color: var(--on-accent); }
.btn-primary:hover { background: var(--accent-hover); border-color: var(--accent-hover); color: var(--on-accent); box-shadow: var(--shadow-glow); }
.btn-danger { color: var(--danger); background: transparent; }
.btn-danger:hover { background: var(--danger-soft); border-color: var(--danger); color: var(--danger); }
.btn-ghost { border-color: transparent; background: transparent; }
.btn-sm { padding: .28rem .65rem; font-size: .88rem; }
.btn-block { width: 100%; }
.btn-row { display: flex; gap: .5rem; flex-wrap: wrap; align-items: center; }

.field { display: flex; flex-direction: column; gap: .3rem; margin-bottom: 1rem; }
.field label { font-weight: 700; font-size: .9rem; letter-spacing: .02em; }
.field .hint { color: var(--text-muted); font-size: .85rem; }
.field-row { display: grid; gap: 0 1rem; grid-template-columns: repeat(auto-fit, minmax(140px, 1fr)); }

/* Every kind that is typed into. Search and email were missing from this list, and turned up as the
   browser's own white box in the middle of a dark page. */
input[type=text], input[type=password], input[type=url], input[type=date], input[type=time], input[type=number],
input[type=search], input[type=email], input[type=tel], input:not([type]), select, textarea {
    font: inherit;
    color: var(--text);
    background: var(--bg-primary);
    border: 1px solid var(--card-border);
    border-radius: var(--radius-sm);
    padding: .5rem .7rem;
    width: 100%;
}
select option { background: var(--bg-secondary); color: var(--text); }
input::placeholder, textarea::placeholder { color: var(--text-muted); }
input[type=color] { width: 3rem; height: 2.4rem; padding: .15rem; border: 1px solid var(--card-border); border-radius: var(--radius-sm); background: var(--bg-primary); }
input[type=checkbox] { accent-color: var(--accent); width: 1.05rem; height: 1.05rem; }
textarea { min-height: 6rem; resize: vertical; }
input:focus, select:focus, textarea:focus { outline: none; border-color: var(--accent); box-shadow: 0 0 0 3px rgba(var(--accent-rgb), .2); }
.checkbox { display: flex; gap: .5rem; align-items: center; font-weight: 600; }

.alert { padding: .7rem .9rem; border-radius: var(--radius-sm); margin-bottom: 1rem; font-size: .95rem; font-weight: 600; border: 1px solid transparent; }
.alert-error { background: var(--danger-soft); color: var(--danger); border-color: color-mix(in srgb, var(--danger) 30%, transparent); }
.alert-ok { background: var(--ok-soft); color: var(--ok); border-color: color-mix(in srgb, var(--ok) 30%, transparent); }
.alert-warn { background: var(--warn-soft); color: var(--warn); border-color: color-mix(in srgb, var(--warn) 30%, transparent); }

.pill {
    display: inline-flex; align-items: center; gap: .3rem;
    padding: .08rem .55rem;
    border-radius: 999px;
    font-size: .76rem; font-weight: 700; letter-spacing: .04em;
    background: var(--surface-2); color: var(--text-secondary);
    border: 1px solid var(--card-border);
    vertical-align: middle;
}
.pill-ok { background: var(--ok-soft); color: var(--ok); border-color: transparent; }
.pill-warn { background: var(--warn-soft); color: var(--warn); border-color: transparent; }
.pill-brand { background: var(--brand-soft); color: var(--accent); border-color: var(--glass-border); }
.pill-danger { background: var(--danger-soft); color: var(--danger); border-color: transparent; }

.subject-dot { width: .7rem; height: .7rem; border-radius: 50%; display: inline-block; flex-shrink: 0; }

.empty { text-align: center; padding: 2rem 1rem; color: var(--text-muted); }
.empty .big { font-size: 2rem; display: block; margin-bottom: .5rem; }

.list { list-style: none; margin: 0; padding: 0; }
.list-item { display: flex; align-items: center; gap: .85rem; padding: .7rem 0; border-top: 1px solid var(--card-border); }
.list-item:first-child { border-top: 0; }
.list-item .grow { flex: 1; min-width: 0; }
.truncate { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }

code { overflow-wrap: anywhere; font-family: "Share Tech Mono", ui-monospace, Consolas, monospace; font-size: .9em; color: var(--text-secondary); }

/* ─── Periods (today / week planner) ─── */

.period {
    display: grid;
    grid-template-columns: 4.5rem 4px 1fr auto;
    gap: .85rem;
    align-items: center;
    padding: .65rem 0;
    border-top: 1px solid var(--card-border);
}
.period:first-child { border-top: 0; }
.period .time { font-family: "Share Tech Mono", ui-monospace, monospace; font-size: .85rem; color: var(--text-muted); line-height: 1.35; }
.period .bar { align-self: stretch; border-radius: 4px; }
.period.is-now { background: var(--brand-soft); margin: 0 -1.25rem; padding: .65rem 1.25rem; box-shadow: inset 3px 0 0 var(--accent); }
.period.is-soft .bar { opacity: .35; }
.period.is-soft .name { color: var(--text-muted); font-weight: 500; }
.period .name { font-weight: 700; font-size: 1.05rem; }

/* ─── Timetable grid ─── */

.tt-scroll { overflow-x: auto; margin: 0 -1.25rem; padding: 0 1.25rem; }
.tt { display: grid; grid-template-columns: repeat(var(--days), minmax(150px, 1fr)); gap: .75rem; min-width: calc(var(--days) * 160px); }
.tt-day h3 { font-size: .8rem; text-transform: uppercase; letter-spacing: .1em; color: var(--text-muted); margin-bottom: .5rem; }
.tt-slot {
    width: 100%;
    text-align: left;
    font: inherit;
    color: var(--text);
    display: block;
    padding: .5rem .65rem;
    margin-bottom: .4rem;
    border-radius: var(--radius-sm);
    border: 1px solid var(--card-border);
    border-left: 4px solid var(--c, var(--card-border));
    background: var(--surface-2);
    cursor: pointer;
    transition: border-color .15s, background .15s;
}
.tt-slot:hover { border-color: var(--glass-border-hover); border-left-color: var(--c, var(--accent)); }
.tt-slot .t { font-family: "Share Tech Mono", ui-monospace, monospace; font-size: .78rem; color: var(--text-muted); }
.tt-slot .n { font-weight: 700; font-size: .98rem; }
.tt-slot.soft { background: transparent; border-style: dashed; border-left-style: solid; }
.tt-add { width: 100%; border: 1px dashed var(--card-border); background: transparent; color: var(--text-muted); border-radius: var(--radius-sm); padding: .45rem; font: inherit; font-weight: 600; cursor: pointer; }
.tt-add:hover { color: var(--accent); border-color: var(--accent); }

/* ─── Theme picker ─── */

.theme-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(92px, 1fr)); gap: .6rem; }
.theme-option {
    font: inherit; color: var(--text); cursor: pointer;
    display: flex; flex-direction: column; align-items: center; gap: .35rem;
    padding: .6rem .4rem .5rem;
    border-radius: var(--radius-sm);
    border: 1px solid var(--card-border);
    background: var(--surface-2);
    font-weight: 700; font-size: .9rem;
}
.theme-option:hover { border-color: var(--glass-border-hover); }
.theme-option.active { border-color: var(--accent); box-shadow: 0 0 0 2px rgba(var(--accent-rgb), .35); }
.theme-swatch {
    width: 100%; height: 38px; border-radius: 7px;
    display: grid; place-items: center;
    border: 1px solid rgba(128, 128, 128, .25);
}
.theme-swatch span { width: 18px; height: 18px; border-radius: 50%; box-shadow: 0 0 10px currentColor; }

/* ─── Drawer (edit panel) ─── */

.drawer-backdrop { position: fixed; inset: 0; background: rgba(0, 0, 0, .6); z-index: 90; animation: fade .15s; backdrop-filter: blur(2px); }
.drawer {
    position: fixed; top: 0; right: 0; bottom: 0;
    width: min(460px, 100%);
    background: var(--surface);
    border-left: 1px solid var(--glass-border);
    z-index: 100;
    display: flex; flex-direction: column;
    box-shadow: -8px 0 40px rgba(0, 0, 0, .35);
    animation: slide .18s ease-out;
}
.drawer-head { display: flex; justify-content: space-between; align-items: center; padding: 1rem 1.25rem; border-bottom: 1px solid var(--card-border); }
.drawer-head h2 { margin: 0; }
.drawer-body { padding: 1.25rem; overflow-y: auto; flex: 1; }
.drawer-foot { padding: 1rem 1.25rem; border-top: 1px solid var(--card-border); display: flex; gap: .5rem; justify-content: space-between; }
@keyframes slide { from { transform: translateX(30px); opacity: 0; } }
@keyframes fade { from { opacity: 0; } }

/* ─── Auth pages ─── */

.auth-wrap { min-height: 100vh; display: grid; place-items: center; padding: 1.5rem; }
.auth-card { width: min(420px, 100%); box-shadow: var(--shadow), var(--shadow-glow); }
.auth-card .brand { justify-content: center; padding-bottom: 1.25rem; font-size: 1.15rem; }

/* ─── Blazor plumbing ─── */

.validation-message { color: var(--danger); font-size: .88rem; font-weight: 600; }
.blazor-error-boundary { background: var(--danger-soft); color: var(--danger); padding: 1rem; border-radius: var(--radius-sm); }
.blazor-error-boundary::after { content: "Something went wrong on this part of the page."; }

::-webkit-scrollbar { width: 10px; height: 10px; }
::-webkit-scrollbar-track { background: transparent; }
::-webkit-scrollbar-thumb { background: rgba(var(--accent-rgb), .25); border-radius: 10px; border: 2px solid transparent; background-clip: padding-box; }
::-webkit-scrollbar-thumb:hover { background: rgba(var(--accent-rgb), .45); background-clip: padding-box; border: 2px solid transparent; }

/* ─── Reports and tables ─── */
.report-table { width: 100%; border-collapse: collapse; font-size: .95rem; }
.report-table th, .report-table td { text-align: left; padding: .5rem .6rem; border-bottom: 1px solid var(--card-border); vertical-align: top; }
.report-table th { color: var(--text-muted); font-weight: 600; font-size: .8rem; text-transform: uppercase; letter-spacing: .04em; }
.report-table td.num, .report-table th.num { text-align: right; white-space: nowrap; }
.table-wrap { overflow-x: auto; }
.rag-mini { display: inline-flex; gap: .35rem; white-space: nowrap; }
.report-note { min-height: 4rem; padding: .75rem; border: 1px dashed var(--card-border); border-radius: 10px; white-space: pre-wrap; outline: none; }
.report-note:empty::before { content: attr(data-placeholder); color: var(--text-muted); }
.chat { display: flex; flex-direction: column; gap: .5rem; }
.chat .bubble { max-width: 85%; padding: .6rem .85rem; border-radius: 14px; white-space: pre-wrap; line-height: 1.45; }
.chat .bubble.me { align-self: flex-end; background: var(--accent); color: var(--on-accent); border-bottom-right-radius: 4px; }
.chat .bubble.tutor { align-self: flex-start; background: var(--bg-tertiary); border: 1px solid var(--card-border); border-bottom-left-radius: 4px; }

/* Printing (e.g. a progress report saved as PDF): plain black on white, no navigation or buttons. */
.print-only { display: none; }
/* Pages anyone can open without signing in (installing the apps). Centred, and wide enough for two cards. */
.public-wrap { width: min(880px, 100%); margin: 0 auto; padding: 2rem clamp(1rem, 4vw, 2rem) 4rem; }

/* ─── A passage or poem a quiz asks about ───
   Sits above the question and stays there for every one of them. Capped in height so a long extract can't push the
   answers off the screen; the text itself scrolls. */
.passage { position: relative; background: rgba(var(--accent-rgb), .05); border-color: rgba(var(--accent-rgb), .25); }
.passage h3 { margin: 0 0 .5rem; font-size: .8rem; text-transform: uppercase; letter-spacing: .05em; color: var(--text-muted); }
/* The text is set out by ReadingPage, as a lesson's reading is. It is measured against the card (cqi),
   and the "Show as typed" button sits up beside the heading rather than on a line of its own. */
.passage-text { container-type: inline-size; max-height: 40vh; overflow: auto; margin-right: -.5rem; padding-right: .5rem; }
.passage .read-bar { position: absolute; top: calc(var(--card-pad, 1.25rem) - .4rem); right: calc(var(--card-pad, 1.25rem) - .25rem); margin: 0; }

/* ─── Tabs ───
   Sections of one page. The buttons are real buttons so the keyboard and a screen reader treat them as controls,
   with aria-selected saying which one is open. */
.tabs { display: flex; gap: .15rem; flex-wrap: wrap; border-bottom: 1px solid var(--card-border); margin-bottom: 1.25rem; }
.tab {
    appearance: none; background: transparent; border: 0; border-bottom: 2px solid transparent;
    color: var(--text-muted); font: inherit; font-weight: 600; cursor: pointer;
    padding: .6rem .9rem; margin-bottom: -1px; border-radius: var(--radius-sm) var(--radius-sm) 0 0;
    transition: color .15s, border-color .15s, background .15s;
}
.tab:hover { color: var(--text); background: rgba(var(--accent-rgb), .07); }
.tab[aria-selected="true"] { color: var(--accent); border-bottom-color: var(--accent); }
.tab .tab-count { color: var(--text-muted); font-weight: 600; font-size: .8rem; margin-left: .35rem; }
.tab[aria-selected="true"] .tab-count { color: inherit; opacity: .75; }

@media (max-width: 720px) {
    .tabs { gap: 0; }
    .tab { padding: .55rem .7rem; font-size: .92rem; }
}

/* ─── Settings: many independent cards ───
   Two hand-made columns left one of them empty halfway down the page, so the cards flow instead and the browser
   balances the columns by height. break-inside keeps a card whole. The page is also allowed to be wider than the
   rest of the site: it's a board of separate cards, not something you read left to right. */
.settings-grid { column-count: 2; column-gap: 1rem; }
.settings-grid > * { break-inside: avoid; margin: 0 0 1rem; display: block; }
.settings-grid > .card + .card { margin-top: 0; }

@media (min-width: 1500px) { .settings-grid { column-count: 3; } }
@media (max-width: 900px) { .settings-grid { column-count: 1; } }

/* ─── Screen time ─── */
.right { text-align: right; }
.nowrap { white-space: nowrap; }
.bar { background: var(--card-border); border-radius: 999px; height: .55rem; overflow: hidden; }
.bar-fill { background: var(--accent); height: 100%; border-radius: 999px; }

@media print {

    :root, :root[data-theme] {
        --bg-primary: #fff; --bg-secondary: #fff; --bg-tertiary: #f4f4f4; --bg-base: #fff;
        --text-primary: #111; --text-secondary: #333; --text-muted: #555; --card-border: #ccc;
        --accent: #c2410c; --on-accent: #fff;
    }
    body { background: #fff !important; color: #111; font-size: 11pt; }
    body::before, .sidenav, .no-print, #blazor-error-ui, #components-reconnect-modal { display: none !important; }
    .shell { display: block; }
    .main { padding: 0; max-width: none; }
    .card { box-shadow: none !important; background: #fff !important; border: 1px solid #ccc; break-inside: avoid; }
    .print-only { display: block; }
    .report-note { border: 0; padding: 0; }
    .report-note:empty { display: none; }
    a { color: inherit; text-decoration: none; }
}

/* Her worksheet, answered in the app. The questions have to read as questions and not as a form: a
   heavier prompt, the hint quieter beneath it, and room between one and the next so a long answer does
   not look like a mistake. */
.sheet-section { margin: 1.25rem 0 .5rem; font-size: 1rem; letter-spacing: .04em; text-transform: uppercase; color: var(--text-muted); }
.sheet-q { margin-bottom: 1.1rem; }
.sheet-q > label { display: block; margin-bottom: .35rem; line-height: 1.45; }
.sheet-q .sheet-hint { margin: 0 0 .35rem; }
.sheet-q .work-text { min-height: 0; }

/* The worksheet's different shapes. Everything here has to work at phone width too: a tick list at a
   hob is on her phone, not the laptop. */
.sheet-short { max-width: 20rem; }
.sheet-working { font-family: Consolas, "Cascadia Mono", monospace; font-size: 1rem; line-height: 1.6; }
.sheet-symbols { display: flex; flex-wrap: wrap; gap: .3rem; margin-bottom: .4rem; }
.sheet-symbols .btn { min-width: 2.4rem; font-size: 1.05rem; padding: .25rem .5rem; }
.sheet-ticks { display: flex; flex-direction: column; gap: .5rem; }
.sheet-tick { display: flex; gap: .6rem; align-items: flex-start; line-height: 1.45; cursor: pointer; }
/* Big enough to hit with a thumb, mid-recipe, with one hand. */
.sheet-tick input { width: 1.35rem; height: 1.35rem; flex: 0 0 auto; margin-top: .1rem; accent-color: var(--accent); }
.sheet-table { width: 100%; border-collapse: collapse; }
.sheet-table th { text-align: left; font-size: .85rem; color: var(--text-muted); padding: .2rem .35rem; font-weight: 600; }
.sheet-table td { padding: .15rem .35rem; }
.sheet-table .work-text { min-height: 0; padding: .4rem .5rem; }

/* Somewhere to draw. Light on the page, and the strokes take the text colour so a drawing is legible in
   both themes without being stored twice. */
.sketch-pad { width: 100%; max-width: 40rem; aspect-ratio: 3 / 2; display: block; touch-action: none;
              background: var(--card); border: 1px solid var(--card-border); border-radius: 10px;
              color: var(--text); cursor: crosshair; margin-bottom: .5rem; }
.sketch .btn-row { align-items: center; gap: .5rem; }

/* ═══ Shared by her portal and the parent hub ═══════════════════════════════════════════════════════
   Everything from here down began in her portal and turned out to be wanted in both. It is at the end
   of the file on purpose: these rules were written to come after the building blocks above. */

/* ─── Two kinds of type ───
   Rajdhani and Orbitron are the look of the thing, and they stay where the look is: the menu, headings,
   buttons, labels, the names of lessons. But Rajdhani is a condensed face drawn for a few words at a
   time, and it was also setting every instruction, question, hint and answer - an hour of reading and
   writing a day in a typeface made for a speedometer. That is most of why the pages felt squashed.

   So what is read at length, and what is typed, is set in the reading face the device already has
   (Segoe UI on a PC, Roboto on a phone): nothing to download, and nothing that can fail to load with no
   internet. Each app sets its own sizes; .shell is the parent hub and .portal is hers. */
:root { --font-read: "Segoe UI Variable Text", "Segoe UI", Roboto, "Helvetica Neue", system-ui, sans-serif; }
:is(.shell, .portal) .main { font-family: var(--font-read); font-weight: 400; line-height: 1.55; }
/* :where, so that these weigh nothing and any rule about one particular thing still beats them. */
:is(.shell, .portal) .main :where(h2, h3, .btn, .pill, .tab, .label, .period .name, .field > label, .sheet-section) { font-family: var(--font-sans); }
:is(.shell, .portal) .main :where(strong, b) { font-weight: 600; }
/* The sign-in pages and the pages anyone can open (installing the apps) have no menu and no .main, and
   were the last things left entirely in the condensed face. */
:is(.auth-card, .public-wrap) { font-family: var(--font-read); font-weight: 400; font-size: .97rem; line-height: 1.55; }
:is(.auth-card, .public-wrap) :where(h1, h2, h3, .btn, .pill, label, .tab) { font-family: var(--font-sans); }
:is(.auth-card, .public-wrap) :where(h1) { font-family: var(--font-display); }
:is(.auth-card, .public-wrap) :where(.btn) { font-size: 1.03rem; }
:is(.auth-card, .public-wrap) :where(strong, b) { font-weight: 600; }

/* ─── The menu, folded ───
   shell.js puts nav-rail on <html> when the menu is folded down to its icons, and nav-over when it has
   been opened out over a window too narrow to give it a column of its own. Neither is ever set while the
   menu is the bar along the bottom (a phone), so nothing here needs to say "unless it is narrow". */
.navlink .nav-count { margin-left: auto; }
.nav-top { display: flex; align-items: center; gap: .25rem; margin-bottom: .85rem; }
.nav-top .brand { padding: 0 .2rem; flex: 1; min-width: 0; }
.nav-toggle {
    flex: 0 0 auto; width: 2.3rem; height: 2.3rem; padding: 0; border-radius: var(--radius-sm);
    border: 1px solid transparent; background: none; color: var(--text-secondary);
    font: inherit; font-size: 1.15rem; line-height: 1; cursor: pointer;
}
.nav-toggle:hover { background: rgba(var(--accent-rgb), .07); border-color: var(--glass-border); color: var(--text); }
/* Nothing to fold when the menu is the bar along the bottom of a phone. */
@media (max-width: 820px) { .nav-top { display: none; } }

html.nav-rail {
    :is(.shell, .portal) .sidenav { width: 4.1rem; padding-left: .5rem; padding-right: .5rem; }
    .nav-top { flex-direction: column; gap: .5rem; }
    .nav-top .brand { padding: 0; flex: none; }
    .brand > span:not(.brand-mark), .navlink .lbl { display: none; }
    /* The same 3px on the right as the marker on the left, so the icon sits in the middle of the strip. */
    .navlink { justify-content: center; padding: .6rem 0; border-right: 3px solid transparent; position: relative; }
    .navlink .ico { width: auto; font-size: 1.2rem; }
    .navlink .nav-count { position: absolute; top: .05rem; right: -.1rem; margin: 0; padding: 0 .3rem; font-size: .62rem; }
}

/* ─── A page that knows its own width ───
   .page is a ruler: something to ask "how wide is this page" of, which is not the screen (the menu may
   be folded or not; her portal may have half the window). Layouts hang off it with @container page.
   Nothing that is position: fixed should be put inside one - it would be measured from here too. */
.page { container: page / inline-size; }

/* ─── Cards that fold ───
   A <details>, so it works with nothing running and remembers nothing: every visit starts with the
   things that matter open. The heading is the whole of what you click. */
.card-accent { border-color: var(--accent); }
.fold > summary { cursor: pointer; list-style: none; }
.fold > summary::-webkit-details-marker { display: none; }
.fold > summary h2::before { content: "▸"; display: inline-block; width: 1.1rem; color: var(--accent); transition: transform .15s; }
.fold[open] > summary h2::before { transform: rotate(90deg); }
.fold:not([open]) > summary { margin-bottom: 0; }
.fold > summary:hover h2 { color: var(--accent); }

/* ─── A long text, set out to be read (ReadingPage) ─── */

/* What she reads beside the worksheet. Fixed-width with its spaces kept, because these sheets line their
   worked examples up with them - and wrapped, because the alternative was a scrollbar along the bottom
   of a long extract and half of every line out of sight.

   Each line is its own block (ReadingText works out --h and --m for it): pushed in by --h characters,
   which is where its words start, with the "3." or "-" in front of them pulled back out by --m. So a
   step that wraps carries on under its own words, and an indented example stays indented all the way
   down. The push is capped so that something centred with forty spaces still has room on a phone.

   The type follows the width of the side it is in (cqi is a hundredth of that), between a size that is
   still comfortable and the full one. Nearly every sheet is under 76 characters a line and 2.02 is what
   makes 76 of them fit, so with any room at all most lines never need to wrap in the first place. */
.reading {
    font-family: ui-monospace, "Cascadia Mono", Consolas, "Courier New", monospace;
    font-size: clamp(.78rem, 2.02cqi, .95rem); line-height: 1.55; tab-size: 4;
}
.rl {
    white-space: pre-wrap; overflow-wrap: anywhere;
    padding-left: min(calc(var(--h, 0) * 1ch), 45%); text-indent: calc(var(--m, 0) * -1ch);
}

/* ─── The reading set out as a page (ReadingPage) ───
   The container stays in the fixed-width face, and that is deliberate: every "how far in" below is in ch,
   and a ch has to be the same width for a paragraph as for the table above it or the two stop lining
   up. The words themselves are put back into the reading face one level down, in .rd-t.

   Nothing here adds space between one block and the next. Blank lines in the original are the only
   gaps (.rd-gap), which is what keeps the lines of a poem together and its stanzas apart. */
.read-bar { display: flex; justify-content: flex-end; margin: -.5rem -.5rem .25rem 0; }
/* How much of the typed indent is kept. All of it, except on a phone - see the narrow rules. */
.reading.page { --depth: 1; }
.rd-gap { height: .85em; }
.rd-rule { margin: .2em 0; border: 0; border-top: 1px solid var(--card-border); }
.rd-h {
    padding-left: min(calc(var(--i, 0) * var(--depth) * 1ch), 45%);
    font-family: var(--font-sans); font-size: 1rem; font-weight: 700; letter-spacing: .05em; line-height: 1.35; color: var(--text);
}
/* The line of capitals at the top, and the ones at the margin that start a section. */
.rd-title { font-family: var(--font-display); font-size: 1.05rem; letter-spacing: .02em; color: var(--accent); }
.rd-section { margin-top: .35em; padding-top: .9em; border-top: 1px solid var(--card-border); color: var(--accent); }
.rd-p { display: flex; padding-left: min(calc(var(--i, 0) * var(--depth) * 1ch), 45%); }
.rd-m { flex: 0 0 calc(var(--g, 0) * 1ch); font-family: var(--font-sans); font-size: 1rem; font-weight: 700; line-height: 1.6; color: var(--accent); }
/* No wider than a line can comfortably be read: with one side given the whole window, a chapter of a
   novel would otherwise run to a hundred and twenty characters a line. */
.rd-t { flex: 1; min-width: 0; max-width: 46rem; font-family: var(--font-read); font-size: .95rem; line-height: 1.65; overflow-wrap: break-word; }
/* Where the ends of the lines matter (verse, mostly), a line too long for the page carries on a little
   further in, so that it can still be seen to be one line. */
.rd-lines > .rd-t > div { padding-left: 1.5em; text-indent: -1.5em; }
.rd-fixed { line-height: 1.55; }
/* The answers to a How it works' "Try this", folded until they are asked for (HowItWorks). */
.how-answers { margin-top: 1.25rem; padding-top: 1rem; border-top: 1px solid var(--card-border); }
.how-answers > summary { cursor: pointer; font-weight: 700; color: var(--accent); margin-bottom: .6rem; }

/* ─── Icons (Icon.razor) ───
   Lucide line icons, drawn in the colour of the text they are in and a little bigger than its letters, so
   they sit on the line the way the emoji they replaced did. A box that wants a particular size sets
   font-size on it, or width and height. */
.i { display: inline-block; width: 1.2em; height: 1.2em; vertical-align: -.22em; flex-shrink: 0; }
.i-emoji { width: auto; height: auto; vertical-align: baseline; }
/* Stars are gold and a streak's flame is orange wherever they are counted: the colour the emoji had, and
   most of what made them read as a reward rather than as one more line drawing. Not when the thing they
   are in is greyed out (a streak that today hasn't counted towards yet). */
.i-star { color: #f5b301; }
.i-flame { color: #ff7a1a; }
.muted > .i-star, .muted > .i-flame { color: inherit; }
/* A tick or a cross that says right or wrong on its own. */
.i.ok { color: var(--ok); }
.i.bad { color: var(--danger); }
/* In a heading the icon is the accent colour, the way OrngeChat's are. */
:is(h1, h2, h3) > .i, :is(h1, h2, h3) > :first-child > .i { color: var(--accent); }
/* Red, amber and green as a dot, where they were the coloured-circle emoji. */
.rag-dot { display: inline-block; width: .7em; height: .7em; border-radius: 50%; vertical-align: .02em; background: var(--dot, var(--text-muted));
    /* A printed report would otherwise drop the colour, which is the whole of what the dot says. */
    print-color-adjust: exact; -webkit-print-color-adjust: exact; }
.rag-dot.r { --dot: #f23f43; }
.rag-dot.a { --dot: #faa61a; }
.rag-dot.g { --dot: #23a55a; }
.rag-dot.none { background: transparent; border: 2px solid var(--text-muted); }

/* Choosing a subject's icon on the hub: a grid of them, the chosen one in the subject's colour. */
.icon-choices { display: grid; grid-template-columns: repeat(auto-fill, minmax(2.6rem, 1fr)); gap: .35rem; }
.icon-choice {
    display: grid; place-items: center; aspect-ratio: 1; padding: 0; cursor: pointer; font-size: 1.05rem;
    border-radius: var(--radius-sm); border: 1px solid var(--card-border); background: var(--surface-2); color: var(--text-secondary);
}
.icon-choice:hover { border-color: var(--glass-border-hover); color: var(--text); }
.icon-choice.on { border-color: var(--c, var(--accent)); color: var(--c, var(--accent)); box-shadow: 0 0 0 2px color-mix(in srgb, var(--c, var(--accent)) 35%, transparent); }
.icon-own { margin-top: .5rem; }
.icon-own > summary { cursor: pointer; }
.icon-own > input { margin: .4rem 0 .25rem; max-width: 10rem; }
