/* ==========================================================================
   Design system — ported from the Twincom admin console look (2026-10-07).
   Clean, dense but breathable: light sidebar with flyout groups, 60px top
   bar, flat 8px cards, uppercase table headers, navy chrome with a single
   red accent. Bootstrap 5.3 (via Tabler) stays the base: this file sets
   `--tw-*` tokens, styles the `tw-*` shell, and re-skins the Tabler
   components every page already uses (bridge section), so templates
   keep their markup. The school's branding colour feeds --tw-primary.
   Fonts are bundled in static/vendor/fonts (no CDN, CLAUDE.md §5.5).
   ========================================================================== */

@font-face { font-family: "Inter"; font-style: normal; font-weight: 400; font-display: swap; src: url("../vendor/fonts/inter-latin-400-normal.73aaa95eab31.woff2") format("woff2"); }
@font-face { font-family: "Inter"; font-style: normal; font-weight: 500; font-display: swap; src: url("../vendor/fonts/inter-latin-500-normal.96948ea7ac03.woff2") format("woff2"); }
@font-face { font-family: "Inter"; font-style: normal; font-weight: 600; font-display: swap; src: url("../vendor/fonts/inter-latin-600-normal.0bf7eadca131.woff2") format("woff2"); }
@font-face { font-family: "Inter"; font-style: normal; font-weight: 700; font-display: swap; src: url("../vendor/fonts/inter-latin-700-normal.2a4c97ec45ef.woff2") format("woff2"); }
@font-face { font-family: "JetBrains Mono"; font-style: normal; font-weight: 400; font-display: swap; src: url("../vendor/fonts/jetbrains-mono-latin-400-normal.0062bf1cab80.woff2") format("woff2"); }
@font-face { font-family: "JetBrains Mono"; font-style: normal; font-weight: 500; font-display: swap; src: url("../vendor/fonts/jetbrains-mono-latin-500-normal.b1734e00c40a.woff2") format("woff2"); }

[x-cloak] { display: none !important; }

/* --------------------------------------------------------------------------
   Tokens
   -------------------------------------------------------------------------- */
:root {
  --brand-primary: #1E2A78;
  --brand-accent: #E63946;

  --tw-primary: var(--brand-primary);
  --tw-primary-hover: color-mix(in srgb, var(--tw-primary), #000 22%);
  --tw-primary-light: color-mix(in srgb, var(--tw-primary) 7%, #fff);
  --tw-accent: var(--brand-accent);
  --tw-secondary: #495057;
  --tw-success: #1E7A46;
  --tw-success-bg: #E6F2EB;
  --tw-warning: #B7791F;
  --tw-warning-bg: #FBF0DF;
  --tw-danger: #8B1A24;
  --tw-danger-bg: #F1E4E5;
  --tw-info: #2C6FBB;
  --tw-info-bg: #E8F0FA;
  --tw-surface: #F8F9FB;
  --tw-surface-raised: #FFFFFF;
  --tw-border: #E3E6EB;
  --tw-text: #222222;
  --tw-muted: #6B7280;

  --tw-radius-card: 8px;
  --tw-radius-control: 6px;
  --tw-shadow-sm: 0 1px 2px rgba(16, 24, 40, 0.04);
  --tw-shadow-md: 0 4px 12px rgba(16, 24, 40, 0.08);
  --tw-sidebar-width: 240px;
  --tw-sidebar-width-collapsed: 64px;
  --tw-navbar-height: 60px;
  --tw-footer-height: 40px;
  --tw-gap: 16px;

  --tw-font-ui: "Inter", -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif;
  --tw-font-mono: "JetBrains Mono", "SFMono-Regular", Consolas, monospace;
}

/* Dark mode: same tokens, re-valued. The brand colour is lifted so it still
   reads as text and still carries white text on buttons. */
:root[data-bs-theme="dark"] {
  --tw-primary: color-mix(in srgb, var(--brand-primary) 55%, #8FA0FF);
  --tw-primary-hover: color-mix(in srgb, var(--tw-primary), #fff 12%);
  --tw-primary-light: color-mix(in srgb, var(--tw-primary) 18%, #0F121A);
  --tw-accent: color-mix(in srgb, var(--brand-accent) 80%, #fff);
  --tw-secondary: #ADB5BD;
  --tw-success: #4CC38A;
  --tw-success-bg: #12301F;
  --tw-warning: #E6A23C;
  --tw-warning-bg: #33280F;
  --tw-danger: #F2727E;
  --tw-danger-bg: #3A1519;
  --tw-info: #6AA5F0;
  --tw-info-bg: #13263D;
  --tw-surface: #0F121A;
  --tw-surface-raised: #171B26;
  --tw-border: #2A3040;
  --tw-text: #E6E8EE;
  --tw-muted: #9AA3B2;
  --tw-shadow-sm: 0 1px 2px rgba(0, 0, 0, 0.4);
  --tw-shadow-md: 0 4px 14px rgba(0, 0, 0, 0.45);
  color-scheme: dark;
}

/* --------------------------------------------------------------------------
   Bridge: Tabler / Bootstrap variables follow the tokens
   -------------------------------------------------------------------------- */
:root, :root[data-bs-theme="dark"] {
  --tblr-font-sans-serif: var(--tw-font-ui);
  --tblr-font-monospace: var(--tw-font-mono);
  --tblr-body-font-family: var(--tw-font-ui);
  --tblr-body-font-size: 14px;
  --tblr-body-bg: var(--tw-surface);
  --tblr-body-color: var(--tw-text);
  --tblr-bg-surface: var(--tw-surface-raised);
  --tblr-bg-surface-secondary: var(--tw-surface);
  --tblr-bg-surface-tertiary: var(--tw-surface);
  --tblr-border-color: var(--tw-border);
  --tblr-border-color-translucent: var(--tw-border);
  --tblr-secondary: var(--tw-muted);
  --tblr-secondary-color: var(--tw-muted);
  --tblr-muted: var(--tw-muted);
  --tblr-border-radius: var(--tw-radius-control);
  --tblr-border-radius-lg: var(--tw-radius-card);
  --tblr-card-border-radius: var(--tw-radius-card);
  --tblr-box-shadow-card: var(--tw-shadow-sm);
  --tblr-shadow-card: var(--tw-shadow-sm);
  --tblr-primary: var(--tw-primary);
  --tblr-primary-rgb: var(--brand-primary-rgb, 30, 42, 120);
  --tblr-primary-fg: #fff;
  --tblr-primary-darken: var(--tw-primary-hover);
  --tblr-primary-lt: var(--tw-primary-light);
  --tblr-link-color: var(--tw-primary);
  --tblr-link-hover-color: var(--tw-primary-hover);
  --tblr-danger: var(--tw-danger);
  --tblr-success: var(--tw-success);
  --tblr-warning: var(--tw-warning);
  --tblr-info: var(--tw-info);
  --bs-body-bg: var(--tw-surface);
  --bs-body-color: var(--tw-text);
  --bs-border-color: var(--tw-border);
}

html, body {
  background: var(--tw-surface);
  color: var(--tw-text);
  font-family: var(--tw-font-ui);
  font-size: 14px;
  -webkit-font-smoothing: antialiased;
}
.tw-mono, code, kbd { font-family: var(--tw-font-mono); }
a { color: var(--tw-primary); }
a:hover { color: var(--tw-primary-hover); }
:focus-visible {
  outline: 2px solid color-mix(in srgb, var(--tw-primary) 45%, transparent);
  outline-offset: 1px;
}
.min-w-0 { min-width: 0; }
.text-none { text-transform: none !important; letter-spacing: 0 !important; }

/* --------------------------------------------------------------------------
   Shell layout
   -------------------------------------------------------------------------- */
.tw-shell {
  display: grid;
  grid-template-columns: var(--tw-sidebar-width) minmax(0, 1fr);
  min-height: 100vh;
  transition: grid-template-columns 0.18s ease;
}
html.sidebar-collapsed .tw-shell { grid-template-columns: var(--tw-sidebar-width-collapsed) minmax(0, 1fr); }
.tw-main { display: flex; flex-direction: column; min-height: 100vh; min-width: 0; }
/* Nothing makes the page itself scroll sideways (the hidden help panel did on phones); wide tables scroll in their own box.
   clip, not hidden: the sticky top bar keeps working. */
html, body { overflow-x: clip; }
.tw-content { flex: 1; padding: 24px 28px 40px; max-width: 1440px; width: 100%; }
.tw-sidebar-scrim { display: none; position: fixed; inset: 0; background: rgba(15, 20, 30, 0.4); z-index: 1030; }

/* --------------------------------------------------------------------------
   Sidebar
   -------------------------------------------------------------------------- */
.tw-sidebar {
  background: var(--tw-surface-raised);
  color: var(--tw-text);
  border-right: 1px solid var(--tw-border);
  display: flex;
  flex-direction: column;
  position: sticky;
  top: 0;
  height: 100vh;
  overflow-y: auto;
  overflow-x: hidden;
  z-index: 1031;
}
.tw-sidebar-header { display: flex; align-items: center; justify-content: space-between; gap: 8px; padding: 16px 16px 12px; flex-shrink: 0; }
.tw-brand { display: flex; align-items: center; gap: 10px; color: var(--tw-text); min-width: 0; text-decoration: none; }
.tw-brand:hover { color: var(--tw-text); }
.tw-brand-logo { flex-shrink: 0; width: 32px; height: 32px; object-fit: contain; border-radius: 6px; }
.tw-brand-mark { display: inline-flex; align-items: center; justify-content: center; background: var(--tw-primary); color: #fff; font-size: 16px; }
.tw-brand-text { display: flex; flex-direction: column; line-height: 1.25; min-width: 0; }
.tw-brand-name { font-weight: 600; font-size: 15px; line-height: 1.2; color: var(--tw-primary); overflow: hidden;
    display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; }  /* two lines, not cut short (review 2026-10-17) */
.tw-brand-tagline { font-size: 11px; line-height: 1.25; color: var(--tw-muted); overflow: hidden; display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; }
.tw-brand-underline { height: 2px; width: 140px; max-width: calc(100% - 32px); margin: 0 16px 14px; background: var(--tw-accent); flex-shrink: 0; }

.tw-sidebar-toggle {
  background: transparent; border: none; color: var(--tw-muted);
  width: 28px; height: 28px; border-radius: var(--tw-radius-control);
  display: inline-flex; align-items: center; justify-content: center; flex-shrink: 0;
}
.tw-sidebar-toggle:hover { background: var(--tw-surface); color: var(--tw-text); }

.tw-sidebar-nav { flex: 1; padding: 4px 8px 12px; display: flex; flex-direction: column; gap: 2px; }

.tw-nav-link, .tw-nav-group-btn {
  display: flex; align-items: center; gap: 12px; width: 100%;
  padding: 9px 12px; border: 0; border-left: 3px solid transparent;
  border-radius: var(--tw-radius-control); background: transparent;
  color: var(--tw-text); font: inherit; font-weight: 500; font-size: 13.5px;
  text-align: left; white-space: nowrap; cursor: pointer; text-decoration: none;
}
.tw-nav-link i, .tw-nav-group-btn > i:first-child { font-size: 16px; width: 18px; text-align: center; flex-shrink: 0; }
.tw-nav-link:hover, .tw-nav-group-btn:hover, .tw-nav-group.is-open > .tw-nav-group-btn { background: var(--tw-surface); color: var(--tw-text); }
.tw-nav-link.active, .tw-nav-group.is-current > .tw-nav-group-btn {
  background: var(--tw-primary-light); border-left-color: var(--tw-primary);
  color: var(--tw-primary); font-weight: 600;
}
.tw-nav-label { overflow: hidden; text-overflow: ellipsis; }
.tw-nav-group { position: relative; }
.tw-nav-chevron { margin-left: auto; font-size: 11px; color: var(--tw-muted); transition: transform .15s ease; }
.tw-nav-group.is-current .tw-nav-chevron { color: var(--tw-primary); }

.tw-nav-flyout {
  position: fixed; z-index: 1080; min-width: 224px; max-width: 280px;
  max-height: calc(100vh - 16px); overflow-y: auto;
  padding: 6px; background: var(--tw-surface-raised);
  border: 1px solid var(--tw-border); border-radius: var(--tw-radius-card);
  box-shadow: var(--tw-shadow-md);
}
.tw-nav-flyout[hidden] { display: none; }
.tw-nav-flyout-title { padding: 6px 10px; font-size: 12px; font-weight: 600; color: var(--tw-muted); }
.tw-nav-flyout-link {
  display: flex; align-items: center; gap: 10px;
  padding: 8px 10px; border-left: 3px solid transparent; border-radius: var(--tw-radius-control);
  color: var(--tw-text); font-size: 13.5px; font-weight: 500; text-decoration: none; white-space: nowrap;
}
.tw-nav-flyout-link:hover { background: var(--tw-surface); color: var(--tw-text); }
.tw-nav-flyout-link.active { background: var(--tw-primary-light); border-left-color: var(--tw-primary); color: var(--tw-primary); font-weight: 600; }
.tw-nav-flyout-link i { color: var(--tw-muted); }

.tw-sidebar-footer { padding: 10px 12px; border-top: 1px solid var(--tw-border); }
.tw-sidebar-user { display: flex; align-items: center; gap: 10px; min-width: 0; }
.tw-sidebar-user .tw-avatar { flex-shrink: 0; }

/* Collapsed (desktop, remembered): icons only */
@media (min-width: 992px) {
  html.sidebar-collapsed .tw-brand-text,
  html.sidebar-collapsed .tw-brand-underline,
  html.sidebar-collapsed .tw-nav-label,
  html.sidebar-collapsed .tw-nav-chevron { display: none !important; }
  html.sidebar-collapsed .tw-sidebar-header { flex-direction: column; justify-content: center; padding: 12px 8px; }
  html.sidebar-collapsed .tw-nav-link,
  html.sidebar-collapsed .tw-nav-group-btn,
  html.sidebar-collapsed .tw-sidebar-user { justify-content: center; }
}

/* --------------------------------------------------------------------------
   Top bar
   -------------------------------------------------------------------------- */
.tw-navbar {
  height: calc(var(--tw-navbar-height) + env(safe-area-inset-top, 0px));
  padding: env(safe-area-inset-top, 0px) max(20px, env(safe-area-inset-right, 0px)) 0 max(20px, env(safe-area-inset-left, 0px));
  background: var(--tw-surface-raised);
  border-bottom: 1px solid var(--tw-border);
  display: flex; align-items: center; gap: 16px;
  position: sticky; top: 0; z-index: 1020;
}
.tw-navbar-burger {
  background: none; border: none; font-size: 22px; color: var(--tw-text);
  width: 34px; height: 34px; display: inline-flex; align-items: center; justify-content: center; flex-shrink: 0;
}
.tw-history-nav { align-items: center; gap: 2px; flex-shrink: 0; }
.tw-history-nav .tw-icon-btn { width: 32px; height: 32px; font-size: 16px; }
/* Shortcut strip under the top bar (2026-10-30): starred pages, then the role's everyday pages; one row that scrolls sideways. */
.tw-shortcuts {
  display: flex; gap: 6px; overflow-x: auto; scrollbar-width: none; -webkit-overflow-scrolling: touch;
  padding: 8px max(20px, env(safe-area-inset-right, 0px)) 8px max(20px, env(safe-area-inset-left, 0px));
  background: var(--tw-surface-raised); border-bottom: 1px solid var(--tw-border);
}
.tw-shortcuts::-webkit-scrollbar { display: none; }
.tw-shortcut {
  flex: 0 0 auto; display: inline-flex; align-items: center; gap: 6px; padding: 5px 12px; border-radius: 999px;
  border: 1px solid var(--tw-border); background: var(--tw-surface); color: var(--tw-text);
  font-size: 13px; font-weight: 500; text-decoration: none; white-space: nowrap;
}
.tw-shortcut i { color: var(--brand-primary, #1e2a78); }
.tw-shortcut.is-pinned i { color: #e0a800; }
.tw-shortcut:hover { background: var(--tw-surface-raised); border-color: var(--brand-primary, #1e2a78); color: var(--tw-text); }
.tw-shortcut.active { background: var(--brand-primary, #1e2a78); border-color: var(--brand-primary, #1e2a78); color: #fff; }
.tw-shortcut.active i { color: #fff; }
@media (max-width: 575.98px) {
  .tw-shortcuts { padding: 8px 10px; }
  .tw-shortcut { padding: 8px 14px; font-size: 14px; }     /* a thumb's size */
}
.tw-history-nav .is-reloading i { display: inline-block; animation: tw-spin .8s linear infinite; }
@keyframes tw-spin { to { transform: rotate(360deg); } }

.tw-search { position: relative; flex: 1 1 auto; min-width: 0; max-width: 640px; }
.tw-search-input { min-width: 0; }
.tw-search-form { position: relative; display: flex; align-items: center; width: 100%; }
.tw-search-icon { position: absolute; left: 12px; color: var(--tw-muted); font-size: 14px; pointer-events: none; }
.tw-search-input {
  width: 100%; border: 1px solid var(--tw-border); background: var(--tw-surface); color: var(--tw-text);
  border-radius: var(--tw-radius-control); padding: 8px 36px 8px 34px; font-size: 13.5px;
}
.tw-search-input:focus { outline: none; border-color: var(--tw-primary); box-shadow: 0 0 0 3px var(--tw-primary-light); background: var(--tw-surface-raised); }
.tw-search-kbd {
  position: absolute; right: 10px; font-size: 11px; color: var(--tw-muted); line-height: 1.4;
  border: 1px solid var(--tw-border); border-radius: 4px; padding: 1px 6px; background: var(--tw-surface-raised); box-shadow: none;
}
.app-search-suggestions { position: absolute; top: calc(100% + 6px); left: 0; right: 0; min-width: 320px; z-index: 1050; }
.app-search-suggestions:empty { display: none; }
.app-search-suggestions .card { box-shadow: var(--tw-shadow-md); }
/* Solid, not glass: a blur inside the top bar's own blur does not work, so the page showed through. */
.app-search-suggestions .card { background: var(--tblr-bg-surface, #fff); -webkit-backdrop-filter: none; backdrop-filter: none; }
.app-search.htmx-request .tw-search-icon { animation: app-pulse 1s infinite; }

.tw-navbar-actions { display: flex; align-items: center; gap: 8px; margin-left: auto; }
.tw-icon-btn {
  position: relative; width: 36px; height: 36px; border-radius: 50%;
  border: none; background: transparent; color: var(--tw-secondary);
  display: inline-flex; align-items: center; justify-content: center; font-size: 17px; text-decoration: none;
}
.tw-icon-btn:hover { background: var(--tw-surface); color: var(--tw-text); }
.tw-icon-btn:disabled { color: var(--tw-border); background: transparent; cursor: default; }
.tw-badge-count {
  position: absolute; top: 2px; right: 1px; min-width: 17px; height: 17px; padding: 0 4px; border-radius: 9px;
  background: var(--tw-accent); color: #fff; border: 1.5px solid var(--tw-surface-raised);
  font-size: 10px; font-weight: 700; line-height: 14px; text-align: center;
}
.tw-chip-btn {
  display: inline-flex; align-items: center; gap: 6px; height: 34px; padding: 0 12px;
  border: 1px solid var(--tw-border); border-radius: 999px; background: var(--tw-surface-raised);
  color: var(--tw-text); font-size: 13px; font-weight: 500;
}
.tw-chip-btn:hover { background: var(--tw-surface); }

.tw-theme-toggle .tw-theme-icon-light { display: none; }
[data-bs-theme="dark"] .tw-theme-toggle .tw-theme-icon-light { display: inline; }
[data-bs-theme="dark"] .tw-theme-toggle .tw-theme-icon-dark { display: none; }
.tw-theme-choice .btn.active { background: var(--tw-primary); border-color: var(--tw-primary); color: #fff; }

.tw-user-menu-btn {
  display: flex; align-items: center; gap: 8px; background: transparent; border: none;
  padding: 4px 8px 4px 4px; border-radius: 999px; color: var(--tw-text);
}
.tw-user-menu-btn:hover { background: var(--tw-surface); }
.tw-avatar {
  width: 32px; height: 32px; border-radius: 50%;
  background: var(--tw-primary-light); color: var(--tw-primary);
  display: inline-flex; align-items: center; justify-content: center; font-weight: 700; font-size: 12px;
}
.tw-avatar-photo { overflow: hidden; }
.tw-avatar-photo img { width: 100%; height: 100%; object-fit: cover; object-position: center 20%; }
.tw-user-meta { flex-direction: column; align-items: flex-start; line-height: 1.2; min-width: 0; }
.tw-user-name { font-weight: 600; font-size: 12.5px; color: var(--tw-text); }
.tw-role-badge { font-size: 11px; color: var(--tw-muted); }
.tw-user-menu-caret { font-size: 10px; color: var(--tw-muted); }

.tw-dropdown {
  border: 1px solid var(--tw-border); border-radius: var(--tw-radius-card);
  box-shadow: var(--tw-shadow-md); padding: 8px; min-width: 240px; background: var(--tw-surface-raised);
}
.tw-dropdown .dropdown-item { border-radius: var(--tw-radius-control); font-size: 13.5px; padding: 7px 10px; }
.tw-dropdown .dropdown-item.active, .tw-dropdown .dropdown-item:active { background: var(--tw-primary-light); color: var(--tw-primary); }
.tw-dropdown-header { font-size: 12px; font-weight: 700; color: var(--tw-muted); padding: 6px 8px; text-transform: uppercase; letter-spacing: 0.04em; }
.tw-notif-dropdown { width: 340px; max-width: calc(100vw - 24px); }
.tw-notif-list { max-height: 60vh; overflow-y: auto; }
.tw-notif-item { white-space: normal; border-bottom: 1px solid var(--tw-border); padding-top: 8px; padding-bottom: 8px; }
.tw-notif-item:last-child { border-bottom: 0; }
.tw-notif-item.is-unread { background: var(--tw-primary-light); }
.tw-notif-time { font-size: 11px; color: var(--tw-muted); margin-top: 2px; }
.tw-notif-dot { width: 8px; height: 8px; border-radius: 50%; margin-top: 6px; flex-shrink: 0; background: var(--tw-info); }
.tw-notif-dot-success { background: var(--tw-success); }
.tw-notif-dot-warning { background: var(--tw-warning); }
.tw-notif-dot-danger { background: var(--tw-danger); }
.tw-notif-dot.is-read { background: var(--tw-border); }

/* --------------------------------------------------------------------------
   Breadcrumbs / page header
   -------------------------------------------------------------------------- */
.tw-breadcrumbs { margin-bottom: 12px; }
.tw-breadcrumbs .breadcrumb { font-size: 12.5px; --tblr-breadcrumb-divider: "›"; }
.tw-breadcrumbs .breadcrumb-item, .tw-breadcrumbs .breadcrumb-item a { color: var(--tw-muted); }
.tw-breadcrumbs .breadcrumb-item a:hover { color: var(--tw-primary); }
.tw-breadcrumbs .breadcrumb-item.active { color: var(--tw-text); font-weight: 600; }
.tw-page-header { display: flex; align-items: flex-start; justify-content: space-between; gap: 16px; margin-bottom: 24px; flex-wrap: wrap; }
.tw-page-title { font-size: 22px; font-weight: 700; margin: 0 0 4px; color: var(--tw-text); line-height: 1.25; }
.tw-page-title:empty { display: none; }
.tw-page-subtitle { font-size: 13.5px; color: var(--tw-muted); margin: 0; max-width: 720px; }
.tw-page-subtitle:empty, .page-pretitle:empty { display: none; }
.tw-page-actions { display: flex; gap: 8px; flex-shrink: 0; flex-wrap: wrap; }
.tw-page-actions:empty { display: none; }
.tw-page-header > .min-w-0 { flex: 1 1 auto; }
.tw-page-tools { display: flex; align-items: center; gap: 2px; flex-shrink: 0; margin-left: -10px; }
.page-pretitle { font-size: 11px; font-weight: 700; text-transform: uppercase; letter-spacing: .06em; color: var(--tw-muted); }

/* --------------------------------------------------------------------------
   Cards, KPI tiles
   -------------------------------------------------------------------------- */
.card {
  background: var(--tw-surface-raised);
  border: 1px solid var(--tw-border);
  border-radius: var(--tw-radius-card);
  box-shadow: var(--tw-shadow-sm);
}
.card-header { border-bottom: 1px solid var(--tw-border); background: transparent; min-height: 52px; }
.card-footer { border-top: 1px solid var(--tw-border); background: var(--tw-surface); }
.card-title { font-size: 15px; font-weight: 700; color: var(--tw-text); }
.card-sm .card-body { padding: 16px 18px; }
.card-link:hover, a.card:hover { box-shadow: var(--tw-shadow-md); }
.subheader { font-size: 12px; font-weight: 700; letter-spacing: .04em; color: var(--tw-muted); }

.stat-card { transition: box-shadow .15s ease, transform .15s ease; }
.stat-card:has(.stretched-link):hover { box-shadow: var(--tw-shadow-md); transform: translateY(-1px); }
.stat-card .avatar { border-radius: var(--tw-radius-control); }

.tw-kpi-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(220px, 1fr)); gap: var(--tw-gap); }
.tw-kpi-card {
  background: var(--tw-surface-raised); border: 1px solid var(--tw-border); border-radius: var(--tw-radius-card);
  padding: 18px 20px; box-shadow: var(--tw-shadow-sm); display: flex; align-items: flex-start; gap: 14px;
  color: var(--tw-text); text-decoration: none; transition: box-shadow .15s ease, transform .15s ease;
}
a.tw-kpi-card:hover { box-shadow: var(--tw-shadow-md); transform: translateY(-1px); color: var(--tw-text); }
.tw-kpi-icon {
  width: 40px; height: 40px; border-radius: var(--tw-radius-control);
  background: var(--tw-primary-light); color: var(--tw-primary);
  display: flex; align-items: center; justify-content: center; font-size: 18px; flex-shrink: 0;
}
.tw-kpi-label { font-size: 12.5px; color: var(--tw-muted); font-weight: 600; margin-bottom: 4px; }
.tw-kpi-value { font-size: 22px; font-weight: 700; color: var(--tw-text); line-height: 1.2; }

/* --------------------------------------------------------------------------
   Tables
   -------------------------------------------------------------------------- */
.table { --tblr-table-bg: transparent; font-size: 13.5px; color: var(--tw-text); }
.table thead th, .table > :not(caption) > * > th {
  background: var(--tw-surface);
  border-bottom: 1px solid var(--tw-border);
  color: var(--tw-primary);
  font-size: 12px; font-weight: 700; text-transform: uppercase; letter-spacing: 0.03em;
  white-space: nowrap;
}
.card-table thead th, .table.card-table > :not(caption) > * > th { padding: 10px 16px; }
.card-table tbody td { padding: 12px 16px; border-bottom: 1px solid var(--tw-border); }
.table tbody tr:last-child td { border-bottom: none; }
.table-hover > tbody > tr:hover > *, .card-table tbody tr:hover > * { --tblr-table-accent-bg: var(--tw-surface); background-color: var(--tw-surface); }
.table-sort-link { color: inherit; text-decoration: none; white-space: nowrap; }
.table-sort-link:hover { color: var(--tw-primary-hover); }
.table-sort-link.active { color: var(--tw-primary); }
.table-sort-link i { font-size: .7rem; }
.table-responsive > .table { margin-bottom: 0; }
.table td.money, .table th.money { text-align: end; font-variant-numeric: tabular-nums; white-space: nowrap; }
.tabular-nums { font-variant-numeric: tabular-nums; }

/* --------------------------------------------------------------------------
   Buttons
   -------------------------------------------------------------------------- */
.btn { border-radius: var(--tw-radius-control); font-weight: 600; font-size: 13.5px; box-shadow: none; }
.btn-sm { font-size: 12.5px; }
.btn-primary { --tblr-btn-bg: var(--tw-primary); --tblr-btn-border-color: var(--tw-primary); --tblr-btn-hover-bg: var(--tw-primary-hover); --tblr-btn-hover-border-color: var(--tw-primary-hover); --tblr-btn-active-bg: var(--tw-primary-hover); --tblr-btn-color: #fff; --tblr-btn-hover-color: #fff;
  background: var(--tw-primary); border-color: var(--tw-primary); color: #fff; }
.btn-primary:hover, .btn-primary:focus { background: var(--tw-primary-hover); border-color: var(--tw-primary-hover); color: #fff; }
.btn-outline-primary { color: var(--tw-primary); border-color: var(--tw-primary); background: transparent; }
.btn-outline-primary:hover { background: var(--tw-primary-light); color: var(--tw-primary); border-color: var(--tw-primary); }
.btn-outline-secondary, .btn:not([class*="btn-"]), .tw-btn-secondary {
  background: var(--tw-surface-raised); border: 1px solid var(--tw-border); color: var(--tw-text);
}
.btn-outline-secondary:hover, .btn:not([class*="btn-"]):hover, .tw-btn-secondary:hover { background: var(--tw-surface); color: var(--tw-text); border-color: var(--tw-border); }
/* Destructive actions are an outline, never a solid red button. */
.btn-danger, .btn-outline-danger {
  background: transparent; border: 1px solid var(--tw-danger); color: var(--tw-danger);
}
.btn-danger:hover, .btn-danger:focus, .btn-outline-danger:hover {
  background: var(--tw-danger-bg); border-color: var(--tw-danger); color: var(--tw-danger);
}
.btn-success { background: var(--tw-success); border-color: var(--tw-success); color: #fff; }
.btn-success:hover { background: color-mix(in srgb, var(--tw-success), #000 15%); color: #fff; }
.btn-ghost-secondary, .btn-link { color: var(--tw-secondary); }
.btn-ghost-secondary:hover { background: var(--tw-surface); }

/* --------------------------------------------------------------------------
   Badges: soft pills
   -------------------------------------------------------------------------- */
.badge { border-radius: 999px; font-weight: 600; font-size: 11.5px; padding: 3px 9px; }
.badge.bg-primary-lt, .badge.bg-azure-lt, .badge.bg-blue-lt, .badge.bg-indigo-lt { background: var(--tw-primary-light) !important; color: var(--tw-primary) !important; }
.badge.bg-green-lt, .badge.bg-success-lt, .badge.bg-teal-lt { background: var(--tw-success-bg) !important; color: var(--tw-success) !important; }
.badge.bg-yellow-lt, .badge.bg-orange-lt, .badge.bg-warning-lt { background: var(--tw-warning-bg) !important; color: var(--tw-warning) !important; }
.badge.bg-red-lt, .badge.bg-danger-lt, .badge.bg-pink-lt { background: var(--tw-danger-bg) !important; color: var(--tw-danger) !important; }
.badge.bg-secondary-lt, .badge.bg-muted-lt { background: rgba(107, 114, 128, 0.12) !important; color: var(--tw-secondary) !important; }
.badge.bg-purple-lt, .badge.bg-cyan-lt { background: var(--tw-info-bg) !important; color: var(--tw-info) !important; }

/* --------------------------------------------------------------------------
   Forms
   -------------------------------------------------------------------------- */
.form-control, .form-select {
  border-radius: var(--tw-radius-control); border-color: var(--tw-border);
  background-color: var(--tw-surface-raised); color: var(--tw-text); font-size: 13.5px;
}
.form-control:focus, .form-select:focus { border-color: var(--tw-primary); box-shadow: 0 0 0 3px var(--tw-primary-light); }
.form-label { font-weight: 600; font-size: 13px; color: var(--tw-text); }
.form-text { font-size: 12px; color: var(--tw-muted); }
.form-check-input:checked { background-color: var(--tw-primary); border-color: var(--tw-primary); }
.asteriskField { color: var(--tw-danger); margin-inline-start: .125rem; }
.input-group-text { background: var(--tw-surface); border-color: var(--tw-border); color: var(--tw-muted); }

/* --------------------------------------------------------------------------
   Tabs, pills, list groups, dropdowns, modals, alerts
   -------------------------------------------------------------------------- */
.nav-tabs { border-bottom-color: var(--tw-border); }
.nav-tabs .nav-link { white-space: nowrap; color: var(--tw-muted); font-weight: 600; font-size: 13.5px; border: 0; border-bottom: 2px solid transparent; }
.nav-tabs .nav-link:hover { color: var(--tw-text); border-bottom-color: var(--tw-border); }
.nav-tabs .nav-link.active { color: var(--tw-primary); background: transparent; border-bottom-color: var(--tw-primary); }
.nav-pills .nav-link { color: var(--tw-secondary); font-weight: 600; border-radius: 999px; }
.nav-pills .nav-link.active { background: var(--tw-primary); color: #fff; }
.list-group-item { border-color: var(--tw-border); background: transparent; color: var(--tw-text); }
.list-group-item-action:hover { background: var(--tw-surface); }
.dropdown-menu { border-color: var(--tw-border); border-radius: var(--tw-radius-card); box-shadow: var(--tw-shadow-md); background: var(--tw-surface-raised); }
.modal-content { border-radius: var(--tw-radius-card); border: 1px solid var(--tw-border); background: var(--tw-surface-raised); }
.alert { border-radius: var(--tw-radius-card); border: 1px solid var(--tw-border); box-shadow: none; }
.alert-success { background: var(--tw-success-bg); color: var(--tw-success); border-color: transparent; }
.alert-warning { background: var(--tw-warning-bg); color: var(--tw-warning); border-color: transparent; }
.alert-danger { background: var(--tw-danger-bg); color: var(--tw-danger); border-color: transparent; }
.alert-info { background: var(--tw-info-bg); color: var(--tw-info); border-color: transparent; }
.pagination .page-link { border-radius: var(--tw-radius-control); color: var(--tw-secondary); }
.pagination .page-item.active .page-link { background: var(--tw-primary); border-color: var(--tw-primary); color: #fff; }
.progress { background: var(--tw-surface); border-radius: 999px; }
.progress-bar { background-color: var(--tw-primary); }

.avatar { border-radius: var(--tw-radius-control); }
.brand-avatar { background: var(--tw-primary-light); color: var(--tw-primary); }

/* Empty states */
.empty, .tw-empty-state { text-align: center; padding: 48px 24px; }
.empty-icon, .tw-empty-state-icon { font-size: 32px; color: var(--tw-muted); margin-bottom: 12px; }
.empty-icon .display-6 { font-size: 32px; }
.empty-title, .tw-empty-state-title { font-weight: 700; font-size: 15px; color: var(--tw-text); margin-bottom: 4px; }
.empty-subtitle, .tw-empty-state-subtitle { font-size: 13px; color: var(--tw-muted); max-width: 360px; margin: 0 auto; }
.tw-empty-state--compact { padding: 24px 16px; }
.tw-empty-state--compact .tw-empty-state-icon { font-size: 22px; margin-bottom: 8px; }

/* Toasts */
.tw-toast-region {
  position: fixed; top: calc(76px + env(safe-area-inset-top, 0px)); right: 20px; z-index: 1090;
  display: flex; flex-direction: column; gap: 8px; width: 360px; max-width: calc(100vw - 32px);
}
.tw-toast {
  border-radius: var(--tw-radius-card); box-shadow: var(--tw-shadow-md);
  border: 1px solid var(--tw-border); padding: 12px 12px 12px 14px; background: var(--tw-surface-raised); color: var(--tw-text);
  width: 100%;
}
.tw-toast-icon { font-size: 16px; margin-right: 10px; margin-top: 1px; }
.tw-toast-success .tw-toast-icon { color: var(--tw-success); }
.tw-toast-error .tw-toast-icon, .tw-toast-danger .tw-toast-icon { color: var(--tw-danger); }
.tw-toast-warning .tw-toast-icon { color: var(--tw-warning); }
.tw-toast-info .tw-toast-icon, .tw-toast-debug .tw-toast-icon { color: var(--tw-info); }

/* Timeline (Tabler) */
.timeline-event-card { border-radius: var(--tw-radius-card); }

/* Footer */
.tw-footer {
  min-height: var(--tw-footer-height); border-top: 1px solid var(--tw-border);
  display: flex; align-items: center; gap: 8px; flex-wrap: wrap; padding: 8px 28px;
  font-size: 12px; color: var(--tw-muted); flex-shrink: 0;
}
.tw-footer-sep { opacity: 0.5; }

/* Sign-in and setup pages (no shell) */
.tw-auth-main { min-height: 100vh; padding: 24px 16px; }
body.auth-page, body.setup-page {
  background: var(--tw-surface);
  background-image: radial-gradient(circle at 20% 0%, color-mix(in srgb, var(--tw-primary) 10%, transparent), transparent 55%);
  min-height: 100vh;
}

/* Skeleton loaders */
.skeleton-line, .tw-skeleton {
  height: .75rem; border-radius: 4px;
  background: linear-gradient(90deg, var(--tw-border) 25%, color-mix(in srgb, var(--tw-border) 55%, var(--tw-surface-raised)) 37%, var(--tw-border) 63%);
  background-size: 400% 100%;
  animation: tw-skeleton-shimmer 1.4s ease infinite;
}
@keyframes tw-skeleton-shimmer { 0% { background-position: 100% 50%; } 100% { background-position: 0 50%; } }
/* Shapes for partials/components/skeleton.html (and the pop-up and page-change skeletons in static/js/app.js). */
.tw-skel { padding: 4px 0; }
.tw-skel .tw-skeleton { display: block; margin-bottom: 8px; }
.tw-skel-field { margin-bottom: 18px; }
.tw-skel-input { height: 38px !important; border-radius: 8px !important; width: 100%; margin-top: 8px; }
.tw-skel-textarea { height: 76px !important; }
.tw-skel-row { display: grid; grid-template-columns: 1fr 1fr; gap: 16px; }
.tw-skel-table { display: grid; gap: 0; }
.tw-skel-tr { display: grid; grid-template-columns: 32px 2fr 1fr 1fr; gap: 16px; align-items: center; padding: 12px 0; border-bottom: 1px solid var(--tw-border); }
.tw-skel-tr .tw-skeleton { margin: 0; }
.tw-skel-th { grid-template-columns: 2fr 2fr 1fr 1fr; padding-top: 4px; }
.tw-skel-th .tw-skeleton { height: 10px; opacity: .6; }
.tw-skel-avatar { width: 32px; height: 32px !important; border-radius: 50% !important; flex-shrink: 0; }
.tw-skel-bars { display: flex; align-items: flex-end; gap: 10px; height: 160px; margin-top: 12px; }
.tw-skel-bars .tw-skeleton { flex: 1 1 0; margin: 0; border-radius: 6px 6px 2px 2px; }
.tw-skel-cards { display: grid; grid-template-columns: repeat(auto-fit, minmax(140px, 1fr)); gap: 12px; margin: 12px 0; }
.tw-skel-card { border: 1px solid var(--tw-border); border-radius: 12px; padding: 14px; }
.tw-skel-head { display: flex; gap: 12px; align-items: center; margin-bottom: 16px; }
.tw-skel-head .tw-skeleton { margin-bottom: 6px; }
/* A page on its way: the old content gives way to its shape (static/js/app.js). */
[data-page-body].tw-page-loading > * { display: none !important; }
[data-page-body].tw-page-loading > .tw-page-skeleton { display: block !important; }
@media (max-width: 575.98px) {
  .tw-skel-row { grid-template-columns: 1fr; }
  .tw-skel-tr { grid-template-columns: 32px 1fr 64px; }
  .tw-skel-tr .tw-skeleton:last-child { display: none; }
  .tw-skel-th { display: none; }
}
@media (prefers-reduced-motion: reduce) { .tw-skeleton, .skeleton-line { animation: none; } }
@keyframes app-pulse { 50% { opacity: .3; } }

/* --------------------------------------------------------------------------
   Responsive: tablet collapses to an icon rail, phones go off-canvas
   -------------------------------------------------------------------------- */
@media (max-width: 1199.98px) and (min-width: 992px) {
  .tw-shell { grid-template-columns: var(--tw-sidebar-width-collapsed) minmax(0, 1fr); }
  .tw-brand-text, .tw-brand-underline, .tw-nav-label, .tw-nav-chevron { display: none !important; }
  .tw-sidebar-header { flex-direction: column; justify-content: center; padding: 12px 8px; }
  .tw-nav-link, .tw-nav-group-btn, .tw-sidebar-user { justify-content: center; }
  [data-sidebar-toggle] { display: none !important; }
}

@media (max-width: 991.98px) {
  .tw-shell, html.sidebar-collapsed .tw-shell { grid-template-columns: minmax(0, 1fr); }
  .tw-sidebar {
    position: fixed; left: 0; top: 0; width: var(--tw-sidebar-width);
    transform: translateX(-100%); transition: transform 0.2s ease;
    padding-top: env(safe-area-inset-top, 0px); padding-bottom: env(safe-area-inset-bottom, 0px);
  }
  html.sidebar-mobile-open .tw-sidebar { transform: translateX(0); box-shadow: var(--tw-shadow-md); }
  html.sidebar-mobile-open .tw-sidebar-scrim { display: block; }
  /* Groups expand inline as an accordion instead of a flyout. */
  .tw-nav-flyout {
    position: static; min-width: 0; max-width: none; max-height: none; overflow: visible;
    margin: 2px 0 4px; padding: 0 0 0 18px; border: 0; box-shadow: none; background: transparent;
  }
  .tw-nav-flyout-title { display: none; }
  .tw-nav-group.is-open .tw-nav-chevron { transform: rotate(90deg); }
  .tw-content { padding: 16px; }
  .tw-navbar { gap: 10px; }
  .tw-search-kbd { display: none; }
  .tw-footer { padding: 8px 16px; }
}

@media (max-width: 575.98px) {
  .tw-navbar { gap: 6px; padding-left: 10px; padding-right: 10px; }
  .tw-navbar-actions { gap: 2px; }
  .tw-navbar .tw-theme-toggle, .tw-navbar .tw-user-menu-caret { display: none; }
  /* Two rows on phones (review 2026-10-20): menu, school name, bell, account; then the search box, full width. */
  /* An app's top bar (2026-10-30, after Apple's and Material's guidelines): one 56px row of Back or logo, the school,
     search, bell and account; the search box opens under it from the search icon. */
  .tw-navbar { height: auto; min-height: calc(56px + env(safe-area-inset-top, 0px)); flex-wrap: wrap; row-gap: 0;
               padding-top: env(safe-area-inset-top, 0px); padding-bottom: 0; }
  /* Only the school's logo and a long search bar (product owner, 2026-10-30). */
  .tw-navbar { flex-wrap: nowrap; gap: 10px; padding-left: 12px; padding-right: 12px; }
  .tw-navbar-school { flex: 0 0 auto; display: flex; align-items: center; height: 56px; text-decoration: none; }
  .tw-navbar-school .tw-brand-logo { width: 36px !important; height: 36px !important; border-radius: 10px; }
  .tw-phone-back { width: 36px; height: 36px; font-size: 18px; flex-shrink: 0; margin-left: -4px; }
  .tw-navbar-school .tw-brand-logo { width: 28px; height: 28px; }
  .tw-navbar-school .tw-brand-name { display: block; white-space: nowrap; }
  .tw-navbar-actions { margin-left: 0; }
  .tw-search { flex: 1 1 auto; max-width: none; }
  .tw-search-input { height: 42px !important; border-radius: 999px !important; padding-left: 40px !important;
    background: var(--tw-surface) !important; border: 1px solid var(--tw-border) !important; }
  .tw-search-input:focus { background: var(--tw-surface-raised) !important; border-color: var(--brand-primary, #1e2a78) !important;
    box-shadow: 0 0 0 3px color-mix(in srgb, var(--brand-primary, #1e2a78) 18%, transparent) !important; }
  .tw-search-icon { left: 15px !important; }
  /* Just under the search bar, whatever the bar's height (a notch makes it taller), across the screen. */
  .app-search-suggestions { position: absolute !important; top: calc(100% + 8px) !important; left: -50px !important; right: -4px !important;
    max-height: calc(100dvh - 200px - env(safe-area-inset-top, 0px)); overflow-y: auto; }
  .tw-tab { position: relative; }
  .tw-tab-badge { position: absolute; top: 4px; left: calc(50% + 4px); min-width: 18px; height: 18px; padding: 0 5px; border-radius: 9px;
    background: #e5484d; color: #fff; font-size: 10.5px; font-weight: 700; line-height: 18px; text-align: center; }
  .tw-notify-card-icon { width: 42px; height: 42px; flex-shrink: 0; border-radius: 12px; display: flex; align-items: center; justify-content: center;
    font-size: 20px; color: var(--brand-primary, #1e2a78); background: color-mix(in srgb, var(--brand-primary, #1e2a78) 12%, transparent); }
  /* The tab bar (partials/tabbar.html): Home, three everyday pages, Menu; fixed at the bottom, above the iPhone's home bar. */
  .tw-tabbar {
    position: fixed; left: 0; right: 0; bottom: 0; z-index: 1030; display: flex;
    padding: 4px 4px env(safe-area-inset-bottom, 0px); background: var(--tw-surface-raised);
    border-top: 1px solid var(--tw-border); box-shadow: 0 -4px 16px rgba(15, 23, 42, .06);
  }
  .tw-tab {
    flex: 1 1 0; min-width: 0; min-height: 52px; display: flex; flex-direction: column; align-items: center; justify-content: center;
    gap: 2px; border: 0; background: none; color: var(--tw-muted); text-decoration: none;
    font-size: 11px; font-weight: 500; -webkit-tap-highlight-color: transparent;
  }
  .tw-tab i { font-size: 21px; line-height: 1; }
  .tw-tab span { max-width: 100%; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
  .tw-tab.active { color: var(--brand-primary, #1e2a78); font-weight: 600; }
  .tw-tab:active { opacity: .6; }
  .tw-main { padding-bottom: calc(64px + env(safe-area-inset-bottom, 0px)); }
  /* Demonstration school: "Back to the website" sits above the tab bar, not under it. */
  body:has(.tw-tabbar) .tw-demo-home { bottom: calc(72px + env(safe-area-inset-bottom, 0px)) !important; right: 12px !important;
    padding: 6px 12px; font-size: 13px; }
  .tw-footer { display: none; }
  /* Page header as in an app: no breadcrumbs (Back is in the top bar), the title with star and help beside it,
     then the page's own buttons in one row. */
  .tw-content { padding: 14px 14px 20px; }
  .tw-breadcrumbs { display: none; }
  .tw-page-header { position: relative; margin-bottom: 16px; gap: 12px; }
  .tw-page-header > .min-w-0 { width: 100%; padding-right: 84px; }
  .tw-page-title { font-size: 22px; }
  .tw-page-tools { position: absolute; top: -6px; right: -6px; margin: 0; }
  .tw-page-actions:not(:has(*)) { display: none; }
  /* Phones have the tab bar instead of the shortcut strip (product owner, 2026-10-30). */
  .tw-shortcuts { display: none; }
  .tw-search-input { height: 44px; }
  .tw-filter-chip span { max-width: 6.5rem; }
  .app-search-suggestions { min-width: 0; }
  .tw-page-actions { width: 100%; }
  .tw-page-actions > .btn { flex: 1 1 auto; }
  .tw-page-actions > .btn-icon { flex: 0 0 auto; }  /* pin and help stay small beside the real buttons */
  /* 44px: the touch size; not 2.75rem, which is only 38.5px here (the root text is 14px). */
  .form-control, .form-select, .btn { min-height: 44px; }
  .btn-sm { min-height: auto; }
  /* 16px text in fields: below that, iPhones zoom the page in on every tap into a field. */
  .form-control, .form-select, .tw-search-input { font-size: 16px; }
}

/* ---- Pop-up forms (#form-modal): the page's own form, without its page around it (static/js/app.js) ---- */
.tw-sheet .modal-dialog { max-width: 600px; }
/* Animations (review 2026-10-20): rise and grow in; the fields fade up as they arrive; a shake for mistakes. */
.tw-sheet.fade .modal-dialog { opacity: 0; transform: translateY(14px) scale(.965);
  transition: transform .28s cubic-bezier(.2, .9, .3, 1.15), opacity .2s ease-out; }
.tw-sheet.show .modal-dialog { opacity: 1; transform: none; }
.tw-sheet.fade:not(.show) .modal-dialog { transition-duration: .18s, .15s; transition-timing-function: ease-in, ease-in; }
.modal-backdrop.fade { transition: opacity .22s ease; }
:root.glass .modal-backdrop.show { -webkit-backdrop-filter: blur(3px); backdrop-filter: blur(3px); }
@keyframes tw-rise-in { from { opacity: 0; transform: translateY(8px); } to { opacity: 1; transform: none; } }
.tw-sheet-enter > *, .tw-sheet-enter form > *, .tw-sheet-enter .card-body > *, .tw-sheet-enter fieldset > .row > * {
  animation: tw-rise-in .32s cubic-bezier(.2, .8, .2, 1) backwards; }
.tw-sheet-enter .card-body > :nth-child(2), .tw-sheet-enter fieldset > .row > :nth-child(2) { animation-delay: .04s; }
.tw-sheet-enter .card-body > :nth-child(3), .tw-sheet-enter fieldset > .row > :nth-child(3) { animation-delay: .08s; }
.tw-sheet-enter .card-body > :nth-child(4) { animation-delay: .12s; }
.tw-sheet-enter .card-body > :nth-child(5) { animation-delay: .16s; }
.tw-sheet-enter .card-body > :nth-child(n+6) { animation-delay: .2s; }
@keyframes tw-shake { 10%, 90% { transform: translateX(-2px); } 20%, 80% { transform: translateX(4px); } 30%, 50%, 70% { transform: translateX(-6px); } 40%, 60% { transform: translateX(6px); } }
.tw-sheet .modal-content.tw-shake { animation: tw-shake .45s cubic-bezier(.36, .07, .19, .97) both; }
@keyframes tw-drop-in { from { opacity: 0; transform: translateY(-100%); } to { opacity: 1; transform: none; } }
.tw-sheet-discard:not([hidden]) { animation: tw-drop-in .22s ease-out both; }
.tw-sheet-footer .btn.tw-saved { background: var(--tblr-success, #2fb344) !important; border-color: var(--tblr-success, #2fb344) !important; color: #fff !important;
  transition: background-color .15s ease; }
@keyframes tw-pop { 0% { transform: scale(.4); opacity: 0; } 70% { transform: scale(1.15); } 100% { transform: scale(1); opacity: 1; } }
.tw-saved .bi-check-lg { display: inline-block; animation: tw-pop .3s ease-out both; }
.tw-sheet .modal-dialog.modal-lg { max-width: 820px; }
.tw-sheet .modal-content { border: 0; border-radius: 16px; overflow: hidden; box-shadow: 0 24px 64px rgba(15, 23, 42, .28); }
.tw-sheet .modal-header { padding: 18px 24px 14px; border-bottom: 1px solid var(--tw-border); align-items: flex-start; gap: 16px; }
.tw-sheet .modal-title { font-size: 17px; font-weight: 650; line-height: 1.3; margin: 0; }
.tw-sheet-subtitle { margin: 4px 0 0; font-size: 13px; color: var(--tw-muted); line-height: 1.4; }
.tw-sheet .modal-body { padding: 20px 24px 24px; overflow-x: hidden; }
.tw-sheet-footer { padding: 12px 24px; border-top: 1px solid var(--tw-border); background: var(--tw-surface); }
.tw-sheet-footer .tw-sheet-actions { display: flex; flex-wrap: wrap; align-items: center; justify-content: flex-end; gap: 8px;
  width: 100%; margin: 0 !important; padding: 0 !important; border: 0 !important; background: none !important; }
.tw-sheet-footer .tw-sheet-actions > * { margin: 0 !important; }
.tw-sheet-footer .tw-sheet-actions .btn:not(.btn-primary):not(.btn-success):not(.btn-danger) { order: -1; }
.tw-sheet-footer .tw-sheet-actions .w-100 { width: auto !important; }
/* The page's cards become plain sections: no box in a box. */
:root .tw-sheet .modal-body .card { border: 0; background: transparent; box-shadow: none; -webkit-backdrop-filter: none; backdrop-filter: none;
  border-radius: 0; margin-bottom: 0; isolation: auto; }
:root .tw-sheet .modal-body .card::before { content: none; }
.tw-sheet .modal-body .card .card-body { padding: 0; }
.tw-sheet .modal-body .card + .card, .tw-sheet .modal-body .card ~ .card { margin-top: 20px; padding-top: 20px; border-top: 1px solid var(--tw-border); }
.tw-sheet .modal-body .card .card-header { padding: 0 0 12px; background: none; border: 0; min-height: 0; }
.tw-sheet .modal-body .card .card-header .card-title { font-size: 12px; font-weight: 650; text-transform: uppercase; letter-spacing: .04em; color: var(--tw-muted); }
.tw-sheet .modal-body .card .card-footer { padding: 16px 0 0; background: none; border: 0; }
.tw-sheet .modal-body [style*="max-width"] { max-width: none !important; }
.tw-sheet .modal-body .row > [class*="col-lg"], .tw-sheet .modal-body .row > [class*="col-xl"] { flex: 0 0 100%; max-width: 100%; }
.tw-sheet .modal-body .row { --tblr-gutter-x: 1rem; }
.tw-sheet .modal-body .row.g-3 { --tblr-gutter-x: 1rem; }
.tw-sheet .modal-body .form-label, .tw-sheet .modal-body label.col-form-label { font-weight: 550; }
.tw-sheet-handle { display: none; }
.tw-sheet-discard { display: flex; flex-wrap: wrap; align-items: center; gap: 8px; padding: 10px 24px;
  background: var(--tblr-warning-lt, #fff4e5); border-bottom: 1px solid var(--tw-border); font-size: 13.5px; }
.tw-sheet-discard[hidden] { display: none; }
[data-bs-theme="dark"] .tw-sheet-discard { background: rgba(247, 103, 7, .15); }
/* The confirm dialog can open over a pop-up form. */
#confirm-modal { z-index: 1070; }
/* Phones: a bottom sheet, within reach of the thumb, sliding up; buttons full width at the bottom. */
@media (max-width: 575.98px) {
  .tw-sheet .modal-dialog, .tw-sheet .modal-dialog.modal-lg { position: fixed; left: 0; right: 0; bottom: 0; margin: 0; max-width: none; width: 100%;
    min-height: 0; height: auto; max-height: 94vh; align-items: flex-end; }
  /* Phones: up from the bottom with a soft spring, down again when closed. */
  .tw-sheet.fade .modal-dialog { opacity: 1; transform: translateY(100%); transition: transform .34s cubic-bezier(.32, .72, 0, 1); }
  .tw-sheet.show .modal-dialog { transform: none; }
  .tw-sheet.fade:not(.show) .modal-dialog { transition: transform .22s ease-in; }
  .tw-sheet .modal-content { border-radius: 20px 20px 0 0; max-height: 94vh; }
  .tw-sheet-handle { display: block; width: 40px; height: 4px; border-radius: 2px; background: var(--tw-border); margin: 8px auto 0; }
  .tw-sheet .modal-header { padding: 10px 16px 12px; }
  .tw-sheet .modal-body { padding: 16px; }
  .tw-sheet-discard { padding: 10px 16px; }
  .tw-sheet-footer { padding: 10px 16px calc(10px + env(safe-area-inset-bottom, 0px)); }
  .tw-sheet-footer .tw-sheet-actions { flex-wrap: nowrap; }
  .tw-sheet-footer .tw-sheet-actions > .btn, .tw-sheet-footer .tw-sheet-actions > button { flex: 1 1 0; }
  .tw-sheet .modal-body .row > [class*="col-md"], .tw-sheet .modal-body .row > [class*="col-sm"] { flex: 0 0 100%; max-width: 100%; }
}
@media (prefers-reduced-motion: reduce) {
  .tw-sheet .modal-dialog, .tw-sheet.fade .modal-dialog, .tw-sheet.fade:not(.show) .modal-dialog { transition: none; transform: none; }
  .tw-sheet-enter *, .tw-sheet .modal-content.tw-shake, .tw-sheet-discard:not([hidden]), .tw-saved .bi-check-lg { animation: none !important; }
}

/* Pupil search box (templates/students/widgets/pupil_search.html) */
.tw-pupil-results:empty { display: none; }
.tw-pupil-results { margin-top: 6px; border: 1px solid var(--tw-border); border-radius: 10px; overflow: hidden; max-height: 280px; overflow-y: auto;
  background: var(--tblr-bg-surface, #fff); box-shadow: var(--tw-shadow-md); animation: tw-rise-in .18s ease-out backwards; }
.tw-pupil-results .list-group-item { border-left: 0; border-right: 0; }
.tw-pupil-results .list-group-item:focus { background: var(--tw-primary-light); outline: none; }
.tw-pupil-chosen { display: flex; align-items: center; gap: 8px; padding: 9px 12px; border: 1px solid var(--tw-border);
  border-radius: var(--tw-radius-control, 8px); background: var(--tw-surface); }
.tw-pupil-chosen.d-none { display: none !important; }

/* Tables on phones: one card per row, each value with its column's name (static/js/app.js, cardTables). */
@media (max-width: 575.98px) {
  table.tw-cards, table.tw-cards tbody, table.tw-cards tfoot { display: block; width: 100%; }
  table.tw-cards thead { display: none; }
  /* Title across the top, then the details two to a line, each value under a small label. */
  table.tw-cards tr { display: grid; grid-template-columns: 1fr 1fr; column-gap: 14px; row-gap: 8px; padding: 12px 16px; border-bottom: 1px solid var(--tw-border); }
  table.tw-cards tr:last-child { border-bottom: 0; }
  table.tw-cards > :not(caption) > * > * { border-bottom-width: 0; box-shadow: none; }
  table.tw-cards td, table.tw-cards th { display: block; padding: 0 !important; text-align: left !important; min-width: 0; overflow-wrap: anywhere; }
  table.tw-cards td::before, table.tw-cards th::before { content: attr(data-label); display: block; color: var(--tw-muted); font-size: 11px; font-weight: 500; line-height: 1.3; }
  table.tw-cards td[data-label=""]::before, table.tw-cards .tw-card-title::before, table.tw-cards .tw-card-actions::before { content: none; }
  table.tw-cards .tw-card-title { grid-column: 1 / -1; font-weight: 600; font-size: 15px; }
  table.tw-cards td[colspan] { grid-column: 1 / -1; }
  table.tw-cards .tw-card-actions { grid-column: 1 / -1; display: flex; justify-content: flex-end; gap: 6px; flex-wrap: wrap; }
  table.tw-cards .tw-card-actions form { display: inline-flex; margin: 0; }
  table.tw-cards .text-truncate { white-space: normal; }
  table.tw-cards .d-none { display: none !important; }
  .table-responsive:has(> table.tw-cards) { overflow-x: visible; }
}

@media print {
  .tw-shell { display: block; }
  .tw-content { padding: 0; max-width: none; }
  body { background: #fff !important; }
  .card { box-shadow: none; border: 1px solid #ddd; }
  a[href]::after { content: none; }
}

@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after { animation: none !important; transition: none !important; }
}
/* Tab strips wrap onto more lines inside card headers: every tab stays visible. */
/* Tab strips scroll sideways inside flex card headers instead of widening the page. */
.card-header > .nav { min-width: 0; flex: 1 1 auto; max-width: 100%; flex-wrap: wrap; row-gap: 2px; }
.tw-main, .tw-content, .card { min-width: 0; }

/* Financial statements */
.statement-table thead th { padding-top: 18px; }
.statement-table .statement-total td { border-top: 2px solid var(--tw-text); border-bottom: 3px double var(--tw-text); }

/* ==========================================================================
   Liquid glass (iOS 26/27 style), on by default as html.glass.
   Frosted, translucent surfaces with a bright specular edge, floating
   sidebar and top bar, rounder corners, over a soft backdrop in the
   school colours. Switched off from Appearance > Glass (slow PCs), and
   automatically when the device asks for reduced transparency, when the
   browser cannot blur, and for print.
   ========================================================================== */
@supports ((backdrop-filter: blur(1px)) or (-webkit-backdrop-filter: blur(1px))) {
  :root.glass {
    --glass-tint: rgba(255, 255, 255, 0.46);
    --glass-tint-strong: rgba(255, 255, 255, 0.76);
    --glass-border: rgba(255, 255, 255, 0.62);
    --glass-edge: rgba(255, 255, 255, 0.95);
    --glass-sheen: rgba(255, 255, 255, 0.55);
    --glass-shadow: 0 10px 34px rgba(15, 23, 42, 0.10), 0 1px 3px rgba(15, 23, 42, 0.06);
    --glass-blur: saturate(185%) blur(22px);
    --tw-radius-card: 18px;
    --tw-radius-control: 12px;
  }
  :root.glass[data-bs-theme="dark"] {
    --glass-tint: rgba(30, 34, 48, 0.48);
    --glass-tint-strong: rgba(30, 34, 48, 0.80);
    --glass-border: rgba(255, 255, 255, 0.12);
    --glass-edge: rgba(255, 255, 255, 0.22);
    --glass-sheen: rgba(255, 255, 255, 0.14);
    --glass-shadow: 0 10px 34px rgba(0, 0, 0, 0.45), 0 1px 3px rgba(0, 0, 0, 0.3);
  }

  /* The backdrop the glass shows through: soft blobs of the school colours. */
  :root.glass body {
    background:
      radial-gradient(1000px 700px at 18% -12%, color-mix(in srgb, var(--brand-primary) 42%, transparent), transparent 62%),
      radial-gradient(800px 600px at 108% 8%, color-mix(in srgb, var(--brand-accent) 30%, transparent), transparent 60%),
      radial-gradient(900px 700px at 45% 118%, color-mix(in srgb, var(--brand-secondary, #38BDF8) 26%, transparent), transparent 62%),
      radial-gradient(700px 500px at -10% 90%, color-mix(in srgb, color-mix(in srgb, var(--brand-primary) 60%, #fff) 22%, transparent), transparent 60%),
      var(--tw-surface);
    background-attachment: fixed;
  }

  /* Glass surfaces */
  :root.glass .tw-sidebar,
  :root.glass .tw-navbar,
  :root.glass .card,
  :root.glass .tw-kpi-card,
  :root.glass .dropdown-menu,
  :root.glass .tw-nav-flyout,
  :root.glass .modal-content,
  :root.glass .tw-toast {
    background-color: var(--glass-tint);
    -webkit-backdrop-filter: var(--glass-blur);
    backdrop-filter: var(--glass-blur);
    border: 1px solid var(--glass-border);
    box-shadow: inset 0 1px 0 var(--glass-edge), inset 0 -1px 0 rgba(255, 255, 255, 0.08), var(--glass-shadow);
  }
  /* Menus and dialogs float over busy content: a denser pane. */
  :root.glass .dropdown-menu,
  :root.glass .tw-nav-flyout,
  :root.glass .modal-content,
  :root.glass .tw-toast { background-color: var(--glass-tint-strong); }
  /* Search suggestions sit inside the top bar's own blur, where a second blur does not work: solid pane. */
  :root.glass .app-search-suggestions .card {
    background-color: var(--tblr-bg-surface, #fff); -webkit-backdrop-filter: none; backdrop-filter: none;
  }

  /* Liquid sheen: light catching the top-left edge. */
  :root.glass .card,
  :root.glass .tw-kpi-card { position: relative; isolation: isolate; }
  :root.glass .card::before,
  :root.glass .tw-kpi-card::before {
    content: ""; position: absolute; inset: 0; border-radius: inherit; pointer-events: none; z-index: -1;
    background: linear-gradient(135deg, var(--glass-sheen), transparent 38%, transparent 70%, rgba(255, 255, 255, 0.10));
    opacity: 0.55;
  }

  /* Floating shell on desktop. */
  @media (min-width: 992px) {
    :root.glass .tw-sidebar { margin: 12px 0 12px 12px; height: calc(100vh - 24px); top: 12px; border-radius: 24px; }
    :root.glass .tw-navbar { margin: 12px 12px 0 12px; border-radius: 20px; top: 12px; }
    :root.glass .tw-shortcuts { background: transparent; border-bottom: 0; padding: 10px 12px 0 12px; }
    :root.glass .tw-content { padding-top: 20px; }
    :root.glass .tw-footer { border-top: 0; }
  }
  @media (max-width: 991.98px) {
    :root.glass .tw-sidebar { border-radius: 0 24px 24px 0; }
    :root.glass .tw-navbar { border-left: 0; border-right: 0; border-top: 0; }
  }

  /* Inside glass, inner pieces stay see-through. */
  :root.glass .card-header,
  :root.glass .card-footer,
  :root.glass .list-group-item,
  :root.glass .table,
  :root.glass .table > :not(caption) > * > * { background-color: transparent; }
  :root.glass .card-footer { border-top-color: var(--glass-border); }
  :root.glass .card-header { border-bottom-color: var(--glass-border); }
  :root.glass .table thead th { background: rgba(255, 255, 255, 0.18); }
  :root.glass[data-bs-theme="dark"] .table thead th { background: rgba(255, 255, 255, 0.04); }
  :root.glass .table tbody td { border-bottom-color: color-mix(in srgb, var(--glass-border) 60%, transparent); }
  :root.glass .grid-sticky thead th,
  :root.glass .grid-sticky td:first-child,
  :root.glass .grid-sticky th:first-child { background: var(--glass-tint-strong); }

  /* Controls: rounder, softly frosted. */
  :root.glass .btn { border-radius: 12px; }
  :root.glass .form-control,
  :root.glass .form-select,
  :root.glass .tw-search-input { background-color: rgba(255, 255, 255, 0.55); border-color: var(--glass-border); border-radius: 12px; }
  :root.glass[data-bs-theme="dark"] .form-control,
  :root.glass[data-bs-theme="dark"] .form-select,
  :root.glass[data-bs-theme="dark"] .tw-search-input { background-color: rgba(255, 255, 255, 0.06); }
  :root.glass .btn-outline-secondary,
  :root.glass .tw-btn-secondary,
  :root.glass .tw-chip-btn { background: rgba(255, 255, 255, 0.35); border-color: var(--glass-border); }
  :root.glass[data-bs-theme="dark"] .btn-outline-secondary,
  :root.glass[data-bs-theme="dark"] .tw-btn-secondary,
  :root.glass[data-bs-theme="dark"] .tw-chip-btn { background: rgba(255, 255, 255, 0.06); }
  :root.glass .btn-primary { box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.35), 0 4px 14px color-mix(in srgb, var(--tw-primary) 30%, transparent); }

  /* Sidebar items: the current page is a raised glass pill. */
  :root.glass .tw-nav-link,
  :root.glass .tw-nav-group-btn,
  :root.glass .tw-nav-flyout-link { border-left: 0; border-radius: 12px; }
  :root.glass .tw-nav-link.active,
  :root.glass .tw-nav-group.is-current > .tw-nav-group-btn,
  :root.glass .tw-nav-flyout-link.active { background: rgba(255, 255, 255, 0.72); box-shadow: inset 0 1px 0 #fff, 0 2px 10px rgba(15, 23, 42, 0.08); }
  :root.glass[data-bs-theme="dark"] .tw-nav-link.active,
  :root.glass[data-bs-theme="dark"] .tw-nav-group.is-current > .tw-nav-group-btn,
  :root.glass[data-bs-theme="dark"] .tw-nav-flyout-link.active { background: rgba(255, 255, 255, 0.12); box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.18); }
  :root.glass .tw-nav-link:hover,
  :root.glass .tw-nav-group-btn:hover,
  :root.glass .tw-icon-btn:hover,
  :root.glass .tw-user-menu-btn:hover { background: rgba(255, 255, 255, 0.4); }
  :root.glass[data-bs-theme="dark"] .tw-nav-link:hover,
  :root.glass[data-bs-theme="dark"] .tw-nav-group-btn:hover,
  :root.glass[data-bs-theme="dark"] .tw-icon-btn:hover { background: rgba(255, 255, 255, 0.08); }
  :root.glass .tw-sidebar-footer { border-top-color: var(--glass-border); }
}

/* After the glass rules, which otherwise turn the chosen Light button white on white. */
.tw-theme-choice .btn.active, .tw-glass-choice .btn.active { background: var(--tw-primary); border-color: var(--tw-primary); color: #fff; }

/* Respect "reduce transparency" (e.g. Windows Transparency effects off)
   unless the user explicitly chose Glass: On (html.glass-forced). */
@media (prefers-reduced-transparency: reduce) {
  :root.glass:not(.glass-forced) .tw-sidebar, :root.glass:not(.glass-forced) .tw-navbar,
  :root.glass:not(.glass-forced) .card, :root.glass:not(.glass-forced) .tw-kpi-card,
  :root.glass:not(.glass-forced) .dropdown-menu, :root.glass:not(.glass-forced) .tw-nav-flyout,
  :root.glass:not(.glass-forced) .modal-content, :root.glass:not(.glass-forced) .tw-toast {
    -webkit-backdrop-filter: none !important; backdrop-filter: none !important;
    background-color: var(--tw-surface) !important;
  }
}
@media print {
  :root.glass body { background: #fff !important; }
  :root.glass .card { -webkit-backdrop-filter: none !important; backdrop-filter: none !important; background: #fff !important; box-shadow: none !important; }
  :root.glass .card::before { display: none; }
}

/* Floating glass sidebar needs room for its margin when collapsed. */
@media (min-width: 992px) {
  html.glass.sidebar-collapsed .tw-shell { grid-template-columns: 78px minmax(0, 1fr); }
}
@media (max-width: 1199.98px) and (min-width: 992px) {
  html.glass .tw-shell { grid-template-columns: 78px minmax(0, 1fr); }
}

:root.glass .nav-tabs, :root.glass .card-header-tabs { background: transparent; }

:root.glass .btn-outline-secondary, :root.glass .tw-btn-secondary { border-color: rgba(15, 23, 42, 0.14); }
:root.glass[data-bs-theme="dark"] .btn-outline-secondary, :root.glass[data-bs-theme="dark"] .tw-btn-secondary { border-color: rgba(255, 255, 255, 0.16); }

/* ---------- Glass off (the default since 2026-11-02): no blur anywhere ----------
   Blur is the slowest thing these pages draw on school PCs and older phones. */
:root:not(.glass) *, :root:not(.glass) *::before, :root:not(.glass) *::after { -webkit-backdrop-filter: none !important; backdrop-filter: none !important; }
:root:not(.glass) .login-orbs { display: none; }

/* ---------- Login page ----------
   With Glass: On, soft colour orbs in the school's colours behind a frosted card. */
.login-orbs { position: fixed; inset: 0; overflow: hidden; pointer-events: none; z-index: 0; }
.login-orbs span { position: absolute; border-radius: 50%; filter: blur(60px); opacity: .55; animation: login-float 18s ease-in-out infinite alternate; }
.login-orbs span:nth-child(1) { width: 40vmax; height: 40vmax; left: 12vw; top: -10vmax; background: var(--brand-primary); }
.login-orbs span:nth-child(2) { width: 30vmax; height: 30vmax; right: 18vw; top: 20vh; background: var(--brand-accent); opacity: .45; animation-duration: 22s; }
.login-orbs span:nth-child(3) { width: 34vmax; height: 34vmax; left: 34vw; bottom: -12vmax; background: var(--brand-secondary, #38BDF8); opacity: .45; animation-duration: 26s; }
.login-orbs span:nth-child(4) { width: 22vmax; height: 22vmax; left: 6vw; bottom: 6vh; background: color-mix(in srgb, var(--brand-primary) 60%, #fff); opacity: .38; animation-duration: 20s; }
@keyframes login-float { from { transform: translate3d(0, 0, 0) scale(1); } to { transform: translate3d(4vmax, 3vmax, 0) scale(1.08); } }
.login-page .tw-auth-main, .login-page .container-tight { position: relative; z-index: 1; }

.login-logo {
  width: 84px; height: 84px; border-radius: 24px; display: inline-flex; align-items: center; justify-content: center;
  background: rgba(255, 255, 255, .7); box-shadow: 0 8px 24px rgba(15, 23, 42, .12), inset 0 1px 0 rgba(255, 255, 255, .9);
}
.login-logo img { max-width: 64px; max-height: 64px; object-fit: contain; }
.login-logo-icon { background: var(--brand-primary); color: var(--brand-on-primary, #fff); font-size: 38px; }
.login-note { color: var(--tw-text); font-weight: 500; text-wrap: balance; }
.login-card .text-secondary { color: color-mix(in srgb, var(--tw-text) 78%, transparent) !important; }

@supports ((backdrop-filter: blur(1px)) or (-webkit-backdrop-filter: blur(1px))) {
  :root.glass body.login-page { background: var(--tw-surface); }
  :root.glass .login-card {
    --glass-tint: rgba(255, 255, 255, 0.30);
    border-radius: 28px;
    -webkit-backdrop-filter: saturate(190%) blur(28px);
    backdrop-filter: saturate(190%) blur(28px);
    box-shadow: inset 0 1px 0 rgba(255, 255, 255, .95), 0 24px 60px rgba(15, 23, 42, .18);
  }
  :root.glass[data-bs-theme="dark"] .login-card { --glass-tint: rgba(24, 28, 40, 0.45); }
  :root.glass .login-card .form-control, :root.glass .login-card .form-select {
    background-color: rgba(255, 255, 255, .55); border-color: rgba(255, 255, 255, .7);
  }
  :root.glass[data-bs-theme="dark"] .login-card .form-control { background-color: rgba(255, 255, 255, .08); border-color: rgba(255, 255, 255, .14); }
  :root.glass .login-card .btn-primary { border-radius: 14px; box-shadow: 0 8px 20px color-mix(in srgb, var(--brand-primary) 35%, transparent); }
  :root.glass .login-note { background: rgba(255, 255, 255, .78); border: 1px solid rgba(255, 255, 255, .9); border-radius: 999px; padding: 8px 18px; align-self: center; max-width: 100%;
    box-shadow: 0 6px 18px rgba(15, 23, 42, .10);
    -webkit-backdrop-filter: blur(12px); backdrop-filter: blur(12px); }
}
@supports ((backdrop-filter: blur(1px)) or (-webkit-backdrop-filter: blur(1px))) {
  :root.glass[data-bs-theme="dark"] .login-note { background: rgba(24, 28, 40, .78); border-color: rgba(255, 255, 255, .14); }
}

/* ==========================================================================
   Light mode, refined (dark mode unchanged): a calm, near-neutral backdrop
   with only a hint of the school colours, crisp white surfaces with fine
   borders and soft shadows, and stronger text for long reading.
   ========================================================================== */
:root:not([data-bs-theme="dark"]) {
  --tw-surface: #F4F6F9;
  --tw-border: #E2E6EC;
  --tw-text: #1B2333;
  --tw-muted: #5B6575;
  --tw-shadow-sm: 0 1px 2px rgba(16, 24, 40, 0.05);
  --tw-shadow-md: 0 6px 18px rgba(16, 24, 40, 0.07);
}
@supports ((backdrop-filter: blur(1px)) or (-webkit-backdrop-filter: blur(1px))) {
  :root.glass:not([data-bs-theme="dark"]) {
    --glass-tint: rgba(255, 255, 255, 0.82);
    --glass-tint-strong: rgba(255, 255, 255, 0.94);
    --glass-border: rgba(15, 23, 42, 0.07);
    --glass-edge: rgba(255, 255, 255, 1);
    --glass-sheen: rgba(255, 255, 255, 0.35);
    --glass-shadow: 0 1px 2px rgba(16, 24, 40, 0.04), 0 8px 24px rgba(16, 24, 40, 0.06);
    --glass-blur: saturate(140%) blur(18px);
  }
  :root.glass:not([data-bs-theme="dark"]) body:not(.login-page) {
    background:
      radial-gradient(1200px 600px at 0% -10%, color-mix(in srgb, var(--brand-primary) 9%, transparent), transparent 60%),
      radial-gradient(900px 500px at 100% 0%, color-mix(in srgb, var(--brand-primary) 6%, transparent), transparent 60%),
      linear-gradient(180deg, #F6F8FB 0%, #EEF1F6 100%);
    background-attachment: fixed;
  }
  :root.glass:not([data-bs-theme="dark"]) .card::before,
  :root.glass:not([data-bs-theme="dark"]) .tw-kpi-card::before { opacity: 0.25; }
}
:root:not([data-bs-theme="dark"]) .card { border-color: var(--tw-border); }
:root:not([data-bs-theme="dark"]) .table thead th { color: #475467; background: #F8FAFC; letter-spacing: .03em; }
:root:not([data-bs-theme="dark"]) .tw-page-title { color: #101828; letter-spacing: -0.01em; }
:root:not([data-bs-theme="dark"]) .tw-sidebar .tw-nav-link { color: #344054; }
:root:not([data-bs-theme="dark"]) .tw-sidebar .tw-nav-link.active,
:root:not([data-bs-theme="dark"]) .tw-sidebar .tw-nav-group.is-current > .tw-nav-group-btn {
  background: color-mix(in srgb, var(--brand-primary) 9%, #fff); color: var(--brand-primary);
  box-shadow: inset 3px 0 0 var(--brand-primary);
}

/* Dark mode: the school's primary colour (often a deep navy) is lifted
   wherever it is used as text, so it stays readable on dark surfaces. */
[data-bs-theme="dark"] .tw-brand-name,
[data-bs-theme="dark"] .tw-sidebar .tw-nav-link.active,
[data-bs-theme="dark"] .tw-sidebar .tw-nav-group.is-current > .tw-nav-group-btn,
[data-bs-theme="dark"] .nav-tabs .nav-link.active,
[data-bs-theme="dark"] .tw-nav-flyout-link.active,
[data-bs-theme="dark"] .text-primary,
[data-bs-theme="dark"] a:not(.btn):not(.tw-nav-link):not(.list-group-item):not(.card):not(.dropdown-item) {
  color: color-mix(in srgb, var(--brand-primary) 40%, #ffffff);
}
[data-bs-theme="dark"] .tw-sidebar .tw-nav-link.active,
[data-bs-theme="dark"] .tw-sidebar .tw-nav-group.is-current > .tw-nav-group-btn {
  background: color-mix(in srgb, var(--brand-primary) 35%, transparent);
  box-shadow: inset 3px 0 0 color-mix(in srgb, var(--brand-primary) 40%, #ffffff);
}

/* ---- Workspace tabs: related pages in one window (apps/core/navigation.py) ----
   Folder tabs, as in desktop software. One thin line (--tw-tab-line) runs
   around the current tab and the panel holding the page, so the page reads
   as sitting under that tab. The current tab's white shape is a separate
   "slider" (static/js/app.js) that glides to the tab you click while the
   page fades; the other tabs stand behind it with a light tinted fill. */
:root {
  --tw-tab-panel: var(--tw-surface-raised, #fff);
  --tw-tab-line: rgba(var(--tblr-primary-rgb, 32, 107, 196), .38);
  --tw-tab-ease: cubic-bezier(.2, .8, .2, 1);
}
/* Dark mode: the page is nearly black, so the current tab and its panel
   step clearly lighter, with a faint light edge on top; the tabs behind
   stay dark. */
:root[data-bs-theme="dark"] { --tw-tab-panel: #232A3B; }
[data-bs-theme="dark"] .tw-workspace-tabs .nav-link { background: rgba(255, 255, 255, .035); color: var(--tw-muted, #9AA3B2); }
[data-bs-theme="dark"] .tw-workspace-tabs .nav-link:hover { background: rgba(255, 255, 255, .08); color: var(--tw-text, #E6E8EE); }
[data-bs-theme="dark"] .tw-workspace-tabs .nav-link.active { color: var(--tw-text, #E6E8EE); }
[data-bs-theme="dark"] .tw-tab-slider { box-shadow: inset 0 1px 0 rgba(255, 255, 255, .14), 0 -4px 14px rgba(0, 0, 0, .35); }
[data-bs-theme="dark"] .tw-tab-content { box-shadow: 0 8px 24px rgba(0, 0, 0, .35); }

.tw-workspace-tabs { position: relative; margin: -8px 0 0; }
.tw-workspace-tabs .nav-tabs {
  position: relative; display: flex; flex-wrap: nowrap; align-items: flex-end; gap: 0; border: 0;
  padding: 4px 0 0 4px; overflow-x: auto; overflow-y: hidden; scrollbar-width: none; scroll-behavior: smooth;
}
.tw-workspace-tabs .nav-tabs::-webkit-scrollbar { display: none; }
.tw-workspace-tabs .nav-item { position: relative; z-index: 1; flex-shrink: 0; padding-right: 2px; }
.tw-workspace-tabs .nav-link {
  position: relative; display: block; margin: 0 0 1px; padding: .5rem 1.05rem; border: 0;
  border-radius: 10px 10px 0 0; color: var(--tblr-secondary); font-weight: 500;
  background: rgba(var(--tblr-primary-rgb, 32, 107, 196), .045);
  transition: color .25s ease, background-color .25s ease, border-color .25s ease;
}
.tw-workspace-tabs .nav-link:hover { color: var(--tblr-body-color); background: rgba(var(--tblr-primary-rgb, 32, 107, 196), .1); }
.tw-workspace-tabs .nav-link:focus-visible { outline: 2px solid var(--tblr-primary); outline-offset: -2px; }
.tw-workspace-tabs .nav-link.active {
  margin-bottom: 0; padding-bottom: calc(.5rem + 1px); color: var(--tblr-primary); font-weight: 600;
  background: var(--tw-tab-panel);
}
/* With the slider running, the current tab is see-through and the slider draws its shape. */
.tw-workspace-tabs.has-slider .nav-link.active { background: transparent; }
.tw-tab-slider {
  position: absolute; z-index: 0; left: 0; top: 4px; width: 0; height: 0; list-style: none; pointer-events: none;
  background: var(--tw-tab-panel); border: 0; border-radius: 10px 10px 0 0;
  box-shadow: 0 -3px 10px rgba(15, 23, 42, .06);
  transition: left .34s var(--tw-tab-ease), width .34s var(--tw-tab-ease), top .34s var(--tw-tab-ease), height .34s var(--tw-tab-ease);
}
.tw-workspace-tabs:not(.has-slider) .tw-tab-slider { display: none; }

/* The panel under the tabs: the page itself. */
.tw-tab-content {
  background: var(--tw-tab-panel); border: 0;
  border-radius: 0 0 14px 14px; padding: 20px; box-shadow: 0 6px 18px rgba(15, 23, 42, .05);
  transition: opacity .2s ease, transform .2s ease;
}
.tw-tab-content > :last-child { margin-bottom: 0; }
@media (max-width: 575.98px) { .tw-tab-content { padding: 12px; border-radius: 0 0 10px 10px; } }

/* Desktop: the tabs wrap instead of scrolling, so they can overlap the
   panel's own (rounded) top edge; the current tab covers the line under it. */
@media (min-width: 768px) {
  .tw-workspace-tabs { z-index: 2; margin-bottom: -1px; }
  .tw-workspace-tabs .nav-tabs { overflow: visible; flex-wrap: wrap; padding: 4px 0 0 16px; box-shadow: none; }
  .tw-tab-content { border-radius: 14px; }
}

/* Switching: the old page fades out as the slider moves, the new one fades in. */
.tw-tab-content.is-leaving { opacity: 0; transform: translateY(4px); }
html.tw-tab-arrive .tw-tab-content { animation: tw-tab-arrive .32s ease backwards; }
@keyframes tw-tab-arrive { from { opacity: 0; transform: translateY(6px); } }
@media (prefers-reduced-motion: reduce) {
  .tw-tab-slider, .tw-tab-content, .tw-workspace-tabs .nav-link { transition: none; }
  html.tw-tab-arrive .tw-tab-content { animation: none; }
}

/* ---- Pick lists: tick pupils in a long list (class allocation, houses, clubs) ---- */
.tw-pick-list { max-height: 60vh; overflow-y: auto; }
/* "Print list" prints every member, not just the page on screen. */
@media print {
  .tw-pick-list { max-height: none; overflow: visible; }
  .tw-pick-row[hidden] { display: flex !important; }
  .tw-pick-pager { display: none !important; }
}
.tw-pick-row { display: flex; align-items: center; gap: .65rem; padding: .5rem .9rem; border-bottom: 1px solid var(--tblr-border-color); cursor: pointer; margin: 0; }
.tw-pick-row:hover { background: var(--tblr-bg-surface-secondary, #f6f8fb); }
.tw-pick-row:has(input:checked) { background: var(--tblr-primary-lt, rgba(32,107,196,.08)); }
.tw-pick-row .form-check-input { margin: 0; flex-shrink: 0; }

/* ---- Search suggestions: the row picked with the arrow keys ---- */
.app-search-suggestions .list-group-item.is-selected { background: var(--tblr-primary-lt, rgba(32, 107, 196, .1)); }

/* Counters on labelled chip buttons sit on the corner (review 2026-10-17). */
.tw-chip-btn .tw-badge-count { top: -6px; right: -6px; }
/* The bell: a dot when only approvals are waiting; approvals and messages listed above the alerts. */
.tw-badge-dot { position: absolute; top: 6px; right: 6px; width: 9px; height: 9px; border-radius: 50%;
  background: var(--tblr-orange, #f76707); border: 1.5px solid var(--tw-surface-raised); }
.tw-notif-shortcuts { padding: 6px 0; border-bottom: 1px solid var(--tw-border); }
.tw-notif-shortcuts .dropdown-item { padding-top: 8px; padding-bottom: 8px; }
/* Only shown while one section is chosen; the choice itself is in the account menu. */
.tw-filter-chip { color: var(--tw-primary); border-color: var(--tw-primary); }
.tw-filter-chip span { max-width: 10rem; }
.tw-filter-chip .bi-x-lg { font-size: 11px; }
.tw-password-wrap { position: relative; }
.tw-password-wrap .form-control { padding-right: 44px; }
.tw-password-eye { position: absolute; top: 0; right: 0; height: 100%; width: 44px; border: 0; background: none;
  color: var(--tw-muted); display: inline-flex; align-items: center; justify-content: center; }
.tw-password-eye:hover { color: var(--tw-text); }
/* Menu items with a grey line under them: the line goes below, and long text wraps instead of widening the menu. */
.tw-account-menu .dropdown-item { flex-wrap: wrap; white-space: normal; }
.tw-account-menu .dropdown-item > div { flex-basis: 100%; }
.tw-notif-list { overflow-x: hidden; }
.tw-notif-item > .d-flex { flex: 1 1 auto; min-width: 0; }
.tw-account-menu { width: 300px; max-width: calc(100vw - 24px); max-height: calc(100vh - 90px); overflow-y: auto; }
/* Sign out stays in view at the foot of the account menu however far it scrolls (product owner, 2026-10-30: "the
   logout button is hidden on desktop"). Solid, so the items scrolling under it do not show through. */
.tw-account-signout { position: sticky; bottom: -8px; z-index: 1; margin: 6px -8px -8px; padding: 6px 8px 8px;
                      border-top: 1px solid var(--tw-border); background: var(--tw-surface-raised); }

/* The installed app's swipe-back cue (static/js/app.js): a round chevron that follows the thumb from the left edge. */
.tw-swipe-cue {
  position: fixed; left: 6px; z-index: 2000; width: 44px; height: 44px; border-radius: 50%; pointer-events: none;
  display: flex; align-items: center; justify-content: center; font-size: 20px; opacity: 0;
  background: var(--tw-surface-raised); color: var(--tw-text); box-shadow: 0 4px 16px rgba(15, 23, 42, .18);
  transition: opacity .12s;
}

/* ==========================================================================
   Phones: an app's look and motion (product owner, 2026-10-30: "the phone view should give the user an app
   experience"; after Apple's Human Interface Guidelines and Material Design 3)
   - section tabs are a row of pills, as iOS segmented controls and Material chips;
   - the page sits straight on the background: no box around boxes, cards with soft corners and no outline;
   - rows and buttons a thumb's size, a press shows on everything tappable;
   - pages slide in (the View Transitions API where the browser has it), pop-up pages slide in from the right
     as a pushed screen, forms rise as sheets from the bottom.
   ========================================================================== */
@media (max-width: 575.98px) {
  .tw-workspace-tabs { margin: 0 -14px 14px; }
  .tw-workspace-tabs .nav-tabs { gap: 8px; padding: 2px 14px; align-items: center; }
  .tw-workspace-tabs .nav-item { padding: 0; }
  .tw-workspace-tabs .nav-link, [data-bs-theme="dark"] .tw-workspace-tabs .nav-link {
    margin: 0; padding: 8px 16px; border-radius: 999px; font-size: 14px;
    background: var(--tw-surface-raised); border: 1px solid var(--tw-border); color: var(--tw-text);
  }
  .tw-workspace-tabs .nav-link.active, .tw-workspace-tabs.has-slider .nav-link.active {
    padding-bottom: 8px; background: var(--brand-primary, #1e2a78); border-color: var(--brand-primary, #1e2a78); color: #fff;
  }
  .tw-tab-slider { display: none !important; }
  .tw-tab-content, :root.glass .tw-tab-content, [data-bs-theme="dark"] .tw-tab-content {
    background: transparent; padding: 0; border-radius: 0; box-shadow: none;
  }

  .card, :root.glass .card { border: 0; border-radius: 16px; box-shadow: 0 1px 2px rgba(15, 23, 42, .05), 0 2px 10px rgba(15, 23, 42, .04); }
  .card + .card, .card.mb-3, .row > [class*="col"] > .card { margin-bottom: 12px; }
  .card-header { min-height: 48px; padding: 12px 16px; border-bottom-color: var(--tw-border); }
  .card-body { padding: 14px 16px; }
  .card-title { font-size: 16px; }
  .list-group-item { padding: 13px 16px; min-height: 52px; }
  .btn { border-radius: 12px; font-weight: 600; }
  .form-control, .form-select { border-radius: 12px; }
  .table { font-size: 14px; }
  .table > :not(caption) > * > * { padding-top: 10px; padding-bottom: 10px; }

  /* A press shows, as on native buttons. */
  .btn, .tw-tab, .list-group-item-action, .card a, .tw-workspace-tabs .nav-link, .tw-icon-btn {
    transition: transform .12s ease, background-color .15s ease, opacity .15s ease; -webkit-tap-highlight-color: transparent;
  }
  .btn:active, .tw-workspace-tabs .nav-link:active, .tw-icon-btn:active { transform: scale(.96); }
  .list-group-item-action:active { background: var(--tw-surface); }

  /* A page coming in. */
  .tw-content { animation: tw-page-in .26s cubic-bezier(.2, .8, .2, 1) backwards; }

}
/* Entrance animations fill "backwards", never "both": a filled transform or opacity keeps the element a stacking
   context, so a pop-up inside it sat under the dark backdrop and every tap closed it (2026-10-30, phones). */
@keyframes tw-page-in { from { opacity: 0; transform: translateY(10px); } to { opacity: 1; transform: none; } }

/* Between pages, where the browser can (Chrome, Safari 18): the page slides while the bars stay put. */
@media (max-width: 575.98px) {
  @view-transition { navigation: auto; }
  .tw-navbar { view-transition-name: tw-topbar; }
  .tw-tabbar { view-transition-name: tw-tabbar; }
  ::view-transition-old(root) { animation: tw-vt-out .2s ease-in both; }
  ::view-transition-new(root) { animation: tw-vt-in .28s cubic-bezier(.2, .8, .2, 1) both; }
  .tw-content { animation: none; }
}
@supports not (view-transition-name: none) {
  @media (max-width: 575.98px) { .tw-content { animation: tw-page-in .26s cubic-bezier(.2, .8, .2, 1) backwards; } }
}
@keyframes tw-vt-out { to { opacity: 0; transform: translateX(-24px); } }
@keyframes tw-vt-in { from { opacity: 0; transform: translateX(32px); } }

@media (prefers-reduced-motion: reduce) {
  .tw-content { animation: none !important; }
  ::view-transition-old(root), ::view-transition-new(root) { animation: none !important; }
  .tw-frame-modal.fade .modal-dialog { transition: none !important; transform: none !important; }
}


/* Phones: the menu grows out of the tab bar's Menu button and shrinks back into it (product owner, 2026-10-30:
   "the menu should animate from the menu icon to give a feel of a phone"); the Menu button turns into a close. */
@media (max-width: 575.98px) {
  .tw-sidebar, :root.glass .tw-sidebar {
    left: 8px; right: 8px; top: auto; bottom: calc(66px + env(safe-area-inset-bottom, 0px));
    width: auto; height: auto; max-height: calc(100dvh - 90px - env(safe-area-inset-bottom, 0px) - env(safe-area-inset-top, 0px));
    margin: 0; border: 0; border-radius: 24px; padding-top: 0; padding-bottom: 6px; z-index: 1045;
    box-shadow: 0 18px 50px rgba(0, 0, 0, .35);
    transform-origin: 90% calc(100% + 34px);   /* the Menu tab, the last of five */
    transform: scale(.1); opacity: 0; visibility: hidden; pointer-events: none;
    transition: transform .34s cubic-bezier(.32, .72, 0, 1), opacity .2s ease, visibility 0s linear .34s;
  }
  /* :root.glass too: the glass menu's own rule outweighs a plain one (2026-10-30: the menu did not open with glass on). */
  html.sidebar-mobile-open .tw-sidebar, :root.glass.sidebar-mobile-open .tw-sidebar {
    transform: none; opacity: 1; visibility: visible; pointer-events: auto;
    transition: transform .38s cubic-bezier(.32, .72, 0, 1), opacity .18s ease, visibility 0s;
  }
  .tw-sidebar-scrim {
    display: block; opacity: 0; pointer-events: none; transition: opacity .25s ease;
    -webkit-backdrop-filter: blur(2px); backdrop-filter: blur(2px);
  }
  html.sidebar-mobile-open .tw-sidebar-scrim { opacity: 1; pointer-events: auto; }
  .tw-tabbar { z-index: 1046; }
  /* The menu's own close button: the Menu tab closes it instead. */
  .tw-sidebar [data-sidebar-close].tw-sidebar-toggle { display: none !important; }
  .tw-tab-menu .bi-x-lg { display: none; }
  html.sidebar-mobile-open .tw-tab-menu .bi-grid { display: none; }
  html.sidebar-mobile-open .tw-tab-menu .bi-x-lg { display: inline-block; animation: tw-spin-in .3s ease both; }
  html.sidebar-mobile-open .tw-tab-menu { color: var(--brand-primary, #1e2a78); font-weight: 600; }
}
@keyframes tw-spin-in { from { transform: rotate(-90deg) scale(.6); opacity: 0; } to { transform: none; opacity: 1; } }
@media (prefers-reduced-motion: reduce) {
  .tw-sidebar, html.sidebar-mobile-open .tw-sidebar { transition: opacity .15s ease, visibility 0s !important; transform: none !important; }
}

/* Sign-in (2026-10-30). Computers keep the glass card above. Phones, after the app login screen the product owner
   chose: the school's colour with slanting pill shapes and fine lines, its logo and name, as a splash that slides up
   as the white sign-in sheet rises (on loading); pill fields with icons inside, a pill button. */
.login-pill, .login-line { display: none; }
@media (min-width: 768px) {
  .login-split { display: flex; flex-direction: column; justify-content: center; min-height: 85vh; }
  /* Computers keep the sign-in's motion too (2026-10-30): the card slides in, the logo pops, the parts rise in turn. */
  .login-page .login-card { animation: login-card-in .7s cubic-bezier(.2, .8, .2, 1) both; }
  .login-page .login-logo { animation: login-pop .6s .25s cubic-bezier(.2, .9, .3, 1.25) both; }
  .login-page .login-school { animation: login-fade-up .5s .35s ease both; }
  .login-page .login-motto { animation: login-fade-up .5s .42s ease both; }
  .login-rise { animation: login-fade-up .5s cubic-bezier(.2, .8, .2, 1) both; }
  .login-rise:nth-child(1) { animation-delay: .45s; }
  .login-rise:nth-child(2) { animation-delay: .52s; }
  .login-rise:nth-child(3) { animation-delay: .59s; }
  .login-rise:nth-child(4) { animation-delay: .66s; }
  .login-rise:nth-child(5) { animation-delay: .73s; }
  .login-split > .d-md-block { animation: login-fade-up .5s .8s ease both; }
}
@keyframes login-card-in { from { opacity: 0; transform: translateY(40px) scale(.97); } to { opacity: 1; transform: none; } }
@media (max-width: 767.98px) {
  /* The card's box steps aside: the colour and the sheet fill the screen. */
  .login-page .login-card, .login-page .login-card-body { display: contents !important; }
  .login-page .login-orbs { display: none; }
  .login-pill, .login-line { display: block; }
  .login-brand.mb-4 { margin-bottom: 0 !important; }
  .login-title.h3 { text-align: center; }
  :root body.login-page, :root.glass body.login-page { background: var(--brand-primary, #1e2a78); }
  .login-page .tw-auth-main { padding: 0; min-height: 100vh; min-height: 100dvh; display: flex; }
  .login-page .login-container { flex: 1; display: flex; padding: 0 !important; max-width: none; }  /* !important: over the template's py-4 */
  .login-split { flex: 1; display: grid; grid-template-columns: minmax(0, 1.1fr) minmax(0, 1fr); overflow: hidden; }

  .login-brand {
    position: relative; overflow: hidden; display: flex; align-items: center; justify-content: center; padding: 48px; color: #fff;
    background: linear-gradient(160deg, color-mix(in srgb, var(--brand-primary, #1e2a78) 88%, #fff) 0%,
                var(--brand-primary, #1e2a78) 55%, color-mix(in srgb, var(--brand-primary, #1e2a78) 78%, #000) 100%);
  }
  .login-brand-inner { position: relative; z-index: 2; text-align: center; max-width: 440px; }
  .login-logo {
    display: inline-flex; align-items: center; justify-content: center; width: 104px; height: 104px; margin-bottom: 20px;
    border-radius: 30px; background: #fff; box-shadow: 0 16px 40px rgba(0, 0, 0, .28);
    animation: login-pop .7s .15s cubic-bezier(.2, .9, .3, 1.25) both;
  }
  .login-logo img { max-width: 76px; max-height: 76px; object-fit: contain; }
  .login-logo-icon { color: var(--brand-primary, #1e2a78); font-size: 48px; }
  .login-school { margin: 0; color: #fff; font-size: 34px; font-weight: 800; line-height: 1.15; animation: login-fade-up .6s .3s ease both; }
  /* White on the school's colour: the card's grey text colour (.login-card .text-secondary) made it unreadable there. */
  .login-motto { margin: 10px 0 0; font-style: italic; opacity: .85; animation: login-fade-up .6s .4s ease both; }
  .login-page .login-brand .login-motto { color: var(--brand-on-primary, #fff) !important; }
  /* Long rounded shapes slanting across the colour, and fine lines between them: the school's accent and second
     colour, each fading out (not mixed into the main colour, which turned a blue and amber school grey). */
  .login-pill {
    position: absolute; z-index: 0; height: 120px; border-radius: 999px; transform: rotate(-38deg); pointer-events: none;
    background: linear-gradient(90deg, color-mix(in srgb, var(--brand-accent, #8b5cf6) 60%, transparent), transparent);
    opacity: .55; animation: login-pill-in 1s cubic-bezier(.2, .8, .2, 1) both;
  }
  .login-pill-2 { background: linear-gradient(90deg, transparent, color-mix(in srgb, var(--brand-secondary, #38bdf8) 55%, transparent)); }
  .login-pill-1 { width: 420px; top: 6%; left: -140px; animation-delay: .1s; }
  .login-pill-2 { width: 360px; bottom: 14%; right: -120px; animation-delay: .25s; }
  .login-pill-3 { width: 260px; top: 44%; left: 30%; height: 90px; opacity: .35; animation-delay: .4s; }
  .login-line { position: absolute; z-index: 1; height: 1px; width: 170px; background: rgba(255, 255, 255, .35); transform: rotate(-38deg); pointer-events: none; }
  .login-line-1 { top: 12%; right: 12%; }
  .login-line-2 { bottom: 26%; left: 8%; }
  .login-line-3 { bottom: 8%; right: 22%; width: 110px; }

  .login-panel { display: flex; align-items: center; justify-content: center; padding: 48px 36px; background: var(--tw-surface-raised); }
  .login-panel-inner { width: 100%; max-width: 380px; }
  .login-title { margin: 0 0 2px; text-align: center; font-size: 30px; font-weight: 800; color: var(--brand-primary, #1e2a78); }
  [data-bs-theme="dark"] .login-title { color: var(--tw-text); }
  .login-lead { margin: 0 0 24px; text-align: center; color: var(--tw-muted); }
  /* Pill fields with their icon inside, and their label above: a placeholder disappears as soon as you type. */
  .login-panel .form-label { margin: 0 0 4px 16px; font-size: 13px; font-weight: 600; }
  .login-panel .form-control {
    height: 52px; border-radius: 999px; padding-left: 48px; font-size: 16px; border: 1.5px solid var(--tw-border);
    background-repeat: no-repeat; background-position: 18px center; background-size: 18px 18px;
  }
  .login-panel .form-control:focus { border-color: var(--brand-primary, #1e2a78); box-shadow: 0 0 0 4px color-mix(in srgb, var(--brand-primary, #1e2a78) 14%, transparent); }
  .login-panel input[name="username"] { background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' fill='%236b7280'%3E%3Cpath d='M8 8a3 3 0 1 0 0-6 3 3 0 0 0 0 6m2-3a2 2 0 1 1-4 0 2 2 0 0 1 4 0m4 8c0 1-1 1-1 1H3s-1 0-1-1 1-4 6-4 6 3 6 4'/%3E%3C/svg%3E"); }
  .login-panel input[name="password"] { background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' fill='%236b7280'%3E%3Cpath d='M8 1a2 2 0 0 1 2 2v4H6V3a2 2 0 0 1 2-2m3 6V3a3 3 0 0 0-6 0v4a2 2 0 0 0-2 2v5a2 2 0 0 0 2 2h6a2 2 0 0 0 2-2V9a2 2 0 0 0-2-2'/%3E%3C/svg%3E"); }
  .login-panel .btn-primary {
    width: 100%; height: 54px; border: 0; border-radius: 999px; font-size: 17px; font-weight: 700;
    color: var(--brand-on-primary, #fff);
    background: linear-gradient(90deg, color-mix(in srgb, var(--brand-primary, #1e2a78) 80%, #fff), var(--brand-primary, #1e2a78));
    box-shadow: 0 12px 24px color-mix(in srgb, var(--brand-primary, #1e2a78) 32%, transparent);
  }
  .login-panel .btn-primary:active { transform: scale(.97); }
  .login-note-phone { margin: 18px 0 6px; text-align: center; font-size: 13px; color: var(--tw-muted); }
  .login-panel .tw-powered { margin-top: 24px; }

  /* A splash in the school's colour (logo and name in the middle) that slides up to the top third
     as the white sheet rises under it. */

    .login-split { display: flex; flex-direction: column; min-height: 100dvh; }
    .login-brand {
      flex: 0 0 auto; height: 38dvh; min-height: 260px; padding: calc(16px + env(safe-area-inset-top, 0px)) 24px 52px;
      animation: login-splash 1.25s cubic-bezier(.65, 0, .2, 1) both;
    }
    .login-logo { width: 84px; height: 84px; border-radius: 24px; margin-bottom: 12px; }
    .login-logo img { max-width: 60px; max-height: 60px; }
    .login-school { font-size: 24px; }
    .login-motto { margin-top: 4px; font-size: 14px; }
    .login-pill { height: 80px; }
    .login-pill-1 { width: 300px; }
    .login-pill-2 { width: 260px; }
    .login-pill-3 { width: 180px; height: 60px; }
    .login-panel {
      flex: 1 0 auto; position: relative; z-index: 3; margin-top: -40px; border-radius: 40px 40px 0 0; align-items: flex-start;
      padding: 34px 24px calc(20px + env(safe-area-inset-bottom, 0px)); box-shadow: 0 -12px 30px rgba(0, 0, 0, .14);
      animation: login-sheet .9s .6s cubic-bezier(.32, .72, 0, 1) both;
    }
    .login-title { font-size: 28px; }
    /* Typing: the header shrinks to a strip, so the keyboard does not cover the Sign in button. It stays shrunk
       (login.html): growing back as the field loses focus would move the button from under the finger. */
    .login-page.tw-typing .login-brand { animation: none; height: 88px; min-height: 88px; padding-bottom: 48px; }
    .login-page.tw-typing :is(.login-logo, .login-motto, .login-pill, .login-line, .login-lead, .login-title) { display: none; }
    .login-page.tw-typing .login-school { font-size: 18px; animation: none; }
    .login-page.tw-typing .login-panel { padding-top: 22px; }
    .login-rise { animation: login-fade-up .45s cubic-bezier(.2, .8, .2, 1) both; }
    .login-rise:nth-child(1) { animation-delay: 1.05s; }
    .login-rise:nth-child(2) { animation-delay: 1.11s; }
    .login-rise:nth-child(3) { animation-delay: 1.17s; }
    .login-rise:nth-child(4) { animation-delay: 1.23s; }
    .login-rise:nth-child(5) { animation-delay: 1.29s; }
    .login-rise:nth-child(6) { animation-delay: 1.35s; }
    .login-rise:nth-child(7) { animation-delay: 1.41s; }
    .login-rise:nth-child(8) { animation-delay: 1.47s; }
}
@keyframes login-pop { from { transform: scale(.3); opacity: 0; } to { transform: none; opacity: 1; } }
@keyframes login-fade-up { from { transform: translateY(16px); opacity: 0; } to { transform: none; opacity: 1; } }
@keyframes login-pill-in { from { transform: rotate(-38deg) translateX(-60px); opacity: 0; } }
@keyframes login-from-left { from { transform: translateX(-100%); } to { transform: none; } }
@keyframes login-from-right { from { transform: translateX(100%); } to { transform: none; } }
/* The splash fills the screen for a moment, then shrinks to the top as the sheet slides up. */
@keyframes login-splash { 0%, 45% { height: 100dvh; } 100% { height: 38dvh; } }
@keyframes login-sheet { from { transform: translateY(100%); } to { transform: none; } }
@media (prefers-reduced-motion: reduce) {
  .login-brand, .login-panel, .login-logo, .login-school, .login-motto, .login-pill, .login-rise, .login-page .login-card, .login-split > .d-md-block { animation: none !important; }
}

/* Phones: the Profile tab and its panel (partials/profile_sheet.html), growing out of the tab like the menu. */
.tw-tab-avatar {
  width: 24px; height: 24px; border-radius: 50%; overflow: hidden; display: inline-flex; align-items: center; justify-content: center;
  background: color-mix(in srgb, var(--brand-primary, #1e2a78) 14%, transparent); color: var(--brand-primary, #1e2a78); font-size: 10px; font-weight: 700;
}
.tw-tab-avatar img { width: 100%; height: 100%; object-fit: cover; }
html.profile-open .tw-tab-profile { color: var(--brand-primary, #1e2a78); font-weight: 600; }
html.profile-open .tw-tab-avatar { box-shadow: 0 0 0 2px var(--brand-primary, #1e2a78); }
.tw-profile-scrim, .tw-profile-sheet { display: none; }
@media (max-width: 575.98px) {
  .tw-profile-scrim {
    display: block; position: fixed; inset: 0; z-index: 1040; background: rgba(15, 20, 30, .4); opacity: 0; pointer-events: none;
    transition: opacity .25s ease; -webkit-backdrop-filter: blur(2px); backdrop-filter: blur(2px);
  }
  html.profile-open .tw-profile-scrim { opacity: 1; pointer-events: auto; }
  .tw-profile-sheet {
    display: block; position: fixed; left: 8px; right: 8px; bottom: calc(66px + env(safe-area-inset-bottom, 0px)); z-index: 1045;
    max-height: calc(100dvh - 90px - env(safe-area-inset-bottom, 0px) - env(safe-area-inset-top, 0px)); overflow-y: auto; overflow-x: hidden;
    padding: 16px 16px 10px; border-radius: 24px; background: var(--tw-surface-raised); box-shadow: 0 18px 50px rgba(0, 0, 0, .3);
    transform-origin: 90% calc(100% + 34px);      /* the Profile tab, the last of five */
    transform: scale(.1); opacity: 0; visibility: hidden; pointer-events: none;
    transition: transform .34s cubic-bezier(.32, .72, 0, 1), opacity .2s ease, visibility 0s linear .34s;
  }
  html.profile-open .tw-profile-sheet {
    transform: none; opacity: 1; visibility: visible; pointer-events: auto;
    transition: transform .38s cubic-bezier(.32, .72, 0, 1), opacity .18s ease, visibility 0s;
  }
  .tw-profile-head { display: flex; align-items: center; gap: 12px; padding: 2px 4px 12px; }
  .tw-profile-head .tw-avatar { width: 48px; height: 48px; font-size: 17px; flex-shrink: 0; }
  .tw-profile-name { font-size: 17px; font-weight: 700; color: var(--tw-text); }
  .tw-profile-role { font-size: 13px; color: var(--tw-muted); }
  .tw-profile-links { display: flex; flex-direction: column; margin-bottom: 10px; border-radius: 14px; background: var(--tw-surface); overflow: hidden; }
  .tw-profile-links > [hidden] { display: none; }
  .tw-profile-links > * + * { border-top: 1px solid var(--tw-border); }
  .tw-profile-links a, .tw-profile-links button {
    display: flex; align-items: center; gap: 12px; width: 100%; min-height: 46px; padding: 10px 14px; border: 0; background: none;
    color: var(--tw-text); font-size: 15px; text-align: left; text-decoration: none;
  }
  .tw-profile-links i { width: 20px; font-size: 17px; color: var(--brand-primary, #1e2a78); }
  .tw-profile-links a:active, .tw-profile-links button:active { background: var(--tw-border); }
  .tw-profile-links .is-danger, .tw-profile-links .is-danger i { color: #d63939; }
  .tw-profile-theme {
    display: flex; align-items: center; justify-content: space-between; gap: 12px; margin-bottom: 10px; padding: 8px 14px;
    border-radius: 14px; background: var(--tw-surface); font-size: 15px; color: var(--tw-text);
  }
  .tw-profile-theme .btn { min-height: 34px; padding: 4px 10px; }
  /* Demonstration school: Switch role in the Profile panel (phones). */
  .tw-profile-label { margin: 2px 4px 6px; font-size: 12px; font-weight: 600; text-transform: uppercase; letter-spacing: .04em; color: var(--tw-text-secondary, #6b7280); }
  .tw-demo-roles .is-current { font-weight: 600; }
  /* The menu now grows out of the Menu tab, fourth of five. */
  .tw-sidebar, :root.glass .tw-sidebar { transform-origin: 70% calc(100% + 34px); }
}
@media (prefers-reduced-motion: reduce) {
  .tw-profile-sheet, html.profile-open .tw-profile-sheet { transition: opacity .15s ease, visibility 0s !important; transform: none !important; }
}

/* Profile photo picker (partials/components/photo_picker.html): the round preview and the two buttons. */
.tw-photo-preview {
  width: 88px; height: 88px; flex-shrink: 0; border-radius: 50%; overflow: hidden; display: flex; align-items: center; justify-content: center;
  background: var(--tw-surface); border: 2px dashed var(--tw-border); color: var(--tw-muted); font-size: 38px;
}
.tw-photo-preview img { width: 100%; height: 100%; object-fit: cover; }
.tw-photo-preview:has(img) { border-style: solid; border-color: var(--brand-primary, #1e2a78); }
/* A computer has no camera to open: one button, "Choose a photo". */
@media (pointer: fine) {
  .tw-photo-camera { display: none !important; }
}
/* Sign out sits in the middle of the Profile panel (2026-10-30). */
.tw-profile-links .is-danger { justify-content: center; font-weight: 600; }

/* Phones: a page in the pop-up is a centred card with round corners that grows out of the tapped tile and shrinks
   back into it (static/js/app.js sets --tw-tap-x/y); it keeps clear of the status bar and the home bar, so its
   Back and close buttons are within reach (2026-10-30). Replaces the slide-in from the right. */
@media (max-width: 575.98px) {
  .tw-frame-modal .modal-dialog, .tw-frame-modal .modal-dialog.modal-fullscreen-sm-down {
    position: fixed; left: 10px; right: 10px; top: calc(12px + env(safe-area-inset-top, 0px)); bottom: calc(12px + env(safe-area-inset-bottom, 0px));
    width: auto; height: auto; max-width: none; margin: 0;
    transform-origin: calc(var(--tw-tap-x, 50vw) - 10px) calc(var(--tw-tap-y, 50vh) - 12px - env(safe-area-inset-top, 0px));
  }
  .tw-frame-modal .modal-dialog .modal-content, .tw-frame-modal .modal-fullscreen-sm-down .modal-content {
    height: 100%; border: 0; border-radius: 22px; overflow: hidden; box-shadow: 0 24px 60px rgba(0, 0, 0, .35);
  }
  .tw-frame-modal .modal-header { min-height: 52px; padding: 8px 10px; }
  .tw-frame-modal .tw-modal-back, .tw-frame-modal .btn-close { min-width: 40px; min-height: 40px; }
  .tw-frame-modal.fade .modal-dialog { opacity: 0; transform: scale(.06); transition: transform .36s cubic-bezier(.32, .72, 0, 1), opacity .22s ease; }
  .tw-frame-modal.show .modal-dialog { opacity: 1; transform: none; }
  .tw-frame-modal.fade:not(.show) .modal-dialog { transition: transform .26s cubic-bezier(.4, 0, 1, 1), opacity .2s ease .06s; }
}
@media (max-width: 575.98px) and (prefers-reduced-motion: reduce) {
  .tw-frame-modal.fade .modal-dialog, .tw-frame-modal.fade:not(.show) .modal-dialog { transform: none !important; transition: opacity .15s ease !important; }
}
/* Phones: the top bar's Back, Forward and Refresh, small, between the logo and the search bar (2026-10-30). */
@media (max-width: 575.98px) {
  .tw-navbar .tw-history-nav { order: 0; gap: 0; flex: 0 0 auto; }
  .tw-navbar .tw-history-nav .tw-icon-btn { width: 30px; height: 36px; font-size: 17px; }
  .tw-navbar .tw-history-nav .tw-icon-btn:disabled { opacity: .3; }
  .tw-navbar .tw-search-input { padding-left: 34px !important; font-size: 15px !important; }
  .tw-navbar .tw-search-icon { left: 12px !important; }
}
/* Phones: only Back beside the logo (2026-10-30), so the search bar keeps most of the bar. */
@media (max-width: 575.98px) {
  .tw-navbar .tw-history-nav [data-tw-history="forward"], .tw-navbar .tw-history-nav [data-tw-history="reload"] { display: none; }
  .tw-navbar .tw-history-nav .tw-icon-btn { width: 34px; }
  .tw-navbar { gap: 6px; }
}
/* Phones: Back on the left, the logo on the right, so the search bar sits in the middle (2026-10-30). */
@media (max-width: 575.98px) {
  .tw-navbar .tw-history-nav { order: -1; }
  .tw-navbar .tw-navbar-school { order: 5; }
}
@media (max-width: 575.98px) {
  .app-search-suggestions { left: -44px !important; right: -46px !important; }
}
/* The installed app's gestures (static/js/app.js): the forward cue at the right edge, and pull-to-refresh. */
.tw-swipe-cue.is-right { left: auto; right: 6px; }
.tw-pull-cue {
  position: fixed; top: calc(60px + env(safe-area-inset-top, 0px)); left: 50%; z-index: 2000; width: 40px; height: 40px;
  border-radius: 50%; display: flex; align-items: center; justify-content: center; pointer-events: none; opacity: 0;
  font-size: 20px; color: var(--tw-muted); background: var(--tw-surface-raised); box-shadow: 0 4px 16px rgba(15, 23, 42, .18);
  transform: translate(-50%, 0); transition: opacity .15s, color .15s;
}
.tw-pull-cue.is-ready { color: var(--brand-primary, #1e2a78); }
.tw-pull-cue.is-loading i { animation: tw-spin .7s linear infinite; }
/* Phones: no Back/Forward/Refresh buttons; the phone's gestures do it (2026-10-30). The logo goes back to the left. */
@media (max-width: 575.98px) {
  .tw-navbar .tw-history-nav { display: none !important; }
  .tw-navbar .tw-navbar-school { order: 0; }
  .app-search-suggestions { left: -50px !important; right: -4px !important; }
}
/* In the installed app the pull-to-refresh above is the only one (no second, built-in pull on Android). */
@media (display-mode: standalone) { html, body { overscroll-behavior-y: contain; } }

/* Phones: from the sign-in screen into the app (2026-10-30). Leaving: the sheet slides down and the school's colour fills
   the screen with the logo in the middle; arriving: the first screen eases in from that colour (static/js/app.js). */
@media (max-width: 767.98px) {
  body.tw-leaving-sign-in .login-panel { animation: tw-sheet-away .38s cubic-bezier(.4, 0, 1, 1) forwards !important; }
  body.tw-leaving-sign-in .login-brand { animation: tw-brand-fill .45s cubic-bezier(.65, 0, .35, 1) forwards !important; }
  body.tw-leaving-sign-in .login-logo { animation: tw-logo-hold .45s ease forwards !important; }
  html.tw-entering-app body { background: var(--brand-primary, #1e2a78); }
  html.tw-entering-app .tw-navbar { animation: tw-enter-top .5s .05s cubic-bezier(.2, .8, .2, 1) both; }
  html.tw-entering-app .tw-content { animation: tw-enter-page .55s cubic-bezier(.2, .8, .2, 1) both !important; }
  html.tw-entering-app .tw-tabbar { animation: tw-enter-bottom .5s .12s cubic-bezier(.2, .8, .2, 1) both; }
  html.tw-entering-app .tw-main { animation: tw-enter-bg .55s ease both; }
}
@keyframes tw-sheet-away { to { transform: translateY(100%); opacity: .6; } }
@keyframes tw-brand-fill { to { height: 100dvh; min-height: 100dvh; } }
@keyframes tw-logo-hold { 50% { transform: scale(1.08); } to { transform: scale(1); } }
@keyframes tw-enter-top { from { transform: translateY(-100%); opacity: 0; } }
@keyframes tw-enter-bottom { from { transform: translateY(100%); opacity: 0; } }
@keyframes tw-enter-page { from { transform: scale(1.06); opacity: 0; } }
@keyframes tw-enter-bg { from { background: var(--brand-primary, #1e2a78); } }
@media (prefers-reduced-motion: reduce) {
  body.tw-leaving-sign-in .login-panel, body.tw-leaving-sign-in .login-brand, body.tw-leaving-sign-in .login-logo,
  html.tw-entering-app .tw-navbar, html.tw-entering-app .tw-content, html.tw-entering-app .tw-tabbar, html.tw-entering-app .tw-main { animation: none !important; }
}

/* Staff sign-up (accounts/join, 2026-10-30): the same glass card over the school's colours on every screen. Phones
   used to get the sign-in page's phone layout (a school-colour splash and a sheet), which these pages are not built
   for: the form sat on the blue (product owner: "match the phone view to the desktop view"). */
.join-page .tw-auth-main, .join-page .container-tight { position: relative; z-index: 1; }
@supports ((backdrop-filter: blur(1px)) or (-webkit-backdrop-filter: blur(1px))) {
  :root.glass body.join-page { background: var(--tw-surface); }
}

/* ---- Phones, 2026-11-11 (product owner: "the menu bar should look like Twincare's"; "when you scroll items down, the
   menu goes past the edge of the phone") ----
   The tab bar is a floating rounded bar above the phone's gesture bar, the current page on a soft pill. The menu and
   the profile panel open above it and never grow taller than the screen: a height that every phone browser
   understands comes first (older ones ignore dvh, which left the menu with no limit), their own content scrolls,
   and the page behind stays still while one of them is open. */
@media (max-width: 575.98px) {
  .tw-tabbar {
    left: max(10px, env(safe-area-inset-left, 0px)); right: max(10px, env(safe-area-inset-right, 0px));
    bottom: calc(10px + env(safe-area-inset-bottom, 0px)); height: 62px; padding: 6px;
    border: 1px solid var(--tw-border); border-radius: 22px;
    box-shadow: 0 10px 30px rgba(15, 23, 42, .16), 0 1px 3px rgba(15, 23, 42, .08);
    user-select: none; -webkit-user-select: none;
  }
  .tw-tab { min-height: 0; border-radius: 16px; font-weight: 600; line-height: 1.1;
    transition: color .15s ease, background-color .15s ease, transform .12s ease; }
  .tw-tab:active { opacity: 1; transform: scale(.94); }
  .tw-tab.active, html.sidebar-mobile-open .tw-tab-menu, html.profile-open .tw-tab-profile {
    color: var(--brand-primary, #1e2a78); background: color-mix(in srgb, var(--brand-primary, #1e2a78) 12%, transparent);
  }
  [data-bs-theme="dark"] .tw-tab.active, [data-bs-theme="dark"] html.sidebar-mobile-open .tw-tab-menu,
  html[data-bs-theme="dark"].sidebar-mobile-open .tw-tab-menu, html[data-bs-theme="dark"].profile-open .tw-tab-profile {
    color: #fff; background: color-mix(in srgb, var(--brand-primary, #1e2a78) 45%, transparent);
  }
  .tw-tab-badge { top: 2px; }
  /* Room under the page for the floating bar. */
  .tw-main { padding-bottom: calc(88px + env(safe-area-inset-bottom, 0px)); }
  body:has(.tw-tabbar) .tw-demo-home { bottom: calc(92px + env(safe-area-inset-bottom, 0px)) !important; }

  /* The menu and the profile panel: above the bar, inside the screen, scrolling within themselves. */
  .tw-sidebar, :root.glass .tw-sidebar, .tw-profile-sheet {
    bottom: calc(84px + env(safe-area-inset-bottom, 0px));
    max-height: calc(100vh - 170px);
    max-height: calc(100dvh - 108px - env(safe-area-inset-bottom, 0px) - env(safe-area-inset-top, 0px));
    overflow-y: auto; overflow-x: hidden; overscroll-behavior: contain; -webkit-overflow-scrolling: touch;
    transform-origin: 90% calc(100% + 44px);
  }
  html.sidebar-mobile-open, html.sidebar-mobile-open body, html.profile-open, html.profile-open body { overflow: hidden; }
}
