/*
 * LIQUID GLASS - student (/dashboard) and teacher (/admin) panels.
 * ================================================================
 * An overlay on the compiled build: it restyles the semantic Tailwind
 * utilities the panels are built from (bg-surface, rounded-panel, shadow-card,
 * bg-primary ...) and never touches the landing page. Every rule is scoped to
 * .dashboard / .admin, or to body.theme-student / body.theme-teacher for MUI
 * portal content (modals, menus, drawer, toasts).
 *
 * Idea: glass that frosts and clears. Surfaces are frosted glass over a slowly
 * drifting wallpaper in the section's own hue; on navigation a frost sheet over
 * the page clears while a beam of light crosses it (see glass.js).
 *
 * To port into src/: copy this file to src/styles/glass.scss, import it last in
 * main.scss, and call initGlass() from glass.js once in index.js.
 */

/* ------------------------------------------------------------------ tokens */
body.theme-student,
body.theme-teacher {
  /* Clearer than frosted: iOS 26 glass lets the colour through and does the
     work with light on its edges, not with milk in its body. */
  --g-fill: rgb(255 255 255 / 0.38);
  --g-fill-strong: rgb(255 255 255 / 0.66);
  --g-fill-nested: rgb(255 255 255 / 0.4);
  --g-well: rgb(255 255 255 / 0.36);
  --g-edge: rgb(255 255 255 / 0.6);
  --g-hair: rgb(255 255 255 / 0.55);
  --g-blur: blur(20px) saturate(210%) brightness(1.04);
  --g-shadow: inset 0 1px 0 rgb(255 255 255 / 0.9),
    inset 0 -1px 1px rgb(255 255 255 / 0.35),
    inset 0 0 14px rgb(255 255 255 / 0.22),
    0 1px 2px rgb(var(--g-tint) / 0.05),
    0 12px 30px -16px rgb(var(--g-tint) / 0.26);
  --g-shadow-lift: inset 0 1px 0 rgb(255 255 255 / 0.95),
    inset 0 -1px 1px rgb(255 255 255 / 0.4),
    inset 0 0 16px rgb(255 255 255 / 0.28),
    0 2px 4px rgb(var(--g-tint) / 0.06),
    0 22px 44px -20px rgb(var(--g-tint) / 0.36);
  --g-r-panel: 2.2rem;
  --g-r-card: 1.6rem;
  --g-r-soft: 1rem;
  /* Damped spring with a ~2% overshoot; the bouncier one is for small things. */
  --g-spring: linear(0, 0.006, 0.025 2.8%, 0.101 6.1%, 0.539 18.9%, 0.721 25.3%,
      0.849 31.5%, 0.937 38.1%, 0.968 41.8%, 0.991 45.7%, 1.006 50.1%,
      1.015 55%, 1.017 63.9%, 1.001);
  --g-bounce: linear(0, 0.009, 0.035 2.1%, 0.141, 0.281 6.7%, 0.723 12.9%,
      0.938 16.7%, 1.017, 1.077, 1.121, 1.149 24.3%, 1.159, 1.163, 1.161,
      1.154 29.9%, 1.129 32.8%, 1.051 39.6%, 1.017 43.1%, 0.991, 0.977 51%,
      0.974 53.8%, 0.975 57.1%, 0.997 69.8%, 1.003 76.9%, 1.004 83.8%, 1);
  --g-out: cubic-bezier(0.22, 1, 0.36, 1);
}

/* Section hues: --g-tint colours shadows, the three blobs paint the wallpaper. */
body.theme-student {
  --g-tint: 76 29 149;
  --g-ground: 243 240 255;
  --g-b1: 167 139 250;
  --g-b2: 240 171 252;
  --g-b3: 147 197 253;
  --g-b4: 196 181 253;
}
body.theme-teacher {
  --g-tint: 8 72 92;
  --g-ground: 236 246 248;
  --g-b1: 94 234 212;
  --g-b2: 125 211 252;
  --g-b3: 165 243 252;
  --g-b4: 153 246 228;
}

/* --------------------------------------------------------------- wallpaper */
body.theme-student,
body.theme-teacher {
  background: rgb(var(--g-ground));
}
body.theme-student::before,
body.theme-teacher::before,
body.theme-student::after,
body.theme-teacher::after {
  content: "";
  position: fixed;
  inset: -25vmax;
  z-index: -1;
  pointer-events: none;
  will-change: transform;
}
body.theme-student::before,
body.theme-teacher::before {
  /* Large overlapping fields, so they melt into one mesh rather than read as spots. */
  background:
    radial-gradient(60vmax 50vmax at 18% 22%, rgb(var(--g-b1) / 0.62), transparent 72%),
    radial-gradient(56vmax 48vmax at 84% 18%, rgb(var(--g-b2) / 0.5), transparent 72%),
    radial-gradient(70vmax 56vmax at 62% 86%, rgb(var(--g-b3) / 0.55), transparent 72%),
    radial-gradient(50vmax 44vmax at 22% 88%, rgb(var(--g-b4) / 0.5), transparent 72%);
  animation: g-drift-a 46s ease-in-out infinite alternate;
}
body.theme-student::after,
body.theme-teacher::after {
  background:
    radial-gradient(46vmax 38vmax at 48% 50%, rgb(255 255 255 / 0.5), transparent 70%),
    radial-gradient(34vmax 30vmax at 76% 64%, rgb(var(--g-b1) / 0.3), transparent 70%);
  animation: g-drift-b 38s ease-in-out infinite alternate;
}
@keyframes g-drift-a {
  0% { transform: translate3d(0, 0, 0) rotate(0deg) scale(1); }
  50% { transform: translate3d(4vmax, -3vmax, 0) rotate(8deg) scale(1.06); }
  100% { transform: translate3d(-3vmax, 3vmax, 0) rotate(-6deg) scale(1.02); }
}
@keyframes g-drift-b {
  0% { transform: translate3d(0, 0, 0) scale(1); }
  100% { transform: translate3d(-6vmax, -4vmax, 0) scale(1.12); }
}

/* The layouts painted their own washes; the wallpaper now shows through. */
.dashboard,
.admin {
  background: transparent !important;
}

/* ----------------------------------------------------------- glass surface */
/* Top-level panels: frosted, edge-lit, soft tinted shadow. */
:is(.dashboard, .admin) :is(.rounded-panel, .rounded-card, .rounded-wide).bg-surface,
:is(.dashboard, .admin) .dash-hero,
body:is(.theme-student, .theme-teacher) [role="dialog"].bg-surface {
  background-color: var(--g-fill) !important;
  -webkit-backdrop-filter: var(--g-blur);
  backdrop-filter: var(--g-blur);
  border-color: var(--g-edge) !important;
  box-shadow: var(--g-shadow) !important;
}
:is(.dashboard, .admin) .rounded-panel,
body:is(.theme-student, .theme-teacher) [role="dialog"].rounded-panel {
  border-radius: var(--g-r-panel);
}
:is(.dashboard, .admin) .rounded-card { border-radius: var(--g-r-card); }
:is(.dashboard, .admin) .rounded-soft,
body:is(.theme-student, .theme-teacher) .MuiDrawer-paper .rounded-soft {
  border-radius: var(--g-r-soft);
}

/* Glass inside glass: no second blur (it doubles the cost for no visible gain),
   just a brighter pane so the row still reads as a separate piece. */
:is(.dashboard, .admin) :is(.rounded-panel, .rounded-card, .dash-hero) :is(.rounded-panel, .rounded-card).bg-surface {
  background-color: var(--g-fill-nested) !important;
  -webkit-backdrop-filter: none;
  backdrop-filter: none;
  box-shadow: inset 0 1px 0 rgb(255 255 255 / 0.8), 0 6px 16px -10px rgb(var(--g-tint) / 0.25) !important;
}

/* Edge light: a 1px rim brightest at the top-left, like light on a lens. */
:is(.dashboard, .admin) :is(.rounded-panel, .rounded-card).bg-surface:not(.fixed, .absolute, .sticky),
:is(.dashboard, .admin) .dash-hero,
body:is(.theme-student, .theme-teacher) [role="dialog"].bg-surface {
  position: relative;
  isolation: isolate;
}
:is(.dashboard, .admin) :is(.rounded-panel, .rounded-card).bg-surface:not(.overflow-x-auto)::before,
:is(.dashboard, .admin) .dash-hero::before,
body:is(.theme-student, .theme-teacher) [role="dialog"].bg-surface::before {
  content: "";
  position: absolute;
  inset: 0;
  border-radius: inherit;
  padding: 1px;
  background: linear-gradient(135deg, rgb(255 255 255 / 0.95), rgb(255 255 255 / 0.15) 38%,
      rgb(255 255 255 / 0.05) 62%, rgb(255 255 255 / 0.7));
  -webkit-mask: linear-gradient(#000 0 0) content-box, linear-gradient(#000 0 0);
  -webkit-mask-composite: xor;
  mask-composite: exclude;
  pointer-events: none;
  z-index: 2;
}
/* Specular: a soft light that follows the pointer (glass.js sets --mx/--my). */
:is(.dashboard, .admin) :is(.rounded-panel, .rounded-card).bg-surface:not(.overflow-x-auto)::after,
:is(.dashboard, .admin) .dash-hero::after {
  content: "";
  position: absolute;
  inset: 0;
  border-radius: inherit;
  pointer-events: none;
  z-index: 1;
  opacity: 0;
  background: radial-gradient(28rem circle at var(--mx, 50%) var(--my, 0%),
      rgb(255 255 255 / 0.8), rgb(255 255 255 / 0.25) 35%, rgb(255 255 255 / 0) 65%);
  -webkit-mask-image: none;
  mask-image: none;
  transition: opacity 320ms var(--g-out);
}
:is(.dashboard, .admin) :is(.rounded-panel, .rounded-card).bg-surface:hover::after,
:is(.dashboard, .admin) .dash-hero:hover::after {
  opacity: 1;
}
/* Content sits above both lights. */
:is(.dashboard, .admin) :is(.rounded-panel, .rounded-card).bg-surface > *,
:is(.dashboard, .admin) .dash-hero > * {
  position: relative;
  z-index: 3;
}
:is(.dashboard, .admin) :is(.rounded-panel, .rounded-card).bg-surface > .absolute {
  position: absolute;
}

/* Hover lift for the panels people act on. */
:is(.dashboard, .admin) section.rounded-panel.bg-surface,
:is(.dashboard, .admin) .group.rounded-panel.bg-surface {
  transition: box-shadow 420ms var(--g-out), transform 520ms var(--g-spring);
}
:is(.dashboard, .admin) section.rounded-panel.bg-surface:hover {
  box-shadow: var(--g-shadow-lift) !important;
  transform: translateY(-2px);
}

/* Stat tiles: 3D tilt toward the pointer (glass.js sets --rx/--ry) and an icon that pops. */
:is(.dashboard, .admin) .group.rounded-panel.bg-surface {
  transform: perspective(900px) rotateX(var(--rx, 0deg)) rotateY(var(--ry, 0deg)) translateY(var(--lift, 0px));
  transform-style: preserve-3d;
}
:is(.dashboard, .admin) .group.rounded-panel.bg-surface:hover {
  --lift: -4px;
  box-shadow: var(--g-shadow-lift) !important;
}
:is(.dashboard, .admin) .group.rounded-panel span.rounded-soft:first-child {
  transition: transform 560ms var(--g-bounce);
}
:is(.dashboard, .admin) .group.rounded-panel:hover span.rounded-soft:first-child {
  transform: scale(1.14) rotate(-8deg);
}
:is(.dashboard, .admin) .group.rounded-panel .text-3xl {
  font-size: 2.8rem;
  font-weight: 700;
}

/* ------------------------------------------------------------ fills, wells */
:is(.dashboard, .admin) .bg-surface-sunken,
body:is(.theme-student, .theme-teacher) [role="dialog"] .bg-surface-sunken,
body:is(.theme-student, .theme-teacher) .MuiDrawer-paper .bg-surface-sunken {
  background-color: var(--g-well) !important;
  box-shadow: inset 0 1px 3px rgb(var(--g-tint) / 0.09), inset 0 -1px 0 rgb(255 255 255 / 0.7);
}
/* Skeletons: a sheen sliding across glass rather than a grey bar. */
:is(.dashboard, .admin) .dash-shimmer {
  background-image: linear-gradient(100deg, rgb(255 255 255 / 0) 20%, rgb(255 255 255 / 0.85) 50%, rgb(255 255 255 / 0) 80%) !important;
  background-size: 220% 100%;
}

/* The focus card when something is due or late: glass tinted by its urgency. */
:is(.dashboard, .admin) .rounded-panel:is(.bg-warning-soft, .bg-danger-soft) {
  --g-tone: var(--c-warning);
  position: relative;
  background-color: rgb(var(--g-tone) / 0.1) !important;
  background-image: radial-gradient(120% 90% at 100% 0%, rgb(var(--g-tone) / 0.16), rgb(var(--g-tone) / 0) 60%);
  -webkit-backdrop-filter: var(--g-blur);
  backdrop-filter: var(--g-blur);
  border-color: var(--g-edge) !important;
  box-shadow: var(--g-shadow), 0 20px 50px -24px rgb(var(--g-tone) / 0.55) !important;
}
:is(.dashboard, .admin) .rounded-panel.bg-danger-soft { --g-tone: var(--c-danger); }
:is(.dashboard, .admin) .bg-primary-soft,
body:is(.theme-student, .theme-teacher) :is([role="dialog"], .MuiDrawer-paper) .bg-primary-soft {
  background-color: rgb(var(--c-primary) / 0.13) !important;
}
:is(.dashboard, .admin) .bg-success-soft { background-color: rgb(var(--c-success) / 0.13) !important; }
:is(.dashboard, .admin) .bg-warning-soft { background-color: rgb(var(--c-warning) / 0.13) !important; }
:is(.dashboard, .admin) .bg-danger-soft { background-color: rgb(var(--c-danger) / 0.12) !important; }
:is(.dashboard, .admin) :is(.border-rule-subtle, .border-rule):not(.rounded-panel, .rounded-card),
body:is(.theme-student, .theme-teacher) [role="dialog"] .border-rule-subtle {
  border-color: rgb(var(--g-tint) / 0.08) !important;
}
:is(.dashboard, .admin) .border-dashed { border-color: rgb(var(--g-tint) / 0.22) !important; }
:is(.dashboard, .admin) hr { border-color: rgb(var(--g-tint) / 0.08); }

/* Progress tracks: a glass groove with a glossy liquid fill. */
:is(.dashboard, .admin) .dash-bar {
  box-shadow: inset 0 1px 0 rgb(255 255 255 / 0.45), 0 0 12px -2px currentColor;
  background-image: linear-gradient(180deg, rgb(255 255 255 / 0.35), rgb(255 255 255 / 0) 60%);
}

/* ---------------------------------------------------------------- the hero */
:is(.dashboard, .admin) .dash-hero {
  background-image: linear-gradient(135deg, rgb(255 255 255 / 0.55), rgb(255 255 255 / 0.18)) !important;
}
:is(.dashboard, .admin) .dash-hero h1 {
  font-size: clamp(2.6rem, 2.2vw, 3.2rem);
  font-weight: 700;
  line-height: 1.15;
  color: rgb(var(--c-ink-strong));
}

/* --------------------------------------------------------------- headings */
:is(.dashboard, .admin) header h1.text-2xl {
  font-size: 2.4rem;
  font-weight: 700;
  line-height: 1.25;
  color: rgb(var(--c-ink-strong));
}
:is(.dashboard, .admin) header .rounded-card.bg-primary-soft {
  height: 4.2rem;
  width: 4.2rem;
  border-radius: 1.2rem;
  background-color: rgb(var(--c-primary)) !important;
  color: #fff;
  box-shadow: inset 0 1px 0 rgb(255 255 255 / 0.4), 0 12px 24px -10px rgb(var(--c-primary) / 0.7);
}

/* ------------------------------------------------------------ the sidebar */
/* Floats as one pane of glass with its own margin, like the iPadOS sidebar. */
:is(.dashboard, .admin) > aside {
  top: 1.2rem !important;
  height: calc(100vh - 2.4rem) !important;
  margin: 1.2rem;
  margin-inline-end: 0;
  border: 1px solid var(--g-edge) !important;
  border-radius: var(--g-r-panel);
  background-color: rgb(255 255 255 / 0.42) !important;
  -webkit-backdrop-filter: var(--g-blur);
  backdrop-filter: var(--g-blur);
  box-shadow: var(--g-shadow);
  transition: width 520ms var(--g-spring) !important;
}
@media (min-width: 768px) {
  :is(.dashboard, .admin) > main.main-wrapper {
    padding-top: 1.2rem;
  }
}

/* Nav rows */
:is(.dashboard, .admin) aside nav ul,
body:is(.theme-student, .theme-teacher) .MuiDrawer-paper nav ul {
  position: relative;
}
:is(.dashboard, .admin) aside li > :is(a, button),
:is(.dashboard, .admin) aside li > span > :is(a, button),
body:is(.theme-student, .theme-teacher) .MuiDrawer-paper li :is(a, button) {
  border-radius: 1.1rem;
  z-index: 1;
  transition: background-color 220ms var(--g-out), color 220ms var(--g-out);
}
:is(.dashboard, .admin) aside li :is(a, button):hover,
body:is(.theme-student, .theme-teacher) .MuiDrawer-paper li :is(a, button):hover {
  background-color: rgb(255 255 255 / 0.55) !important;
}
:is(.dashboard, .admin) aside li :is(a, button) > svg:first-of-type,
body:is(.theme-student, .theme-teacher) .MuiDrawer-paper li :is(a, button) > svg:first-of-type {
  transition: transform 520ms var(--g-bounce);
}
:is(.dashboard, .admin) aside li :is(a, button):hover > svg:first-of-type,
body:is(.theme-student, .theme-teacher) .MuiDrawer-paper li :is(a, button):hover > svg:first-of-type {
  transform: scale(1.18) translateX(-2px);
}
:is(.dashboard, .admin) aside li :is(a, button):active > svg:first-of-type {
  transform: scale(0.88);
}

/* The lens: one pill of glass that glides to the active row (glass.js). When it
   is in place the rows' own active fill and marker step aside for it. */
.g-lens {
  position: absolute;
  top: 0;
  left: 0;
  z-index: 0;
  border-radius: 1.1rem;
  pointer-events: none;
  background:
    linear-gradient(180deg, rgb(255 255 255 / 0.55), rgb(255 255 255 / 0) 55%),
    rgb(var(--c-primary) / 0.16);
  box-shadow: inset 0 1px 0 rgb(255 255 255 / 0.95),
    inset 0 0 0 1px rgb(255 255 255 / 0.55),
    0 10px 24px -10px rgb(var(--c-primary) / 0.55);
  opacity: 0;
  transition: transform 640ms var(--g-spring), width 640ms var(--g-spring),
    height 640ms var(--g-spring), opacity 240ms var(--g-out);
}
.g-lens.is-on { opacity: 1; }
.g-lens.is-instant { transition: none; }
.g-lens.is-moving { animation: g-lens-stretch 640ms var(--g-out); }
@keyframes g-lens-stretch {
  0% { filter: none; }
  30% { filter: blur(0.4px) saturate(1.4); scale: 0.92 1.18; }
  100% { filter: none; scale: 1 1; }
}
.g-has-lens li :is(a, button):is(.bg-primary, .bg-warning, .bg-danger, .bg-success)-soft {
  background-color: transparent !important;
}
.g-has-lens li :is(a, button) > span.absolute.bg-primary {
  display: none;
}

/* Mobile menu button and drawer */
:is(.dashboard, .admin) .fixed button.bg-surface {
  border-radius: 1.4rem;
  background-color: rgb(255 255 255 / 0.55) !important;
  -webkit-backdrop-filter: var(--g-blur);
  backdrop-filter: var(--g-blur);
  border-color: var(--g-edge) !important;
  box-shadow: var(--g-shadow) !important;
}
body:is(.theme-student, .theme-teacher) .MuiDrawer-paper {
  background-color: rgb(255 255 255 / 0.6) !important;
  -webkit-backdrop-filter: var(--g-blur);
  backdrop-filter: var(--g-blur);
  border-radius: 2.2rem 0 0 2.2rem;
  box-shadow: inset 1px 0 0 var(--g-edge), -20px 0 60px -20px rgb(var(--g-tint) / 0.35) !important;
}

/* ---------------------------------------------------------------- buttons */
/* Primary: a drop of tinted glass. Highlight on the top lip, coloured glow
   underneath, a shine that crosses it on hover, a squish on press. */
:is(.dashboard, .admin) :is(a, button):is(.bg-primary, .bg-warning, .bg-danger, .bg-success),
body:is(.theme-student, .theme-teacher) [role="dialog"] :is(a, button):is(.bg-primary, .bg-warning, .bg-danger, .bg-success),
body:is(.theme-student, .theme-teacher) .MuiButton-containedPrimary {
  position: relative;
  overflow: hidden;
  border-radius: 1.1rem;
  background-image: linear-gradient(180deg, rgb(255 255 255 / 0.22), rgb(255 255 255 / 0) 55%);
  box-shadow: inset 0 1px 0 rgb(255 255 255 / 0.45),
    inset 0 -1px 0 rgb(0 0 0 / 0.08),
    0 12px 26px -12px rgb(var(--g-glow, var(--c-primary)) / 0.8) !important;
  transition: transform 520ms var(--g-bounce), box-shadow 320ms var(--g-out),
    background-color 200ms var(--g-out) !important;
}
:is(.dashboard, .admin) :is(a, button):is(.bg-primary, .bg-warning, .bg-danger, .bg-success)::after,
body:is(.theme-student, .theme-teacher) [role="dialog"] :is(a, button):is(.bg-primary, .bg-warning, .bg-danger, .bg-success)::after,
body:is(.theme-student, .theme-teacher) .MuiButton-containedPrimary::after {
  content: "";
  position: absolute;
  inset: -50% auto -50% -60%;
  width: 40%;
  transform: translateX(-120%) skewX(-18deg);
  background: linear-gradient(90deg, rgb(255 255 255 / 0), rgb(255 255 255 / 0.55), rgb(255 255 255 / 0));
  pointer-events: none;
}
:is(.dashboard, .admin) :is(a, button):is(.bg-primary, .bg-warning, .bg-danger, .bg-success):hover::after,
body:is(.theme-student, .theme-teacher) [role="dialog"] :is(a, button):is(.bg-primary, .bg-warning, .bg-danger, .bg-success):hover::after,
body:is(.theme-student, .theme-teacher) .MuiButton-containedPrimary:hover::after {
  transform: translateX(520%) skewX(-18deg);
  transition: transform 900ms var(--g-out);
}
:is(.dashboard, .admin) :is(a, button):is(.bg-primary, .bg-warning, .bg-danger, .bg-success):hover,
body:is(.theme-student, .theme-teacher) [role="dialog"] :is(a, button):is(.bg-primary, .bg-warning, .bg-danger, .bg-success):hover,
body:is(.theme-student, .theme-teacher) .MuiButton-containedPrimary:hover {
  transform: translateY(-2px);
  box-shadow: inset 0 1px 0 rgb(255 255 255 / 0.5),
    inset 0 -1px 0 rgb(0 0 0 / 0.08),
    0 18px 34px -12px rgb(var(--g-glow, var(--c-primary)) / 0.9) !important;
}
:is(.dashboard, .admin) :is(a, button):is(.bg-primary, .bg-warning, .bg-danger, .bg-success):active,
body:is(.theme-student, .theme-teacher) [role="dialog"] :is(a, button):is(.bg-primary, .bg-warning, .bg-danger, .bg-success):active,
body:is(.theme-student, .theme-teacher) .MuiButton-containedPrimary:active {
  transform: scale(0.95);
  transition-duration: 120ms !important;
}
:is(.dashboard, .admin) :is(a, button):is(.bg-primary, .bg-warning, .bg-danger, .bg-success):focus-visible,
body:is(.theme-student, .theme-teacher) [role="dialog"] :is(a, button):is(.bg-primary, .bg-warning, .bg-danger, .bg-success):focus-visible,
body:is(.theme-student, .theme-teacher) .MuiButton-containedPrimary.Mui-focusVisible { outline: 2px solid rgb(var(--g-glow, var(--c-primary))); outline-offset: 2px; }
:is(.dashboard, .admin) :is(a, button):is(.bg-primary, .bg-warning, .bg-danger, .bg-success):disabled,
body:is(.theme-student, .theme-teacher) .MuiButton-containedPrimary.Mui-disabled { box-shadow: none !important; background-image: none; }

/* Quiet buttons and links: pills of glass that appear under the pointer. */
:is(.dashboard, .admin) button.rounded-soft:not(.bg-primary, [class*="hover:bg-"]):hover,
:is(.dashboard, .admin) a.rounded-soft:not(.bg-primary, [class*="hover:bg-"]):hover {
  background-color: rgb(255 255 255 / 0.6) !important;
}
:is(.dashboard, .admin, [role="dialog"]) :is(a, button).bg-primary-soft.hover\:bg-primary:hover { background-color: rgb(var(--c-primary)) !important; }
:is(.dashboard, .admin, [role="dialog"]) :is(a, button).bg-warning-soft.hover\:bg-warning:hover { background-color: rgb(var(--c-warning)) !important; }
:is(.dashboard, .admin, [role="dialog"]) :is(a, button).bg-danger-soft.hover\:bg-danger:hover { background-color: rgb(var(--c-danger)) !important; }

/* Table row actions: tone fill plus a springy pop. */
:is(.dashboard, .admin) td button.rounded-soft {
  transition: transform 460ms var(--g-bounce), background-color 200ms var(--g-out), color 200ms var(--g-out) !important;
}
:is(.dashboard, .admin) td button.rounded-soft:hover {
  transform: scale(1.16);
}
:is(.dashboard, .admin) td button.rounded-soft:active {
  transform: scale(0.9);
}

/* Liquid drop: ripple spawned at the pointer on press (glass.js). */
.g-drop {
  position: absolute;
  width: 1rem;
  height: 1rem;
  margin: -0.5rem 0 0 -0.5rem;
  border-radius: 50%;
  pointer-events: none;
  background: radial-gradient(circle, rgb(255 255 255 / 0.7), rgb(255 255 255 / 0.15) 60%, rgb(255 255 255 / 0) 70%);
  animation: g-drop 620ms var(--g-out) forwards;
  z-index: 4;
}
@keyframes g-drop {
  from { transform: scale(0); opacity: 1; }
  to { transform: scale(var(--g-drop-size, 40)); opacity: 0; }
}
.g-dropping { overflow: hidden; }
.g-dropping:not(.absolute, .fixed, .sticky) { position: relative; }

/* ------------------------------------------------------------- list rows */
:is(.dashboard, .admin) :is(a, button, div).group.rounded-card.bg-surface {
  transition: transform 520ms var(--g-spring), background-color 220ms var(--g-out), box-shadow 320ms var(--g-out) !important;
}
:is(.dashboard, .admin) :is(a, button).group.rounded-card.bg-surface:hover {
  transform: translateX(-4px);
  background-color: rgb(255 255 255 / 0.75) !important;
}
:is(.dashboard, .admin) .group.rounded-card > span.rounded-pill:first-child {
  transition: height 420ms var(--g-spring), box-shadow 320ms var(--g-out);
}
:is(.dashboard, .admin) .group.rounded-card:hover > span.rounded-pill:first-child {
  height: 4rem;
  box-shadow: 0 0 12px 1px currentColor;
}

/* ------------------------------------------------------------------ tables */
:is(.dashboard, .admin) .overflow-x-auto.rounded-panel thead tr {
  background-color: rgb(255 255 255 / 0.35) !important;
}
:is(.dashboard, .admin) .overflow-x-auto.rounded-panel tbody tr {
  transition: background-color 200ms var(--g-out);
}
:is(.dashboard, .admin) .overflow-x-auto.rounded-panel tbody tr:hover {
  background-color: rgb(255 255 255 / 0.55);
}

/* --------------------------------------------------------------- MUI parts */
body:is(.theme-student, .theme-teacher) .MuiOutlinedInput-root {
  border-radius: 1.1rem;
  background-color: rgb(255 255 255 / 0.55);
  transition: background-color 200ms var(--g-out), box-shadow 260ms var(--g-out);
}
body:is(.theme-student, .theme-teacher) .MuiOutlinedInput-root .MuiOutlinedInput-notchedOutline {
  border-color: rgb(var(--g-tint) / 0.16);
}
body:is(.theme-student, .theme-teacher) .MuiOutlinedInput-root:hover {
  background-color: rgb(255 255 255 / 0.75);
}
body:is(.theme-student, .theme-teacher) .MuiOutlinedInput-root.Mui-focused {
  background-color: rgb(255 255 255 / 0.85);
  box-shadow: 0 0 0 4px rgb(var(--c-primary) / 0.16);
}
/* The search field in the admin pages is a hand-built input + button pair. */
:is(.dashboard, .admin) input:not([type="checkbox"], [type="radio"], [type="file"]) {
  background-color: rgb(255 255 255 / 0.55);
}

body:is(.theme-student, .theme-teacher) :is(.MuiMenu-paper, .MuiPopover-paper, .MuiAutocomplete-paper, .MuiPickersPopper-paper, .MuiDialog-paper) {
  background-color: rgb(255 255 255 / 0.72) !important;
  -webkit-backdrop-filter: var(--g-blur);
  backdrop-filter: var(--g-blur);
  border: 1px solid var(--g-edge);
  border-radius: 1.4rem !important;
  box-shadow: var(--g-shadow-lift) !important;
}
body:is(.theme-student, .theme-teacher) .MuiMenuItem-root {
  margin: 0.2rem 0.6rem;
  border-radius: 1rem;
}
body:is(.theme-student, .theme-teacher) .MuiTooltip-tooltip {
  background-color: rgb(30 30 46 / 0.72);
  -webkit-backdrop-filter: blur(12px);
  backdrop-filter: blur(12px);
  border-radius: 1rem;
  font-size: 1.2rem;
}

/* Modals: the page behind dims and blurs; the sheet springs in. */
body:is(.theme-student, .theme-teacher) .MuiModal-root > .MuiBackdrop-root {
  background-color: rgb(var(--g-tint) / 0.16);
  -webkit-backdrop-filter: blur(10px) saturate(120%);
  backdrop-filter: blur(10px) saturate(120%);
}
body:is(.theme-student, .theme-teacher) [role="dialog"].bg-surface {
  background-color: var(--g-fill-strong) !important;
  animation: g-sheet-in 620ms var(--g-spring) both;
}
@keyframes g-sheet-in {
  from { opacity: 0; transform: translateY(2.4rem) scale(0.94); }
  40% { opacity: 1; }
  to { opacity: 1; transform: none; }
}

/* Toasts */
body:is(.theme-student, .theme-teacher) .Toastify__toast {
  background-color: rgb(255 255 255 / 0.7);
  -webkit-backdrop-filter: var(--g-blur);
  backdrop-filter: var(--g-blur);
  border: 1px solid var(--g-edge);
  border-radius: 1.4rem;
  box-shadow: var(--g-shadow-lift);
}

/* ----------------------------------------------------------- entrances */
/* Cards keep their staggered order but arrive on a spring and slightly scaled. */
@keyframes dash-rise {
  from { opacity: 0; transform: translateY(2rem) scale(0.97); }
  to { opacity: 1; transform: none; }
}
:is(.dashboard, .admin) .dash-rise {
  animation-duration: 760ms;
  animation-timing-function: var(--g-spring);
  animation-delay: calc(min(var(--i, 0), 12) * 70ms);
}

/* Page change, run by glass.js: the page rises from the direction you travelled
   in the menu while a frost sheet over it clears and a beam of light crosses. */
main.main-wrapper.g-enter {
  position: relative;
  animation: g-page-in 760ms var(--g-spring) both;
}
main.main-wrapper.g-enter[data-g-dir="up"] { --g-from: -2.8rem; }
@keyframes g-page-in {
  from { transform: translateY(var(--g-from, 2.8rem)); }
  to { transform: none; }
}
main.main-wrapper.g-enter::before,
main.main-wrapper.g-enter::after {
  content: "";
  position: absolute;
  pointer-events: none;
  z-index: 30;
}
main.main-wrapper.g-enter::before {
  inset: 0;
  background-color: rgb(var(--g-ground) / 0.55);
  -webkit-backdrop-filter: blur(22px) saturate(160%);
  backdrop-filter: blur(22px) saturate(160%);
  animation: g-frost-clear 620ms var(--g-out) forwards;
}
@keyframes g-frost-clear {
  from { opacity: 1; }
  to { opacity: 0; }
}
/* The beam is one screen tall, unskewed and horizontal-only, so its sides fade
   to nothing and it never spills past main into the sidebar. */
main.main-wrapper.g-enter::after {
  top: 0;
  height: 100vh;
  width: 40%;
  right: 0;
  opacity: 0;
  background: linear-gradient(90deg, rgb(255 255 255 / 0) 10%, rgb(255 255 255 / 0.45) 42%,
      rgb(255 255 255 / 0.8) 50%, rgb(255 255 255 / 0.45) 58%, rgb(255 255 255 / 0) 90%);
  animation: g-beam 860ms var(--g-out) 40ms forwards;
}
@keyframes g-beam {
  0% { transform: translateX(0); opacity: 0; }
  15% { opacity: 1; }
  100% { transform: translateX(-150%); opacity: 0; }
}

/* Native selects (students page file-type picker) join the glass. */
:is(.dashboard, .admin) select {
  background-color: rgb(255 255 255 / 0.55);
  border: 1px solid var(--g-edge);
  border-radius: 1rem;
  box-shadow: var(--g-shadow);
}

/* ------------------------------------------------------------- a11y, perf */
@media (prefers-reduced-motion: reduce) {
  main.main-wrapper.g-enter::after { display: none; }
  body.theme-student::before,
  body.theme-teacher::before,
  body.theme-student::after,
  body.theme-teacher::after,
  main.main-wrapper.g-enter,
  main.main-wrapper.g-enter::after,
  .g-lens.is-moving {
    animation: none !important;
  }
  main.main-wrapper.g-enter::before {
    animation-duration: 240ms;
  }
  :is(.dashboard, .admin) .dash-rise {
    animation-name: g-fade;
    animation-duration: 240ms;
  }
  @keyframes g-fade { from { opacity: 0; } to { opacity: 1; } }
  .g-lens,
  :is(.dashboard, .admin) *,
  body:is(.theme-student, .theme-teacher) [role="dialog"] {
    transition-duration: 1ms !important;
    animation-duration: 1ms;
  }
  :is(.dashboard, .admin) .group.rounded-panel.bg-surface { --rx: 0deg !important; --ry: 0deg !important; }
}
/* Browsers without backdrop-filter get an opaque pane so text stays readable. */
@supports not ((backdrop-filter: blur(1px)) or (-webkit-backdrop-filter: blur(1px))) {
  :is(.dashboard, .admin) :is(.rounded-panel, .rounded-card).bg-surface,
  :is(.dashboard, .admin) > aside,
  body:is(.theme-student, .theme-teacher) [role="dialog"].bg-surface {
    background-color: rgb(255 255 255 / 0.92) !important;
  }
}

/* Coloured buttons glow in their own tone. */
:is(.dashboard, .admin, [role="dialog"]) .bg-warning { --g-glow: var(--c-warning); }
:is(.dashboard, .admin, [role="dialog"]) .bg-danger { --g-glow: var(--c-danger); }
:is(.dashboard, .admin, [role="dialog"]) .bg-success { --g-glow: var(--c-success); }

/* Legibility on glass: nothing under 12px, and the logout red darkened enough
   to hold 4.5:1 on the translucent sidebar. */
:is(.dashboard, .admin) .text-xs,
body:is(.theme-student, .theme-teacher) :is([role="dialog"], .MuiDrawer-paper) .text-xs {
  font-size: 1.2rem;
}
:is(.dashboard, .admin) aside .text-danger,
body:is(.theme-student, .theme-teacher) .MuiDrawer-paper .text-danger {
  color: rgb(180 35 24);
}
/* Short cards keep their own height instead of stretching to a tall neighbour. */
.dash-home > .grid.grid-cols-3 { align-items: start; }
/* ...and on wide screens the progress panel rides along beside the longer column. */
@media (min-width: 768px) {
  .dash-home > .grid.grid-cols-3 > section:last-child {
    position: sticky !important;
    top: 7.6rem;
  }
}
/* Figures in tables line up. */
:is(.dashboard, .admin) td { font-variant-numeric: tabular-nums; }

/* =====================================================================
 * V2 - TIGHTER, ALIGNED, CLEARER GLASS, AND THE ISLAND
 * Overrides the scale above: one step smaller everywhere, one content
 * column, and a Dynamic Island that carries the page title and time.
 * ===================================================================== */

/* ---------------------------------------------------------------- density */
:is(.dashboard, .admin) main .p-2xl { padding: 2.4rem; }
:is(.dashboard, .admin) main .p-xl { padding: 2rem; }
:is(.dashboard, .admin) main .p-lg { padding: 1.6rem; }
:is(.dashboard, .admin) main .gap-2xl { gap: 2.4rem; }
:is(.dashboard, .admin) main .gap-xl { gap: 1.6rem; }
:is(.dashboard, .admin) main .gap-lg { gap: 1.4rem; }
:is(.dashboard, .admin) main header.pt-xl { padding-top: 0; }
:is(.dashboard, .admin) main :is(th, td).py-md { padding-top: 1.1rem; padding-bottom: 1.1rem; }
:is(.dashboard, .admin) .group.rounded-panel .mt-md { margin-top: 1rem; }
:is(.dashboard, .admin) .group.rounded-panel span.rounded-soft:first-child,
:is(.dashboard, .admin) section.rounded-panel > div > div > span.rounded-soft:first-child {
  height: 2.8rem;
  width: 2.8rem;
  border-radius: 0.9rem;
}

/* ------------------------------------------------------------- alignment */
/* One centred content column, so wide screens don't stretch cards into
   strips, and every page shares the same left and right edge. */
:is(.dashboard, .admin) > main.main-wrapper > * {
  width: 100%;
  max-width: 128rem;
  margin-inline: auto;
}
/* Page header: icon, title and action share one centre line. */
:is(.dashboard, .admin) main header > div:first-child { align-items: center; }

/* -------------------------------------------------------------- sidebar */
:is(.dashboard, .admin) > aside.w-\[26rem\] { width: 23.6rem !important; }
:is(.dashboard, .admin) > aside > div { padding-top: 1.1rem; padding-bottom: 1.6rem; gap: 1.6rem; }
:is(.dashboard, .admin) aside nav > ul { gap: 0.4rem; }
:is(.dashboard, .admin) aside li :is(a, button).h-\[4rem\],
body:is(.theme-student, .theme-teacher) .MuiDrawer-paper li :is(a, button).h-\[4rem\] {
  height: 3.8rem;
}
:is(.dashboard, .admin) aside img.h-\[4rem\] { height: 3.4rem; }

/* ---------------------------------------------------------------- island */
/* Room above the content for the island; it shares a centre line with the
   sidebar's logo row (aside top 1.2rem + row padding 1.1rem + half a row). */
:is(.dashboard, .admin) > main.main-wrapper { padding-top: 7.2rem; }
@media (min-width: 768px) {
  :is(.dashboard, .admin) > main.main-wrapper { padding-top: 8rem; }
}

/* The island: Live Activities. A black pill that shows the one thing that
   matters most right now, with a ring that fills as it nears. */
body.theme-student { --g-live-primary: 196 181 253; }
body.theme-teacher { --g-live-primary: 94 234 212; }

.g-island {
  position: fixed;
  top: 2.4rem;
  left: 50%;
  z-index: 35;
  height: 3.8rem;
  min-width: 12rem;
  border-radius: 999px;
  color: #fff;
  font-family: "Sans-Mobile", sans-serif;
  overflow: hidden;
  transform: translateX(-50%);
  background-color: rgb(8 8 14 / 0.88);
  -webkit-backdrop-filter: blur(14px) saturate(180%);
  backdrop-filter: blur(14px) saturate(180%);
  box-shadow: inset 0 1px 0 rgb(255 255 255 / 0.2),
    inset 0 0 0 1px rgb(255 255 255 / 0.07),
    0 12px 32px -12px rgb(var(--g-tint) / 0.6);
  transition: width 620ms var(--g-spring), height 620ms var(--g-spring),
    border-radius 620ms var(--g-spring), opacity 300ms var(--g-out), translate 600ms var(--g-spring);
}
.g-refract .g-island {
  -webkit-backdrop-filter: url(#g-lensing) blur(10px) saturate(190%);
  backdrop-filter: url(#g-lensing) blur(10px) saturate(190%);
}
.g-island:not(.is-ready) { opacity: 0.9; }
.g-island.is-open { border-radius: 2.6rem; }
.g-island.is-hidden { opacity: 0; translate: 0 -1.2rem; pointer-events: none; }
.g-island.is-bump { animation: g-bump 560ms var(--g-bounce); }
@keyframes g-bump { 0%, 100% { scale: 1; } 35% { scale: 1.07 1.1; } }

/* Views stack absolutely so the pill takes the size of the one showing. */
.g-island__view {
  position: absolute;
  top: 0;
  left: 50%;
  translate: -50% 0;
  width: max-content;
  transition: opacity 260ms var(--g-out), filter 360ms var(--g-out), transform 520ms var(--g-spring);
}
.g-island__view[aria-hidden="true"] { opacity: 0; filter: blur(6px); transform: scale(0.92); pointer-events: none; }

.g-island__compact {
  display: flex;
  align-items: center;
  gap: 0.8rem;
  height: 3.8rem;
  max-width: min(46rem, calc(100vw - 3.2rem));
  padding: 0 0.7rem 0 1.6rem;
  border: 0;
  border-radius: 999px;
  background: none;
  color: inherit;
  font: inherit;
  cursor: pointer;
}
.g-island__compact:focus-visible,
.g-island__row:focus-visible { outline: 2px solid rgb(var(--g-live-primary)); outline-offset: -2px; }
.g-island__compact > * { transition: transform 280ms var(--g-out), opacity 200ms var(--g-out), filter 280ms var(--g-out); }
.g-island__compact.is-out > * { transform: translateY(-1.4rem); opacity: 0; filter: blur(4px); }
.g-island__compact.is-in > * { transform: translateY(1.4rem); opacity: 0; transition: none; }

.g-island__title {
  min-width: 0;
  overflow: hidden;
  font-size: 1.3rem;
  font-weight: 600;
  white-space: nowrap;
  text-overflow: ellipsis;
}
.g-island__meta {
  flex: none;
  padding: 0.3rem 0.9rem;
  border-radius: 999px;
  font-size: 1.2rem;
  font-weight: 600;
  white-space: nowrap;
  font-variant-numeric: tabular-nums;
  background: rgb(255 255 255 / 0.08);
}

/* Glyph with a progress ring; colours are tuned for the dark pill. */
.g-glyph {
  --tone: 255 255 255;
  position: relative;
  display: grid;
  place-items: center;
  flex: none;
  width: 2.6rem;
  height: 2.6rem;
  border-radius: 50%;
  color: rgb(var(--tone));
  background: rgb(var(--tone) / 0.16);
}
.g-glyph > svg:not(.g-ring) { width: 1.5rem; height: 1.5rem; fill: currentColor; }
.g-ring {
  position: absolute;
  inset: -0.3rem;
  width: calc(100% + 0.6rem);
  height: calc(100% + 0.6rem);
  rotate: -90deg;
  fill: none;
  stroke: rgb(var(--tone));
  stroke-width: 2.2;
  stroke-linecap: round;
  filter: drop-shadow(0 0 3px rgb(var(--tone) / 0.7));
}
[data-tone="primary"] { --tone: var(--g-live-primary); }
[data-tone="success"] { --tone: 110 231 183; }
[data-tone="warning"] { --tone: 252 211 77; }
[data-tone="danger"] { --tone: 252 165 165; }
[data-tone="neutral"] { --tone: 226 232 240; }
.g-island__meta[data-tone], .g-island__sub[data-tone] { color: rgb(var(--tone)); }

.g-island__full {
  display: flex;
  flex-direction: column;
  gap: 0.2rem;
  width: min(38rem, calc(100vw - 3.2rem));
  padding: 1.2rem;
}
.g-island__head { padding: 0.2rem 1rem 0.6rem; font-size: 1.2rem; color: rgb(255 255 255 / 0.55); }
.g-island__row {
  display: flex;
  align-items: center;
  gap: 1.2rem;
  width: 100%;
  padding: 0.9rem 1rem;
  border: 0;
  border-radius: 1.6rem;
  background: none;
  color: inherit;
  font: inherit;
  text-align: start;
  cursor: pointer;
  transition: background-color 180ms var(--g-out), transform 400ms var(--g-bounce);
}
.g-island__row:hover { background: rgb(255 255 255 / 0.08); }
.g-island__row:active { transform: scale(0.97); }
.g-island__row .g-glyph { width: 4rem; height: 4rem; border-radius: 1.3rem; }
.g-island__row .g-glyph > svg:not(.g-ring) { width: 2rem; height: 2rem; }
.g-island__row .g-ring { inset: -0.4rem; width: calc(100% + 0.8rem); height: calc(100% + 0.8rem); }
.g-island__row .g-glyph:has(.g-ring) { border-radius: 50%; }
.g-island__text { display: flex; flex-direction: column; gap: 0.2rem; flex: 1; min-width: 0; }
.g-island__big { font-size: 1.4rem; font-weight: 600; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.g-island__sub { font-size: 1.2rem; white-space: nowrap; }
.g-island__go { font-size: 2rem; line-height: 1; color: rgb(255 255 255 / 0.35); }

@media (max-width: 767px) {
  .g-island { top: 1.2rem; }
}

/* ------------------------------------------------------- phone tab bar */
.g-tabbar { display: none; }
/* Phones AND tablets (touch tablets in landscape too): floating glass tab bar + sheet instead of the
   side drawer / sidebar. Keep in sync with COMPACT_QUERY in admin/homework/modals/AddAnswer.js. */
@media (max-width: 1079px), (pointer: coarse) and (max-width: 1400px) {
  html.g-tabbar-on :is(.dashboard, .admin) > div.fixed.z-40 { display: none; }
  html.g-tabbar-on :is(.dashboard, .admin) > aside { display: none !important; }
  html.g-tabbar-on :is(.dashboard, .admin) > main.main-wrapper { padding-bottom: calc(11rem + env(safe-area-inset-bottom)); }

  .g-tabbar {
    position: fixed;
    bottom: calc(1.2rem + env(safe-area-inset-bottom));
    left: 50%;
    z-index: 40;
    display: flex;
    align-items: stretch;
    width: min(calc(100vw - 2.4rem), 44rem);
    height: 6.4rem;
    padding: 0.6rem;
    border: 1px solid rgb(255 255 255 / 0.65);
    border-radius: 999px;
    transform: translateX(-50%);
    background: rgb(255 255 255 / 0.5);
    -webkit-backdrop-filter: blur(24px) saturate(210%) brightness(1.05);
    backdrop-filter: blur(24px) saturate(210%) brightness(1.05);
    box-shadow: var(--g-shadow-lift);
    transition: width 560ms var(--g-spring), height 560ms var(--g-spring), opacity 260ms var(--g-out);
  }
  .g-tabbar[hidden] { display: none; }
  .g-tabbar.is-min { width: min(calc(100vw - 9rem), 30rem); height: 5.2rem; }
  html.g-sheet-open .g-tabbar { opacity: 0; pointer-events: none; }
}

.g-tab {
  position: relative;
  z-index: 1;
  display: flex;
  flex: 1 1 0;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 0.2rem;
  min-width: 0;
  padding: 0;
  border: 0;
  border-radius: 999px;
  background: none;
  color: rgb(var(--c-ink-muted));
  font: inherit;
  font-family: "Sans-Mobile", sans-serif;
  text-decoration: none;
  cursor: pointer;
  -webkit-tap-highlight-color: transparent;
  transition: color 200ms var(--g-out), transform 420ms var(--g-bounce);
}
.g-tab:active { transform: scale(0.88); }
.g-tab:focus-visible { outline: 2px solid rgb(var(--c-primary)); outline-offset: -3px; }
.g-tab[aria-current="page"] { color: rgb(var(--c-primary-active)); }
.g-tab__icon { position: relative; display: grid; place-items: center; }
.g-tab__icon > svg { width: 2.3rem; height: 2.3rem; fill: currentColor; transition: transform 420ms var(--g-bounce); }
.g-tab[aria-current="page"] .g-tab__icon > svg { transform: translateY(-1px) scale(1.06); }
.g-tab__label {
  max-height: 1.8rem;
  font-size: 1.2rem;
  font-weight: 600;
  line-height: 1.5;
  white-space: nowrap;
  transition: opacity 220ms var(--g-out), max-height 420ms var(--g-spring);
}
.g-tabbar.is-min .g-tab__label { opacity: 0; max-height: 0; }

.g-badge {
  position: absolute;
  top: -0.5rem;
  left: -1rem;
  display: grid;
  place-items: center;
  min-width: 1.8rem;
  height: 1.8rem;
  padding: 0 0.45rem;
  border: 2px solid rgb(255 255 255 / 0.95);
  border-radius: 999px;
  background: rgb(var(--c-danger));
  color: #fff;
  font-size: 1rem;
  font-weight: 700;
  line-height: 1;
  font-variant-numeric: tabular-nums;
  animation: g-badge-in 520ms var(--g-bounce);
}
.g-badge[hidden] { display: none; }
@keyframes g-badge-in { from { scale: 0; } to { scale: 1; } }

.g-tablens {
  position: absolute;
  top: 0.6rem;
  bottom: 0.6rem;
  left: 0;
  border-radius: 999px;
  pointer-events: none;
  background: linear-gradient(180deg, rgb(255 255 255 / 0.7), rgb(255 255 255 / 0.15)), rgb(var(--c-primary) / 0.14);
  box-shadow: inset 0 1px 0 rgb(255 255 255 / 0.95), inset 0 0 0 1px rgb(255 255 255 / 0.5),
    0 6px 16px -8px rgb(var(--c-primary) / 0.6);
  opacity: 0;
  transition: transform 600ms var(--g-spring), width 600ms var(--g-spring), opacity 200ms var(--g-out);
}
.g-tablens.is-on { opacity: 1; }
.g-tablens.is-moving { animation: g-tab-stretch 600ms var(--g-out); }
@keyframes g-tab-stretch { 35% { scale: 1.22 0.9; } 100% { scale: 1 1; } }

/* ---------------------------------------------------------- More sheet */
.g-scrim {
  position: fixed;
  inset: 0;
  z-index: 55;
  background: rgb(var(--g-tint, 40 40 60) / 0.18);
  -webkit-backdrop-filter: blur(6px);
  backdrop-filter: blur(6px);
  opacity: 0;
  pointer-events: none;
  transition: opacity 300ms var(--g-out);
}
html.g-sheet-open .g-scrim { opacity: 1; pointer-events: auto; }
.g-sheet {
  position: fixed;
  right: 0.8rem;
  bottom: calc(0.8rem + env(safe-area-inset-bottom));
  left: 0.8rem;
  z-index: 60;
  max-width: 52rem;
  max-height: 82vh;
  margin: 0 auto;
  padding: 0.8rem 1.6rem 1.8rem;
  overflow-y: auto;
  overscroll-behavior: contain;
  border: 1px solid rgb(255 255 255 / 0.7);
  border-radius: 3.2rem;
  font-family: "Sans-Mobile", sans-serif;
  background: rgb(255 255 255 / 0.68);
  -webkit-backdrop-filter: blur(30px) saturate(200%);
  backdrop-filter: blur(30px) saturate(200%);
  box-shadow: var(--g-shadow-lift), 0 -10px 60px -20px rgb(var(--g-tint, 40 40 60) / 0.4);
  transform: translateY(calc(100% + 3rem));
  visibility: hidden;
  transition: transform 560ms var(--g-spring), visibility 0s linear 560ms;
}
html.g-sheet-open .g-sheet { transform: none; visibility: visible; transition: transform 560ms var(--g-spring), visibility 0s; }
.g-sheet.is-dragging { transition: none; }
.g-sheet__grab { width: 3.8rem; height: 0.5rem; margin: 0.4rem auto 1.2rem; border-radius: 999px; background: rgb(var(--g-tint, 40 40 60) / 0.22); touch-action: none; }
.g-sheet__head { display: flex; align-items: center; justify-content: space-between; gap: 1rem; margin-bottom: 1.6rem; touch-action: none; }
.g-sheet__who { display: flex; align-items: center; gap: 1rem; min-width: 0; }
.g-sheet__who b { display: block; font-size: 1.5rem; color: rgb(var(--c-ink-strong)); }
.g-sheet__who small { display: block; font-size: 1.2rem; color: rgb(var(--c-ink-muted)); }
.g-sheet__avatar {
  display: grid; place-items: center; flex: none;
  width: 4.2rem; height: 4.2rem; border-radius: 50%;
  background: rgb(var(--c-primary)); color: #fff; font-size: 1.7rem; font-weight: 700;
  box-shadow: inset 0 1px 0 rgb(255 255 255 / 0.4), 0 8px 18px -8px rgb(var(--c-primary) / 0.8);
}
.g-sheet__close {
  display: grid; place-items: center; flex: none;
  width: 4.4rem; height: 4.4rem; border: 0; border-radius: 50%;
  background: rgb(118 118 140 / 0.14); color: rgb(var(--c-ink-muted)); cursor: pointer;
  transition: background-color 200ms var(--g-out);
}
.g-sheet__close:hover { background: rgb(118 118 140 / 0.24); }
.g-sheet__close svg { width: 2rem; height: 2rem; fill: currentColor; }
.g-sheet__grid { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: 0.8rem; }
@media (min-width: 420px) { .g-sheet__grid { grid-template-columns: repeat(4, minmax(0, 1fr)); } }
.g-tile {
  display: flex; flex-direction: column; align-items: center; gap: 0.7rem;
  min-height: 9.6rem; padding: 1.3rem 0.4rem 1.1rem;
  border-radius: 2rem;
  background: rgb(255 255 255 / 0.5);
  box-shadow: inset 0 1px 0 rgb(255 255 255 / 0.9), 0 4px 14px -8px rgb(var(--g-tint, 40 40 60) / 0.25);
  color: rgb(var(--c-ink));
  text-align: center;
  text-decoration: none;
  -webkit-tap-highlight-color: transparent;
  transition: transform 420ms var(--g-bounce), background-color 200ms var(--g-out);
}
html.g-sheet-open .g-tile { animation: g-tile-in 520ms var(--g-spring) both; animation-delay: calc(var(--i, 0) * 28ms + 80ms); }
@keyframes g-tile-in { from { opacity: 0; transform: translateY(1.4rem) scale(0.9); } }
.g-tile:active { transform: scale(0.93); }
.g-tile:hover { background: rgb(255 255 255 / 0.75); }
.g-tile:focus-visible { outline: 2px solid rgb(var(--c-primary)); outline-offset: 2px; }
.g-tile__icon {
  position: relative; display: grid; place-items: center;
  width: 4.4rem; height: 4.4rem; border-radius: 1.4rem;
  background: rgb(var(--c-primary) / 0.12); color: rgb(var(--c-primary-active));
}
.g-tile__icon svg { width: 2.2rem; height: 2.2rem; fill: currentColor; }
.g-tile.is-active .g-tile__icon {
  background: rgb(var(--c-primary)); color: #fff;
  box-shadow: inset 0 1px 0 rgb(255 255 255 / 0.4), 0 8px 18px -8px rgb(var(--c-primary) / 0.8);
}
.g-tile__label { font-size: 1.2rem; font-weight: 600; line-height: 1.4; }
.g-sheet__logout {
  display: block; width: 100%; height: 4.8rem; margin-top: 1.4rem;
  border: 0; border-radius: 1.8rem;
  background: rgb(var(--c-danger) / 0.1); color: rgb(180 35 24);
  font: inherit; font-family: "Sans-Mobile", sans-serif; font-size: 1.4rem; font-weight: 700; cursor: pointer;
  transition: transform 420ms var(--g-bounce), background-color 200ms var(--g-out);
}
.g-sheet__logout:hover { background: rgb(var(--c-danger) / 0.16); }
.g-sheet__logout:active { transform: scale(0.97); }
.g-sheet__logout:focus-visible, .g-sheet__close:focus-visible { outline: 2px solid rgb(var(--c-primary)); outline-offset: 2px; }

@media (prefers-reduced-motion: reduce) {
  .g-island, .g-island__view, .g-island__compact > *, .g-tabbar, .g-tablens, .g-sheet, .g-tile { transition-duration: 1ms !important; }
  .g-island.is-bump, .g-tablens.is-moving, .g-badge, html.g-sheet-open .g-tile { animation: none !important; }
}

/* Phones: stat tiles two to a row, like an iOS widget grid, instead of four
   full-width slabs. */
@media (max-width: 639px) {
  .dash-home > .grid.grid-cols-4 { grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 1rem; }
  .dash-home > .grid.grid-cols-4 .group.rounded-panel { padding: 1.4rem; }
  .dash-home > .grid.grid-cols-4 .group.rounded-panel .text-3xl { font-size: 2.4rem; }
}

/* =========================================================================
 * LAPIS & ILLUMINATION - teacher (/admin) panel.
 * The staff desk as the illuminated page of a Persian mathematical treatise:
 * a deep lapis field, bright frosted glass for the work, ink for text, and gold
 * leaf only where it says "you are here" (the active nav row, the current tab,
 * the page title). State colours stay inside their badges. Everything below is
 * scoped to body.theme-teacher / .admin; the student panel is untouched.
 * ========================================================================= */
body.theme-teacher {
  --g-tint: 8 12 42;
  --g-ground: 22 32 79;            /* #16204F */
  --g-b1: 51 70 181;               /* lapis */
  --g-b2: 30 28 112;               /* indigo */
  --g-b3: 24 52 136;               /* ultramarine */
  --g-b4: 38 46 128;
  --g-gold: 201 162 74;            /* #C9A24A gold leaf */
  --g-live-primary: 176 190 255;   /* island ring in light lapis; gold stays for "you are here" */
  /* Bright glass: the lapis shows at the edges, the work sits on near-white. */
  --g-fill: rgb(244 246 252 / 0.9);
  --g-fill-strong: rgb(248 249 253 / 0.95);
  --g-fill-nested: rgb(255 255 255 / 0.6);
  --g-well: rgb(236 239 249 / 0.82);
  --g-edge: rgb(255 255 255 / 0.55);
  --g-hair: rgb(255 255 255 / 0.4);
  --g-shadow: inset 0 1px 0 rgb(255 255 255 / 0.95),
    inset 0 0 0 1px rgb(255 255 255 / 0.25),
    0 1px 2px rgb(var(--g-tint) / 0.3),
    0 18px 40px -18px rgb(var(--g-tint) / 0.7);
  --g-shadow-lift: inset 0 1px 0 rgb(255 255 255 / 1),
    inset 0 0 0 1px rgb(255 255 255 / 0.3),
    0 2px 4px rgb(var(--g-tint) / 0.35),
    0 26px 52px -22px rgb(var(--g-tint) / 0.8);
  scrollbar-color: rgb(255 255 255 / 0.28) transparent;
}
body.theme-teacher::after {
  /* Slow light in lighter lapis; no white haze, and no gold (gold only marks "you are here"). */
  background:
    radial-gradient(46vmax 38vmax at 40% 55%, rgb(44 62 158 / 0.5), transparent 70%),
    radial-gradient(36vmax 30vmax at 80% 12%, rgb(30 28 112 / 0.45), transparent 70%);
}
body.theme-teacher ::selection { background: rgb(200 208 250 / 0.6); color: rgb(20 22 40); }

/* Ink on the lapis ground is light; every surface puts dark ink back. :where()
   keeps these at zero specificity so a component's own text utility still wins. */
:where(body.theme-teacher) :where(.admin) {
  --c-ink-strong: 248 246 238;
  --c-ink: 230 233 246;
  --c-ink-muted: 198 205 234;
  --c-ink-subtle: 172 182 222;
  --c-rule: 255 255 255;
  --c-rule-subtle: 255 255 255;
  color: rgb(var(--c-ink));
}
:where(body.theme-teacher) :where(.admin) :where(.bg-surface, .bg-surface-sunken, .bg-canvas, .bg-white,
  .dash-hero, .MuiPaper-root, .MuiOutlinedInput-root, table, input, select, textarea,
  .bg-primary-soft, .bg-success-soft, .bg-warning-soft, .bg-danger-soft) {
  --c-ink-strong: 23 27 40;
  --c-ink: 26 29 46;               /* #1A1D2E ink */
  --c-ink-muted: 70 78 104;
  --c-ink-subtle: 78 86 112;       /* 5.5:1 on the glass */
  --c-rule: 215 220 233;
  --c-rule-subtle: 232 235 242;
  color: rgb(var(--c-ink));
}
.admin .border-dashed { border-color: rgb(255 255 255 / 0.22) !important; }
.admin :is(.bg-surface, .MuiPaper-root) .border-dashed { border-color: rgb(var(--g-tint) / 0.18) !important; }
.admin hr { border-color: rgb(255 255 255 / 0.12); }
.admin :is(.bg-surface, .MuiPaper-root) hr { border-color: rgb(var(--g-tint) / 0.1); }

/* Buttons that sit straight on the lapis: outlined and text buttons turn to light ink. */
.admin :is(.MuiButton-outlined, .MuiButton-text):not(:is(.bg-surface, .MuiPaper-root, table, [role="dialog"]) *) {
  color: rgb(236 239 250);
  border-color: rgb(255 255 255 / 0.32);
}
.admin :is(.MuiButton-outlined, .MuiButton-text):not(:is(.bg-surface, .MuiPaper-root, table, [role="dialog"]) *):hover {
  background-color: rgb(255 255 255 / 0.1);
  border-color: rgb(255 255 255 / 0.55);
}
.admin :is(.MuiButton-outlined, .MuiButton-text).Mui-disabled:not(:is(.bg-surface, .MuiPaper-root, table, [role="dialog"]) *) {
  color: rgb(255 255 255 / 0.38);
  border-color: rgb(255 255 255 / 0.14);
}
/* Primary buttons keep a light rim so they read on lapis as well as on glass. */
.admin .MuiButton-containedPrimary:not(.Mui-disabled),
.admin :is(a, button).bg-primary {
  box-shadow: inset 0 1px 0 rgb(255 255 255 / 0.32), inset 0 0 0 1px rgb(255 255 255 / 0.18),
    0 10px 22px -12px rgb(var(--g-tint) / 0.9) !important;
}

/* Floating labels of fields that stand on the lapis get a small glass tab. */
.admin .MuiInputLabel-shrink:not(:is(.bg-surface, .MuiPaper-root) *) {
  padding: 0 0.6rem;
  border-radius: 0.6rem;
  background: rgb(248 249 253);
}
.admin .MuiFormHelperText-root:not(:is(.bg-surface, .MuiPaper-root) *):not(.Mui-error) { color: rgb(198 205 234); }
.admin .MuiFormHelperText-root.Mui-error:not(:is(.bg-surface, .MuiPaper-root) *) { color: rgb(255 168 158); }
.admin .MuiOutlinedInput-root .MuiOutlinedInput-notchedOutline { border-color: rgb(var(--g-tint) / 0.18); }

/* Page title: the illuminated heading, with a double rule of gold leaf under it. */
.admin header h1.text-2xl {
  width: fit-content;
  padding-bottom: 0.5rem;
  border-bottom: 3px double rgb(var(--g-gold) / 0.85);
  color: rgb(var(--c-ink-strong));
}

/* Sidebar: one pane of dark lapis glass with light ink. */
.admin > aside {
  border-color: rgb(255 255 255 / 0.12) !important;
  background-color: rgb(10 15 44 / 0.52) !important;
  -webkit-backdrop-filter: blur(22px) saturate(160%);
  backdrop-filter: blur(22px) saturate(160%);
  box-shadow: inset 0 1px 0 rgb(255 255 255 / 0.12), 0 24px 48px -24px rgb(0 0 0 / 0.6) !important;
}
:where(body.theme-teacher) :where(.admin > aside),
:where(body.theme-teacher) :where(.admin > aside) :where(.bg-surface, .bg-surface-sunken, .bg-primary-soft) {
  --c-ink-strong: 250 248 240;
  --c-ink: 232 235 247;
  --c-ink-muted: 196 203 232;
  --c-ink-subtle: 160 170 212;
  --c-primary: 226 196 122;        /* the active row reads in pale gold */
  --c-primary-active: 236 210 140;
  color: rgb(var(--c-ink));
}
.admin > aside .bg-surface,
.admin > aside .bg-surface-sunken { background-color: rgb(255 255 255 / 0.06) !important; box-shadow: none !important; }
.admin > aside :is(.border-rule, .border-rule-subtle) { border-color: rgb(255 255 255 / 0.1) !important; }
.admin > aside li :is(a, button):hover { background-color: rgb(255 255 255 / 0.08) !important; }
.admin > aside :is(a, button):focus-visible { outline: 2px solid rgb(var(--g-gold)); outline-offset: -2px; }
/* Signature: the lens on the active row is a double rule of gold leaf. */
.admin > aside .g-lens {
  background: linear-gradient(180deg, rgb(255 255 255 / 0.12), rgb(255 255 255 / 0.03));
  box-shadow: inset 0 0 0 1px rgb(var(--g-gold) / 0.9),
    inset 0 0 0 4px rgb(var(--g-gold) / 0.3),
    0 10px 24px -14px rgb(0 0 0 / 0.7);
}

/* Mobile menu button (above the tab-bar breakpoint) joins the rail. */
.admin .fixed button.bg-surface {
  background-color: rgb(10 15 44 / 0.55) !important;
  border-color: rgb(255 255 255 / 0.14) !important;
  color: rgb(236 239 250);
}

/* Tablet and phone tab bar: the same dark lapis glass, the current tab in gold. */
body.theme-teacher .g-tabbar {
  border-color: rgb(255 255 255 / 0.14);
  background: rgb(10 15 44 / 0.6);
  -webkit-backdrop-filter: blur(24px) saturate(160%);
  backdrop-filter: blur(24px) saturate(160%);
  box-shadow: inset 0 1px 0 rgb(255 255 255 / 0.14), 0 22px 44px -18px rgb(0 0 0 / 0.65);
}
body.theme-teacher .g-tab { color: rgb(196 203 232); }
body.theme-teacher .g-tab[aria-current="page"] { color: rgb(236 210 140); }
body.theme-teacher .g-tab:focus-visible { outline-color: rgb(var(--g-gold)); }
body.theme-teacher .g-tablens {
  background: linear-gradient(180deg, rgb(255 255 255 / 0.14), rgb(255 255 255 / 0.03));
  box-shadow: inset 0 0 0 1px rgb(var(--g-gold) / 0.85), inset 0 0 0 4px rgb(var(--g-gold) / 0.25),
    0 6px 16px -10px rgb(0 0 0 / 0.6);
}
body.theme-teacher .g-badge { border-color: rgb(16 23 60); }

/* The More sheet stays bright glass; its scrim takes the lapis. */
body.theme-teacher .g-scrim { background: rgb(8 12 42 / 0.45); }
body.theme-teacher .g-tile.is-active .g-tile__icon {
  background: rgb(var(--g-gold));
  color: rgb(26 29 46);
  box-shadow: inset 0 1px 0 rgb(255 255 255 / 0.5), 0 8px 18px -8px rgb(var(--g-gold) / 0.7);
}

/* Modal backdrops carry the lapis instead of a neutral grey. */
body.theme-teacher .MuiModal-root > .MuiBackdrop-root:not(.MuiBackdrop-invisible) { background-color: rgb(8 12 42 / 0.5); }

/* Fields read as bright glass wherever they stand, on the lapis or on a card. */
body.theme-teacher .admin .MuiOutlinedInput-root,
body.theme-teacher .admin input:not([type="checkbox"], [type="radio"], [type="file"]):not(.MuiInputBase-input) {
  background-color: rgb(248 249 253 / 0.92);
}
body.theme-teacher .admin .MuiOutlinedInput-root:hover { background-color: rgb(250 251 254 / 0.97); }
body.theme-teacher .admin .MuiOutlinedInput-root.Mui-focused { background-color: rgb(255 255 255); }
/* Disabled filled buttons and soft lapis chips that stand straight on the lapis. */
.admin .MuiButton-contained.Mui-disabled:not(:is(.bg-surface, .MuiPaper-root, table, [role="dialog"]) *) {
  background-color: rgb(255 255 255 / 0.08) !important;
  color: rgb(255 255 255 / 0.42) !important;
  box-shadow: inset 0 0 0 1px rgb(255 255 255 / 0.12) !important;
}
.admin .bg-primary-soft:not(:is(.bg-surface, .MuiPaper-root, table, header, aside) *):not(header .rounded-card) {
  --c-primary: 200 208 250;
  background-color: rgb(255 255 255 / 0.1) !important;
}
/* MUI fields own their fill; the inner <input> stays clear so no square corners show on the lapis. */
body.theme-teacher .admin .MuiInputBase-input { background-color: transparent !important; }

/* Finish review fixes. */
/* The top island joins the rail as dark lapis glass. */
body.theme-teacher .g-island {
  background-color: rgb(10 15 44 / 0.62);
  -webkit-backdrop-filter: blur(22px) saturate(160%);
  backdrop-filter: blur(22px) saturate(160%);
  box-shadow: inset 0 1px 0 rgb(255 255 255 / 0.14), inset 0 0 0 1px rgb(255 255 255 / 0.08),
    0 12px 32px -12px rgb(0 0 0 / 0.6);
}
/* The logo is drawn for a light ground: it gets a small glass plate on the dark rail. */
.admin > aside img[alt="عطار"] { background: rgb(248 249 253 / 0.92); border-radius: 0.8rem; padding: 0.25rem 0.5rem; }
.admin > aside .text-danger { color: rgb(255 160 150); }
/* Native selects read as bright glass with ink. */
body.theme-teacher .admin select {
  background-color: rgb(248 249 253 / 0.92);
  color: rgb(26 29 46);
  border: 1px solid rgb(var(--g-tint) / 0.18);
  border-radius: 0.6rem;
}
/* State colours live in bordered badges, at AA. */
.admin .bg-warning-soft { color: rgb(140 56 4); }
.admin .bg-success-soft { color: rgb(14 92 36); }
.admin :is(.bg-success-soft, .bg-warning-soft, .bg-danger-soft):not(.rounded-panel) {
  box-shadow: inset 0 0 0 1px color-mix(in srgb, currentColor 35%, transparent);
}
/* Numbers stay ink; their state shows in the icon tile and badges, not the figure. */
.admin td .text-danger { color: rgb(var(--c-ink-strong)); font-weight: 700; }
.admin .group.rounded-panel .text-3xl { color: rgb(var(--c-ink-strong)) !important; }
