/* Palette: the validated reference instance, values unchanged. */
:root {
  color-scheme: light;
  --surface-1: #fcfcfb;
  --plane: #f9f9f7;
  --text-primary: #0b0b0b;
  --text-secondary: #52514e;
  --text-muted: #898781;
  --grid: #e1e0d9;
  --baseline: #c3c2b7;
  --border: rgba(11, 11, 11, 0.10);
  --series-1: #2a78d6;
  --series-1-soft: #cde2fb;
  --good: #0ca30c;
  --warning: #fab219;
  --serious: #ec835a;
  --critical: #d03b3b;
  --ghost: rgba(11, 11, 11, 0.04);
  --glass: rgba(252, 252, 251, 0.72);
  --glass-edge: rgba(255, 255, 255, 0.7);
  --glass-rim: rgba(11, 11, 11, 0.08);
  --glass-shadow: 0 10px 30px rgba(0, 0, 0, 0.14), 0 2px 6px rgba(0, 0, 0, 0.06);
}
@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) {
    color-scheme: dark;
    --surface-1: #1a1a19;
    --plane: #0d0d0d;
    --text-primary: #ffffff;
    --text-secondary: #c3c2b7;
    --text-muted: #898781;
    --grid: #2c2c2a;
    --baseline: #383835;
    --border: rgba(255, 255, 255, 0.10);
    --series-1: #3987e5;
    --series-1-soft: #184f95;
    --ghost: rgba(255, 255, 255, 0.06);
    --glass: rgba(38, 38, 36, 0.72);
    --glass-edge: rgba(255, 255, 255, 0.16);
    --glass-rim: rgba(255, 255, 255, 0.08);
    --glass-shadow: 0 10px 30px rgba(0, 0, 0, 0.5), 0 2px 6px rgba(0, 0, 0, 0.3);
  }
}
:root[data-theme="dark"] {
  color-scheme: dark;
  --surface-1: #1a1a19;
  --plane: #0d0d0d;
  --text-primary: #ffffff;
  --text-secondary: #c3c2b7;
  --text-muted: #898781;
  --grid: #2c2c2a;
  --baseline: #383835;
  --border: rgba(255, 255, 255, 0.10);
  --series-1: #3987e5;
  --series-1-soft: #184f95;
  --ghost: rgba(255, 255, 255, 0.06);
  --glass: rgba(38, 38, 36, 0.72);
  --glass-edge: rgba(255, 255, 255, 0.16);
  --glass-rim: rgba(255, 255, 255, 0.08);
  --glass-shadow: 0 10px 30px rgba(0, 0, 0, 0.5), 0 2px 6px rgba(0, 0, 0, 0.3);
}

/* 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:
   chat 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;
}

* { 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);
}

/* ---------------------------------------------------------- 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 the chat log) 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: 208px 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 20px;
  background: var(--surface-1); border-bottom: 1px solid var(--border);
}
.topbar .brand { flex: 0 1 auto; min-width: 0; display: flex; align-items: baseline; gap: 12px; }
.topbar .brand h1 { font-size: 17px; flex: 0 0 auto; }
.topbar .brand p { margin: 0; font-size: 12px; color: var(--text-muted); }
.topbar .spacer { flex: 1 1 auto; }
.topbar button.primary { padding: 6px 14px; }
#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) { #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: 8px 0;
}
.setting-row + .setting-row { border-top: 1px solid var(--grid); }
.setting-row > label, .setting-row > span { font-weight: 550; }
.setting-row .viewtoggle button { font-size: 13px; padding: 6px 12px; }
.setting-row select {
  font: inherit; padding: 7px 10px; border-radius: 8px;
  border: 1px solid var(--border); background: var(--plane); 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;
  padding: 16px 12px; background: var(--surface-1);
  border-right: 1px solid var(--border);
}

.sidenav { display: flex; flex-direction: column; gap: 2px; height: 100%; }
/* 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: 10px; width: 100%;
  border: none; background: none; text-align: left;
  padding: 8px 10px; color: var(--text-secondary);
}
.sidenav .tab svg { flex: 0 0 auto; }
.sidenav .tab:hover { background: var(--ghost); color: var(--text-primary); }
.sidenav .tab[aria-selected="true"] {
  background: var(--ghost); color: var(--text-primary); font-weight: 550;
  box-shadow: inset 3px 0 0 var(--series-1);
}

@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, #net-text { display: none; }
  .topbar .pill { padding: 6px; }
  /* Language and theme move to Settings; Sync shrinks to an icon. */
  .topbar #lang-select, .topbar #theme-btn { display: none; }
  .topbar #sync-btn { display: none; }  /* phones pull down to sync */
  #sync-label {
    position: absolute; width: 1px; height: 1px; overflow: hidden;
    clip: rect(0 0 0 0); clip-path: inset(50%); white-space: nowrap;
  }
  /* The tabs become a floating glass bar, like iOS: icons only (the labels
     stay for screen readers), content scrolls underneath, a lens behind the
     current tab, a press that responds on touch-down. */
  .sidebar {
    grid-area: auto; position: fixed; z-index: 40; overflow: visible;
    left: 0; right: 0; margin: 0 auto; width: max-content; height: auto;
    bottom: calc(10px + env(safe-area-inset-bottom, 0px));
    padding: 5px; border-radius: 999px; border: 1px solid var(--glass-rim);
    background: var(--glass);
    -webkit-backdrop-filter: blur(22px) saturate(180%); backdrop-filter: blur(22px) saturate(180%);
    box-shadow: var(--glass-shadow), inset 0 1px 0 var(--glass-edge);
  }
  .sidenav { flex-direction: row; gap: 2px; }
  .sidenav .tab {
    width: 58px; height: 46px; padding: 0; justify-content: center;
    border-radius: 999px; color: var(--text-secondary);
    transition: transform 0.1s ease-out, background-color 0.2s ease, color 0.2s ease;
  }
  .sidenav .tab svg { width: 24px; height: 24px; }
  .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 { background: none; color: var(--text-secondary); }
  .sidenav .tab:active { transform: scale(0.9); }
  .sidenav .tab[aria-selected="true"] {
    color: var(--series-1); box-shadow: none;
    background: color-mix(in srgb, var(--series-1) 14%, transparent);
  }
  /* Room to scroll the last card out from under the bar. */
  .wrap { padding: 16px 16px calc(84px + env(safe-area-inset-bottom, 0px)); }
  #toasts { bottom: calc(76px + 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; }

button {
  font: inherit; cursor: pointer; border-radius: 8px;
  border: 1px solid var(--border); background: var(--surface-1);
  color: var(--text-primary); padding: 8px 14px;
}
button:hover:not(:disabled) { background: var(--ghost); }
button:disabled { opacity: 0.55; cursor: default; }
button.primary {
  background: var(--series-1); border-color: transparent; color: #fff; font-weight: 550;
}
button.primary:hover:not(:disabled) { filter: brightness(1.08); background: var(--series-1); }
.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;
  border: 1px solid var(--border); 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: 1px solid var(--border);
  border-radius: 10px; padding: 14px 16px;
}
.tile .label {
  font-size: 12px; color: var(--text-secondary); text-transform: uppercase;
  letter-spacing: 0.04em; margin-bottom: 6px;
}
.tile .value { font-size: 27px; font-weight: 600; letter-spacing: -0.02em; line-height: 1.1; }
.tile .sub { font-size: 12px; color: var(--text-muted); margin-top: 4px; }

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

.card {
  background: var(--surface-1); border: 1px solid var(--border);
  border-radius: 10px; margin-bottom: 16px; overflow: hidden;
}
.card > h2 {
  margin: 0; padding: 12px 16px; font-size: 14px; font-weight: 600;
  border-bottom: 1px solid var(--border);
  display: flex; align-items: center; gap: 10px;
}
.card > h2 .count { color: var(--text-muted); font-weight: 400; }
.card .body { padding: 14px 16px; }

.banner {
  border-radius: 10px; padding: 12px 16px; margin-bottom: 16px;
  border: 1px solid var(--border); background: var(--surface-1);
  display: flex; gap: 10px; align-items: flex-start;
}
.banner .mark { font-weight: 700; }
.banner.warn { border-left: 3px solid var(--warning); }
.banner.err  { border-left: 3px solid var(--critical); }
.banner code {
  background: var(--ghost); padding: 1px 5px; border-radius: 4px; font-size: 12px;
}

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

.progress-track {
  height: 6px; border-radius: 3px; background: var(--grid); overflow: hidden;
}
.progress-fill {
  height: 100%; border-radius: 3px; background: var(--series-1);
  width: 0%; transition: width 0.3s ease;
}
.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;
  border-top: 1px solid var(--border); padding-top: 10px;
}
.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: 14px; background: var(--series-1);
  border-radius: 0 4px 4px 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: 6px; 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: 7px; padding: 8px 10px; display: block;
}
.course-list button.active { background: var(--series-1); color: #fff; }
.course-list button.active .meta { color: rgba(255, 255, 255, 0.82); }
.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 16px; border-bottom: 1px solid var(--border); }
input[type="search"] {
  flex: 1 1 auto; font: inherit; padding: 8px 12px; border-radius: 8px;
  border: 1px solid var(--border); background: var(--plane); color: var(--text-primary);
}
input[type="search"]:focus { outline: 2px solid var(--series-1); outline-offset: -1px; }

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

table { width: 100%; border-collapse: collapse; font-size: 13px; }
thead th {
  text-align: left; font-weight: 550; font-size: 12px; color: var(--text-secondary);
  padding: 8px 16px; border-bottom: 1px solid var(--border);
  position: sticky; top: 0; background: var(--surface-1);
}
tbody td { padding: 8px 16px; border-bottom: 1px solid var(--grid); vertical-align: top; }
tbody tr:hover { background: var(--ghost); }
tbody tr:last-child td { border-bottom: none; }
td.num { font-variant-numeric: tabular-nums; color: var(--text-secondary); white-space: nowrap; }
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; }
.tag {
  display: inline-block; font-size: 11px; padding: 1px 6px; border-radius: 4px;
  border: 1px solid var(--border); color: var(--text-secondary);
}
.tag.missing { border-color: var(--critical); color: var(--critical); }

.empty { padding: 36px 16px; text-align: center; color: var(--text-muted); }

/* 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: 10px 16px; font-size: 12px; color: var(--text-muted); border-top: 1px solid var(--border); }

.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: 550;
  margin-bottom: 5px; color: var(--text-primary);
}
.field input[type="text"], .field input[type="url"], .field input[type="password"] {
  width: 100%; font: inherit; padding: 9px 12px; border-radius: 8px;
  border: 1px solid var(--border); background: var(--plane);
  color: var(--text-primary);
}
.field input:focus { outline: 2px solid var(--series-1); outline-offset: -1px; }
.field input:disabled { opacity: 0.6; cursor: not-allowed; }

.hint { display: block; font-size: 12px; color: var(--text-muted); margin-top: 5px; }
.hint code { background: var(--ghost); padding: 1px 5px; border-radius: 4px; }
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: 8px; padding: 10px 14px; margin-bottom: 14px; font-size: 13px;
  border: 1px solid var(--border); background: var(--plane);
  display: flex; gap: 9px; align-items: flex-start;
}
.result .mark { font-weight: 700; flex: 0 0 auto; }
.result.ok { border-left: 3px solid var(--good); }
.result.ok .mark { color: var(--good); }
.result.bad { border-left: 3px solid var(--critical); }
.result.bad .mark { color: var(--critical); }
.result.busy { border-left: 3px solid var(--series-1); }

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

/* -------------------------------------------------------------- chat */

.chatpanel {
  display: flex; flex-direction: column; gap: 12px;
  flex: 1 1 auto; min-height: 280px; width: 100%; max-width: 780px; margin: 0 auto;
}
.chatpanel[hidden] { display: none; }
.chatlog { flex: 1 1 auto; overflow-y: auto; display: flex; flex-direction: column; gap: 10px; padding: 4px 2px; }
.chat-empty { margin: auto 0; text-align: center; }
.chat-empty .hint { max-width: 46ch; margin: 0 auto; font: 15px/1.55 var(--font-read); }
.chat-empty h2 { margin: 0 0 6px; font-size: 22px; font-weight: 600; letter-spacing: -0.01em; }
.chat-suggest { display: flex; flex-wrap: wrap; justify-content: center; gap: 8px; margin-top: 16px; }
.chat-suggest button {
  border: 1px solid var(--border); border-radius: 999px; padding: 6px 12px;
  background: var(--surface-1); color: var(--text-secondary); font-size: 13px;
}
.bubble {
  max-width: 85%; padding: 10px 14px; border-radius: 12px;
  font: 15px/1.6 var(--font-read);
  white-space: pre-wrap; overflow-wrap: anywhere;
}
.bubble.user { align-self: flex-end; background: var(--series-1); color: #fff; }
.bubble.assistant {
  align-self: flex-start; background: var(--surface-1); border: 1px solid var(--border);
}
.bubble.pending { color: var(--text-muted); }
.bubble.note { color: var(--text-secondary); background: var(--ghost); border-style: dashed; }
.bubble.note.bad { color: var(--critical); }
.chatbox {
  display: flex; gap: 8px; align-items: flex-end;
  background: var(--surface-1); border: 1px solid var(--border);
  border-radius: 12px; padding: 8px;
}
.chatbox textarea {
  flex: 1 1 auto; resize: none; border: none; outline: none; background: none;
  color: var(--text-primary); font: 15px/1.5 var(--font-read); padding: 6px 8px; max-height: 160px;
}

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

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

.viewtoggle { display: inline-flex; border: 1px solid var(--border); border-radius: 7px; overflow: hidden; }
.viewtoggle button {
  border: none; border-radius: 0; background: none; font-size: 12px;
  padding: 4px 10px; color: var(--text-secondary); font-weight: 400;
}
.viewtoggle button + button { border-left: 1px solid var(--border); }
.viewtoggle button.active { background: var(--series-1); color: #fff; font-weight: 550; }
.viewtoggle button:hover:not(.active) { background: var(--ghost); }

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

.grid-scroll { max-height: 620px; overflow-y: auto; padding: 14px 16px; }
.filegrid {
  display: grid; gap: 12px;
  grid-template-columns: repeat(auto-fill, minmax(160px, 1fr));
}
.filecard {
  display: flex; flex-direction: column; gap: 6px;
  border: 1px solid var(--border); border-radius: 9px; padding: 12px;
  background: var(--plane); text-decoration: none; color: inherit;
  min-width: 0;
}
.filecard:hover { border-color: var(--series-1); background: var(--ghost); }
.filecard .ext {
  align-self: flex-start; font-size: 10px; font-weight: 700; letter-spacing: 0.06em;
  padding: 3px 7px; border-radius: 4px;
  background: var(--series-1-soft); color: var(--series-1);
}
.filecard .fname {
  font-size: 13px; font-weight: 500; color: var(--series-1);
  overflow-wrap: anywhere; display: -webkit-box;
  -webkit-line-clamp: 2; -webkit-box-orient: vertical; overflow: hidden;
}
.filecard .fmeta {
  font-size: 11px; 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;
}
.toast {
  display: flex; align-items: center; gap: 12px;
  background: var(--surface-1); color: var(--text-primary);
  border: 1px solid var(--border); border-left: 3px solid var(--series-1);
  border-radius: 9px; padding: 11px 14px; font-size: 13px;
  box-shadow: 0 6px 20px rgba(0, 0, 0, 0.14);
  animation: toastin 0.18s ease-out;
  max-width: min(400px, calc(100vw - 32px));
}
.toast.bad { border-left-color: var(--critical); }
.toast .msg { flex: 1 1 auto; }
.toast button { padding: 4px 10px; font-size: 12px; }
@keyframes toastin { from { opacity: 0; transform: translateY(8px); } }
@media (prefers-reduced-motion: reduce) { .toast { animation: none; } }


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

button.linkish {
  border: none; background: none; padding: 2px 4px;
  color: var(--series-1); font-size: 13px; font-weight: 500;
}
button.linkish:hover { background: var(--ghost); }

.duelist { list-style: none; margin: 0; padding: 0; }
.duelist li {
  display: flex; align-items: center; gap: 12px;
  padding: 11px 16px; border-bottom: 1px solid var(--grid);
}
.duelist li:last-child { border-bottom: none; }
.duelist li:hover { background: var(--ghost); }

.duelist .when {
  flex: 0 0 168px; font-size: 13px; font-variant-numeric: tabular-nums;
  color: var(--text-secondary); white-space: nowrap;
}
.duelist .when .rel { display: block; font-size: 11px; color: var(--text-muted); }
.duelist li.overdue .when .rel { color: var(--critical); font-weight: 600; }
.duelist li.soon .when .rel { color: var(--serious); font-weight: 600; }

.duelist .what { flex: 1 1 auto; min-width: 0; }
.duelist .what .nm {
  font-size: 13px; font-weight: 500;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.duelist .what .crs { font-size: 11px; color: var(--text-muted); }

/* Status never rides on colour alone - each carries its own word. */
.state {
  flex: 0 0 auto; font-size: 11px; padding: 2px 8px; border-radius: 999px;
  border: 1px solid var(--border); color: var(--text-secondary);
  display: inline-flex; align-items: center; gap: 5px;
}
.state::before { content: ""; width: 7px; height: 7px; border-radius: 50%; background: currentColor; }
.state.done   { color: var(--good); }
.state.todo   { color: var(--serious); }
.state.graded { color: var(--series-1); }

.duelist .go {
  flex: 0 0 auto; font-size: 12px; color: var(--series-1);
  text-decoration: none; padding: 5px 10px; border-radius: 7px;
  border: 1px solid var(--border); white-space: nowrap;
}
.duelist .go:hover { background: var(--ghost); }

@media (max-width: 620px) {
  .duelist li { flex-wrap: wrap; }
  .duelist .when { flex-basis: 100%; }
  .duelist .what { flex-basis: 100%; }
}


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

.hits-scroll { max-height: 620px; overflow-y: auto; }
.hits { list-style: none; margin: 0; padding: 0; }
.hits li { padding: 11px 16px; border-bottom: 1px solid var(--grid); }
.hits li:last-child { border-bottom: none; }
.hits li:hover { background: var(--ghost); }

.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: 13px; }
.hits .head a:hover { text-decoration: underline; }
.hits .page {
  font-size: 11px; color: var(--text-secondary);
  border: 1px solid var(--border); border-radius: 4px; padding: 1px 6px;
  font-variant-numeric: tabular-nums;
}
.hits .crs { font-size: 11px; color: var(--text-muted); }
.hits .snip {
  font: 13px/1.55 var(--font-read); color: var(--text-secondary); margin-top: 5px;
  overflow-wrap: anywhere;
}
.hits .snip mark {
  background: var(--series-1-soft); color: var(--text-primary);
  padding: 0 2px; border-radius: 3px;
}


/* ------------------------------------------------- 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;
}
.filecard.picked { border-color: var(--series-1); background: var(--ghost); }

.selbar {
  display: flex; align-items: center; gap: 10px; flex-wrap: wrap;
  padding: 11px 16px; border-top: 1px solid var(--border);
  background: var(--plane);
}
.selbar .spacer { flex: 1 1 auto; }
.selcount { font-size: 13px; font-weight: 550; }
.chatopen { display: inline-flex; }
.chatopen button { border-radius: 8px 0 0 8px; }
.chatopen select {
  font: inherit; font-size: 13px; border: 1px solid var(--border);
  border-left: none; border-radius: 0 8px 8px 0; padding: 8px 8px;
  background: var(--surface-1); color: var(--text-primary); cursor: pointer;
}

.hint select {
  font: inherit; font-size: 12px; padding: 1px 4px; border-radius: 5px;
  border: 1px solid var(--border); background: var(--plane);
  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; }

#lang-select {
  font: inherit; font-size: 13px; padding: 7px 8px; border-radius: 8px;
  border: 1px solid var(--border); background: var(--surface-1);
  color: var(--text-primary); cursor: pointer;
}

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

.codebox {
  margin-top: 14px; padding: 14px 16px; border-radius: 9px;
  background: var(--plane); border: 1px solid var(--border);
  border-left: 3px solid var(--good);
  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(> .chatpanel: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:hover:not(:disabled) { background: var(--ghost); border-color: var(--critical); }

.tag.away { border-style: dashed; color: var(--text-muted); }
.filecard.away { opacity: 0.72; }
.course-list .group {
  padding: 12px 10px 4px; font-size: 11px; letter-spacing: 0.05em;
  text-transform: uppercase; color: var(--text-muted); font-weight: 600;
}

details.history { border-top: 1px solid var(--border); }
details.history > summary {
  cursor: pointer; padding: 10px 16px; font-size: 13px; color: var(--text-secondary);
  list-style-position: inside;
}
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 */

.connlist, .kindlist { list-style: none; margin: 0; padding: 6px; }
.connlist li, .kindlist li { margin: 0; }
.connempty { padding: 14px 10px; color: var(--text-muted); font-size: 13px; }
.connitem {
  width: 100%; display: flex; align-items: center; gap: 12px; text-align: left;
  border: 1px solid transparent; background: none; padding: 10px; border-radius: 9px;
}
.connitem:hover:not(:disabled) { background: var(--ghost); }
.connitem.active { border-color: var(--series-1); background: var(--ghost); }
.connitem .avatar {
  flex: 0 0 auto; width: 36px; height: 36px; border-radius: 9px;
  display: grid; place-items: center; font-weight: 650; font-size: 16px;
  background: var(--series-1-soft); color: var(--series-1);
}
.connitem .avatar.plus { background: var(--ghost); 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: 30px; height: 30px; justify-content: center; padding: 0; border-radius: 8px; }
.addmenu { border-top: 1px solid var(--border); padding: 12px 10px 4px; }
.addmenu .hint { margin: 0 6px 6px; }

/* --------------------------------------------------------- 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; }
.authcard {
  width: 100%; max-width: 420px; background: var(--surface-1);
  border: 1px solid var(--border); border-radius: 14px; padding: 24px;
}
.authbrand { display: flex; align-items: center; gap: 14px; margin-bottom: 20px; }
.authbrand img { border-radius: 10px; }
.authbrand h1 { margin: 0; font-size: 22px; font-weight: 650; letter-spacing: -0.01em; }
.authbrand p { margin: 2px 0 0; color: var(--text-secondary); font: 14px/1.4 var(--font-read); }
.authtabs { display: flex; margin-bottom: 12px; }
.authtabs button { flex: 1 1 0; }
#auth-intro { margin: 0 0 14px; font-size: 13px; 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: 420px; text-align: center; color: var(--text-secondary); font: 15px/1.55 var(--font-read); }

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

.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 14px;
  background: var(--surface-1); border-bottom: 1px solid var(--border);
}
.viewer-bar strong { min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.viewer-bar .spacer { flex: 1 1 auto; }
.viewer-bar .icon-btn { font-size: 20px; line-height: 1; padding: 4px 11px; }
.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: #fff; }
.viewer-body img, .viewer-body video { max-width: 100%; max-height: 100%; object-fit: contain; }
.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: 12px; position: relative; }
.rd > .hint { text-align: center; margin-top: 30vh; }
.rd-pages { display: flex; flex-direction: column; align-items: center; gap: 12px; padding-bottom: 40vh; }
.rd-page {
  position: relative; width: 100%; max-width: 900px; background: #fff;
  box-shadow: 0 1px 3px rgba(0, 0, 0, 0.18); border-radius: 2px; 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: #ffd43b; }
.rd-hl.c-green, .rd-dot.c-green { background: #8ce99a; }
.rd-hl.c-blue, .rd-dot.c-blue { background: #a5d8ff; }
.rd-hl.c-pink, .rd-dot.c-pink { background: #fcc2d7; }
.rd-hl { opacity: .7; }
.rd-untils { z-index: 3; }
.rd-until { position: absolute; left: 0; right: 0; height: 0; border-top: 2px dashed var(--series-1); opacity: .45; }
.rd-until.latest { opacity: 1; border-top-style: solid; }
.rd-until span {
  position: absolute; right: 6px; top: 0; transform: translateY(-50%);
  font: 600 11px var(--font-ui); color: #fff; background: var(--series-1); padding: 2px 8px; border-radius: 999px;
}

/* each class's stretch (previous Until here → its own), labelled */
.rd-band { position: absolute; left: 0; right: 0; border-left: 4px solid color-mix(in srgb, var(--series-1) 55%, transparent);
  background: color-mix(in srgb, var(--series-1) 5%, transparent); }
.rd-band.alt { border-left-color: color-mix(in srgb, var(--series-1) 30%, transparent); background: color-mix(in srgb, var(--series-1) 2.5%, transparent); }
.rd-band span { position: absolute; left: 8px; top: 4px; font: 600 10px var(--font-ui); color: var(--series-1);
  background: rgba(255, 255, 255, 0.85); padding: 1px 6px; border-radius: 999px; }
/* the class, live: your last "we're here" (solid) and the guess (soft) */
.rd-here { position: absolute; left: 0; right: 0; height: 0; border-top: 2px solid #e8590c; }
.rd-here span { position: absolute; left: 8px; top: 0; transform: translateY(-50%); font: 600 11px var(--font-ui);
  color: #fff; background: #e8590c; padding: 2px 8px; border-radius: 999px; }
.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, rgba(232, 89, 12, 0.16), transparent); }
.rd-transit::after { content: ""; position: absolute; left: 0; right: 0; top: -1px; border-top: 2px dotted #e8590c; 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: #e8590c; background: #fff; border: 1px solid #e8590c; padding: 1px 8px; border-radius: 999px; }
@keyframes rd-breathe { 50% { opacity: .35; } }
@media (prefers-reduced-motion: reduce) { .rd-transit::after { animation: none; } }
/* "I don't get this" */
.rd-hl.c-lost { background: repeating-linear-gradient(-45deg, rgba(240, 62, 62, .35) 0 6px, rgba(240, 62, 62, .15) 6px 12px); opacity: 1; }
.rd-dot.rd-lost, .rd-dot.c-lost { background: #ffe3e3; color: #c92a2a; font: 700 13px var(--font-ui); line-height: 20px; text-align: center; }
.rd-tool.rd-tool-live { color: #e8590c; border-color: #e8590c; }
.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: #e8590c; }
.rd-chip button { background: rgba(255, 255, 255, 0.18); color: inherit; border: 0; border-radius: 999px; padding: 3px 10px; font-weight: 600; }

/* 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(--series-1) 30%, 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: 8px; min-width: 0; }
.rd-tool { padding: 5px 10px; font-size: 13px; white-space: nowrap; }

/* the little bar over a selection or a highlight */
.rd-bar {
  position: fixed; z-index: 70; display: flex; align-items: center; gap: 6px; padding: 6px 8px;
  border-radius: 999px; background: var(--surface-1); border: 1px solid var(--border);
  box-shadow: 0 8px 24px rgba(0, 0, 0, 0.18);
}
.rd-dot { display: inline-block; width: 24px; height: 24px; border-radius: 50%; border: 2px solid rgba(0, 0, 0, 0.08); 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 10px; 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(--series-1); }
.rd-guide-tag {
  position: absolute; right: 18px; top: 0; transform: translateY(-50%);
  font: 600 12px var(--font-ui); color: #fff; background: var(--series-1); 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 14px; max-width: calc(100% - 24px);
  border-radius: 14px; background: var(--surface-1); border: 1px solid var(--border); box-shadow: 0 8px 24px rgba(0, 0, 0, 0.18);
  font-size: 13px; color: var(--text-secondary);
}

/* "Until here: p. 12 (Mon) — Go", when a file opens */
.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 14px; border-radius: 999px;
  background: rgba(20, 20, 20, 0.86); color: #fff; border: 0; font-size: 13px; box-shadow: 0 8px 24px rgba(0, 0, 0, 0.2);
}
.rd-chip b { color: #9cc8ff; }
.rd-flash {
  position: absolute; left: 50%; top: 70px; transform: translateX(-50%); z-index: 6; padding: 6px 12px; border-radius: 999px;
  background: rgba(20, 20, 20, 0.86); color: #fff; font-size: 13px; pointer-events: none;   /* always over a white page */
}

/* Marks: the side panel */
.rd-panel {
  position: fixed; z-index: 60; width: min(360px, 100vw); overflow-y: auto;
  background: var(--surface-1); border-left: 1px solid var(--border); box-shadow: -8px 0 24px rgba(0, 0, 0, 0.12);
  padding: 0 14px 14px; display: flex; flex-direction: column; gap: 2px;
}
.rd-panel-h { position: sticky; top: 0; display: flex; align-items: center; justify-content: space-between; padding: 12px 0 8px; background: var(--surface-1); }
.rd-panel h4 { margin: 12px 0 4px; font-size: 12px; color: var(--text-muted); text-transform: uppercase; letter-spacing: .04em; }
.rd-row {
  display: flex; align-items: center; gap: 10px; width: 100%; text-align: left; padding: 8px; border: 0; border-radius: 8px;
  background: none; color: var(--text-primary); font-size: 14px;
}
.rd-row:hover { background: var(--ghost); }
.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; }
.rd-none { padding: 8px 0; }
.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; }

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

.author {
  display: flex; align-items: center; gap: 10px; margin: 16px 0 12px;
  color: var(--text-muted); font-size: 12px;
}
.author::before, .author::after { content: ""; flex: 1 1 auto; height: 1px; background: var(--border); }
.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; }
.signin-row {
  display: flex; align-items: center; gap: 14px; padding: 10px 12px;
  border: 1px solid var(--border); border-radius: 9px; font-size: 13px;
}
.signin-row .k { font-weight: 600; }
.signin-row .v { flex: 1 1 auto; color: var(--text-secondary); overflow-wrap: anywhere; }
#gadmin-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: 14px; }
.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: 10px 16px; display: flex; flex-direction: column; gap: 8px; }
.signin-row .k { margin-right: 6px; }
#recovery-box .codebox strong, #invite-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).
   A quiet sand colour: noticeable, not alarming. */
:root { --demo-bg: #f6eed8; --demo-ink: #4b3f1e; --demo-line: #e4d6ae; --demo-btn: rgba(255,255,255,.7); }
@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) { --demo-bg: #2e291c; --demo-ink: #eadcb4; --demo-line: #4a4029; --demo-btn: rgba(255,255,255,.08); }
}
:root[data-theme="dark"] { --demo-bg: #2e291c; --demo-ink: #eadcb4; --demo-line: #4a4029; --demo-btn: rgba(255,255,255,.08); }
.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: 600 12px var(--font-ui); color: var(--demo-ink); background: var(--demo-bg);
  border-top: 1px solid var(--demo-line); }
.demo-strip b { font-weight: 800; letter-spacing: .05em; text-transform: uppercase; font-size: 11px; }
.demo-strip .sep { opacity: .45; }
.demo-strip label { display: inline-flex; gap: 5px; align-items: center; cursor: pointer; }
.demo-strip a, .demo-strip button { font: inherit; color: var(--demo-ink); background: var(--demo-btn);
  border: 1px solid var(--demo-line); border-radius: 6px; padding: 3px 9px; cursor: pointer; text-decoration: none; }
.demo-strip .push { margin-left: auto; display: inline-flex; gap: 6px; }
.demo-menu { position: fixed; z-index: 901; bottom: 46px; max-height: 60vh; overflow: auto;
  width: min(340px, calc(100vw - 32px)); padding: 6px; border-radius: 10px; display: grid; gap: 2px;
  background: var(--surface-1); border: 1px solid var(--border); box-shadow: 0 8px 28px rgba(0,0,0,.2); }
.demo-menu button { text-align: left; font: 13px var(--font-ui); color: var(--text-primary); background: none;
  border: 0; border-radius: 6px; padding: 7px 10px; cursor: pointer; }
.demo-menu button:hover, .demo-menu button.on { background: var(--ghost); }
.demo-menu small { display: block; color: var(--text-secondary); font-size: 12px; }
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: 5px;
  border-radius: 10px; background: var(--surface-1); border: 1px solid var(--border);
  box-shadow: 0 10px 32px rgba(0, 0, 0, .22); font: 13px var(--font-ui); color: var(--text-primary);
  animation: halo-in .12s ease-out; }
@keyframes halo-in { from { opacity: 0; transform: translateY(-3px) scale(.98); } }
.halo-h { padding: 6px 9px 3px; font-size: 11px; font-weight: 600; color: var(--text-muted);
  text-transform: uppercase; letter-spacing: .05em; }
.halo-item { display: flex; align-items: center; gap: 8px; width: 100%; padding: 6px 9px; border: 0; border-radius: 6px;
  background: none; color: inherit; font: inherit; text-align: left; cursor: pointer; }
.halo-item:hover, .halo-item:focus-visible { background: var(--ghost); outline: none; }
.halo-item:disabled { opacity: .45; cursor: default; }
.halo-ic { width: 16px; flex: none; text-align: center; color: var(--text-secondary); }
.halo-l { flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.halo-k { color: var(--text-muted); font-size: 11px; }
.halo-sep { height: 1px; margin: 4px 6px; background: var(--border); }
.halo-foot { padding: 6px 9px 3px; font-size: 11px; color: var(--text-muted); border-top: 1px solid var(--border); margin-top: 4px; }
.halo-note { position: fixed; left: 50%; bottom: 24px; transform: translateX(-50%); z-index: 1001; padding: 8px 14px;
  border-radius: 999px; background: var(--text-primary); color: var(--plane); font: 600 13px var(--font-ui); }
@media (prefers-reduced-motion: reduce) { .halo { animation: none; } }

/* Glass falls back to solid when the person asks for less transparency or
   more contrast. */
@media (max-width: 760px) and (prefers-reduced-transparency: reduce) {
  .sidebar { background: var(--surface-1); -webkit-backdrop-filter: none; backdrop-filter: none; }
}
@media (max-width: 760px) and (prefers-contrast: more) {
  .sidebar { background: var(--surface-1); -webkit-backdrop-filter: none; backdrop-filter: none;
    border-color: 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 chat box sits just above the floating bar. */
  .wrap:has(> .chatpanel:not([hidden])) { padding-bottom: calc(80px + env(safe-area-inset-bottom, 0px)); }
}

/* Android: a Material 3 navigation bar instead of the iOS glass pill.
   Full width along the bottom, tonal surface, icon over label, a pill
   indicator behind the current tab's icon. */
@media (max-width: 760px) {
  html.os-android .sidebar {
    left: 0; right: 0; bottom: 0; width: auto; margin: 0;
    padding: 12px 0 calc(16px + env(safe-area-inset-bottom, 0px));
    border: none; border-radius: 0; box-shadow: none;
    -webkit-backdrop-filter: none; backdrop-filter: none;
    background: color-mix(in srgb, var(--series-1) 7%, var(--surface-1));
  }
  html.os-android .sidenav { gap: 0; }
  html.os-android .sidenav .tab {
    position: relative; isolation: isolate;
    flex: 1 1 0; width: auto; min-width: 0; height: auto;
    flex-direction: column; gap: 4px; padding: 0;
    border-radius: 0; background: none; color: var(--text-secondary);
  }
  html.os-android .sidenav .tab::before {
    content: ""; position: absolute; z-index: -1; top: 0; left: 50%;
    width: 64px; height: 32px; margin-left: -32px; border-radius: 999px;
    background: transparent; transform: scaleX(0.5); opacity: 0;
    transition: transform 0.2s cubic-bezier(0.2, 0, 0, 1), opacity 0.2s, background-color 0.1s;
  }
  html.os-android .sidenav .tab svg { margin: 4px 0; }
  html.os-android .sidenav .tab span {
    position: static; width: auto; height: auto; overflow: hidden; clip: auto; clip-path: none;
    max-width: 100%; text-overflow: ellipsis;
    font-size: 12px; font-weight: 500; letter-spacing: 0.04em; line-height: 16px;
  }
  html.os-android .sidenav .tab:active { transform: none; }
  html.os-android .sidenav .tab:active::before { opacity: 1; transform: none;
    background: color-mix(in srgb, var(--text-primary) 8%, transparent); }
  html.os-android .sidenav .tab[aria-selected="true"] { background: none; color: var(--text-primary); }
  html.os-android .sidenav .tab[aria-selected="true"] svg { color: var(--series-1); }
  html.os-android .sidenav .tab[aria-selected="true"]::before {
    opacity: 1; transform: none; background: color-mix(in srgb, var(--series-1) 22%, transparent);
  }
  html.os-android .wrap { padding-bottom: calc(100px + env(safe-area-inset-bottom, 0px)); }
  html.os-android .wrap:has(> .chatpanel:not([hidden])) { padding-bottom: calc(92px + env(safe-area-inset-bottom, 0px)); }
  html.os-android #toasts { bottom: calc(92px + env(safe-area-inset-bottom, 0px)); }
  html.os-android body.has-demo-strip .sidebar { bottom: 48px; }
}

/* 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: 1px solid var(--border); box-shadow: 0 2px 8px rgba(0, 0, 0, 0.12);
  }
  .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) {
    .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(> .chatpanel:not([hidden])) { padding-bottom: 12px; }
  /* Typing in the chat: the conversation gets all the room. */
  html.kb-open .wrap:has(> .chatpanel:not([hidden])) > :is(#banners, #alerts) { display: none; }
  html.kb-open .chatpanel { min-height: 0; }
}

/* "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;
  padding: 24px; background: rgba(0, 0, 0, 0.45);
  -webkit-backdrop-filter: blur(6px); backdrop-filter: blur(6px);
}
.update-card {
  width: min(340px, 100%); padding: 24px 22px 14px; border-radius: 18px; text-align: center;
  background: var(--surface-1); border: 1px solid var(--border); box-shadow: 0 20px 50px rgba(0, 0, 0, 0.3);
  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: 650; }
.update-card p { margin: 0 0 6px; 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; }

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

.duelist .what .nm .kind,
.newslist .kind, .coursepage .kind {
  font-size: 10px; font-weight: 600; letter-spacing: .02em; text-transform: uppercase;
  color: var(--text-muted); margin-right: 6px;
}
.duelist .moved {
  display: inline-block; white-space: nowrap;
  margin-left: 8px; padding: 0 6px; border-radius: 999px;
  color: var(--serious); border: 1px solid currentColor; font-weight: 600;
}
.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: 11px; color: var(--text-muted); }
.duelist .personal {
  display: inline-block; white-space: nowrap; margin-left: 8px; padding: 0 6px;
  border-radius: 999px; color: var(--series-1); border: 1px solid currentColor; font-weight: 600;
}
.state.event { color: var(--text-secondary); }

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

.changelist, .newslist { list-style: none; margin: 0; padding: 0; }
.changelist li { padding: 9px 16px; border-bottom: 1px solid var(--grid); }
.changelist li:last-child, .newslist li:last-child { border-bottom: none; }
.changelist .txt { font-size: 13px; }
.changelist li.moved .txt { color: var(--serious); font-weight: 500; }
.changelist .meta, .newslist .meta { font-size: 11px; color: var(--text-muted); margin-top: 2px; }

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

.newslist li { border-bottom: 1px solid var(--grid); }
.newslist .head {
  display: block; width: 100%; text-align: left; padding: 10px 16px;
  background: none; border: 0; border-radius: 0; color: inherit; font: inherit; cursor: pointer;
}
.newslist .head:hover { background: var(--ghost); }
.newslist .top { display: flex; align-items: baseline; gap: 6px; min-width: 0; }
.newslist .subj { font-size: 13px; font-weight: 500; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.newslist li.announce .kind { color: var(--series-1); }
.newslist .fresh {
  flex: 0 0 auto; font-size: 10px; font-weight: 600; padding: 0 6px; border-radius: 999px;
  color: var(--good); border: 1px solid currentColor;
}
.newslist .body { padding: 0 16px 12px; }
.newslist .atts { list-style: none; margin: 6px 0; padding: 0; display: flex; flex-wrap: wrap; gap: 6px; }
.newslist .reply { margin-top: 10px; padding-left: 10px; border-left: 2px solid var(--grid); }
.newslist .go, .coursepage a { color: var(--series-1); }
.newslist .go { display: inline-block; margin-top: 8px; font-size: 12px; }

/* What teachers wrote, cleaned on the server: read as text, in Lora. */
.richtext { font-family: var(--font-read); font-size: 14px; line-height: 1.5; overflow-wrap: anywhere; }
.richtext > :first-child { margin-top: 0; }
.richtext > :last-child { margin-bottom: 0; }
.richtext p { margin: 0 0 .6em; }
.richtext ul, .richtext ol { margin: 0 0 .6em; padding-left: 1.3em; }
.richtext table { border-collapse: collapse; font-size: 13px; }
.richtext td, .richtext th { border: 1px solid var(--grid); padding: 3px 6px; }
.richtext a { color: var(--series-1); }
.richtext img { max-width: 100%; height: auto; border-radius: 6px; display: block; margin: 6px 0; }

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

.gradegrid { display: grid; gap: 12px 24px; grid-template-columns: repeat(auto-fill, minmax(260px, 1fr)); padding: 12px 16px; }
.gcourse h3 { display: flex; gap: 8px; margin: 0 0 4px; font-size: 13px; font-weight: 600; }
.gcourse h3 .total { margin-left: auto; color: var(--series-1); font-variant-numeric: tabular-nums; }
.gcourse ul { list-style: none; margin: 0; padding: 0; }
.gcourse li { display: flex; gap: 8px; padding: 4px 0; font-size: 13px; border-bottom: 1px solid var(--grid); }
.gcourse li:last-child { border-bottom: none; }
.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); cursor: help; }
.gcourse .g { flex: 0 0 auto; font-variant-numeric: tabular-nums; font-weight: 500; }

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

.coursepage { padding: 4px 16px 12px; max-height: 520px; overflow-y: auto; }
.psec { padding: 8px 0; border-bottom: 1px solid var(--grid); }
.psec:last-child { border-bottom: none; }
.psec h3 { margin: 0 0 4px; font-size: 13px; font-weight: 600; }
.psec ul { list-style: none; margin: 4px 0 0; padding: 0; }
.psec li { padding: 4px 0; font-size: 13px; }
.psec li .host { margin-left: 6px; font-size: 11px; 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(--ghost); border-radius: 8px; padding: 8px; word-break: break-word;
}
