/* Palette: Planner (Zaen, 2026-11-08; DESIGN.md). A well-kept paper planner:
   warm off-white ground, white sheets with a soft shadow, a warm "well" for
   fields and quiet blocks, warm inks, one calm ink blue, and soft subject
   tints. Separation comes from space and tints, not lines. */
:root {
  color-scheme: light;
  --surface-1: #ffffff;
  --plane: #fbfaf7;
  --well: #f4f2ed;
  --wash: #fcf5f0;
  --text-primary: #2b2a28;
  --text-secondary: #5c5852;
  --text-muted: #736f68;
  --grid: #ebe8e1;
  --baseline: #d6d2c9;
  --border: rgba(43, 42, 40, 0.08);
  --series-1: #3a5a8e;
  --series-1-soft: #e2e9f3;
  --on-series-1: #ffffff;
  --good: #3d7a46;
  --warning: #b8862a;
  --serious: #c46f48;
  --critical: #b4443c;
  --ghost: rgba(43, 42, 40, 0.045);
  --glass: rgba(251, 250, 247, 0.86);
  --glass-edge: rgba(255, 255, 255, 0.8);
  --glass-rim: rgba(43, 42, 40, 0.07);
  --glass-shadow: 0 12px 32px rgba(43, 42, 40, 0.12), 0 2px 6px rgba(43, 42, 40, 0.05);
  --soft: 0 1px 2px rgba(43, 42, 40, 0.05), 0 4px 16px rgba(43, 42, 40, 0.04);
  --lift: 0 2px 6px rgba(43, 42, 40, 0.06), 0 12px 32px rgba(43, 42, 40, 0.10);
  --focus: 0 0 0 3px rgba(58, 90, 142, 0.28);
  /* subject tints: soft grounds and their deep text tones */
  --sage: #e4ece1; --sage-ink: #3b5838;
  --sky: #e0eaf3; --sky-ink: #2e5070;
  --peach: #f6e5d9; --peach-ink: #784428;
  --lilac: #eae4f1; --lilac-ink: #554676;
  --butter: #f3edd5; --butter-ink: #625417;
  --rose: #f3e2e4; --rose-ink: #773843;
  --next: #fbe8de; --next-ink: #863c22; --next-dot: #d0714e;
}
@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) {
    color-scheme: dark;
    --surface-1: #22211e;
    --plane: #191816;
    --well: #2a2825;
    --wash: #2b2420;
    --text-primary: #ece8e1;
    --text-secondary: #bdb7ad;
    --text-muted: #958f85;
    --grid: #2e2c29;
    --baseline: #3c3a35;
    --border: rgba(236, 232, 225, 0.09);
    --series-1: #9cb6e4;
    --series-1-soft: #2a3445;
    --on-series-1: #16181c;
    --good: #84c08a;
    --warning: #e0b85a;
    --serious: #e39270;
    --critical: #ec8a80;
    --ghost: rgba(236, 232, 225, 0.05);
    --glass: rgba(34, 33, 30, 0.86);
    --glass-edge: rgba(255, 255, 255, 0.10);
    --glass-rim: rgba(236, 232, 225, 0.08);
    --glass-shadow: 0 12px 32px rgba(0, 0, 0, 0.45), 0 2px 6px rgba(0, 0, 0, 0.3);
    --soft: 0 1px 2px rgba(0, 0, 0, 0.3), 0 6px 18px rgba(0, 0, 0, 0.22);
    --lift: 0 2px 6px rgba(0, 0, 0, 0.35), 0 14px 36px rgba(0, 0, 0, 0.4);
    --focus: 0 0 0 3px rgba(156, 182, 228, 0.35);
    --sage: #2b3529; --sage-ink: #bcd5b5;
    --sky: #253240; --sky-ink: #b9d0e6;
    --peach: #3b2b21; --peach-ink: #f1c9ad;
    --lilac: #302b3b; --lilac-ink: #d4c9e8;
    --butter: #35311f; --butter-ink: #e6d89e;
    --rose: #3b2729; --rose-ink: #edc0c7;
    --next: #43281d; --next-ink: #f3b89c; --next-dot: #e08a66;
  }
}
:root[data-theme="dark"] {
  color-scheme: dark;
  --surface-1: #22211e;
  --plane: #191816;
  --well: #2a2825;
  --wash: #2b2420;
  --text-primary: #ece8e1;
  --text-secondary: #bdb7ad;
  --text-muted: #958f85;
  --grid: #2e2c29;
  --baseline: #3c3a35;
  --border: rgba(236, 232, 225, 0.09);
  --series-1: #9cb6e4;
  --series-1-soft: #2a3445;
  --on-series-1: #16181c;
  --good: #84c08a;
  --warning: #e0b85a;
  --serious: #e39270;
  --critical: #ec8a80;
  --ghost: rgba(236, 232, 225, 0.05);
  --glass: rgba(34, 33, 30, 0.86);
  --glass-edge: rgba(255, 255, 255, 0.10);
  --glass-rim: rgba(236, 232, 225, 0.08);
  --glass-shadow: 0 12px 32px rgba(0, 0, 0, 0.45), 0 2px 6px rgba(0, 0, 0, 0.3);
  --soft: 0 1px 2px rgba(0, 0, 0, 0.3), 0 6px 18px rgba(0, 0, 0, 0.22);
  --lift: 0 2px 6px rgba(0, 0, 0, 0.35), 0 14px 36px rgba(0, 0, 0, 0.4);
  --focus: 0 0 0 3px rgba(156, 182, 228, 0.35);
  --sage: #2b3529; --sage-ink: #bcd5b5;
  --sky: #253240; --sky-ink: #b9d0e6;
  --peach: #3b2b21; --peach-ink: #f1c9ad;
  --lilac: #302b3b; --lilac-ink: #d4c9e8;
  --butter: #35311f; --butter-ink: #e6d89e;
  --rose: #3b2729; --rose-ink: #edc0c7;
  --next: #43281d; --next-ink: #f3b89c; --next-dot: #e08a66;
}

/* Fonts ship with the app (OFL-licensed, Latin subset, one variable file
   each) so it looks the same offline and on every OS, and so the CSP can
   keep font-src to 'self'. DM Sans runs the interface; Lora is for reading:
   your notes, Luna's answers and quoted file text. */
@font-face {
  font-family: "DM Sans"; src: url("dm-sans.woff2") format("woff2");
  font-weight: 100 1000; font-style: normal; font-display: swap;
}
@font-face {
  font-family: "Lora"; src: url("lora.woff2") format("woff2");
  font-weight: 400 700; font-style: normal; font-display: swap;
}
:root {
  --font-ui: "DM Sans", system-ui, -apple-system, "Segoe UI", sans-serif;
  --font-read: "Lora", Georgia, "Times New Roman", serif;
  /* Motion (DESIGN.md, Motion): a confident deceleration, no bounce.
     quick = feedback, state = a state change, move = things arriving. */
  --ease-out: cubic-bezier(0.16, 1, 0.3, 1);
  --t-quick: 120ms;
  --t-state: 220ms;
  --t-move: 380ms;
  /* Planner measures: the inset inside a card, and the radii. */
  --card-pad: 20px;
  --r-chip: 6px; --r-btn: 9px; --r-field: 10px; --r-block: 12px; --r-sheet: 14px;
}
@media (max-width: 620px) { :root { --card-pad: 16px; } }
/* --raised: a chip lifted off a well (the chosen segment, an active row).
   White in light; in dark a step lighter than the well, so it still rises. */
:root { --raised: var(--surface-1); }
@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) { --raised: color-mix(in srgb, var(--text-primary) 11%, var(--surface-1)); }
}
:root[data-theme="dark"] { --raised: color-mix(in srgb, var(--text-primary) 11%, var(--surface-1)); }

* { box-sizing: border-box; }

/* A rule that sets `display` otherwise beats the `hidden` attribute, which
   silently leaves "hidden" elements on screen. Settle it once. */
[hidden] { display: none !important; }

body {
  margin: 0;
  background: var(--plane);
  color: var(--text-primary);
  font: 14px/1.5 var(--font-ui);
  -webkit-font-smoothing: antialiased; -moz-osx-font-smoothing: grayscale;
  caret-color: var(--series-1);
  accent-color: var(--series-1);
}

/* The parts the browser draws carry the palette too: selection, the caret,
   scrollbars, focus, links, numbers in data. */
:root { scrollbar-color: var(--baseline) transparent; }
* { scrollbar-width: thin; }
::selection { background: color-mix(in srgb, var(--series-1) 24%, transparent); }
h1, h2, h3, h4 { color: var(--text-primary); text-wrap: balance; }
a { color: var(--series-1); text-underline-offset: 3px; text-decoration-thickness: from-font; }
:where(:focus-visible) { outline: 2px solid color-mix(in srgb, var(--series-1) 60%, transparent); outline-offset: 2px; }
table, time, .num, input[type="number"] { font-variant-numeric: tabular-nums; }
code, kbd, pre, samp { font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace; font-size: 0.92em; }
:where(:not(pre) > code, kbd) { background: var(--well); padding: 1px 5px; border-radius: 5px; }
::placeholder { color: color-mix(in srgb, var(--text-muted) 85%, var(--text-primary)); opacity: 1; }

/* Fields: a warm well, no border; the ink-blue ring when you're in one.
   :where() keeps these at zero weight, so any component can restyle a field. */
:where(input:not([type="checkbox"], [type="radio"], [type="range"], [type="color"], [type="file"], [type="hidden"]), select, textarea) {
  font: inherit; color: var(--text-primary);
  background: var(--well); border: 0; border-radius: 10px; padding: 8px 12px;
  transition: box-shadow 0.15s ease-out, background-color 0.15s ease-out;
}
:where(input:not([type="checkbox"], [type="radio"], [type="range"], [type="color"], [type="file"], [type="hidden"]), select, textarea):focus {
  outline: none; box-shadow: var(--focus);
}
:where(input, select, textarea):disabled { opacity: 0.6; cursor: not-allowed; }
:where(input, select, textarea)[aria-invalid="true"] { box-shadow: 0 0 0 2px color-mix(in srgb, var(--critical) 55%, transparent); }
:where(select) { cursor: pointer; }
input[type="checkbox"], input[type="radio"] { accent-color: var(--series-1); }
/* Reduced motion: the system's wish, unless Settings pins it (<html
   data-motion="reduce" | "full">, set by app.js from "meteoro.motion").
   Every duration goes to ~0, so state still changes without movement. */
@media (prefers-reduced-motion: reduce) {
  :root:not([data-motion="full"]) *, :root:not([data-motion="full"]) *::before, :root:not([data-motion="full"]) *::after {
    transition-duration: 0.01ms !important; animation-duration: 0.01ms !important; animation-iteration-count: 1 !important; scroll-behavior: auto !important; }
}
:root[data-motion="reduce"] *, :root[data-motion="reduce"] *::before, :root[data-motion="reduce"] *::after {
  transition-duration: 0.01ms !important; animation-duration: 0.01ms !important; animation-iteration-count: 1 !important; scroll-behavior: auto !important; }
:root[data-motion="reduce"]::view-transition-group(*), :root[data-motion="reduce"]::view-transition-old(*), :root[data-motion="reduce"]::view-transition-new(*) { animation: none !important; }

/* ---------------------------------------------------------- shell */

/* The app is one screen, like a native app, in the browser and installed:
   the page itself never scrolls or bounces; the top bar and tabs stay put
   and only the content area (and Home's panels) scroll. Only the app page
   carries html.app, so Planetarium pages sharing this file scroll as usual. */
html.app, html.app body { height: 100%; overflow: hidden; overscroll-behavior: none; }
html.app body { height: 100dvh; -webkit-tap-highlight-color: transparent; }
html.app button, html.app .topbar, html.app .sidebar { -webkit-user-select: none; user-select: none; -webkit-touch-callout: none; }
html.app button, html.app a, html.app input, html.app select, html.app textarea { touch-action: manipulation; }

/* Installed on a phone (viewport-fit=cover), keep clear of the notch and the
   home bar. The insets are 0 everywhere else. */
.shell {
  display: grid; height: 100%;
  padding: 0 env(safe-area-inset-right, 0px) 0 env(safe-area-inset-left, 0px);
  grid-template-columns: 228px minmax(0, 1fr);
  grid-template-rows: auto minmax(0, 1fr);
  grid-template-areas: "top top" "side main";
}
.wrap {
  grid-area: main; display: flex; flex-direction: column; min-height: 0;
  overflow-y: auto; overscroll-behavior: contain;
  /* Full-width scroller (scrollbar at the window edge), content capped at 1180px. */
  padding: 24px max(24px, calc((100% - 1132px) / 2)) calc(64px + env(safe-area-inset-bottom, 0px));
}

.topbar {
  grid-area: top; z-index: 5;
  display: flex; align-items: center; gap: 12px;
  height: calc(56px + env(safe-area-inset-top, 0px));
  padding: env(safe-area-inset-top, 0px) 16px 0 24px;
  /* Planner: the top bar is the same warm ground as the sidebar, no line;
     the white sheets below are what rise. */
  background: var(--plane);
}
.topbar .brand { flex: 0 1 auto; min-width: 0; display: flex; align-items: center; gap: 10px; }
.topbar .brand img { border-radius: 7px; }
.topbar .brand h1 { font-size: 16px; font-weight: 600; letter-spacing: -0.01em; flex: 0 0 auto; }
.topbar .brand p { margin: 0; font-size: 12px; color: var(--text-muted); }
.topbar .spacer { flex: 1 1 auto; }
/* The bell: opens the notification center; its dot says a sync failed
   (red), one is running or something new came in (ink blue). */
#bell-btn { position: relative; }
#bell-btn[aria-expanded="true"] { background: var(--well); color: var(--text-primary); }
.bell-dot { position: absolute; top: 5px; right: 6px; width: 8px; height: 8px; border-radius: 50%;
  background: var(--series-1); box-shadow: 0 0 0 2px var(--plane); }
.bell-dot[hidden] { display: none; }
.bell-dot.err { background: var(--critical); }
.bell-dot.run { animation: pulse 1.2s ease-in-out infinite; }
#sync-btn { display: inline-flex; align-items: center; gap: 7px; }
#sync-btn svg { flex: 0 0 auto; }
#sync-btn.busy svg { animation: spin 0.9s linear infinite; }
@keyframes spin { to { transform: rotate(360deg); } }
@media (prefers-reduced-motion: reduce) { :root:not([data-motion="full"]) { #sync-btn.busy svg { animation: none; opacity: 0.5; } } }

/* Settings rows: a name on the left, its control on the right. */
.setting-row {
  display: flex; align-items: center; justify-content: space-between; gap: 12px;
  padding: 10px 0; min-height: 52px;
}
.setting-row > label, .setting-row > span { font-weight: 500; }
.setting-row .viewtoggle button { font-size: 13px; padding: 6px 14px; }
.setting-row select {
  font: inherit; padding: 8px 32px 8px 12px; border-radius: 10px;
  border: 0; background: var(--well); color: var(--text-primary);
}
/* Only phones need these (their top bar has no room). */
@media (min-width: 761px) { .phone-only { display: none !important; } }

.sidebar {
  grid-area: side; overflow-y: auto;
  /* Planner: a light sidebar on the ground, no edge; the current item is
     a white chip lifted off it. */
  padding: 12px 12px 16px 14px; background: var(--plane);
}

.sidenav { display: flex; flex-direction: column; gap: 2px; height: 100%; }
/* collapsed: icons only, the words stay for screen readers and come back as tooltips */
.sidebar { display: flex; flex-direction: column; }
/* The fold button (hamburger) leads the top bar, centred over the sidebar's icons. */
.topbar:has(> .brand > .side-collapse) { padding-left: 16px; }
.topbar .side-collapse { display: grid; place-items: center; flex: 0 0 auto; width: 30px; height: 30px; padding: 0; border-radius: 8px; }
.shell { transition: grid-template-columns .2s cubic-bezier(.2,.8,.2,1); }
@media (prefers-reduced-motion: reduce) { :root:not([data-motion="full"]) { .shell { transition: none; } } }
@media (min-width: 761px) {
  /* A slimmer top bar and less air under it on computers (Zaen, 2026-11-09:
     "there's still a bit of a space") */
  .topbar { height: calc(48px + env(safe-area-inset-top, 0px)); }
  .sidebar { padding-top: 4px; }
  .shell.side-mini { grid-template-columns: 60px minmax(0, 1fr); }
  .shell.side-mini .sidebar { padding: 4px 8px 12px; overflow-x: hidden; }
  .shell.side-mini .sidenav .tab { justify-content: center; position: relative; padding: 0; }
  .shell.side-mini .sidenav .tab > span:not(.side-badge) { position: absolute; width: 1px; height: 1px; overflow: hidden; clip-path: inset(50%); white-space: nowrap; }
  .shell.side-mini .sidenav .side-badge { position: absolute; top: 2px; right: 4px; font-size: 9px; }
  .shell.side-mini .sidegroups { display: none; }
  body:has(.shell.side-mini:not([hidden]) > .sidebar) .try-panel.at-bl, body:has(.shell.side-mini:not([hidden]) > .sidebar) .try-panel.at-tl { left: calc(60px + 16px); }
}
/* Settings and (for admins) the Planetarium sit at the bottom */
.sidenav .tab.end { margin-top: auto; }
a.tab { text-decoration: none; }
.sidenav .tab {
  display: flex; align-items: center; gap: 12px; width: 100%; min-height: 38px;
  border: none; border-radius: 9px; background: none; text-align: left;
  padding: 0 10px; color: var(--text-secondary); font-weight: 450;
}
.sidenav .tab svg { flex: 0 0 auto; }
.sidenav .tab:hover:not(:disabled) { background: var(--ghost); color: var(--text-primary); }
.sidenav .tab:active:not(:disabled) { background: color-mix(in srgb, var(--text-primary) 7%, transparent); }
.sidenav .tab[aria-selected="true"],
.sidenav .tab[aria-selected="true"]:hover:not(:disabled) {
  background: var(--surface-1); color: var(--text-primary); font-weight: 550;
  box-shadow: var(--soft);
}
.sidenav .tab[aria-selected="true"]:focus-visible { box-shadow: var(--soft), var(--focus); }

/* ---- You: the chip at the bottom of the sidebar (design user-view). */
.avatar {
  display: inline-grid; place-items: center; flex: 0 0 auto;
  width: 28px; height: 28px; border-radius: 50%;
  /* you, in a soft subject tint: personal, not a brand blue */
  background: var(--sage); color: var(--sage-ink);
  font-style: normal; font-size: 12px; font-weight: 600; line-height: 1;
}
.avatar.big { width: 56px; height: 56px; font-size: 22px; }
.userchip {
  display: flex; align-items: center; gap: 2px; order: 1; margin-top: auto;
  padding-top: 12px;
}
.sidenav:has(#planetarium-link:not([hidden])) .userchip { margin-top: 6px; }
/* Only with the chip there: a tab still running yesterday's page with
   today's styles (an update waiting) must keep its old, working layout. */
.sidenav:has(> .userchip) #planetarium-link { margin-top: auto; }
.sidenav:has(> .userchip) .ph-side:not([hidden]) ~ #planetarium-link { margin-top: 0; }
#tab-btn-user { display: flex; flex: 1 1 auto; min-width: 0; min-height: 44px; padding: 6px 8px; gap: 10px; }
.userbtn .who { display: flex; align-items: baseline; gap: 5px; min-width: 0; flex: 1 1 auto; }
/* the name wins: the role gives way first */
.userbtn .who-name { flex: 0 1 auto; min-width: 0; font-weight: 550; color: var(--text-primary); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.userbtn .who-role { flex: 0 100 auto; min-width: 0; overflow: hidden; text-overflow: ellipsis; color: var(--text-muted); font-size: 13px; font-weight: 400; white-space: nowrap; }
.userbtn .who-role:not(:empty)::before { content: "· "; }
.userbtn .chev { flex: 0 0 auto; color: var(--text-muted); }
/* the gear: Settings, an icon beside you */
.userchip #tab-btn-settings { width: auto; flex: 0 0 auto; margin-top: 0; padding: 0 10px; min-height: 38px; color: var(--text-muted); }
.userchip #tab-btn-settings:hover:not(:disabled) { color: var(--text-primary); }
.userchip #tab-btn-settings > span {
  position: absolute; width: 1px; height: 1px; overflow: hidden; clip-path: inset(50%); white-space: nowrap;
}
.userchip #tab-btn-settings[aria-selected="true"] { color: var(--text-primary); }
@media (min-width: 761px) {
  /* folded sidebar: you over the gear, icons only */
  .shell.side-mini .userchip { flex-direction: column; gap: 2px; }
  .shell.side-mini #tab-btn-user { padding: 6px 0; }
  .shell.side-mini #tab-btn-user .avatar { position: static; width: 28px; height: 28px; clip-path: none; }
  .shell.side-mini #tab-btn-user .chev { display: none; }
}

/* ---- your user view */
.uv-head { display: flex; align-items: center; gap: 16px; margin: 4px 0 20px; }
.uv-who { min-width: 0; }
.uv-who h1 { margin: 0; font-size: 22px; font-weight: 600; letter-spacing: -0.005em; line-height: 1.25; overflow-wrap: anywhere; }
.uv-who p { margin: 2px 0 0; color: var(--text-muted); font-size: 13px; }
.uv-settings { display: inline-flex; align-items: center; gap: 6px; }
/* your name: one block you can right-click (or tap, on a phone) to edit */
.uv-who { flex: 0 1 auto; margin-right: auto; border-radius: 10px; padding: 4px 10px; margin-left: -10px;
  margin-top: -4px; margin-bottom: -4px; transition: background-color 0.15s ease-out; }
@media (hover: hover) { .uv-who:hover { background: var(--ghost); } }
.uv-who:focus-visible { outline: none; box-shadow: var(--focus); }
@media (hover: none) { .uv-who { cursor: pointer; } }
.uv-edit {
  display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 0 14px;
  max-width: 560px; margin: -4px 0 18px;
}
.uv-edit-hint { grid-column: 1 / -1; margin: -2px 0 0; font-size: 12px; color: var(--text-muted); }
.uv-edit .actions { grid-column: 1 / -1; display: flex; align-items: center; gap: 8px; margin-top: 4px; }
@media (max-width: 620px) { .uv-edit { grid-template-columns: minmax(0, 1fr); } }

/* Signing in: quiet rows, a small action at the end of each; fields only
   when you ask for them (Zaen: "make it more subtle") */
button.quiet {
  border: 0; background: none; padding: 5px 10px; border-radius: 8px;
  color: var(--series-1); font-size: 13px; font-weight: 500; flex: 0 0 auto;
}
button.quiet:hover:not(:disabled) { background: var(--well); }
button.quiet:active:not(:disabled) { background: color-mix(in srgb, var(--text-primary) 7%, var(--well)); }
button.quiet.danger { color: var(--critical); }
button.quiet.danger:hover:not(:disabled) { border-color: transparent; background: color-mix(in srgb, var(--critical) 8%, var(--surface-1)); }
#acct-card .signins { margin: 0; }
#acct-card .passkeys { margin: 0; gap: 0; }
#acct-card .signin-row {
  /* plain rows: the space between them is the separation, no rule */
  border: 0; border-radius: 0; background: none;
  padding: 10px 0; gap: 12px; font-size: 14px; min-height: 48px;
}
#acct-card .signin-row .k { min-width: 92px; font-weight: 550; }
#acct-card .signin-row.sub { padding: 6px 0 6px 104px; font-size: 13px; }
#acct-card .signin-row.sub .k { min-width: 0; font-weight: 500; color: var(--text-primary); }
#acct-card .signin-row.sub .v { color: var(--text-muted); }
#acct-card .acct-pw { padding: 12px 0 14px; }
#acct-card .acct-pw .actions { display: flex; gap: 8px; }
#acct-card .apps { margin-top: 24px; }
#acct-card .acct-foot { display: flex; justify-content: flex-end; margin-top: 20px; }
@media (max-width: 620px) {
  #acct-card .signin-row.sub { padding-left: 12px; }
  #acct-card .signin-row .k { min-width: 0; }
}

/* the panes: the app's segmented control (.viewtoggle) */
.uv-nav { margin: 0 0 20px; max-width: 100%; overflow-x: auto; scrollbar-width: none; }
.uv-nav::-webkit-scrollbar { display: none; }
.uv-nav button { flex: 0 0 auto; }

@media (max-width: 760px) {
  .shell {
    grid-template-columns: minmax(0, 1fr); grid-template-rows: auto minmax(0, 1fr);
    grid-template-areas: "top" "main";
  }
  /* One row on a phone: the site line, Force and the status words go;
     the status dot stays, and all of it is still on the Sync tab. */
  .topbar { padding: env(safe-area-inset-top, 0px) 12px 0 16px; gap: 8px; }
  .topbar .brand p, .topbar .check { display: none; }
  /* The tabs become one Planner bar, the same on iPhone and Android: a
     white sheet floating a little above the bottom edge, icons only (the
     labels stay for screen readers), the current tab a soft ink-blue wash,
     a press that answers on touch-down. No glass, no costume. */
  .topbar .side-collapse { display: none; }
  .sidebar {
    grid-area: auto; position: fixed; z-index: 40; overflow: visible;
    left: 12px; right: 12px; margin: 0 auto; width: auto; max-width: 480px; height: auto;
    bottom: calc(10px + env(safe-area-inset-bottom, 0px));
    padding: 6px; border: 0; border-radius: 14px;
    background: var(--surface-1); box-shadow: var(--lift);
  }
  .sidenav { flex-direction: row; gap: 2px; }
  .sidenav .tab {
    /* eight tabs still fit a 360px phone */
    flex: 1 1 0; width: auto; min-width: 0; min-height: 0; height: 46px; padding: 0; justify-content: center;
    border-radius: 10px; color: var(--text-muted);
    transition: transform 0.15s ease-out, background-color 0.15s ease-out, color 0.15s ease-out;
  }
  .sidenav .tab svg { width: 22px; height: 22px; }
  .sidenav .tab span {
    position: absolute; width: 1px; height: 1px; overflow: hidden;
    clip: rect(0 0 0 0); clip-path: inset(50%); white-space: nowrap;
  }
  .sidenav .tab.end { margin-top: 0; }
  .sidenav .tab:hover:not(:disabled) { background: none; color: var(--text-secondary); }
  .sidenav .tab:active:not(:disabled) { transform: scale(0.94); background: var(--well); }
  .sidenav .tab[aria-selected="true"],
  .sidenav .tab[aria-selected="true"]:hover:not(:disabled) {
    color: var(--series-1); box-shadow: none; background: var(--series-1-soft);
  }
  .sidenav .tab[aria-selected="true"]:focus-visible { box-shadow: var(--focus); }
  /* You on a phone: the avatar takes Settings' place in the bar; Settings
     is a button in your user view. */
  .userchip { display: contents; }
  .userchip #tab-btn-settings { display: none; }
  .sidenav:has(> .userchip) #planetarium-link { margin-top: 0; }
  #tab-btn-user { flex: 1 1 0; padding: 0; gap: 0; min-height: 0; }
  #tab-btn-user .chev { display: none; }
  #tab-btn-user .avatar { width: 28px; height: 28px; }
  #tab-btn-user[aria-selected="true"] .avatar { box-shadow: 0 0 0 2px var(--surface-1), 0 0 0 4px var(--series-1); }
  /* The content stops above the bar (Zaen, 2026-11-08: "make sure the
     viewport stops above the toolbar"): nothing scrolls under it.
     --bar-room = the bar, its gap below and a little air above. */
  :root { --bar-room: calc(78px + env(safe-area-inset-bottom, 0px)); }
  .wrap { padding: 16px 16px 16px; margin-bottom: var(--bar-room); }
  #toasts { bottom: calc(80px + env(safe-area-inset-bottom, 0px)); }
  body.has-demo-strip #toasts { bottom: calc(128px + env(safe-area-inset-bottom, 0px)); }
}

/* ---------------------------------------------------------- header */

header.top {
  display: flex; align-items: center; gap: 16px; flex-wrap: wrap;
  margin-bottom: 20px;
}
.brand { min-width: 0; flex: 1 1 auto; }
.brand h1 { margin: 0; font-size: 19px; font-weight: 650; letter-spacing: -0.01em; }
.brand p {
  margin: 2px 0 0; color: var(--text-secondary); font-size: 13px;
  /* The data directory can be a long path - keep it on one line. */
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.spacer { flex: 0 0 auto; }

/* Buttons. Plain = secondary: a warm well, no line. .primary = the one ink-blue
   action. .danger = a soft critical tint. .ghost / .icon-btn = no fill until
   hovered. Transparent border keeps sizes, so a state may still colour it. */
button {
  font: inherit; font-weight: 500; cursor: pointer; border-radius: 9px;
  border: 1px solid transparent; background: var(--well);
  color: var(--text-primary); padding: 8px 14px;
  transition: background-color 0.15s ease-out, color 0.15s ease-out, box-shadow 0.15s ease-out;
}
button:hover:not(:disabled) { background: color-mix(in srgb, var(--text-primary) 7%, var(--well)); }
button:active:not(:disabled) { background: color-mix(in srgb, var(--text-primary) 11%, var(--well)); }
button:focus-visible { outline: none; box-shadow: var(--focus); }
button:disabled { opacity: 0.5; cursor: default; }
button[aria-busy="true"] { cursor: progress; opacity: 0.75; }
button.primary {
  background: var(--series-1); border-color: transparent; color: var(--on-series-1); font-weight: 550;
}
button.primary:hover:not(:disabled) { background: color-mix(in srgb, var(--series-1) 86%, var(--text-primary)); }
button.primary:active:not(:disabled) { background: color-mix(in srgb, var(--series-1) 78%, var(--text-primary)); }
button.ghost, button.icon-btn { background: transparent; color: var(--text-secondary); }
button.ghost:hover:not(:disabled), button.icon-btn:hover:not(:disabled) { background: var(--well); color: var(--text-primary); }
.icon-btn { padding: 7px 9px; line-height: 0; display: inline-flex; align-items: center; }

.pill {
  display: inline-flex; align-items: center; gap: 6px;
  font-size: 12px; padding: 4px 10px; border-radius: 999px;
  background: var(--well); color: var(--text-secondary);
}
.pill .dot { width: 8px; height: 8px; border-radius: 50%; background: var(--text-muted); }
.pill.ok .dot { background: var(--good); }
.pill.run .dot { background: var(--series-1); animation: pulse 1.2s ease-in-out infinite; }
.pill.err .dot { background: var(--critical); }
@keyframes pulse { 50% { opacity: 0.35; } }

/* ------------------------------------------------------- stat tiles */

.tiles {
  display: grid; gap: 12px; margin-bottom: 16px;
  grid-template-columns: repeat(auto-fit, minmax(160px, 1fr));
}
.tile {
  background: var(--surface-1); border: 0; box-shadow: var(--soft);
  border-radius: 14px; padding: 16px 18px;
}
.tile .label { font-size: 13px; color: var(--text-secondary); margin-bottom: 6px; }
.tile .value { font-size: 26px; font-weight: 600; letter-spacing: -0.02em; line-height: 1.15; font-variant-numeric: tabular-nums; }
.tile .sub { font-size: 12px; color: var(--text-muted); margin-top: 4px; }

/* ------------------------------------------------------------ cards */

/* A card is a white sheet on the warm ground: a soft shadow, no line. Its
   heading sits on the sheet with space under it, not a rule. Never nest one:
   inside a card, group with --well blocks or space. */
.card {
  background: var(--surface-1); border: 0; box-shadow: var(--soft);
  border-radius: 14px; margin-bottom: 20px; overflow: hidden;
}
.card > h2 {
  margin: 0; padding: 16px var(--card-pad) 4px; font-size: 15px; font-weight: 600; letter-spacing: -0.005em;
  display: flex; align-items: center; gap: 10px;
}
.card > h2 .count { color: var(--text-muted); font-weight: 400; font-variant-numeric: tabular-nums; }
.card .body { padding: 12px var(--card-pad) 18px; }

/* Banners speak through a soft tint and a small dot, never a coloured edge. */
.banner {
  border-radius: 12px; padding: 12px 16px; margin-bottom: 16px;
  border: 0; background: var(--well); color: var(--text-primary);
  display: flex; gap: 10px; align-items: flex-start;
}
.banner .mark { font-weight: 700; }
.banner.warn { background: color-mix(in srgb, var(--warning) 14%, var(--surface-1)); }
.banner.err  { background: color-mix(in srgb, var(--critical) 8%, var(--surface-1)); }
.banner.warn .mark { color: color-mix(in srgb, var(--warning) 60%, var(--text-primary)); }
.banner.err .mark { color: var(--critical); }
.banner code {
  background: color-mix(in srgb, var(--text-primary) 7%, transparent); padding: 1px 5px; border-radius: 5px; font-size: 12px;
}

/* --------------------------------------------------------- progress */

.progress-track {
  height: 6px; border-radius: 999px; background: var(--well); overflow: hidden;
}
.progress-fill {
  height: 100%; border-radius: 999px; background: var(--series-1);
  width: 0%; transition: width 0.3s cubic-bezier(.2, .8, .2, 1);
}
.progress-meta {
  display: flex; justify-content: space-between; gap: 12px;
  font-size: 12px; color: var(--text-secondary); margin-top: 8px;
}
.progress-meta .now {
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap; flex: 1 1 auto;
}
.log {
  margin-top: 12px; max-height: 190px; overflow-y: auto;
  font: 12px/1.6 ui-monospace, SFMono-Regular, Menlo, monospace;
  background: var(--well); border-radius: 10px; padding: 10px 12px;
}
.log div { display: flex; gap: 8px; }
.log .lv { flex: 0 0 auto; width: 12px; font-weight: 700; }
.log .ok .lv { color: var(--good); }
.log .warn .lv { color: var(--serious); }
.log .error .lv { color: var(--critical); }
.log .info .lv { color: var(--text-muted); }
.log .msg { color: var(--text-secondary); word-break: break-word; }

/* ----------------------------------------------------------- charts */

.chart-row { display: flex; align-items: center; gap: 10px; padding: 3px 0; }
.chart-row .name {
  flex: 0 0 34%; font-size: 13px; color: var(--text-secondary);
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.chart-row .bar-track { flex: 1 1 auto; height: 14px; position: relative; }
.chart-row .bar {
  height: 12px; margin-top: 1px; background: var(--series-1);
  border-radius: 0 6px 6px 0; min-width: 2px;
}
.chart-row .val {
  flex: 0 0 auto; font-size: 12px; color: var(--text-secondary);
  font-variant-numeric: tabular-nums; min-width: 72px; text-align: right;
}
.chart-row:hover .name, .chart-row:hover .val { color: var(--text-primary); }

/* ------------------------------------------------------------ split */

.split { display: grid; gap: 16px; grid-template-columns: 300px 1fr; align-items: start; }
.split.charts { grid-template-columns: 1fr 1fr; }
.split.two { grid-template-columns: 1fr 1fr; margin-bottom: 16px; }
.split.two[hidden] { display: none; }
.stack { display: grid; gap: 16px; align-content: start; min-width: 0; }
@media (max-width: 860px) {
  .split, .split.charts, .split.two { grid-template-columns: 1fr; }
}

.course-list { list-style: none; margin: 0; padding: 8px; max-height: 620px; overflow-y: auto; }
.course-list li { margin: 0; }
.course-list button {
  width: 100%; text-align: left; border: none; background: none;
  border-radius: 9px; padding: 8px 10px; display: block; font-weight: 400;
}
.course-list button:has(.ctag) {
  display: grid; grid-template-columns: 58px minmax(0, 1fr); column-gap: 8px; align-items: center;
}
.course-list button .ctag { grid-row: 1 / span 2; justify-self: start; max-width: 100%; }
.course-list button.active { background: var(--series-1-soft); color: var(--text-primary); font-weight: 550; }
.course-list button.active .meta { color: var(--text-secondary); }
.course-list .nm {
  font-size: 13px; display: block; overflow: hidden;
  text-overflow: ellipsis; white-space: nowrap;
}
.course-list .meta { font-size: 11px; color: var(--text-muted); }

/* ----------------------------------------------------------- search */

.searchbar { display: flex; gap: 8px; padding: 12px var(--card-pad) 10px; }
input[type="search"] {
  flex: 1 1 auto; font: inherit; padding: 9px 12px; border-radius: 10px;
  border: 0; background: var(--well); color: var(--text-primary);
}
input[type="search"]:focus { outline: none; box-shadow: var(--focus); }

/* ------------------------------------------------------------ table */

/* Tables: no lines at all. The header is a quiet muted label row; rows are
   kept apart by their height and a soft well wash on hover. */
table { width: 100%; border-collapse: collapse; font-size: 13px; font-variant-numeric: tabular-nums; }
thead th {
  text-align: left; font-weight: 500; font-size: 12px; color: var(--text-muted);
  padding: 10px var(--card-pad) 6px;
  position: sticky; top: 0; background: var(--surface-1); z-index: 1;
}
tbody td { padding: 11px var(--card-pad); vertical-align: top; }
tbody tr { transition: background-color 0.15s ease-out; }
tbody tr:hover { background: color-mix(in srgb, var(--well) 60%, transparent); }
td.num, th.num { font-variant-numeric: tabular-nums; color: var(--text-secondary); white-space: nowrap; }
th.num { text-align: right; }
td .path { font-size: 11px; color: var(--text-muted); }
td a { color: var(--series-1); text-decoration: none; font-weight: 500; }
td a:hover { text-decoration: underline; }
/* A tag is a small soft label: a tint, no outline. */
.tag {
  display: inline-block; font-size: 11px; font-weight: 500; padding: 2px 8px; border-radius: 6px;
  border: 0; background: var(--well); color: var(--text-secondary);
}
.tag.missing { background: color-mix(in srgb, var(--critical) 8%, var(--surface-1)); color: var(--critical); }

.empty { padding: 40px var(--card-pad); text-align: center; color: var(--text-muted); text-wrap: balance; }

/* Scrolls in both directions: on a narrow screen the right-hand columns
   would otherwise be clipped with no way to reach them. */
.table-scroll { max-height: 620px; overflow: auto; }
.table-scroll table { min-width: 560px; }
.foot { padding: 12px var(--card-pad); font-size: 12px; color: var(--text-muted); }

.check {
  display: inline-flex; align-items: center; gap: 6px;
  font-size: 13px; color: var(--text-secondary); cursor: pointer;
}
.check input { accent-color: var(--series-1); cursor: pointer; }

/* ------------------------------------------------------ settings form */

.field { margin-bottom: 14px; }
.field > label {
  display: block; font-size: 13px; font-weight: 500;
  margin-bottom: 6px; color: var(--text-primary);
}
.field input[type="text"], .field input[type="url"], .field input[type="password"],
.field input[type="email"], .field input[type="number"], .field select, .field textarea {
  width: 100%; font: inherit; padding: 9px 12px; border-radius: 10px;
  border: 0; background: var(--well);
  color: var(--text-primary);
}
.field input:focus, .field select:focus, .field textarea:focus { outline: none; box-shadow: var(--focus); }
.field input:disabled { opacity: 0.6; cursor: not-allowed; }

.hint { display: block; font-size: 12px; color: var(--text-muted); margin-top: 6px; }
.hint code { background: var(--well); padding: 1px 5px; border-radius: 5px; }
p.hint { margin: 0 0 16px; max-width: 62ch; }

.row2 { display: grid; gap: 14px; grid-template-columns: 1fr 1fr; }
@media (max-width: 620px) { .row2 { grid-template-columns: 1fr; } }

.advanced { margin: 4px 0 16px; }
.advanced summary {
  cursor: pointer; font-size: 13px; color: var(--text-secondary);
  padding: 6px 0; user-select: none;
}
.advanced summary:hover { color: var(--text-primary); }
.advanced[open] summary { margin-bottom: 10px; }

.actions { display: flex; align-items: center; gap: 10px; flex-wrap: wrap; }
/* A class rather than a style attribute: the CSP forbids inline styles,
   which is what keeps a downloaded bundle from smuggling one in. */
.actions.spaced { margin-top: 12px; }
.actions .spacer { flex: 1 1 auto; }

.result {
  border-radius: 12px; padding: 10px 14px; margin-bottom: 14px; font-size: 13px;
  border: 0; background: var(--well);
  display: flex; gap: 9px; align-items: flex-start;
}
.result .mark { font-weight: 700; flex: 0 0 auto; }
.result.ok { background: color-mix(in srgb, var(--good) 8%, var(--surface-1)); }
.result.ok .mark { color: var(--good); }
.result.bad { background: color-mix(in srgb, var(--critical) 8%, var(--surface-1)); }
.result.bad .mark { color: var(--critical); }
.result.busy { background: var(--series-1-soft); }

.locked-note {
  font-size: 12px; color: var(--serious); margin-top: 5px; display: block;
}

.card > h2 .spacer { flex: 1 1 auto; }

/* ------------------------------------------------------- view toggle */

/* The segmented control: a warm well track, the chosen segment a raised
   white chip. Calm: the selection is a sheet, not a blue block. */
.viewtoggle {
  display: inline-flex; gap: 2px; padding: 3px; border: 0; border-radius: 11px;
  background: var(--well);
}
.viewtoggle button {
  border: none; border-radius: 8px; background: none; font-size: 12px;
  padding: 4px 12px; color: var(--text-secondary); font-weight: 500;
}
.viewtoggle button.active { background: var(--raised); color: var(--text-primary); font-weight: 550; box-shadow: var(--soft); }
.viewtoggle button:hover:not(.active):not(:disabled) { background: color-mix(in srgb, var(--text-primary) 6%, var(--well)); color: var(--text-primary); }
.viewtoggle button:focus-visible { box-shadow: var(--focus); }
.viewtoggle button.active:focus-visible { box-shadow: var(--soft), var(--focus); }

/* --------------------------------------------------------- file grid */

/* Files as tiles: quiet well blocks on the sheet (never cards in a card);
   the type is a small raised chip, the name in ink, the rest muted. */
.grid-scroll { max-height: 620px; overflow-y: auto; padding: 8px var(--card-pad) var(--card-pad); }
.filegrid {
  display: grid; gap: 10px;
  grid-template-columns: repeat(auto-fill, minmax(min(150px, 100%), 1fr));
}
@media (max-width: 620px) { .filegrid { grid-template-columns: repeat(auto-fill, minmax(min(130px, 100%), 1fr)); } }
.filecard {
  display: flex; flex-direction: column; gap: 8px;
  border: 1px solid transparent; border-radius: var(--r-block); padding: 12px 14px 12px;
  background: var(--well); text-decoration: none; color: inherit;
  min-width: 0; min-height: 104px;
  transition: background-color 0.15s ease-out;
}
.filecard:hover { background: color-mix(in srgb, var(--text-primary) 6%, var(--well)); }
.filecard:active { background: color-mix(in srgb, var(--text-primary) 10%, var(--well)); }
.filegrid .filecard.picked { background: var(--series-1-soft); border-color: transparent; }
.filecard .ext {
  align-self: flex-start; font-size: 11px; font-weight: 600; letter-spacing: 0.02em;
  padding: 2px 7px; border-radius: var(--r-chip);
  background: var(--raised); color: var(--text-secondary);
}
.filecard .fname {
  font-size: 13px; font-weight: 500; line-height: 1.35; color: var(--text-primary);
  overflow-wrap: anywhere; display: -webkit-box;
  -webkit-line-clamp: 2; -webkit-box-orient: vertical; overflow: hidden;
}
.filecard:hover .fname { color: var(--series-1); }
.filecard .fmeta {
  font-size: 12px; color: var(--text-muted); margin-top: auto;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}

/* ------------------------------------------------------------ toasts */

.toasts {
  position: fixed; right: 16px; bottom: calc(16px + env(safe-area-inset-bottom, 0px)); z-index: 50;
  display: flex; flex-direction: column; gap: 8px; align-items: flex-end;
}
/* A toast: a warm white note with a lifted shadow; a small dot says ok / bad. */
.toast {
  display: flex; align-items: center; gap: 12px;
  background: var(--surface-1); color: var(--text-primary);
  border: 0; border-radius: 12px; padding: 12px 14px 12px 16px; font-size: 13px;
  box-shadow: var(--lift);
  animation: toastin var(--t-state) var(--ease-out);
  max-width: min(400px, calc(100vw - 32px));
}
.toast::before { content: ""; flex: 0 0 auto; width: 7px; height: 7px; border-radius: 50%; background: var(--series-1); }
.toast.bad::before { background: var(--critical); }
.toast .msg { flex: 1 1 auto; }
.toast button { flex: 0 0 auto; white-space: nowrap; padding: 4px 10px; font-size: 12px; border-radius: 8px; }
.toast button[aria-label]:last-child { background: transparent; color: var(--text-muted); padding: 4px 8px; }
.toast button:last-child:hover { background: var(--well); color: var(--text-primary); }
/* Close: the drawn × (Icons "close"), centred on a 28px square target. */
.toast button.toast-x {
  display: inline-grid; place-items: center; width: 28px; height: 28px; padding: 0; margin: -4px -6px -4px 0;
  border-radius: 8px; color: var(--text-muted);
}
.toast button.toast-x .ic-svg { display: block; width: 14px; height: 14px; }
body.has-demo-strip .toasts { bottom: calc(60px + env(safe-area-inset-bottom, 0px)); }
@keyframes toastin { from { opacity: 0; transform: translateY(8px); } }
@media (prefers-reduced-motion: reduce) { :root:not([data-motion="full"]) { .toast { animation: none; } } }


/* ---------------------------------------------------------- deadlines */

button.linkish {
  border: none; background: none; padding: 3px 6px; border-radius: var(--r-chip);
  color: var(--series-1); font-size: 13px; font-weight: 500;
}
button.linkish:hover:not(:disabled) { background: var(--well); }
button.linkish:active:not(:disabled) { background: color-mix(in srgb, var(--text-primary) 7%, var(--well)); }

/* Tasks page: tags by kind (assignments, quizzes, events). Soft well pills;
   the chosen one takes the ink-blue wash. Space under them, no rule. */
.due-kinds { display: flex; flex-wrap: wrap; gap: 6px; padding: 8px var(--card-pad) 10px; }
button.due-kind { display: inline-flex; align-items: center; gap: 6px; padding: 5px 12px; border-radius: 999px;
  font: 500 12.5px var(--font-ui); color: var(--text-secondary); background: var(--well);
  border: 0; cursor: pointer; }
button.due-kind:hover:not(:disabled) { color: var(--text-primary); background: color-mix(in srgb, var(--text-primary) 7%, var(--well)); }
button.due-kind:active:not(:disabled) { background: color-mix(in srgb, var(--text-primary) 11%, var(--well)); }
.due-kind .n { font-weight: 400; color: var(--text-muted); font-variant-numeric: tabular-nums; }
button.due-kind[aria-pressed="true"] { color: var(--text-primary); font-weight: 600; background: var(--series-1-soft); }
.due-kind[aria-pressed="true"] .n { color: var(--text-secondary); }

/* The rows: soft blocks inset in the sheet, separated by space alone; the
   inset (card-pad − 12px, + the row's 12px) keeps their text on the card's edge. */
.duelist { list-style: none; margin: 0; padding: 2px calc(var(--card-pad) - 12px) 10px; }
.duelist li {
  display: flex; align-items: center; gap: 14px;
  padding: 11px 12px; border-radius: var(--r-block);
  transition: background-color 0.15s ease-out;
}
.duelist li + li { margin-top: 2px; }
.duelist li:hover { background: color-mix(in srgb, var(--well) 60%, var(--surface-1)); }
/* The next thing to do: the first open row that's due soon (or late) gets a
   warm wash and its relative date becomes a small "next" badge. One per list. */
.duelist > li:is(.overdue, .soon):not(.mine-done):not(li:is(.overdue, .soon):not(.mine-done) ~ li) { background: var(--wash); }
.duelist > li:is(.overdue, .soon):not(.mine-done):not(li:is(.overdue, .soon):not(.mine-done) ~ li):hover { background: color-mix(in srgb, var(--next) 70%, var(--wash)); }
.duelist > li:is(.overdue, .soon):not(.mine-done):not(li:is(.overdue, .soon):not(.mine-done) ~ li) .when .rel {
  display: flex; width: max-content; align-items: center; gap: 6px; margin-top: 3px;
  padding: 1px 8px 1px 7px; border-radius: 999px;
  background: var(--next); color: var(--next-ink); font-weight: 600;
}
.duelist > li:is(.overdue, .soon):not(.mine-done):not(li:is(.overdue, .soon):not(.mine-done) ~ li) .when .rel::before {
  content: ""; width: 6px; height: 6px; border-radius: 50%; background: var(--next-dot);
}
/* The row a Meteoro link pointed at (/?item=…), lit for a moment: a soft
   ink-blue wash that fades back when the light goes off. */
.duelist li.asked, .duelist li.asked:hover { background: var(--series-1-soft); transition: background-color .6s ease-out; }

.duelist .when {
  flex: 0 0 160px; font-size: 13px; font-variant-numeric: tabular-nums;
  color: var(--text-secondary); white-space: nowrap;
}
.duelist .when .rel { display: block; font-size: 12px; color: var(--text-muted); }
.duelist li.overdue .when .rel { color: color-mix(in srgb, var(--critical) 80%, var(--text-primary)); font-weight: 600; }
.duelist li.soon .when .rel { color: color-mix(in srgb, var(--serious) 60%, var(--text-primary)); font-weight: 600; }

.duelist .what { flex: 1 1 auto; min-width: 0; }
.duelist .what .nm {
  font-size: 14px; font-weight: 500; line-height: 1.35;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.duelist .what .crs { font-size: 12px; color: var(--text-muted); margin-top: 3px; }
/* A course's tag: its shorthand on a soft tab of its colour (--c, the same
   one Home gives it), as on Home's index tabs. */
.ctag {
  display: inline-block; max-width: 12ch; overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
  vertical-align: baseline; padding: 1px 7px; border-radius: var(--r-chip);
  font-size: 11px; font-weight: 600; line-height: 1.5; letter-spacing: 0.02em;
  background: color-mix(in srgb, var(--c, var(--text-muted)) 14%, var(--surface-1));
  color: color-mix(in srgb, var(--c, var(--text-muted)) 60%, var(--text-primary));
}
.duelist .crs .ctag { margin-right: 8px; }
/* Files table: the course cell leads with the same tag, then the name. */
td.crs { min-width: 200px; }
td.crs .ctag { float: left; margin: 1px 8px 0 0; }
td.crs .crs-name { display: block; overflow: hidden; color: var(--text-secondary); }

/* Status never rides on colour alone - each carries its own word: a soft
   tint, a dot in the state's colour, the word in a deep tone of it. */
.state {
  --dot: var(--text-muted);
  flex: 0 0 auto; font-size: 12px; font-weight: 500; padding: 3px 10px 3px 8px; border-radius: 999px;
  border: 0; color: var(--text-secondary); background: var(--well);
  display: inline-flex; align-items: center; gap: 6px; white-space: nowrap;
}
.state::before { content: ""; width: 6px; height: 6px; border-radius: 50%; background: var(--dot); }
.state.done   { --dot: var(--good); color: color-mix(in srgb, var(--good) 60%, var(--text-primary));
  background: color-mix(in srgb, var(--good) 12%, var(--surface-1)); }
.state.todo   { --dot: var(--serious); color: color-mix(in srgb, var(--serious) 60%, var(--text-primary));
  background: color-mix(in srgb, var(--serious) 12%, var(--surface-1)); }
.state.graded { --dot: var(--series-1); color: color-mix(in srgb, var(--series-1) 60%, var(--text-primary));
  background: var(--series-1-soft); }

.duelist .go {
  flex: 0 0 auto; font-size: 12px; font-weight: 500; color: var(--series-1);
  text-decoration: none; padding: 5px 10px; border-radius: var(--r-btn);
  background: var(--well); white-space: nowrap;
}
.duelist .go:hover { background: color-mix(in srgb, var(--text-primary) 7%, var(--well)); }

@media (max-width: 620px) {
  .duelist { padding: 2px calc(var(--card-pad) - 12px) 8px; }
  .duelist li { flex-wrap: wrap; gap: 6px 12px; padding: 12px; }
  .duelist .more { margin-left: auto; }
  .duelist .when { flex-basis: 100%; }
  .duelist .mine + .when { flex-basis: calc(100% - 36px); }   /* the tick shares the date's line */
  .duelist .what { flex-basis: 100%; }
}


/* ------------------------------------------------- full-text results */

.hits-scroll { max-height: 620px; overflow-y: auto; }
.hits { list-style: none; margin: 0; padding: 0; }
/* Passages found inside files: kept apart by space and a soft wash on hover,
   the words you searched for marked like a highlighter on paper. */
.hits li { padding: 14px var(--card-pad) 16px; transition: background-color 0.15s ease-out; }
.hits li + li { margin-top: 2px; }
.hits li:hover { background: color-mix(in srgb, var(--well) 60%, transparent); }

.hits .head { display: flex; align-items: baseline; gap: 8px; flex-wrap: wrap; }
.hits .head a { color: var(--series-1); text-decoration: none; font-weight: 500; font-size: 14px; }
.hits .head a:hover { text-decoration: underline; }
.hits .page {
  font-size: 11px; font-weight: 500; color: var(--text-secondary);
  background: var(--well); border-radius: var(--r-chip); padding: 1px 7px;
  font-variant-numeric: tabular-nums;
}
.hits .crs { font-size: 12px; color: var(--text-muted); }
.hits .snip {
  font: 14px/1.6 var(--font-read); color: var(--text-secondary); margin-top: 6px;
  overflow-wrap: anywhere;
}
.hits .snip mark {
  background: var(--butter); color: var(--butter-ink);
  padding: 0 3px; border-radius: 4px;
}


/* ------------------------------------------------- selection + chat */

th.pick, td.pick { width: 34px; padding-right: 0; }
td.pick input, th.pick input { cursor: pointer; accent-color: var(--series-1); }

.filecard { position: relative; }
.filecard .pickbox {
  position: absolute; top: 8px; right: 8px;
  accent-color: var(--series-1); cursor: pointer;
}
/* What to do with the picked files: a quiet well strip at the foot of the sheet. */
.selbar {
  display: flex; align-items: center; gap: 10px; flex-wrap: wrap;
  margin: 8px calc(var(--card-pad) - 8px) calc(var(--card-pad) - 8px);
  padding: 10px 12px; border-radius: var(--r-block);
  background: var(--well);
}
.selbar .spacer { flex: 1 1 auto; }
.selcount { font-size: 13px; font-weight: 550; }
.chatopen { display: inline-flex; }
.chatopen button { border-radius: var(--r-btn) 0 0 var(--r-btn); }
.chatopen select {
  font: inherit; font-size: 13px; border: 0; margin-left: 1px;
  border-radius: 0 var(--r-btn) var(--r-btn) 0; padding: 8px 8px;
  background: var(--surface-1); color: var(--text-primary); cursor: pointer;
}
.chatopen select:focus-visible { outline: none; box-shadow: var(--focus); }

.hint select {
  font: inherit; font-size: 12px; padding: 2px 6px; border-radius: var(--r-chip);
  border: 0; background: var(--well);
  color: var(--text-primary);
}

.kv { display: grid; grid-template-columns: max-content 1fr; gap: 6px 16px; font-size: 13px; }
.kv dt, .kv .k { color: var(--text-secondary); }
.kv .v { color: var(--text-primary); font-variant-numeric: tabular-nums; }


.diagbox {
  margin: 14px 0 0; padding: 12px 14px; max-height: 320px; overflow: auto;
  background: var(--well); border: 0; border-radius: var(--r-block);
  font: 11px/1.55 ui-monospace, SFMono-Regular, Menlo, monospace;
  color: var(--text-secondary); white-space: pre-wrap; overflow-wrap: anywhere;
}

/* A code or link to hand on: a soft green tint says "ready", no edge. */
.codebox {
  margin-top: 14px; padding: 14px 16px; border-radius: var(--r-block);
  background: color-mix(in srgb, var(--good) 9%, var(--surface-1)); border: 0;
  display: flex; align-items: center; gap: 14px; flex-wrap: wrap;
  font-size: 13px; color: var(--text-secondary);
}
.codebox strong {
  font: 700 26px/1 ui-monospace, SFMono-Regular, Menlo, monospace;
  letter-spacing: 0.14em; color: var(--text-primary);
  user-select: all;
}

/* The chat fills the viewport, so drop the page's usual bottom padding. */
.wrap:has(> .homepanel:not([hidden])) { padding-bottom: 24px; }

/* ============================================================ Meteoro */

.grow { flex: 1 1 auto; }
.topbar .brand img { flex: 0 0 auto; border-radius: 6px; align-self: center; }
.topbar .brand { align-items: center; gap: 10px; }

.pill.off .dot { background: var(--warning); }
.pill.warn .dot { background: var(--warning); }

button.danger { color: var(--critical); }
button.danger:not(.quiet) { color: color-mix(in srgb, var(--critical) 88%, var(--text-primary)); background: color-mix(in srgb, var(--critical) 8%, var(--surface-1)); }
button.danger:not(.quiet):hover:not(:disabled) { background: color-mix(in srgb, var(--critical) 12%, var(--surface-1)); }
button.danger:not(.quiet):active:not(:disabled) { background: color-mix(in srgb, var(--critical) 16%, var(--surface-1)); }

/* Gone from the school: the tag empties to a dashed outline. */
.tag.away { background: transparent; outline: 1px dashed var(--baseline); outline-offset: -1px; color: var(--text-muted); }
.filecard.away { opacity: 0.72; }
.course-list .group {
  padding: 16px 10px 4px; font-size: 12px;
  color: var(--text-muted); font-weight: 600;
}

details.history { margin-top: 4px; }
details.history > summary {
  cursor: pointer; padding: 12px var(--card-pad); font-size: 13px; color: var(--text-secondary);
  list-style-position: inside; border-radius: var(--r-btn);
  transition: color .15s ease-out;
}
details.history > summary:hover { color: var(--text-primary); }
details.history .table-scroll { max-height: 340px; }
#connector-panels .log { max-height: 240px; margin-top: 10px; }
#connector-panels .progress-track { margin-top: 12px; }
#connector-panels .kv { margin-bottom: 4px; }
#offline-info { margin-top: 12px; }
#version-line { text-align: right; padding: 4px 2px; }

/* ------------------------------------------------- connected platforms */

/* Your schools: rows that line up with the card's inset (10px of their own
   padding inside the list's), lit by a soft wash when hovered or chosen. */
.connlist, .kindlist {
  list-style: none; margin: 0; padding: 4px calc(var(--card-pad) - 10px) 12px;
  display: flex; flex-direction: column; gap: 2px;
}
.connlist li, .kindlist li { margin: 0; }
.connempty { padding: 10px 10px 8px; color: var(--text-muted); font-size: 13px; }
.connitem {
  width: 100%; display: flex; align-items: center; gap: 12px; text-align: left;
  border: 0; background: none; padding: 10px; border-radius: var(--r-block);
  transition: background-color .15s ease-out;
}
.connitem:hover:not(:disabled) { background: color-mix(in srgb, var(--well) 60%, transparent); }
.connitem:active:not(:disabled) { background: var(--well); }
.connitem.active, .connitem.active:hover:not(:disabled) { background: var(--series-1-soft); }
.connitem.active .nm { font-weight: 600; }
.connitem .avatar {
  flex: 0 0 auto; width: 36px; height: 36px; border-radius: var(--r-field);
  display: grid; place-items: center; font-weight: 650; font-size: 16px;
  background: var(--sky); color: var(--sky-ink);
}
.connitem.active .avatar { background: var(--surface-1); color: var(--series-1); }
.connitem .avatar.plus { background: var(--well); color: var(--text-secondary); }
.connitem .words { flex: 1 1 auto; min-width: 0; display: flex; flex-direction: column; }
.connitem .nm { font-weight: 550; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.connitem .sub {
  font-size: 12px; color: var(--text-muted); overflow: hidden; text-overflow: ellipsis;
  white-space: nowrap; font-variant-numeric: tabular-nums;
}
.connitem .pill { flex: 0 0 auto; }
.add-btn { width: 32px; height: 32px; justify-content: center; padding: 0; border-radius: var(--r-btn); }
.addmenu { margin-top: 8px; padding: 4px 0 0; }
.addmenu .hint { margin: 0 var(--card-pad) 4px; }

/* --------------------------------------------------------- sign in */

.authpage {
  height: 100%; overflow-y: auto; overscroll-behavior: contain;
  display: flex; flex-direction: column; align-items: center; gap: 16px;
  padding: calc(24px + env(safe-area-inset-top, 0px)) calc(16px + env(safe-area-inset-right, 0px))
    calc(24px + env(safe-area-inset-bottom, 0px)) calc(16px + env(safe-area-inset-left, 0px));
}
/* Centred when it fits, scrollable from the top when it doesn't (keyboard up). */
.authpage::before, .authpage::after { content: ""; flex: 1 0 0; }
/* One calm white sheet on the warm ground: a soft shadow, no line. */
.authcard {
  width: 100%; max-width: 420px; background: var(--surface-1);
  border: 0; border-radius: var(--r-sheet); padding: 32px 32px 24px;
  box-shadow: var(--soft);
}
@media (max-width: 620px) { .authcard { padding: 24px 20px 20px; } }
.authbrand { display: flex; align-items: center; gap: 14px; margin-bottom: 24px; }
.authbrand img { border-radius: var(--r-field); }
.authbrand h1 { margin: 0; font-size: 22px; font-weight: 650; letter-spacing: -0.01em; }
.authbrand p { margin: 3px 0 0; color: var(--text-secondary); font: 14px/1.45 var(--font-read); }
.authtabs { display: flex; margin-bottom: 16px; }
.authtabs button { flex: 1 1 0; }
#auth-intro { margin: 0 0 16px; font-size: 13.5px; line-height: 1.5; color: var(--text-secondary); }
#auth-form .field { margin-bottom: 12px; }
#auth-result { margin: 4px 0 12px; }
#auth-form .actions { display: flex; align-items: center; gap: 10px; }
#auth-form .actions .spacer { flex: 1 1 auto; }
.authfoot { max-width: 400px; margin: 0; padding: 0 8px; text-align: center; color: var(--text-secondary); font: 15px/1.55 var(--font-read); }

/* ---------------------------------------------------------- viewer */

/* Paper inks: what is drawn ON the teacher's page. The page is the document
   and stays white in both themes, so the marks on it keep the light inks
   (a light-blue line on a white page would vanish in dark). The reader's
   only raw colours live here; the highlighters are real highlighter tones. */
:root {
  --paper: #ffffff;
  --paper-ink: #3a5a8e; --paper-on-ink: #ffffff;
  --paper-live: #b4532a; --paper-live-ink: #8a3c1c;
  --paper-lost: #b4443c;
  --paper-label: rgba(255, 255, 255, 0.9);
  --hl-yellow: #ffd43b; --hl-green: #8ce99a; --hl-blue: #a5d8ff; --hl-pink: #fcc2d7;
}

/* Reading on good paper: the ground stays warm and quiet, the bar sits on
   it with no line, and the page is the only white thing on screen. */
.viewer {
  position: fixed; inset: 0; z-index: 50; display: flex; flex-direction: column;
  background: var(--plane);
  padding: env(safe-area-inset-top, 0px) env(safe-area-inset-right, 0px)
    env(safe-area-inset-bottom, 0px) env(safe-area-inset-left, 0px);
}
.viewer-bar {
  display: flex; align-items: center; gap: 10px; padding: 10px 16px 10px 20px; min-height: 56px;
  background: var(--plane);
}
.viewer-bar strong { min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; font-weight: 600; font-size: 14px; }
.viewer-bar .spacer { flex: 1 1 auto; }
.viewer-bar .icon-btn { font-size: 20px; line-height: 1; padding: 4px 11px; }
@media (max-width: 620px) {
  .viewer-bar { gap: 6px; padding: 8px 10px 8px 14px; min-height: 52px; }
  /* the tools and Download keep their size; the file's name gives way */
  .viewer-bar strong { flex: 1 1 0; }
  .viewer-bar .rd-tools { flex: none; }
  .viewer-bar .spacer { display: none; }
}
.viewer-body { flex: 1 1 auto; min-height: 0; display: flex; align-items: center; justify-content: center; }
.viewer-body iframe { width: 100%; height: 100%; border: 0; background: var(--paper); }
/* A Word file (web/docs.js): its frame, and what to do if it can't be drawn */
.viewer-body, .po-reader { position: relative; }
.viewer-body iframe.dv-frame, .po-reader .dv-frame { display: block; width: 100%; height: 100%; border: 0; background: var(--plane); }
.dv-note {
  position: absolute; inset: auto 16px 16px; margin: 0 auto; max-width: 420px;
  display: flex; align-items: center; gap: 12px; justify-content: space-between;
  padding: 10px 10px 10px 16px; border-radius: var(--r-block);
  background: var(--surface-1); box-shadow: var(--lift); font-size: 14px;
}
.dv-note p { margin: 0; }
.dv-toggle button { font-size: 13px; padding: 4px 10px; }
/* the switch keeps its size; the file's name gives way first */
.rd-tools:has(> .dv-toggle) { flex: none; }
.viewer-body img, .viewer-body video { max-width: 100%; max-height: 100%; object-fit: contain; border-radius: var(--r-chip); }
.viewer-body audio { width: min(560px, 90%); }
.viewer-pages { font-size: 12px; color: var(--text-muted); white-space: nowrap; font-variant-numeric: tabular-nums; }
/* The reader (reader.js): PDFs drawn by pdf.js on every device — pages
   stacked, fit to width, scrolled; the text selectable on top; your marks
   (highlights, Until here lines) on top of that. */
.rd, .viewer-body.rd { display: block; overflow-y: auto; overscroll-behavior: contain; padding: 8px 16px 16px; position: relative; }
.rd > .hint { text-align: center; margin-top: 30vh; }
.rd-pages { display: flex; flex-direction: column; align-items: safe center; gap: 16px; padding-bottom: 40vh; }
.rd.rd-zoomed { overflow-x: auto; }
/* − 100% + in the reader's tools */
.rd-zoom { display: inline-flex; align-items: center; gap: 2px; padding: 2px; border-radius: var(--r-btn); background: var(--well); }
.rd-zoom .rd-tool { padding: 3px 9px; min-width: 28px; background: transparent; font-variant-numeric: tabular-nums; }
.rd-zoom .rd-tool:hover:not(:disabled) { background: var(--surface-1); }
.rd-zoom .rd-zoom-p { min-width: 48px; font-size: 12px; color: var(--text-secondary); }
@media (max-width: 620px) { .rd, .viewer-body.rd { padding: 4px 8px 12px; } .rd-pages { gap: 10px; } }
.rd-page {
  /* zoom (reader.js): 1 = fit to width; wider than the panel scrolls sideways */
  position: relative; width: calc(100% * var(--rd-zoom, 1)); max-width: calc(900px * var(--rd-zoom, 1)); flex: none; background: var(--paper);
  box-shadow: var(--soft); border-radius: 3px; overflow: hidden;
  --user-unit: 1;
}
.rd-page canvas { position: absolute; inset: 0; display: block; width: 100%; height: 100%; }
.rd-hls, .rd-untils { position: absolute; inset: 0; pointer-events: none; }
.rd-hls { z-index: 1; mix-blend-mode: multiply; }
.rd-hl { position: absolute; border-radius: 2px; }
.rd-hl.c-yellow, .rd-dot.c-yellow { background: var(--hl-yellow); }
.rd-hl.c-green, .rd-dot.c-green { background: var(--hl-green); }
.rd-hl.c-blue, .rd-dot.c-blue { background: var(--hl-blue); }
.rd-hl.c-pink, .rd-dot.c-pink { background: var(--hl-pink); }
.rd-hl { opacity: .7; }
.rd-untils { z-index: 3; }
/* (scoped to the page: the bar's "Until here" button carries .rd-until too) */
.rd-untils > .rd-until { position: absolute; left: 0; right: 0; height: 0; border-top: 2px dashed var(--paper-ink); opacity: .45; }
.rd-untils > .rd-until.latest { opacity: 1; border-top-style: solid; }
.rd-untils > .rd-until span {
  position: absolute; right: 8px; top: 0; transform: translateY(-50%);
  font: 600 11px var(--font-ui); color: var(--paper-on-ink); background: var(--paper-ink); padding: 2px 9px; border-radius: 999px;
}

/* each class's stretch (previous Until here → its own): a soft wash, labelled */
.rd-band { position: absolute; left: 0; right: 0; background: color-mix(in srgb, var(--paper-ink) 6%, transparent); }
.rd-band.alt { background: color-mix(in srgb, var(--paper-ink) 3%, transparent); }
.rd-band span { position: absolute; left: 8px; top: 6px; font: 600 11px var(--font-ui); color: var(--paper-ink);
  background: var(--paper-label); padding: 1px 8px; border-radius: 999px; }
/* the class, live: your last "we're here" (solid) and the guess (soft) */
/* Planner: a hairline in the live tone and a small soft tab with a dot, no thick stripe. */
.rd-untils > .rd-here { position: absolute; left: 0; right: 0; height: 0;
  border-top: 1px solid color-mix(in srgb, var(--paper-live) 55%, transparent); }
.rd-untils > .rd-here span { position: absolute; left: 8px; top: 0; transform: translateY(-50%); font: 600 11px var(--font-ui);
  display: inline-flex; align-items: center; gap: 6px;
  color: var(--paper-live-ink); background: color-mix(in srgb, var(--paper-live) 12%, var(--paper)); padding: 2px 9px 2px 7px; border-radius: 999px; }
.rd-untils > .rd-here span::before { content: ""; width: 6px; height: 6px; border-radius: 50%; background: var(--paper-live); }
.rd-transit { position: absolute; left: 0; right: 0; height: 0; }
.rd-transit-band { position: absolute; left: 0; right: 0; min-height: 14px; transform: translateY(-50%);
  background: linear-gradient(to bottom, transparent, color-mix(in srgb, var(--paper-live) 14%, transparent), transparent); }
.rd-transit::after { content: ""; position: absolute; left: 0; right: 0; top: -1px; border-top: 2px dotted var(--paper-live); animation: rd-breathe 2.4s ease-in-out infinite; }
.rd-transit span { position: absolute; right: 8px; top: 0; transform: translateY(-50%); z-index: 1; font: 600 11px var(--font-ui);
  color: var(--paper-live-ink); background: color-mix(in srgb, var(--paper-live) 10%, var(--paper)); padding: 2px 9px; border-radius: 999px; }
@keyframes rd-breathe { 50% { opacity: .35; } }
@media (prefers-reduced-motion: reduce) { :root:not([data-motion="full"]) { .rd-transit::after { animation: none; } } }
/* "I don't get this" */
.rd-hl.c-lost { background: repeating-linear-gradient(-45deg, color-mix(in srgb, var(--paper-lost) 34%, transparent) 0 6px, color-mix(in srgb, var(--paper-lost) 14%, transparent) 6px 12px); opacity: 1; }
.rd-dot.rd-lost, .rd-dot.c-lost { background: var(--rose); color: var(--rose-ink); font: 700 13px var(--font-ui); line-height: 24px; text-align: center; }
.rd-tool.rd-tool-live { color: color-mix(in srgb, var(--serious) 65%, var(--text-primary)); background: color-mix(in srgb, var(--serious) 12%, var(--surface-1)); }
.rd-tool.rd-tool-live:hover { background: color-mix(in srgb, var(--serious) 18%, var(--surface-1)); }
.rd-class-acts { display: flex; gap: 8px; padding: 6px 0 4px; }
.rd-class-acts button { flex: 1; font-size: 13px; padding: 6px 8px; }
.rd-chip.rd-callback { background: var(--peach); color: var(--peach-ink); }
.rd-chip button { background: var(--well); color: var(--text-primary); border: 0; border-radius: 999px; padding: 4px 12px; font-weight: 600; }
.rd-chip.rd-callback button { background: color-mix(in srgb, var(--peach-ink) 12%, var(--peach)); color: var(--peach-ink); }

/* pdf.js's text layer: the page's words, invisible, exactly over the drawing */
.rd-page .textLayer {
  position: absolute; inset: 0; z-index: 2; overflow: clip; line-height: 1; text-align: initial;
  -webkit-text-size-adjust: none; text-size-adjust: none; forced-color-adjust: none; transform-origin: 0 0;
  --min-font-size: 1;
  --text-scale-factor: calc(var(--total-scale-factor) * var(--min-font-size));
  --min-font-size-inv: calc(1 / var(--min-font-size));
}
.rd-page .textLayer :is(span, br) { color: transparent; position: absolute; white-space: pre; cursor: text; transform-origin: 0% 0%; }
.rd-page .textLayer > :not(.markedContent),
.rd-page .textLayer .markedContent span:not(.markedContent) {
  z-index: 1; --font-height: 0; font-size: calc(var(--text-scale-factor) * var(--font-height));
  --scale-x: 1; --rotate: 0deg;
  transform: rotate(var(--rotate)) scaleX(var(--scale-x)) scale(var(--min-font-size-inv));
}
.rd-page .textLayer .markedContent { display: contents; }
.rd-page .textLayer ::selection { background: color-mix(in srgb, var(--paper-ink) 26%, transparent); }
.rd-page .textLayer br::selection { background: transparent; }
.rd-page .textLayer .endOfContent { display: block; position: absolute; inset: 100% 0 0; z-index: 0; cursor: default; user-select: none; }
.rd-page .textLayer.selecting .endOfContent { top: 0; }

/* the reader's tools in the viewer bar */
.rd-tools { display: flex; align-items: center; gap: 6px; min-width: 0; }
.rd-tool { padding: 5px 11px; font-size: 13px; white-space: nowrap; }

/* Write on it: text boxes and white-outs over the page (web/reader.js). The
   boxes use Helvetica/Arial because that's the font Save as PDF writes with:
   what you see is where the words land. The page stays paper-white in dark
   mode, so the ink is a pen's near-black in both. */
.rd-page { container-type: inline-size; }
.rd-ink { position: absolute; inset: 0; z-index: 4; pointer-events: none; }
.rd-writing .rd-ink { pointer-events: auto; cursor: text; }
.rd-writing[data-write="cover"] .rd-ink { cursor: crosshair; }
.rd-writing .rd-page .textLayer { pointer-events: none; }
.rd-text { position: absolute; }
.rd-text-in {
  font-family: Helvetica, Arial, sans-serif; font-size: calc(var(--fs, .02) * 100cqw); line-height: 1.2;
  white-space: pre; color: #121217; min-width: 1ch; min-height: 1.2em; outline: none; caret-color: var(--paper-ink, #121217);
}
.rd-cover { position: absolute; background: #fff; }
.rd-cover.drawing { outline: 1.5px dashed var(--paper-ink); outline-offset: -1px; background: color-mix(in srgb, #fff 85%, transparent); }
.rd-ink-grip, .rd-ink-x { display: none; }
.rd-writing .rd-text, .rd-writing .rd-cover { pointer-events: auto; }
.rd-writing .rd-text { outline: 1px dashed color-mix(in srgb, var(--paper-ink) 35%, transparent); outline-offset: 2px; border-radius: 1px; }
.rd-writing .rd-cover { outline: 1px dashed color-mix(in srgb, var(--paper-ink) 45%, transparent); }
.rd-writing[data-write="cover"] .rd-cover { cursor: move; }
.rd-writing[data-write="text"] .rd-cover { cursor: text; }
.rd-writing .rd-text:focus-within { outline: 1.5px solid var(--paper-ink); }
.rd-writing :is(.rd-text, .rd-cover):is(:hover, :focus-within) > :is(.rd-ink-grip, .rd-ink-x) { display: grid; }
.rd-ink-grip { position: absolute; left: -16px; top: 0; width: 12px; height: max(100%, 14px); cursor: move; place-items: center;
  border-radius: 3px; background: color-mix(in srgb, var(--paper-ink) 14%, transparent); touch-action: none; }
.rd-cover > .rd-ink-grip { display: none !important; }
.rd-text.rd-typing > .rd-ink-x { display: none !important; }   /* typing: no × under your words (thread 191) */
.rd-ink-x { position: absolute; right: -10px; top: -10px; width: 20px; height: 20px; padding: 0; border: 0; border-radius: 50%;
  place-items: center; background: var(--surface-1); color: var(--text-primary); box-shadow: var(--lift); cursor: pointer; }
.rd-ink-x:hover { color: var(--critical); }
.rd-tool.rd-write[aria-pressed="true"] { background: var(--well); color: var(--text-primary); }
.rd-writebar {
  position: sticky; top: 6px; z-index: 6; display: flex; flex-wrap: wrap; align-items: center; gap: 6px;
  width: fit-content; max-width: 100%; margin: 0 auto 10px; padding: 6px 6px 6px 8px;
  border-radius: var(--r-block); background: var(--surface-1); box-shadow: var(--lift); font-size: 13px;
}
.rd-writebar[hidden] { display: none; }
.rd-writebar button { font-size: 13px; padding: 5px 11px; }
.rd-writebar .rd-size { min-width: 34px; padding: 5px 8px; font-weight: 600; }
.rd-write-hint { color: var(--text-muted); padding: 0 6px; }
@media (max-width: 620px) { .rd-write-hint { display: none; } }
/* the last resorts: small, quiet, at the bar's end */
.rd-write-extra { display: inline-flex; gap: 2px; margin-left: 4px; padding-left: 6px; border-left: 1px solid var(--border); }
.rd-writebar .rd-small { font-size: 12px; padding: 4px 8px; background: none; color: var(--text-muted); border: 0; border-radius: var(--r-chip); cursor: pointer; }
.rd-writebar .rd-small:hover { background: var(--well); color: var(--text-primary); }
.rd-tool.rd-handin { margin-left: 4px; }
/* dark pages: in the dark theme only; the file itself stays as it is */
.rd-darkbtn { display: none; padding: 5px 8px; }
.rd-darkbtn .ic-svg { display: block; }
:root[data-theme="dark"] .rd-darkbtn:not([hidden]) { display: inline-flex; }
/* no shadow there: inverted, it glowed white round the page (Zaen, 2026-11-10: "that white halo") */
:root[data-theme="dark"] .rd-darkpages .rd-page { filter: invert(.9) hue-rotate(180deg); box-shadow: none; background: #fff; }
@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) .rd-darkbtn:not([hidden]) { display: inline-flex; }
  :root:not([data-theme="light"]) .rd-darkpages .rd-page { filter: invert(.9) hue-rotate(180deg); box-shadow: none; background: #fff; }
}

/* the little bar over a selection or a highlight: a lifted note, no line */
.rd-bar {
  position: fixed; z-index: 70; display: flex; align-items: center; gap: 6px; padding: 6px 8px;
  border-radius: 999px; background: var(--surface-1); box-shadow: var(--lift);
}
.rd-dot { display: inline-block; width: 24px; height: 24px; border-radius: 50%; border: 0; box-shadow: inset 0 0 0 1px var(--border); padding: 0; flex: none; }
.rd-dot[aria-pressed="true"] { box-shadow: 0 0 0 2px var(--surface-1), 0 0 0 4px var(--text-primary); }
.rd-act { padding: 4px 11px; font-size: 13px; border-radius: 999px; }
.rd-act.bad { color: var(--critical); }

/* Until here: the guide line across the middle of the screen */
.rd-guide { position: fixed; z-index: 65; pointer-events: none; }
.rd-guide-line { position: absolute; left: 0; right: 0; top: 30%; height: 0; border-top: 2px solid var(--paper-ink); }
.rd-guide-tag {
  position: absolute; right: 18px; top: 0; transform: translateY(-50%);
  font: 600 12px var(--font-ui); color: var(--paper-on-ink); background: var(--paper-ink); padding: 3px 10px; border-radius: 999px;
}
.rd-guide-card {
  position: absolute; left: 50%; bottom: 18px; transform: translateX(-50%); pointer-events: auto;
  display: flex; align-items: center; gap: 8px; padding: 8px 8px 8px 16px; max-width: calc(100% - 24px);
  border-radius: var(--r-block); background: var(--surface-1); box-shadow: var(--lift);
  font-size: 13px; color: var(--text-secondary);
}

/* "Until here: p. 12 (Mon) — Go", when a file opens: a warm white note over the page */
.rd-chip {
  position: absolute; left: 50%; bottom: calc(18px + env(safe-area-inset-bottom, 0px)); transform: translateX(-50%); z-index: 5;
  display: flex; gap: 10px; align-items: center; padding: 8px 10px 8px 16px; border-radius: 999px;
  background: var(--surface-1); color: var(--text-primary); border: 0; font-size: 13px; box-shadow: var(--lift);
}
button.rd-chip { padding-right: 16px; }
button.rd-chip:hover { background: color-mix(in srgb, var(--text-primary) 4%, var(--surface-1)); }
.rd-chip b { color: var(--series-1); font-weight: 600; }
.rd-flash {
  position: absolute; left: 50%; top: 70px; transform: translateX(-50%); z-index: 6; padding: 6px 14px; border-radius: 999px;
  background: var(--surface-1); color: var(--text-primary); font-size: 13px; box-shadow: var(--lift); pointer-events: none;
}

/* Marks: the side panel, a lifted sheet; groups by space, not rules */
.rd-panel {
  position: fixed; z-index: 60; width: min(360px, 100vw); overflow-y: auto;
  background: var(--surface-1); box-shadow: var(--lift);
  padding: 0 16px 16px; display: flex; flex-direction: column; gap: 2px;
}
.rd-panel-h { position: sticky; top: 0; display: flex; align-items: center; justify-content: space-between; padding: 14px 0 8px 8px; background: var(--surface-1); font-weight: 600; }
.rd-panel h4 { margin: 16px 0 4px; padding: 0 8px; font-size: 12px; font-weight: 600; color: var(--text-secondary); }
.rd-row {
  display: flex; align-items: center; gap: 10px; width: 100%; text-align: left; padding: 8px; border: 0; border-radius: var(--r-btn);
  background: none; color: var(--text-primary); font-size: 14px; transition: background-color 0.15s ease-out;
}
.rd-row:hover { background: var(--well); }
.rd-row:active { background: color-mix(in srgb, var(--text-primary) 7%, var(--well)); }
.rd-row .rd-dot { width: 12px; height: 12px; }
.rd-row-t { flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; font-family: var(--font-read); }
.rd-row-k { color: var(--text-muted); font-size: 12px; white-space: nowrap; font-variant-numeric: tabular-nums; }
.rd-none { padding: 8px; }
.rd-panel-acts { position: sticky; bottom: 0; display: flex; gap: 8px; justify-content: flex-end; padding-top: 12px; margin-top: auto; background: var(--surface-1); }
@media (max-width: 760px) {
  .rd-tools .rd-count { display: none; }
  .rd-guide-card span { display: none; }
}
.viewer-other { text-align: center; padding: 24px; }
.viewer-other p { color: var(--text-secondary); margin: 0 0 16px; }

/* The viewer's own drawings (web/viewer.js): code, tables, a ZIP's contents,
   slides' text. A page of their own that scrolls; the file is the content. */
.vw { align-self: stretch; flex: 1 1 auto; min-width: 0; overflow: auto; padding: 16px 20px 32px; background: var(--plane); }
.vw:focus-visible { outline: none; box-shadow: inset var(--focus); }
.vw-note { margin: 0 0 12px; color: var(--text-muted); font-size: 13px; }
.vw-wait { color: var(--text-secondary); font-size: 14px; }
.vw-lines {
  margin: 0; counter-reset: ln; font: 13px/1.6 ui-monospace, "SF Mono", Menlo, Consolas, monospace;
  color: var(--text-primary); tab-size: 4; white-space: pre-wrap; overflow-wrap: anywhere;
}
.vw-lines > span { display: block; padding-left: 4.2em; text-indent: -4.2em; }
.vw-lines > span::before {
  counter-increment: ln; content: counter(ln); display: inline-block; width: 3.4em; margin-right: .8em; text-indent: 0;
  text-align: right; color: var(--text-muted); user-select: none; font-variant-numeric: tabular-nums;
}
.vw-read { display: block; }
.vw-read > * { max-width: 70ch; margin-left: auto; margin-right: auto; }
.vw-md { padding: 0; }
.vw-slide { padding: 14px 0 6px; font: 15px/1.6 var(--font-read); }
.vw-slide + .vw-slide { border-top: 1px solid var(--grid); }
.vw-slide h3 { margin: 0 0 6px; font-family: var(--font-ui, inherit); font-size: 12px; font-weight: 600; line-height: 1.4; color: var(--text-muted); }
.vw-slide p { margin: 0 0 6px; }
.vw-tabs { display: flex; gap: 4px; flex-wrap: wrap; margin: 0 0 12px; }
.vw-tabs button { font-size: 13px; padding: 4px 12px; border-radius: 999px; }
.vw-tabs button[aria-selected="true"] { background: var(--sky); color: var(--sky-ink); }
.vw-table { overflow: auto; border-radius: var(--r-block); box-shadow: var(--soft); background: var(--surface-1); width: max-content; max-width: 100%; }
.vw-table table { border-collapse: collapse; font-size: 13px; }
.vw-table td, .vw-table th { padding: 5px 10px; border-bottom: 1px solid var(--grid); white-space: nowrap; max-width: 40ch; overflow: hidden; text-overflow: ellipsis; text-align: left; }
.vw-table td + td { border-left: 1px solid var(--grid); }
.vw-table td.num { text-align: right; font-variant-numeric: tabular-nums; }
.vw-table .vw-rn { position: sticky; left: 0; background: var(--well); color: var(--text-muted); font-weight: 400; text-align: right; font-variant-numeric: tabular-nums; }
.vw-entries { list-style: none; margin: 0; padding: 0; max-width: 640px; }
.vw-entries .vw-dir { padding: 12px 8px 4px; font-size: 12px; font-weight: 600; color: var(--text-secondary); }
.vw-entries button {
  display: flex; width: 100%; justify-content: space-between; gap: 12px; padding: 8px; border: 0; border-radius: var(--r-btn);
  background: none; color: var(--text-primary); font-size: 14px; text-align: left;
}
.vw-entries button:hover { background: var(--well); }
.vw-size { color: var(--text-muted); font-size: 12px; font-variant-numeric: tabular-nums; }
.vw-nest { align-self: stretch; flex: 1 1 auto; min-width: 0; min-height: 0; display: flex; flex-direction: column; }
.vw-back { align-self: flex-start; margin: 10px 20px 0; font-size: 13px; padding: 4px 12px; }
.vw-inner { position: relative; flex: 1 1 auto; min-height: 0; display: flex; align-items: center; justify-content: center; }
.vw-inner img, .vw-inner video { max-width: 100%; max-height: 100%; object-fit: contain; }

/* ------------------------------------------------------ Google sign-in */

/* "or use a password": space sets it apart, not rules. */
.author {
  display: flex; align-items: center; justify-content: center; margin: 20px 0 12px;
  color: var(--text-muted); font-size: 12.5px;
}
.google-btn {
  width: 100%; display: flex; align-items: center; justify-content: center; gap: 10px;
  padding: 10px 14px; font-weight: 550;
}
.signins { margin: 4px 0 16px; }
/* A row inside a card: a quiet well block, no outline. */
.signin-row {
  display: flex; align-items: center; gap: 14px; padding: 10px 14px;
  border: 0; border-radius: var(--r-block); background: var(--well); font-size: 13px;
}
/* buttons on the well rise off it, as the segmented control's chosen segment does */
.peoplelist .signin-row button:not(.primary):not(.linkish):not(.quiet):not(.ghost):not(.danger) { background: var(--raised); }
.peoplelist .signin-row button:not(.primary):not(.linkish):not(.quiet):not(.ghost):not(.danger):hover:not(:disabled) {
  background: color-mix(in srgb, var(--text-primary) 6%, var(--raised));
}
.signin-row .k { font-weight: 600; }
.signin-row .v { flex: 1 1 auto; color: var(--text-secondary); overflow-wrap: anywhere; }
#gadmin-info, #agents-info { margin-bottom: 14px; overflow-wrap: anywhere; }

/* ------------------------------------------------------------ passkeys */

.authbtns { display: flex; flex-direction: column; gap: 10px; margin: 4px 0 4px; }
.passkey-btn {
  width: 100%; display: flex; align-items: center; justify-content: center; gap: 10px;
  padding: 11px 14px; font-size: 15px;
}
#auth-form .actions { margin-top: 4px; }
#auth-submit { width: 100%; }
.authlang { display: flex; justify-content: flex-end; margin-top: 16px; }
.passkeys { margin-bottom: 14px; display: flex; flex-direction: column; gap: 8px; }
.passkeys-head { display: flex; align-items: center; gap: 10px; }
.passkeys .hint { margin: 0; }
.peoplelist { list-style: none; margin: 0; padding: 8px var(--card-pad) var(--card-pad); display: flex; flex-direction: column; gap: 8px; }
/* inside a card's .body the inset is already there: the rows' edges sit on the heading's */
.card .body .peoplelist { padding-left: 0; padding-right: 0; }
.signin-row .k { margin-right: 6px; }
#recovery-box .codebox strong, #inv-box strong {
  font: 600 14px/1.5 var(--font-ui); letter-spacing: 0; overflow-wrap: anywhere;
}

/* Demo strip (web/demo.js): the app running on a fake school (the Orrery).
   The butter tint, edge to edge: plainly a demo, never an alarm. */
.demo-strip { position: fixed; left: 0; right: 0; bottom: 0; z-index: 900;
  display: flex; flex-wrap: wrap; align-items: center; gap: 6px 12px;
  padding: 8px 16px calc(8px + env(safe-area-inset-bottom, 0px));
  font: 500 12px var(--font-ui); color: var(--butter-ink); background: var(--butter); }
.demo-strip b { display: inline-flex; align-items: center; gap: 7px; font-weight: 700; font-size: 12px; }
.demo-strip b::before { content: ""; width: 7px; height: 7px; border-radius: 999px; background: currentColor; }
.demo-strip .sep { opacity: .45; }
.demo-strip label { display: inline-flex; gap: 6px; align-items: center; cursor: pointer; }
.demo-strip input[type="checkbox"] { accent-color: var(--butter-ink); }
.demo-strip a, .demo-strip button { font: inherit; font-weight: 600; color: var(--butter-ink);
  background: color-mix(in srgb, var(--surface-1) 55%, var(--butter)); border: 1px solid transparent;
  border-radius: 8px; padding: 3px 10px; cursor: pointer; text-decoration: none;
  transition: background-color .15s ease-out; }
.demo-strip a:hover, .demo-strip button:hover { background: color-mix(in srgb, var(--surface-1) 85%, var(--butter)); }
.demo-strip a:active, .demo-strip button:active { background: color-mix(in srgb, var(--butter-ink) 12%, var(--butter)); }
.demo-strip a:focus-visible, .demo-strip button:focus-visible { outline: none; box-shadow: var(--focus); }
.demo-strip .push { margin-left: auto; display: inline-flex; gap: 6px; }
.demo-menu { position: fixed; z-index: 901; bottom: 48px; max-height: 60vh; overflow: auto;
  width: min(340px, calc(100vw - 32px)); padding: 6px; border-radius: var(--r-block); display: grid; gap: 2px;
  background: var(--surface-1); border: 0; box-shadow: var(--lift); }
.demo-menu button { text-align: left; font: 13px/1.35 var(--font-ui); color: var(--text-primary); background: none;
  border: 0; border-radius: 8px; padding: 8px 10px; cursor: pointer; transition: background-color .15s ease-out; }
.demo-menu button:hover { background: var(--ghost); }
.demo-menu button.on { background: var(--series-1-soft); font-weight: 550; }
.demo-menu button:focus-visible { outline: none; box-shadow: inset 0 0 0 2px var(--series-1); }
.demo-menu small { display: block; margin-top: 1px; color: var(--text-secondary); font-size: 12px; font-weight: 400; }
body.has-demo-strip { padding-bottom: 48px; }

/* Halo: our right-click menu (halo.js) */
.halo { position: fixed; left: 0; top: 0; z-index: 1000; min-width: 220px; max-width: 320px; padding: 6px;
  border-radius: var(--r-block); background: var(--surface-1); border: 0;
  box-shadow: var(--lift); font: 13px/1.35 var(--font-ui); color: var(--text-primary);
  animation: halo-in var(--t-quick) var(--ease-out); }
@keyframes halo-in { from { opacity: 0; transform: translateY(-3px); } }
.halo-h { padding: 8px 10px 4px; font-size: 12px; font-weight: 600; color: var(--text-muted); }
.halo-h:first-child { padding-top: 4px; }
.halo-item { display: flex; align-items: center; gap: 10px; width: 100%; padding: 7px 10px; border: 0; border-radius: 8px;
  background: none; color: inherit; font: inherit; text-align: left; cursor: pointer;
  transition: background-color .15s ease-out; }
.halo-item:hover { background: var(--ghost); }
.halo-item:focus-visible { background: var(--ghost); outline: none; box-shadow: inset 0 0 0 2px var(--series-1); }
.halo-item:active:not(:disabled) { background: var(--well); }
.halo-item:disabled { opacity: .5; cursor: default; }
/* icons are drawn SVGs (window.Icons in halo.js): one 16px box, one stroke; an unknown glyph falls back to text */
.halo-ic { width: 16px; height: 16px; flex: none; display: inline-grid; place-items: center; overflow: hidden;
  font-size: 13px; line-height: 1; text-align: center; color: var(--text-secondary); }
.halo-ic > svg { display: block; width: 16px; height: 16px; }
.halo-item:hover .halo-ic, .halo-item:focus-visible .halo-ic { color: var(--text-primary); }
.halo-l { flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.halo-k { color: var(--text-muted); font-size: 12px; font-variant-numeric: tabular-nums; }
.halo-sep { height: 1px; margin: 5px 10px; background: var(--grid); }
.halo-foot { padding: 8px 10px 4px; font-size: 12px; color: var(--text-muted); }
.halo-note { position: fixed; left: 50%; bottom: 24px; transform: translateX(-50%); z-index: 1001; padding: 10px 16px;
  border-radius: var(--r-block); background: var(--surface-1); color: var(--text-primary); box-shadow: var(--lift);
  font: 500 13px var(--font-ui); }
body.has-demo-strip .halo-note { bottom: 64px; }
@media (prefers-reduced-motion: reduce) { :root:not([data-motion="full"]) { .halo { animation: none; } } }
/* Dark: a sheet over a sheet is the same charcoal, and a shadow barely shows
   on it, so floating things get a faint edge there (light needs none). */
@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) :is(.halo, .halo-note, .demo-menu, .try-panel, .ph-compose, .ph-modal) {
    box-shadow: var(--lift), 0 0 0 1px var(--border); }
}
:root[data-theme="dark"] :is(.halo, .halo-note, .demo-menu, .try-panel, .ph-compose, .ph-modal) {
  box-shadow: var(--lift), 0 0 0 1px var(--border); }

/* The phone tab bar is solid already; asked for more contrast, it gets an edge. */
@media (max-width: 760px) and (prefers-contrast: more) {
  .sidebar { border: 1px solid var(--text-muted); }
}
@media (max-width: 760px) {
  /* One row, scrolled sideways, so the tab bar above it stays reachable. */
  .demo-strip { flex-wrap: nowrap; overflow-x: auto; white-space: nowrap; scrollbar-width: none; }
  .demo-strip .push { margin-left: 12px; }
  body.has-demo-strip .sidebar { bottom: calc(58px + env(safe-area-inset-bottom, 0px)); }
  /* (the strip's 48px is body padding already, so the room stays the same) */
  .wrap:has(> .homepanel:not([hidden])) { padding-bottom: 16px; }
}

/* Android and iPhone share the one Planner tab bar above (2026-11-08: the
   Material 3 and iOS-glass costumes are gone). html.os-android stays set
   by the app for anything that still needs to tell them apart. */

/* Pull to sync (phones): the ring waits under the top bar and drops in as
   the content is pulled; it spins while syncing. */
.pull { display: none; }
@media (max-width: 760px) {
  .topbar { position: relative; }
  .pull {
    display: flex; align-items: center; justify-content: center;
    position: fixed; z-index: 4; left: 0; right: 0; margin: 0 auto;
    top: calc(56px + env(safe-area-inset-top, 0px) - 42px);
    width: 36px; height: 36px; border-radius: 999px; opacity: 0; pointer-events: none;
    background: var(--surface-1); color: var(--text-muted);
    border: 0; box-shadow: var(--lift);
  }
  .pull.ready { color: var(--series-1); }
  .pull.busy svg { animation: spin 0.9s linear infinite; }
  .wrap.springing, .pull.springing { transition: transform 0.4s cubic-bezier(0.2, 0.9, 0.3, 1), opacity 0.3s; }
  @media (prefers-reduced-motion: reduce) { :root:not([data-motion="full"]) {
    .wrap.springing, .pull.springing { transition: opacity 0.2s; }
    .pull.busy svg { animation: none; }
  } }
}

/* Keyboard up (phones): fit above it, no tab bar, no gap. */
@media (max-width: 760px) {
  html.app.kb-open body { height: var(--vvh, 100dvh); }
  html.kb-open .sidebar { display: none; }
  html.kb-open .wrap,
  html.kb-open .wrap:has(> .homepanel:not([hidden])) { padding-bottom: 12px; margin-bottom: 0; }
  /* Typing a note in Home: the notes get all the room. */
  html.kb-open .wrap:has(> .homepanel:not([hidden])) > :is(#banners, #alerts) { display: none; }
}

/* "Meteoro has been updated": a centred card over a dimmed app. */
.update {
  position: fixed; inset: 0; z-index: 1000; display: flex; align-items: center; justify-content: center;
  /* a warm dim of the ground, not a black veil */
  padding: 24px; background: color-mix(in srgb, var(--plane) 72%, transparent);
}
.update-card {
  width: min(340px, 100%); padding: 28px 24px 16px; border-radius: 14px; text-align: center;
  background: var(--surface-1); border: 0; box-shadow: var(--lift);
  display: flex; flex-direction: column; align-items: center; gap: 10px;
}
.update-card img { border-radius: 11px; }
.update-card h2 { margin: 4px 0 0; font-size: 18px; font-weight: 600; }
.update-card p { margin: 0 0 8px; color: var(--text-secondary); font-size: 14px; }
.update-card .primary { width: 100%; padding: 11px; font-size: 15px; }
.update-card .link { border: none; background: none; color: var(--text-secondary); padding: 8px; }
.update-card .link:hover:not(:disabled) { background: var(--well); color: var(--text-primary); }
/* "What's new in 2026.11.2": the release's notes (Epoch), one per line. */
.update-card .whatsnew {
  align-self: stretch; margin: 0 0 8px; padding-left: 20px; text-align: left;
  max-height: 40vh; overflow: auto; color: var(--text-secondary); font-size: 14px; line-height: 1.45;
}
.update-card .whatsnew li + li { margin-top: 4px; }

/* The version icon (Epoch) beside the bell; a red ! when this app is too
   old to save. The card it opens: what this device runs, and for admins
   what the server's channels point at. */
#version-btn { position: relative; }
#version-btn[aria-expanded="true"] { background: var(--well); color: var(--text-primary); }
.version-alert {
  position: absolute; top: 2px; right: 1px; min-width: 14px; height: 14px; padding: 0 3px;
  border-radius: 7px; background: var(--critical); color: var(--surface-1); box-shadow: 0 0 0 2px var(--plane);
  font-size: 10px; font-weight: 700; line-height: 14px; text-align: center;
}
.version-alert[hidden] { display: none; }
.update-card.version-card { width: min(440px, 100%); padding: 18px 20px 16px; align-items: stretch; text-align: left; }
.version-head { display: flex; align-items: center; justify-content: space-between; gap: 8px; }
.version-head h2 { margin: 0; }
.version-state { margin: 0; padding: 8px 10px; border-radius: 8px; background: var(--well);
  color: var(--text-primary); font-size: 14px; }
.version-state.readonly { background: color-mix(in srgb, var(--critical) 14%, var(--surface-1)); color: var(--critical); font-weight: 600; }
.version-state.available { color: var(--text-primary); font-weight: 600; }
.version-card .kv { grid-template-columns: 10.5em 1fr; word-break: break-word; }
.version-sec { display: flex; flex-direction: column; gap: 6px; }
.version-sec h3 { margin: 6px 0 0; font-size: 12px; font-weight: 600; color: var(--text-secondary);
  text-transform: uppercase; letter-spacing: .04em; }
.version-card .whatsnew { margin: 0; }
.version-releases { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: 4px;
  max-height: 22vh; overflow: auto; font-size: 13px; }
.version-releases li { display: grid; grid-template-columns: max-content 1fr; gap: 0 10px; }
.version-releases b { font-weight: 600; font-variant-numeric: tabular-nums; }
.version-releases span { color: var(--text-secondary); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }

/* ------------------------------------- deadlines: kinds and moved dates */

.duelist .what .nm .kind,
.newslist .kind, .coursepage .kind {
  /* a small sentence-case tag: a well chip, no caps, no tracking */
  display: inline-block; vertical-align: 1px; padding: 0 7px; border-radius: var(--r-chip);
  font-size: 11.5px; font-weight: 500; line-height: 18px;
  color: var(--text-secondary); background: var(--well); margin-right: 8px;
}
/* Small word-chips beside the course name: a soft tint of the chip's
   colour carries it, and the word says whose it is. No edges. */
.duelist .moved, .duelist .personal {
  display: inline-block; white-space: nowrap; vertical-align: 1px;
  margin-left: 8px; padding: 0 7px; border-radius: 999px; font-size: 11.5px; font-weight: 600;
}
.duelist .moved {
  color: color-mix(in srgb, var(--serious) 60%, var(--text-primary));
  background: color-mix(in srgb, var(--serious) 12%, var(--surface-1));
}
.duelist .personal { color: var(--lilac-ink); background: var(--lilac); }
/* An exam, whatever the school made it (an assignment, a quiz, an event):
   the serious tint and its word (Zaen, 2026-11-09). */
.duelist .what .nm .kind.exam {
  font-weight: 600;
  color: color-mix(in srgb, var(--serious) 60%, var(--text-primary));
  background: color-mix(in srgb, var(--serious) 12%, var(--surface-1));
}
.duelist .tick { margin-left: 8px; white-space: nowrap; }
.tick.done, .tick.passed { color: var(--good); }
.tick.failed { color: var(--critical); }
.tick.todo { color: var(--text-muted); }
.psec li .tick { display: inline-block; width: 1.2em; font-weight: 700; }
.psec h3 .progress { margin-left: 8px; font-weight: 400; font-size: 12px; color: var(--text-muted); font-variant-numeric: tabular-nums; }
.state.event { --dot: var(--sky-ink); color: var(--sky-ink); background: var(--sky); }

/* ------------------------------------------------------- what changed */

/* Both lists: rows inset in the sheet, separated by space, no rules. */
.changelist, .newslist { list-style: none; margin: 0; padding: 2px 8px 10px; }
.changelist li { padding: 9px 12px; border-radius: var(--r-block); }
.changelist .txt { font-size: 13.5px; line-height: 1.4; }
.changelist li.moved .txt { color: var(--text-primary); font-weight: 500; }
.changelist li.moved .txt::before {
  content: ""; display: inline-block; width: 6px; height: 6px; margin-right: 8px; vertical-align: 2px;
  border-radius: 50%; background: var(--serious);
}
.changelist .meta, .newslist .meta { font-size: 12px; color: var(--text-muted); margin-top: 2px; }

/* ------------------------------------------------- announcements, forums */

.newslist li + li { margin-top: 2px; }
.newslist .head {
  display: block; width: 100%; text-align: left; padding: 10px 12px;
  background: none; border: 0; border-radius: var(--r-block); color: inherit; font: inherit; cursor: pointer;
}
.newslist .head:hover { background: color-mix(in srgb, var(--well) 60%, var(--surface-1)); }
.newslist .head:active { background: var(--well); }
.newslist .head[aria-expanded="true"] { background: none; }
.newslist .top { display: flex; align-items: baseline; gap: 6px; min-width: 0; }
.newslist .subj { font-size: 14px; font-weight: 500; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.newslist .head[aria-expanded="true"] .subj { font-weight: 600; white-space: normal; }
.newslist li.announce .kind { color: color-mix(in srgb, var(--series-1) 70%, var(--text-primary)); background: var(--series-1-soft); }
.newslist .fresh {
  flex: 0 0 auto; font-size: 11px; font-weight: 600; padding: 0 7px; border-radius: 999px;
  color: color-mix(in srgb, var(--good) 60%, var(--text-primary));
  background: color-mix(in srgb, var(--good) 12%, var(--surface-1));
}
.newslist .body { padding: 2px 12px 14px; }
.newslist .atts { list-style: none; margin: 8px 0; padding: 0; display: flex; flex-wrap: wrap; gap: 6px; }
.newslist .atts .linkish { background: var(--well); padding: 4px 10px; border-radius: var(--r-btn); font-size: 12.5px; }
.newslist .atts .linkish:hover:not(:disabled) { background: color-mix(in srgb, var(--text-primary) 7%, var(--well)); }
/* a reply: a quiet well block, not a line down its side */
.newslist .reply { margin-top: 10px; padding: 10px 14px; border-radius: var(--r-block); background: var(--well); }
.newslist .reply .meta { margin: 0 0 4px; }
.newslist .go, .coursepage a { color: var(--series-1); text-underline-offset: 3px; }
.newslist .go { display: inline-block; margin-top: 10px; font-size: 12.5px; font-weight: 500; }

/* What teachers wrote, cleaned on the server: read as text, in Lora. */
.richtext { font-family: var(--font-read); font-size: 15px; line-height: 1.6; overflow-wrap: anywhere; color: var(--text-primary); }
.richtext > :first-child { margin-top: 0; }
.richtext > :last-child { margin-bottom: 0; }
.richtext p { margin: 0 0 .7em; }
.richtext ul, .richtext ol { margin: 0 0 .7em; padding-left: 1.3em; }
.richtext li + li { margin-top: .2em; }
.richtext table { border-collapse: collapse; font-size: 13.5px; }
.richtext td, .richtext th { border-bottom: 1px solid var(--grid); padding: 5px 10px 5px 0; text-align: left; vertical-align: top; }
.richtext th { font-family: var(--font-ui); font-size: 12px; font-weight: 600; color: var(--text-secondary); }
.richtext a { color: var(--series-1); text-underline-offset: 3px; }
.richtext img { max-width: 100%; height: auto; border-radius: var(--r-field); display: block; margin: 8px 0; }

/* ------------------------------------------------------------- grades */

/* Each course is a quiet well block on the sheet; grades in a column. */
.gradegrid { display: grid; gap: 12px; grid-template-columns: repeat(auto-fill, minmax(280px, 1fr)); padding: 8px var(--card-pad) var(--card-pad); }
.gcourse { background: var(--well); border-radius: var(--r-block); padding: 12px 16px 10px; }
.gcourse h3 { display: flex; align-items: baseline; gap: 8px; margin: 0 0 6px; font-size: 14px; font-weight: 600; line-height: 1.35; }
.gcourse h3 .total { margin-left: auto; color: var(--text-primary); font-weight: 600; font-variant-numeric: tabular-nums; white-space: nowrap; }
.gcourse ul { list-style: none; margin: 0; padding: 0; }
.gcourse li { display: flex; gap: 12px; padding: 5px 0; font-size: 13.5px; color: var(--text-secondary); }
.gcourse .nm { flex: 1 1 auto; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.gcourse .nm.hasfb { text-decoration: underline dotted var(--text-muted); text-underline-offset: 3px; cursor: help; }
.gcourse .g { flex: 0 0 auto; font-variant-numeric: tabular-nums; font-weight: 600; color: var(--text-primary); }

/* -------------------------------------------------------- course page */

.coursepage { padding: 4px var(--card-pad) 16px; max-height: 520px; overflow-y: auto; }
.psec { padding: 9px 0; }
.psec + .psec { margin-top: 2px; }
.psec h3 { margin: 0 0 6px; font-size: 14px; font-weight: 600; }
.psec h3:last-child { margin-bottom: 0; }
.psec ul:empty { display: none; }
.psec ul { list-style: none; margin: 6px 0 0; padding: 0; }
.psec li { padding: 5px 0; font-size: 13.5px; }
.psec li .host { margin-left: 6px; font-size: 12px; color: var(--text-muted); }
.psec li.label { padding: 6px 0; }
/* the demo strip sits at the bottom: the viewer ends above it */
body.has-demo-strip .viewer { bottom: 48px; }
.boot-detail {
  display: block; width: 100%; max-height: 90px; overflow: auto; text-align: left;
  font: 11px/1.4 ui-monospace, Menlo, monospace; color: var(--text-muted);
  background: var(--well); border: 0; border-radius: var(--r-field); padding: 8px 10px; word-break: break-word;
  transition: background-color .15s ease-out;
}
.boot-detail:hover { background: color-mix(in srgb, var(--text-primary) 6%, var(--well)); }

/* ------------------------------------------------------------ Kepler */
/* Roles, step-up and the history (meteoro/kepler.py). */

/* Settings by level: you, then this server. */
/* A plain heading on the ground for each level, more room above than below. */
.settings-level { margin: 36px 4px 4px; font-size: 16px; font-weight: 650; color: var(--text-primary);
  letter-spacing: -0.005em; }
p.settings-level-hint { margin: 0 4px 14px; }

.role-pill { display: inline-block; margin-right: 8px; padding: 2px 9px; border-radius: 999px;
  font-size: 11px; font-weight: 600; color: var(--text-secondary);
  background: var(--well); border: 0; }
.signin-row .role-pill { background: var(--raised); }
.role-pill.role-owner, .signin-row .role-pill.role-owner { color: var(--series-1); background: var(--series-1-soft); }
.role-pill.role-admin { color: var(--text-primary); }
.role-select { font: inherit; font-size: 13px; padding: 6px 10px; border-radius: var(--r-field);
  border: 0; background: var(--raised); color: var(--text-primary); cursor: pointer; }
.role-select:focus-visible { outline: none; box-shadow: var(--focus); }

#inv-form select, #inv-form input[type="date"] { width: 100%; font: inherit; padding: 10px 12px; border-radius: var(--r-field);
  border: 0; background: var(--well); color: var(--text-primary); }
#inv-form select:focus-visible, #inv-form input[type="date"]:focus-visible { outline: none; box-shadow: var(--focus); }
#inv-form .codebox { margin-top: 14px; }
.inv-code { color: var(--text-secondary); }
.inv-code b { font: 700 15px ui-monospace, SFMono-Regular, Menlo, monospace; letter-spacing: .08em;
  color: var(--text-primary); user-select: all; }
.invitelist { padding-top: 0; padding-bottom: 16px; }
.invite.used, .invite.ended, .invite.revoked { opacity: .62; }

/* What admins did: your own account (Account card) and the whole log. */
.notices { margin-top: 18px; display: flex; flex-direction: column; gap: 8px; }
.noticelist { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: 6px; }
.notice { display: flex; flex-wrap: wrap; gap: 2px 12px; padding: 10px 14px; border-radius: var(--r-block);
  border: 0; background: var(--well); font-size: 13px; }
.notice .what { flex: 1 1 260px; color: var(--text-primary); }
.notice .when { color: var(--text-muted); font-size: 12px; font-variant-numeric: tabular-nums; }
/* New since you last looked: a warm tint and a small dot, not an edge. */
.notice.new { background: color-mix(in srgb, var(--warning) 14%, var(--surface-1)); }
.notice.new .what::before { content: ""; display: inline-block; width: 6px; height: 6px; margin: 0 8px 1px 0;
  border-radius: 50%; background: var(--warning); vertical-align: middle; }

/* "Confirm it's you": a small sheet over everything. */
.stepup-scrim { position: fixed; inset: 0; z-index: 1100; display: grid; place-items: center; padding: 16px;
  /* a warm dim of the ground, like "Meteoro has been updated" */
  background: color-mix(in srgb, var(--plane) 72%, transparent); }
.stepup-scrim[hidden] { display: none; }
.stepup { width: min(400px, 100%); padding: 24px 24px 20px; border-radius: var(--r-sheet);
  background: var(--surface-1); border: 0; box-shadow: var(--lift);
  display: flex; flex-direction: column; gap: 12px; }
.stepup h2 { margin: 0; font-size: 18px; }
.stepup p.hint { margin: 0; }
.stepup .field { margin: 0; }
.stepup .field input { width: 100%; }
.stepup .result { margin: 0; }

/* While unlocked: a pill at the top, counting down. */
.kepler-strip { position: fixed; z-index: 60; top: calc(10px + env(safe-area-inset-top, 0px)); left: 50%;
  transform: translateX(-50%); display: flex; align-items: center; gap: 10px; max-width: calc(100vw - 32px);
  padding: 5px 6px 5px 12px; border-radius: 999px; font: 600 12px var(--font-ui); white-space: nowrap;
  color: var(--text-primary); background: color-mix(in srgb, var(--warning) 16%, var(--surface-1));
  border: 0; box-shadow: var(--lift); }
.kepler-strip[hidden] { display: none; }
@media (max-width: 620px) {
  .kepler-strip { top: calc(64px + env(safe-area-inset-top, 0px)); }
  /* the pill floats under the top bar: the page moves down so it covers nothing */
  body:has(#kepler-strip:not([hidden])) .wrap { padding-top: 60px; }
}
#kepler-strip-text { overflow: hidden; text-overflow: ellipsis; font-variant-numeric: tabular-nums; }
.kepler-dot { flex: 0 0 auto; width: 8px; height: 8px; border-radius: 50%; background: var(--warning); }
.kepler-strip button { padding: 3px 11px; font-size: 12px; border-radius: 999px; background: var(--surface-1); }
.kepler-strip button:hover:not(:disabled) { background: color-mix(in srgb, var(--text-primary) 6%, var(--surface-1)); }
.copy-scratch { position: fixed; top: 0; left: -9999px; opacity: 0; }

/* ------------------------------------------- connect an app (web/connect/) */

.connect-h { margin: 22px 0 8px; font-size: 13.5px; font-weight: 650; color: var(--text-primary); }
.connect-list { margin: 0; padding: 0; list-style: none; }
.connect-list { display: flex; flex-direction: column; gap: 4px; }
.connect-list li {
  position: relative; padding: 3px 0 3px 24px; font: 14px/1.5 var(--font-read); color: var(--text-primary);
}
.connect-list li::before { content: "✓"; position: absolute; left: 2px; color: var(--good); font-weight: 700; }
.connect-list.no li::before { content: "✕"; color: var(--text-muted); }
.connect-acts { display: flex; justify-content: flex-end; align-items: center; gap: 10px; margin-top: 20px; }
.apps { margin-top: 18px; display: flex; flex-direction: column; gap: 6px; }
.apps .hint { margin: 0; overflow-wrap: anywhere; }

/* ---------------------------------------------- Nebula's directory */
/* Your groups in the sidebar (computers), the Groups tab, and "where
   you're enrolled" in Settings (meteoro/nebula/directory.py). */
.side-badge { margin-left: auto; min-width: 18px; padding: 0 6px; border-radius: 999px;
  font-size: 11px; font-weight: 650; line-height: 18px; text-align: center;
  color: var(--on-series-1); background: var(--series-1); }
.sidegroups { list-style: none; margin: 2px 0 8px; padding: 0 0 0 30px; display: flex; flex-direction: column; gap: 2px; }
.sidegroups:empty { display: none; }
.sidegroup { width: 100%; display: block; text-align: left; border: none; background: none;
  padding: 5px 10px; font-size: 13px; color: var(--text-secondary); white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
  border-radius: var(--r-btn); transition: background-color .15s ease-out, color .15s ease-out; }
.sidegroup::before { content: ""; display: inline-block; width: 5px; height: 5px; margin: 0 9px 2px 0;
  vertical-align: middle; border-radius: 50%; background: var(--baseline); transition: background-color .15s ease-out; }
.sidegroup:hover { background: var(--ghost); color: var(--text-primary); }
.sidegroup.on, .sidegroup.on:hover { color: var(--text-primary); font-weight: 550; background: var(--surface-1); box-shadow: var(--soft); }
.sidegroup.on::before { background: var(--series-1); }
@media (max-width: 760px) {
  .sidegroups { display: none; }
  .side-badge { position: absolute; top: 4px; right: 8px; margin: 0; min-width: 16px; line-height: 16px; font-size: 10px; }
  /* Labels are hidden on phones (they're spans); the badges are spans too, but stay. */
  .sidenav .tab .side-badge { width: auto; height: auto; overflow: visible; clip: auto; clip-path: none; white-space: nowrap; }
  .sidenav .tab { position: relative; }
}

.grp-pad { margin: 4px var(--card-pad) 0; }
/* Your groups are quiet well rows; the open one turns soft ink blue. */
.grp-row { cursor: pointer; transition: background-color .15s ease-out; }
.grp-row:hover { background: color-mix(in srgb, var(--text-primary) 5%, var(--well)); }
.grp-row:active { background: color-mix(in srgb, var(--text-primary) 8%, var(--well)); }
.grp-row.on { background: var(--series-1-soft); }
.grp-row.on .k { color: var(--text-primary); }
.grp-row.ended { opacity: .6; }
.grp-about { margin: 0 0 16px; color: var(--text-primary); max-width: 68ch; }
.grp-sub { display: block; margin: 24px 0 10px; font-size: 13px; font-weight: 600; }
.grp-acts { display: inline-flex; flex-wrap: wrap; gap: 6px; margin-left: auto; }
.grp-acts button { padding: 4px 10px; font-size: 12px; }
.notice .grp-acts button:not(.primary):not(.danger) { background: var(--raised); }
.grp-invite { display: flex; align-items: flex-end; gap: 10px; margin-top: 16px; flex-wrap: wrap; }
.grp-invite .field { flex: 1 1 220px; margin: 0; }
.grp-invite .field input { width: 100%; }
.grp-list-row { margin-top: 16px; align-items: end; }
.grp-end { display: flex; align-items: flex-end; }
#grp-propose-unit, #enrol-unit, #grp-new input[type="date"] { width: 100%; font: inherit; padding: 9px 12px;
  border-radius: var(--r-field); border: 0; background: var(--well); color: var(--text-primary); }
#grp-propose-unit:focus, #enrol-unit:focus, #grp-new input[type="date"]:focus { outline: none; box-shadow: var(--focus); }
.grp-links { border: none; padding: 0; margin: 0 0 16px; display: flex; flex-direction: column; gap: 6px; }
.grp-links legend { font-size: 13px; font-weight: 600; margin-bottom: 8px; padding: 0; }
.grp-link { display: flex; align-items: center; gap: 8px; font-size: 13px; color: var(--text-secondary); cursor: pointer; }
.grp-link:hover { color: var(--text-primary); }
.enrol .when { display: inline-flex; gap: 6px; align-items: center; }
/* What only the platform shows (not yet confirmed by you): a lighter block, not a dashed edge. */
.enrol.seen { background: color-mix(in srgb, var(--well) 50%, var(--surface-1)); }
.enrol-declare { margin-top: 16px; align-items: end; grid-template-columns: 1fr 1fr auto; }
@media (max-width: 620px) { .enrol-declare { grid-template-columns: 1fr; } }

/* ------------------------------------------------ things to try, anywhere */
/* The design checklist following you into the app and Nebula (web/try.js),
   and the highlight Show puts on what it points at. */
.try-panel { position: fixed; z-index: 70; width: min(360px, calc(100vw - 32px));
  max-height: min(70vh, 560px); overflow: auto; padding: 12px 16px 14px; border-radius: var(--r-block);
  background: var(--surface-1); border: 0; box-shadow: var(--lift);
  font-size: 13px; line-height: 1.45; color: var(--text-primary); }
.try-panel.closed { padding-bottom: 12px; }
.try-panel-head { display: flex; align-items: center; gap: 6px; }
.try-panel-title { flex: 1 1 auto; text-align: left; border: none; background: none; padding: 2px 0;
  font: 600 13px/1.35 var(--font-ui); color: var(--text-primary); cursor: pointer; }
.try-panel-link { font-size: 12px; color: var(--series-1); text-decoration: none; }
.try-panel-link:hover { text-decoration: underline; text-underline-offset: 3px; }
.try-panel-x, .try-panel-grip { display: inline-grid; place-items: center; width: 26px; height: 26px; flex: none;
  border: none; border-radius: 8px; background: none; padding: 0; line-height: 1; color: var(--text-muted); cursor: pointer;
  transition: background-color .15s ease-out, color .15s ease-out; }
.try-panel-x { font-size: 18px; }
.try-panel-x:hover, .try-panel-grip:hover { background: var(--ghost); color: var(--text-primary); }
.try-panel-item { padding: 12px 0 10px; }
.try-panel-item + .try-panel-item { margin-top: 6px; }
.try-panel-item:first-of-type { margin-top: 6px; }
.try-panel-item p { margin: 0 0 8px; }
.try-panel-item textarea { width: 100%; margin: 6px 0; font: inherit; padding: 8px 10px; border-radius: var(--r-field);
  border: 0; background: var(--well); color: var(--text-primary); }
.try-panel-item textarea:focus { outline: none; box-shadow: var(--focus); }
.try-panel-acts { display: flex; flex-wrap: wrap; gap: 6px; }
.try-panel-acts > *, .try-panel-item > button { font: inherit; font-size: 12px; font-weight: 500; padding: 4px 11px;
  border-radius: 8px; border: 1px solid transparent; background: var(--well); color: var(--text-primary);
  cursor: pointer; text-decoration: none; transition: background-color .15s ease-out; }
.try-panel-acts > *:hover:not(:disabled), .try-panel-item > button:hover:not(:disabled) {
  background: color-mix(in srgb, var(--text-primary) 7%, var(--well)); }
.try-panel-acts > *:active:not(:disabled), .try-panel-item > button:active:not(:disabled) {
  background: color-mix(in srgb, var(--text-primary) 11%, var(--well)); }
.try-panel-acts > *:focus-visible, .try-panel-item > button:focus-visible { outline: none; box-shadow: var(--focus); }
.try-panel .try-go { color: var(--series-1); font-weight: 600; }
.try-panel .try-ok { background: var(--series-1); color: var(--on-series-1); font-weight: 600; }
.try-panel .try-ok:hover:not(:disabled) { background: color-mix(in srgb, var(--series-1) 86%, var(--text-primary)); }
.try-panel .try-ok:active:not(:disabled) { background: color-mix(in srgb, var(--series-1) 78%, var(--text-primary)); }
.try-panel button:disabled { opacity: .5; cursor: default; }
.try-panel-all, .try-panel-more, .try-panel-msg { margin: 10px 0 0; color: var(--text-muted); }
.try-flash { outline: 2px solid var(--warning); outline-offset: 4px; border-radius: 8px; transition: outline-color .2s ease-out; }
/* Its corner: dragged there by the head (it snaps to the nearest one), or
   the grip sends it to the other side. Remembered on this device. */
.try-panel.at-bl { left: 16px; bottom: 16px; }
.try-panel.at-br { right: 16px; bottom: 16px; }
.try-panel.at-tl { left: 16px; top: 16px; }
.try-panel.at-tr { right: 16px; top: 16px; }
/* In the app on computers, the left corners start past the sidebar, so the
   panel never covers its tabs or the feedback toggle above Settings. */
@media (min-width: 761px) {
  body:has(.shell:not([hidden]) > .sidebar) .try-panel.at-bl, body:has(.shell:not([hidden]) > .sidebar) .try-panel.at-tl { left: calc(228px + 16px); }
}
.try-panel-head { touch-action: none; cursor: grab; }
.try-panel.dragging { cursor: grabbing; box-shadow: var(--lift), var(--lift); }
.try-panel.dragging .try-panel-head { cursor: grabbing; }
.try-panel-grip { font-size: 14px; cursor: grab; }
/* the demo strip owns the bottom 48px: the low corners sit above it */
body.has-demo-strip .try-panel.at-bl, body.has-demo-strip .try-panel.at-br { bottom: 64px; }
@media (max-width: 760px) {
  /* Phones: full width; it sits at the top or the bottom. */
  .try-panel, .try-panel[class*="at-"] { left: 16px; right: 16px; width: auto; }
  .try-panel.at-bl, .try-panel.at-br { top: auto; bottom: calc(76px + env(safe-area-inset-bottom, 0px)); }
  .try-panel.at-tl, .try-panel.at-tr { bottom: auto; top: calc(12px + env(safe-area-inset-top, 0px)); }
  body.has-demo-strip .try-panel.at-bl, body.has-demo-strip .try-panel.at-br { bottom: calc(124px + env(safe-area-inset-bottom, 0px)); }
}
@media (prefers-reduced-motion: reduce) { :root:not([data-motion="full"]) { .try-flash { transition: none; } } }

/* Perihelion (web/perihelion.js): feedback that comes back to its place.
   Dots: grey ring = sent, desaturated green = fixed (check it), yellow = a
   choice for you, blue = what it was about is gone. */
.ph-dot { display: inline-block; flex: 0 0 auto; width: 12px; height: 12px; border-radius: 50%;
  border: 2px solid transparent; vertical-align: -1px; }
.ph-dot.ph-sent { border-color: var(--text-muted); background: transparent; }
.ph-dot.ph-check { background: color-mix(in srgb, var(--good) 48%, var(--text-muted)); }
.ph-dot.ph-choice { background: var(--warning); }
.ph-dot.ph-gone { background: var(--series-1); }
/* OK'd: a ✓ for one round, then gone */
.ph-dot.ph-ok { background: transparent; border-color: var(--text-muted); position: relative; }
.ph-dot.ph-ok::after { content: "✓"; position: absolute; inset: -4px 0 0 0; pointer-events: none; font-size: 10px; line-height: 14px;
  text-align: center; color: var(--text-muted); }
.ph-dot.ph-on-page.ph-ok { opacity: .7; }
.try-panel-item.done { opacity: .55; }
.ph-layer { position: fixed; inset: 0; pointer-events: none; z-index: 66; }
.ph-dot.ph-on-page { position: fixed; width: 14px; height: 14px; padding: 0; cursor: pointer; pointer-events: auto;
  box-shadow: 0 0 0 2px var(--surface-1), var(--soft); }
.ph-dot.ph-on-page:hover, .ph-dot.ph-on-page:focus-visible { transform: scale(1.25); outline: none; }
.ph-dot.ph-on-page:focus-visible { box-shadow: 0 0 0 2px var(--surface-1), 0 0 0 4px var(--series-1); }
.ph-dot.ph-on-page.ph-sent { background: var(--surface-1); }
@media (prefers-reduced-motion: no-preference) {
  .ph-dot.ph-on-page { transition: transform .15s ease-out; }
  .ph-dot.ph-on-page.ph-check, .ph-dot.ph-on-page.ph-choice { animation: ph-breathe 2.6s ease-in-out 2; }
  @keyframes ph-breathe { 50% { transform: scale(1.2); } }
}
/* what a comment is about, while you write it / pick it */
.ph-target, .ph-hover { outline: 2px solid var(--series-1); outline-offset: 2px; border-radius: 4px; }
html.ph-picking, html.ph-picking * { cursor: crosshair !important; }
.ph-compose { position: fixed; z-index: 80; width: min(340px, calc(100vw - 16px)); padding: 14px 16px; border-radius: var(--r-block);
  background: var(--surface-1); border: 0; box-shadow: var(--lift);
  font-size: 13px; line-height: 1.45; color: var(--text-primary); }
.ph-compose-name, .ph-where { margin: 0 0 8px; font-size: 12px; color: var(--text-secondary); word-break: break-word; }
.ph-compose textarea, .ph-modal textarea { width: 100%; box-sizing: border-box; font: inherit; font-size: 13px; padding: 8px 10px;
  border-radius: var(--r-field); border: 0; background: var(--well); color: var(--text-primary); resize: vertical; }
.ph-compose textarea:focus, .ph-modal textarea:focus { outline: none; box-shadow: var(--focus); }
.ph-acts { display: flex; flex-wrap: wrap; justify-content: flex-end; gap: 6px; margin-top: 10px; }
.ph-acts > button, .ph-acts > a, .ph-option { font: inherit; font-size: 13px; font-weight: 500; padding: 6px 12px;
  border-radius: var(--r-btn); border: 1px solid transparent; background: var(--well); color: var(--text-primary);
  cursor: pointer; text-decoration: none; transition: background-color .15s ease-out; }
.ph-acts > button:hover:not(:disabled), .ph-acts > a:hover, .ph-option:hover {
  background: color-mix(in srgb, var(--text-primary) 7%, var(--well)); }
.ph-acts > button:active:not(:disabled), .ph-acts > a:active, .ph-option:active {
  background: color-mix(in srgb, var(--text-primary) 11%, var(--well)); }
.ph-acts > button:focus-visible, .ph-acts > a:focus-visible, .ph-option:focus-visible { outline: none; box-shadow: var(--focus); }
.ph-acts > .ph-primary { background: var(--series-1); color: var(--on-series-1); font-weight: 600; }
.ph-acts > .ph-primary:hover:not(:disabled) { background: color-mix(in srgb, var(--series-1) 86%, var(--text-primary)); }
.ph-acts > .ph-primary:active:not(:disabled) { background: color-mix(in srgb, var(--series-1) 78%, var(--text-primary)); }
.ph-acts > button:disabled { opacity: .5; cursor: default; }
.ph-acts > .ph-send { color: var(--series-1); }
.ph-acts > .ph-go { color: var(--series-1); font-weight: 600; margin-right: auto; }
/* the modal a dot opens */
.ph-modal { width: min(460px, calc(100vw - 32px)); max-height: min(80vh, 640px); padding: 18px 20px; border-radius: var(--r-sheet);
  border: 0; background: var(--surface-1); color: var(--text-primary); box-shadow: var(--lift);
  font-size: 13px; line-height: 1.45; }
/* a warm dim of the ground, like "Confirm it's you" */
.ph-modal::backdrop { background: color-mix(in srgb, var(--plane) 72%, transparent); }
.ph-modal-head { display: flex; align-items: center; gap: 8px; }
.ph-modal-head h3 { flex: 1 1 auto; margin: 0; font: 600 15px/1.35 var(--font-ui); }
.ph-x { display: inline-grid; place-items: center; width: 28px; height: 28px; flex: none; border: none; border-radius: 8px;
  background: none; font-size: 20px; line-height: 1; padding: 0; color: var(--text-muted); cursor: pointer;
  transition: background-color .15s ease-out, color .15s ease-out; }
.ph-x:hover { background: var(--ghost); color: var(--text-primary); }
.ph-where { margin-top: 4px; }
.ph-talk { display: grid; gap: 8px; margin: 8px 0 12px; }
/* read status under a thread, like a chat's ticks (perihelion.js receipt) */
.ph-receipt { display: flex; align-items: center; flex-wrap: wrap; gap: 0 6px; margin: -4px 0 12px; justify-content: flex-end;
  font-size: 12px; color: var(--text-muted); }
.ph-receipt svg { flex: none; }
.ph-receipt.ph-r2 svg, .ph-receipt.ph-r3 svg { color: var(--series-1); }
.ph-receipt.ph-r3 { color: var(--text-secondary); }
.ph-receipt.ph-r4 svg { color: var(--good); }
.ph-receipt-at { color: var(--text-muted); }
.ph-receipt-by { color: var(--text-secondary); text-decoration: underline; text-underline-offset: 2px; }
.ph-receipt-by:hover { color: var(--text-primary); }
.ph-msg { padding: 10px 12px; border-radius: var(--r-field); background: var(--well); }
.ph-msg.ph-by-agent { background: var(--series-1-soft); }
.ph-msg small { display: block; color: var(--text-muted); font-size: 11px; margin-bottom: 2px; }
.ph-msg p { margin: 0; white-space: pre-wrap; }
.ph-edit { float: right; margin-top: -2px; border: none; background: none; padding: 0 2px; font: inherit; font-size: 12px;
  color: var(--series-1); cursor: pointer; }
.ph-msg textarea { margin-top: 4px; }
.ph-chip { display: inline-block; margin: 2px 0 4px; padding: 2px 9px; border-radius: 999px; font-size: 12px; font-weight: 500;
  background: var(--butter); color: var(--butter-ink); }
.ph-trail { margin: 0 0 10px; color: var(--text-secondary); }
.ph-trail summary { cursor: pointer; font-size: 12px; }
.ph-trail ol { margin: 6px 0 0; padding-left: 18px; font-size: 12px; }
.ph-issue { display: inline-block; margin: 0 0 10px; font-size: 12px; color: var(--series-1); }
.ph-options { display: flex; flex-wrap: wrap; gap: 6px; margin: 0 0 8px; }
.ph-option[aria-pressed="true"], .ph-option[aria-pressed="true"]:hover { background: var(--butter); color: var(--butter-ink); font-weight: 600; }
/* the toggle above Settings, and the link to the agents' issues */
.ph-side { display: flex; align-items: center; gap: 2px; margin-top: auto; }
.ph-side[hidden] { display: none; }
.ph-side .ph-tab { flex: 1 1 auto; }
.ph-side + .tab.end { margin-top: 0; }
.ph-side .ph-tab[aria-pressed="false"] { color: var(--text-muted); }
.ph-side .ph-tab[aria-pressed="true"] svg circle { fill: color-mix(in srgb, var(--good) 48%, var(--text-muted)); }
.ph-issues { padding: 6px 9px; border-radius: 8px; color: var(--text-muted); text-decoration: none; font-size: 13px;
  transition: background-color .15s ease-out, color .15s ease-out; }
.ph-issues:hover { background: var(--ghost); color: var(--text-primary); }
/* the panel's sources (web/try.js) */
.try-panel-h { margin: 12px 0 0; font: 600 12px var(--font-ui); color: var(--text-secondary); }
.try-panel-extra { display: flex; gap: 6px; margin-top: 10px; }
.try-panel-extra > button { font: inherit; font-size: 12px; font-weight: 500; padding: 4px 10px; border-radius: 8px;
  border: 0; background: none; color: var(--text-secondary); cursor: pointer; transition: background-color .15s ease-out; }
.try-panel-extra > button:hover { background: var(--ghost); color: var(--text-primary); }
.try-panel-extra > button:focus-visible { outline: none; box-shadow: var(--focus); }
.try-panel-open { display: flex; align-items: baseline; gap: 6px; width: 100%; margin: 0 0 4px; padding: 0; border: none;
  background: none; font: inherit; text-align: left; color: var(--text-primary); cursor: pointer; }
.try-panel-open:hover { text-decoration: underline; text-underline-offset: 3px; }
.try-panel .try-panel-item > .try-panel-open,
.try-panel .try-panel-item > .try-panel-open:hover, .try-panel .try-panel-item > .try-panel-open:active {
  border: none; padding: 0; border-radius: 0; background: none; font-size: 13px; line-height: 1.4; }
.try-panel-sub { margin: 0 0 8px !important; font-size: 12px; color: var(--text-secondary); }
.try-panel.empty { padding-bottom: 12px; }
.try-panel-mini { border: none; background: none; padding: 2px 4px; font: inherit; font-size: 12px;
  color: var(--series-1); cursor: pointer; }
/* Phones: no room in the bottom bar; the toggle is the orbit in the top bar. */
.ph-top { position: relative; }
.ph-top[hidden] { display: none !important; }
.ph-top[aria-pressed="false"] { color: var(--text-muted); }
.ph-top[aria-pressed="true"] svg circle { fill: color-mix(in srgb, var(--good) 48%, var(--text-muted)); }
.ph-top .ph-count { position: absolute; top: -4px; right: -4px; min-width: 16px; padding: 0 4px; border-radius: 999px;
  background: var(--warning); color: var(--text-primary); font-size: 10px; font-weight: 700; line-height: 16px;
  text-align: center; box-shadow: 0 0 0 2px var(--plane); }
@media (prefers-color-scheme: dark) { :root:not([data-theme="light"]) .ph-top .ph-count { color: var(--surface-1); } }
:root[data-theme="dark"] .ph-top .ph-count { color: var(--surface-1); }
.ph-top .ph-count[hidden] { display: none; }
@media (max-width: 760px) {
  .sidebar .ph-side { display: none !important; }
}

/* ------------------------------- your word: done / skipped, beside the school */

/* The round tick: done in Meteoro (the school isn't told). Empty, a soft
   ring; done, a calm green disc; skipped, a muted one with a dash. */
.mine {
  flex: 0 0 auto; width: 22px; height: 22px; padding: 0; border-radius: 50%;
  display: inline-flex; align-items: center; justify-content: center;
  border: 1.5px solid var(--baseline); background: transparent; cursor: pointer;
  color: transparent; font-size: 12px; font-weight: 700; line-height: 1;
  transition: background-color 0.15s ease-out, border-color 0.15s ease-out, color 0.15s ease-out;
}
button.mine:hover:not(:disabled) { border-color: var(--good); color: var(--good); background: color-mix(in srgb, var(--good) 10%, var(--surface-1)); }
.mine:focus-visible { outline: none; box-shadow: var(--focus); }
.mine.on.done, button.mine.on.done:hover:not(:disabled) { background: var(--good); border-color: var(--good); color: var(--surface-1); }
.mine.on.skipped, button.mine.on.skipped:hover:not(:disabled) { background: var(--text-muted); border-color: var(--text-muted); color: var(--surface-1); }
.psec li .mine { width: 18px; height: 18px; font-size: 10px; margin-right: 8px; vertical-align: middle; }
.duelist li.mine-done .what .nm { color: var(--text-muted); }
.duelist li.mine-done .what .nm-t {
  text-decoration: line-through;
  text-decoration-color: color-mix(in srgb, var(--text-muted) 70%, transparent);
}
.mine svg { width: 12px; height: 12px; }
.psec li .mine svg { width: 10px; height: 10px; }

/* Crossing a task off (the one authored moment, DESIGN.md Motion): the disc
   fills, the check draws like a pen stroke, then the title is struck through
   left to right and greys. Only the row you just ticked (li.just-done). */
.duelist li.just-done .mine.on { animation: pl-tick var(--t-state) var(--ease-out) both; }
.duelist li.just-done .mine.on svg path {
  stroke-dasharray: 14; stroke-dashoffset: 14;
  animation: pl-draw 260ms 120ms var(--ease-out) forwards;
}
.duelist li.just-done .what .nm { animation: pl-mute var(--t-move) 160ms var(--ease-out) both; }
.duelist li.just-done .what .nm-t {
  text-decoration-color: transparent;
  background: linear-gradient(currentColor, currentColor) no-repeat 0 58% / 0% 1.5px;
  -webkit-box-decoration-break: clone; box-decoration-break: clone;
  animation: pl-strike var(--t-move) 200ms var(--ease-out) forwards;
}
@keyframes pl-tick { from { transform: scale(0.7); background-color: transparent; border-color: var(--baseline); } }
@keyframes pl-draw { to { stroke-dashoffset: 0; } }
@keyframes pl-strike { to { background-size: 100% 1.5px; } }
@keyframes pl-mute { from { color: var(--text-primary); } }

/* Arriving: a page, the Classes sidebar, a sheet. Content is visible by
   default; the animation only plays when it appears. */
.tabpanel:not([hidden]) { animation: pl-in var(--t-state) var(--ease-out); }
.pt-app:not(.pt-side-closed) .pt-side { animation: pl-side var(--t-move) var(--ease-out); }
.stepup-scrim:not([hidden]) { animation: pl-fade var(--t-state) var(--ease-out); }
.stepup-scrim:not([hidden]) .stepup, .ph-modal[open], .update-card { animation: pl-sheet var(--t-move) var(--ease-out); }
@keyframes pl-in { from { opacity: 0; transform: translateY(4px); } }
@keyframes pl-side { from { opacity: 0; transform: translateX(16px); } }
@keyframes pl-fade { from { opacity: 0; } }
@keyframes pl-sheet { from { opacity: 0; transform: translateY(8px) scale(0.98); } }

/* Pressing: a small give, then back. */
:where(button.primary, button.danger, button.ghost, .icon-btn, .viewtoggle button, .pt-side-btn, .pt-tab, button:not([class])) {
  transition-property: background-color, color, box-shadow, border-color, transform;
}
:where(button.primary, button.danger, button.ghost, .icon-btn, .viewtoggle button, .pt-side-btn, .pt-tab, button:not([class])):active:not(:disabled) {
  transform: scale(0.97); transition-duration: var(--t-quick);
}

/* "You: redo" - your word, beside the school's state (which stays). Your
   word is a lilac tint that starts with "You:"; a clash with the school turns it warm. */
.duelist .yours {
  display: inline-block; white-space: nowrap; vertical-align: 1px; margin-left: 8px; padding: 0 7px;
  border-radius: 999px; font-size: 11.5px; font-weight: 600;
  color: var(--lilac-ink); background: var(--lilac);
}
.duelist .yours.conflict {
  color: color-mix(in srgb, var(--serious) 60%, var(--text-primary));
  background: color-mix(in srgb, var(--serious) 12%, var(--surface-1));
}
.duelist .yours.done, .duelist .yours.skipped {
  color: color-mix(in srgb, var(--good) 60%, var(--text-primary));
  background: color-mix(in srgb, var(--good) 12%, var(--surface-1));
}
.duelist .yours.done.conflict, .duelist .yours.skipped.conflict {
  color: color-mix(in srgb, var(--serious) 60%, var(--text-primary));
  background: color-mix(in srgb, var(--serious) 12%, var(--surface-1));
}
.state.own { --dot: var(--lilac-ink); color: var(--lilac-ink); background: var(--lilac); }
.psec li.with-mine { display: flex; align-items: flex-start; gap: 0; }
.psec li.with-mine .mine { margin-top: 3px; }
.psec li.with-mine .label-body { flex: 1 1 auto; min-width: 0; }

/* A tab with nothing in it yet says so. */
.tab-empty { text-align: center; padding: 56px 16px; color: var(--text-muted); font-size: 14px; }

/* A Due row is a task: the whole row opens it (Polaris); "…" has the rest. */
.duelist li.task { cursor: pointer; }
.duelist li.task:active { background: var(--well); }
.duelist li.task:focus-visible { outline: 2px solid var(--series-1); outline-offset: -2px; }
.duelist .more {
  flex: 0 0 auto; width: 32px; height: 30px; padding: 0; border-radius: var(--r-btn);
  border: 0; background: transparent; color: var(--text-muted);
  font-size: 16px; line-height: 1; cursor: pointer;
}
.duelist .more:hover:not(:disabled) { background: var(--well); color: var(--text-primary); }
.duelist li:hover .more { color: var(--text-secondary); }
/* The circle on a task is a status, not a button. */
.mine.mark { cursor: default; }
.mine.mark:hover { border-color: var(--baseline); color: transparent; }
.mine.mark.on:hover { color: var(--surface-1); }
.mine.mark.on.done:hover { border-color: var(--good); }
.mine.mark.on.skipped:hover { border-color: var(--text-muted); }
.mine.mark.blank { visibility: hidden; }
.psec li .mine.mark { width: 18px; height: 18px; font-size: 10px; margin-right: 8px; vertical-align: middle; }

/* Halo's Alt preview: what an Alt + right-click would take (web/halo.js) */
.halo-pick { outline: 2px solid var(--series-1); outline-offset: 2px; border-radius: 4px; }

/* ------------------------------------------------ notification center */
/* A sheet from the right edge over a warm dim (like "Confirm it's you"). */
.notif-scrim { position: fixed; inset: 0; z-index: 60;
  background: color-mix(in srgb, var(--plane) 55%, transparent); }
.notif-scrim[hidden] { display: none; }
.notif {
  position: fixed; z-index: 61; top: 0; right: 0; bottom: 0;
  width: min(380px, 100vw); overflow-y: auto; overscroll-behavior: contain;
  padding: calc(14px + env(safe-area-inset-top, 0px)) 20px calc(24px + env(safe-area-inset-bottom, 0px));
  background: var(--surface-1); box-shadow: var(--lift);
  border-radius: var(--r-sheet) 0 0 var(--r-sheet);
  transform: translateX(104%); visibility: hidden;
  transition: transform 0.26s cubic-bezier(0.2, 0.8, 0.2, 1), visibility 0s linear 0.26s;
}
.notif.open { transform: none; visibility: visible; transition: transform 0.26s cubic-bezier(0.2, 0.8, 0.2, 1); }
@media (prefers-reduced-motion: reduce) { :root:not([data-motion="full"]) { .notif, .notif.open { transition: none; } } }
@media (max-width: 760px) { .notif { border-radius: 0; } }
.notif-head { display: flex; align-items: center; justify-content: space-between; gap: 12px; margin-bottom: 8px; }
.notif-head h2 { margin: 0; font-size: 18px; }
.notif-sec { padding: 14px 0; }
.notif-sec + .notif-sec { border-top: 1px solid var(--grid); }
.notif-sec h3 { margin: 0 0 10px; font-size: 12px; font-weight: 600; letter-spacing: 0.02em;
  text-transform: uppercase; color: var(--text-muted); }
.notif-sync { display: flex; align-items: center; justify-content: space-between; gap: 12px; flex-wrap: wrap; }
.notif-schools, .notif-list { list-style: none; margin: 0; padding: 0; }
.notif-schools { margin-top: 12px; display: grid; gap: 6px; font-size: 13px; }
.notif-schools li { display: flex; justify-content: space-between; gap: 12px; }
.notif-schools .nm { color: var(--text-secondary); }
.notif-schools .v { color: var(--text-primary); font-variant-numeric: tabular-nums; }
.notif-list li { display: flex; align-items: baseline; gap: 12px; padding: 9px 0; font-size: 13px; }
.notif-list li + li { border-top: 1px solid var(--grid); }
.notif-list .msg { flex: 1 1 auto; color: var(--text-primary); }
.notif-list li.bad .msg { color: var(--critical); }
.notif-list time { flex: 0 0 auto; color: var(--text-muted); font-variant-numeric: tabular-nums; font-size: 12px; }
.notif #notif-empty { margin: 0; }
.notif-store { margin: 0; }
.notif-feeds { margin-top: 8px; }
.notif-feeds li { align-items: baseline; }
.notif-feeds .nm { display: flex; align-items: baseline; gap: 8px; min-width: 0; }
.notif-feeds .nm .pill { padding: 0; background: none; align-self: center; }
.notif-feeds .nm .sub { color: var(--text-muted); font-size: 12px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.notif-feeds .v { flex: 0 0 auto; }
.notif-jump { display: inline-flex; align-items: center; gap: 5px; color: inherit; text-decoration: none;
  border-radius: var(--r-chip); margin: -2px -4px; padding: 2px 4px; }
.notif-jump svg { opacity: 0.7; }
.notif-jump:hover { color: var(--series-1); background: var(--well); }
.notif-jump:hover svg { opacity: 1; }
.notif-jump:focus-visible { outline: none; box-shadow: var(--focus); }

/* A link that does a button's job (Settings → Your calendar app: "add to
   Google Calendar" opens Google): the secondary button's well. */
a.button {
  display: inline-flex; align-items: center; padding: 8px 14px; border-radius: var(--r-btn);
  background: var(--well); color: var(--text-primary); text-decoration: none; font-weight: 500;
  border: 1px solid transparent; transition: background-color 150ms var(--ease-out);
}
a.button:hover { background: color-mix(in srgb, var(--text-primary) 7%, var(--well)); }
a.button:active { background: color-mix(in srgb, var(--text-primary) 11%, var(--well)); }
a.button:not([href]) { opacity: .5; pointer-events: none; }
.almanac-apps { flex-wrap: wrap; }
/* Settings → Your calendar app, set up: one line and Change */
.almanac-folded { display: flex; align-items: center; justify-content: space-between; gap: 12px; flex-wrap: wrap; }
.almanac-folded[hidden], #almanac-open[hidden] { display: none; }
.almanac-line { display: flex; align-items: center; gap: 10px; flex-wrap: wrap; min-width: 0; }
.almanac-line .hint { margin: 0; }

/* ask(): "are you sure?" as a Planner sheet (web/ask.js), in place of the
   browser's confirm(). Same family as "Confirm it's you": a white sheet on
   a warm dim of the ground, lifted, arriving like every sheet. */
.ask { width: min(420px, calc(100vw - 32px)); max-height: min(80vh, 640px); padding: 0; border: 0;
  border-radius: var(--r-sheet); background: var(--surface-1); color: var(--text-primary); box-shadow: var(--lift); }
.ask::backdrop { background: color-mix(in srgb, var(--plane) 72%, transparent); }
.ask[open] { animation: pl-sheet var(--t-move) var(--ease-out); }
.ask form { padding: 22px 24px 18px; }
.ask h2 { margin: 0 0 8px; font-size: 17px; font-weight: 600; line-height: 1.35; }
.ask p { margin: 0 0 8px; font-size: 14px; line-height: 1.5; color: var(--text-secondary); }
.ask ul { margin: 2px 0 8px; padding-left: 18px; font-size: 14px; line-height: 1.5; color: var(--text-secondary); }
.ask li + li { margin-top: 4px; }
.ask-actions { display: flex; justify-content: flex-end; gap: 8px; margin-top: 18px; }
/* hold to confirm (the heaviest asks): the button fills while pressed */
.ask button.hold { position: relative; overflow: hidden; isolation: isolate; touch-action: none;
  -webkit-user-select: none; user-select: none; -webkit-touch-callout: none; }
.ask button.hold::before { content: ""; position: absolute; inset: 0; z-index: -1; transform-origin: left center;
  transform: scaleX(var(--hold, 0)); background: color-mix(in srgb, var(--critical) 22%, transparent); }
.ask-hold-hint { margin: 8px 0 0; text-align: right; font-size: 12px; color: var(--text-muted); }

/* Two cards side by side in Settings (This app + Your calendars; Agents +
   Sign in with Google), stacked on narrow screens (Zaen, 2026-11-09). */
.settings-pair { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 0 20px; align-items: start; }
.settings-pair > .card[hidden] { display: none; }
.settings-pair:not(:has(> .card:not([hidden]) + .card:not([hidden]))) { grid-template-columns: minmax(0, 1fr); }
@media (max-width: 860px) { .settings-pair { grid-template-columns: minmax(0, 1fr); } }
/* admin-only cards: a small lock beside the heading */
.h-lock { display: inline-grid; place-items: center; width: 16px; height: 16px; color: var(--text-muted); }
.h-lock svg { width: 14px; height: 14px; }

/* Cupola (web/cupola.js, thread 183): feedback and Claude
   sessions in one, a bar of icons on its left. Docked at the right of
   Planetarium pages; the floating panel in the app. */
.cp { display: grid; grid-template-columns: 36px minmax(0, 1fr); gap: 14px; align-items: start; }
.cp-bar { position: sticky; top: 0; display: flex; flex-direction: column; gap: 4px; }
.cp-tool { position: relative; display: grid; place-items: center; width: 36px; height: 36px; padding: 0; border: 0; border-radius: 10px;
  background: none; color: var(--text-secondary); cursor: pointer; transition: background-color .16s ease-out, color .16s ease-out; }
.cp-tool:hover { background: var(--well); color: var(--text-primary); }
.cp-tool:focus-visible { outline: none; box-shadow: var(--focus); }
.cp-tool[aria-pressed="true"] { background: var(--series-1-soft); color: var(--series-1); }
.cp-tool-end { margin-top: 10px; }
.cp-tool-end::before { content: ""; position: absolute; top: -7px; left: 8px; right: 8px; border-top: 1px solid var(--border); }
.cp-badge { position: absolute; top: 2px; right: 1px; min-width: 15px; height: 15px; padding: 0 4px; border-radius: 999px;
  background: var(--good); color: var(--surface-1); font: 600 9.5px/15px var(--font-ui); text-align: center; }
.cp-badge[hidden] { display: none; }
.cp-body { min-width: 0; }
.cp-head { display: flex; align-items: center; gap: 10px; margin: 4px 0 12px; }
.cp-title { flex: 1 1 auto; margin: 0; font: 600 15px var(--font-ui); }
.cp-link { font-size: 12px; color: var(--series-1); text-decoration: none; }
.cp-link:hover { text-decoration: underline; text-underline-offset: 3px; }
.cp-quiet { white-space: nowrap; font: 12px var(--font-ui); color: var(--text-secondary); background: none; border: 0; padding: 2px 4px; border-radius: 6px; cursor: pointer; }
.cp-quiet:hover { color: var(--text-primary); background: var(--well); }
.cp-x { display: grid; place-items: center; width: 24px; height: 24px; padding: 0; border: 0; border-radius: 7px; background: none;
  color: var(--text-muted); font-size: 17px; cursor: pointer; }
.cp-x:hover { background: var(--well); color: var(--text-primary); }
.cp-empty { margin: 4px 0; font-size: 13px; color: var(--text-muted); }
.cp-group + .cp-group { margin-top: 18px; }
.cp-group h3 { margin: 0 0 4px; font: 600 12px var(--font-ui); color: var(--text-muted); cursor: help; }
.cp-row { display: flex; align-items: flex-start; gap: 10px; padding: 9px 0; }
.cp-row + .cp-row { border-top: 1px solid var(--border); }
.cp-row-here { margin: 0 -8px; padding: 9px 8px; border-radius: 9px; background: var(--series-1-soft); }
.cp-row-here + .cp-row, .cp-row + .cp-row-here { border-top-color: transparent; }
.cp-row-ic { flex: none; margin-top: 2px; color: var(--text-muted); }
.cp-row.ss-working .cp-row-ic { color: var(--series-1); animation: cp-tick 2.4s linear infinite; }
.cp-row.ss-waiting .cp-row-ic { color: var(--good); }
.cp-row.ss-asking .cp-row-ic { color: var(--serious); }
.cp-row.ss-starting .cp-row-ic { color: var(--series-1); }
.cp-row.ss-scheduled .cp-row-ic { color: var(--text-secondary); }
@keyframes cp-tick { to { transform: rotate(360deg); } }
@media (prefers-reduced-motion: reduce) { .cp-row.ss-working .cp-row-ic { animation: none; } }
.cp-row-what { flex: 1 1 auto; min-width: 0; display: grid; gap: 1px; }
.cp-row-title { font-size: 13px; font-weight: 600; color: var(--text-primary); text-decoration: none; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
a.cp-row-title:hover { color: var(--series-1); }
.cp-row-state { font-size: 12px; color: var(--text-secondary); }
.cp-row-note { font-size: 12px; color: var(--text-muted); line-height: 1.4; }
.cp-row-picking .cp-row-note { color: var(--series-1); }
.cp-row-open { flex: none; display: grid; place-items: center; width: 26px; height: 26px; border-radius: 7px; color: var(--text-muted); }
.cp-row-open:hover { background: var(--well); color: var(--text-primary); }
.cp-tree { min-width: 0; overflow: hidden; list-style: none; margin: -2px 0 8px 7px; padding: 0 0 0 16px; border-left: 1.5px solid var(--grid);
  display: grid; gap: 2px; }
.cp-leaf { position: relative; min-width: 0; }
.cp-leaf::before { content: ""; position: absolute; left: -16px; top: 13px; width: 11px; border-top: 1.5px solid var(--grid); }
.cp-leaf-a { display: flex; align-items: center; gap: 7px; padding: 5px 6px; margin-left: -6px; border-radius: 7px; font-size: 12.5px;
  color: var(--text-secondary); text-decoration: none; min-width: 0; }
.cp-leaf-a:hover { background: var(--well); color: var(--text-primary); }
.cp-leaf-a:focus-visible { outline: none; box-shadow: var(--focus); }
.cp-leaf-dot { flex: none; width: 7px; height: 7px; border-radius: 50%; background: var(--text-muted); }
.cp-leaf-t { flex: 1 1 auto; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.cp-leaf-tag { flex: none; font-size: 11px; color: var(--text-muted); }
.cp-leaf.st-check .cp-leaf-dot { background: var(--good); }
.cp-leaf.st-check .cp-leaf-tag { color: var(--good); font-weight: 600; }
.cp-leaf.st-choice .cp-leaf-dot { background: var(--warning); }
.cp-leaf.st-choice .cp-leaf-tag { color: var(--warning); font-weight: 600; }
.cp-leaf.st-check .cp-leaf-a, .cp-leaf.st-choice .cp-leaf-a { color: var(--text-primary); }
.cp-leaf.st-working .cp-leaf-dot { background: var(--series-1); }
.cp-plan { margin: 0 0 14px; padding: 12px 14px; border-radius: var(--r-block); background: var(--well); font-size: 13px; }
.cp-plan-h { margin: 0 0 6px; font-weight: 600; }
.cp-plan ul { margin: 0 0 10px; padding-left: 18px; color: var(--text-secondary); }
.cp-plan li + li { margin-top: 3px; }
.cp-plan-acts { display: flex; justify-content: flex-end; gap: 8px; }
.cp-btn { font: 500 12px var(--font-ui); padding: 5px 13px; border: 0; border-radius: 999px; background: var(--surface-1); color: var(--text-primary); cursor: pointer; }
.cp-btn.primary { background: var(--series-1); color: var(--on-series-1); font-weight: 600; }
.cp-btn:disabled { opacity: .6; cursor: default; }
/* Feedback rows (web/try.js) inside the docked panel look as in the floating one */
.cp-feed .try-panel-item { padding: 10px 0 8px; }
.cp-feed .try-panel-item + .try-panel-item { border-top: 1px solid var(--border); margin-top: 0; }
.cp-feed .try-panel-open { display: flex; align-items: flex-start; gap: 8px; width: 100%; padding: 0; border: 0; background: none; text-align: left;
  font: 13px/1.45 var(--font-ui); color: var(--text-primary); cursor: pointer; }
.cp-feed .try-panel-open:hover { color: var(--series-1); }
.cp-feed .try-panel-open .ph-dot { position: static; flex: none; margin-top: 4px; }
.cp-feed .try-panel-sub { margin: 4px 0 6px; font-size: 12px; color: var(--text-muted); }
.cp-feed .try-panel-acts { display: flex; flex-wrap: wrap; gap: 6px; }
.cp-feed .try-panel-acts > * { font: 500 12px var(--font-ui); padding: 4px 11px; border: 0; border-radius: 999px; background: var(--well);
  color: var(--text-primary); text-decoration: none; cursor: pointer; }
.cp-feed .try-go { color: var(--series-1); font-weight: 600; }
.cp-feed .try-ok { background: var(--series-1) !important; color: var(--on-series-1) !important; font-weight: 600; }
.cp-feed button:disabled { opacity: .5; cursor: default; }
/* floating (in the app's panel): its head already says Feedback and Issues */
.try-panel:has(.cp) { width: min(420px, calc(100vw - 32px)); }
/* docked at the right of the Planetarium, its bar is on the right too (thread 186) */
.cp-docked { grid-template-columns: minmax(0, 1fr) 36px; }
.cp-docked > .cp-bar { order: 2; }
.cp-floating { margin-top: 10px; }
.cp-floating .cp-title, .cp-floating .cp-link { display: none; }
.cp-floating .cp-head { justify-content: flex-end; margin-top: 0; }
