/* ═══════════════════════════════════════════════════════════════════════════
   LIQUID.CSS - the polish layer, shared by all three apps.

   The same Aluma, finished: what floats is frosted glass with a fine rim of
   light, menus and sheets and toasts arrive softly, and (with liquid.js)
   buttons, chips and tabs give under a finger and spring back. That is the
   whole layer. It does NOT restyle cards, panels, rows, tiles or the page
   ground, and it changes no colour family, no type, no spacing, no size, no
   position and no corner: every element's box measures what it measured
   before (checked by diffing every rendered element's rect, before and
   after, across the three apps; see tests/test_liquid_layer.py for what is
   pinned in source).

   House rules (all but the screenshot note are pinned by a test):
     - Spends --lq-* tokens from liquid-tokens.css and mints NONE. This file
       never opens a block on the document root and never names the page
       ground token, because in index.html it loads after redesign.css
       (tests/test_beta_redesign.py).
     - Never sets a corner. In index.html it sits between phone.css and
       rounded.css, and tests/test_phone_shell.py reads this file's RAW text
       for the shape property's name, comments included. The one element this
       layer mounts (the done ring) gets its corner inline from the button it
       spreads from, so shape stays rounded.css's decision.
     - Animates transform-family properties (scale, translate) and opacity
       only, and every entrance fills BACKWARDS only: a `both` fill would hold
       the end state and silently cancel the exit transitions parents.js and
       educators.js run on the same elements (.toast.is-leaving).
     - Glass never lowers contrast. The fill keeps each surface's own colour;
       the backdrop is lifted toward the fill (brightness above 1 on light,
       below 1 on dark) before the fill goes on, so over the light page and
       white cards a frosted menu composites to the same white it was.
       Dialogs stay opaque (glass over a scrim is grey glass), and so does
       the student's notification panel (clean.css: a list of sentences
       behind glass read as text printed over text).
     - A note for whoever screenshots this: Chromium's default headless
       compositor draws a backdrop-filter as a blurred copy with the
       UNBLURRED content faded through the fill, i.e. ghost text, which no
       GPU-composited browser does. Launch it with --use-gl=angle
       --use-angle=swiftshader to see the frost as a student will.
     - Reduced motion: every entrance here is cancelled by rule (section 6)
       as well as by token. Reduced transparency, or no backdrop-filter at
       all: every surface this file frosts goes solid (section 7, LAST, so
       nothing above can out-rank the opt-out).

   Scopes. The three apps share class names (.topbar, .toast, .modal), so
   every rule is scoped:
     student    body[data-theme]              (index.html stamps it at boot)
     family     html[data-app="family"]       (parents.html)
     educators  html[data-app="educators"]    (educators.html)
   ═══════════════════════════════════════════════════════════════════════════ */


/* ── 1. FAMILY: THE BAR, WHILE THE PAGE SCROLLS UNDER IT ─────────────────── */
/* parents.js adds .is-scrolled once the page has moved, and parents.css
   answers with an 86% bar, saturate(1.4) blur(16px). It keeps all three
   numbers it had (the alpha exactly; the saturation exactly; the blur a
   little wider, so what passes under is a smear rather than a ghost) and
   gains the brightness lift, so the ink on the bar - the brand, the family's
   name - sits on a ground at least as light as before, never darker. The
   hairline under the bar is parents.css's own and stays: it is the rim.

   The educator bar is left solid. educators.css made it so on purpose ("a
   see-through bar ghosted the status and the tiles scrolling under it"),
   and its words sit right on it: glass there could only take contrast away.

   The family glass is painted on ::before, NOT on the bar itself. An element
   with a backdrop-filter becomes the backdrop root of everything inside it,
   and the account menu lives inside this bar: frosted from the bar itself,
   the menu's own frost could only see the bar's strip, and the page under
   the menu would show through it unblurred. On ::before the bar is no root,
   and the menu frosts the real page. */
html[data-app="family"] .topbar.is-scrolled {
  background: transparent;
  -webkit-backdrop-filter: none;
  backdrop-filter: none;
}
html[data-app="family"] .topbar::before {
  content: "";
  position: absolute;
  inset: 0;
  z-index: -1;
  pointer-events: none;
  opacity: 0;
  background: var(--lq-fill-bar-fam);
  -webkit-backdrop-filter: blur(var(--lq-blur)) saturate(var(--lq-sat-bar)) brightness(var(--lq-bright));
  backdrop-filter: blur(var(--lq-blur)) saturate(var(--lq-sat-bar)) brightness(var(--lq-bright));
  transition: opacity 0.2s var(--lq-ease-out);
}
html[data-app="family"] .topbar.is-scrolled::before { opacity: 1; }


/* ── 2. FAMILY + EDUCATORS: MENUS, TOASTS AND THE SHEET BEHIND A DIALOG ── */
/* Menus and toasts were opaque white on a drop shadow, with words on them.
   Their face stays exactly that: solid --*-surface across the content box,
   under every word. Only the padding around it - the 8 to 16px bezel - is
   glass, 86% of the same white over a lifted, blurred page, so colour that
   passes under a menu's edge (a filled button, the invite card's blue)
   glows faintly through its rim while nothing a reader looks at changes.
   The shadow is each app's own --*-shadow-pop, restated after the rim
   because box-shadow is one list. The face's corner follows the menu's own
   (the browser insets it by the padding), so no corner is set here. */
html[data-app] :is(.acct-menu, .sp-menu, .toast) {
  -webkit-backdrop-filter: blur(var(--lq-blur)) saturate(var(--lq-sat)) brightness(var(--lq-bright));
  backdrop-filter: blur(var(--lq-blur)) saturate(var(--lq-sat)) brightness(var(--lq-bright));
}
html[data-app="family"] :is(.acct-menu, .sp-menu, .toast) {
  background: linear-gradient(var(--f-surface), var(--f-surface)) content-box,
              linear-gradient(var(--lq-fill-pop), var(--lq-fill-pop)) padding-box;
  box-shadow: var(--lq-rim), var(--f-shadow-pop);
}
html[data-app="educators"] :is(.acct-menu, .sp-menu, .toast) {
  background: linear-gradient(var(--e-surface), var(--e-surface)) content-box,
              linear-gradient(var(--lq-fill-pop), var(--lq-fill-pop)) padding-box;
  box-shadow: var(--lq-rim), var(--e-shadow-pop);
}

/* A dialog stays opaque: it sits on a dimmed page, and glass over a scrim is
   grey glass, which would lower the contrast of every word in it. What
   frosts instead is the scrim itself - the page behind a dialog goes soft as
   well as dim, the way the student app's dialogs already do (12px there). */
html[data-app] .modal-backdrop {
  -webkit-backdrop-filter: blur(var(--lq-blur-scrim));
  backdrop-filter: blur(var(--lq-blur-scrim));
}


/* ── 3. STUDENT: THE FLOATING LAYERS IT ALREADY HAS ──────────────────────── */
/* The student app's palette and toasts are already glass (--glass-bg, 76%,
   blurred). They keep their fill. They gain the lift on the frost (and, on
   the light theme only, the palette's own 1.2 saturation), and the palette
   a rim in front of the shadow it already had
   (--shadow-xl, measured in both themes). The selectors carry the same id
   as the legacy rules they answer (styles.css: `#palette-backdrop .palette`
   and `#toast-wrap .toast`, whose shadow is also !important), so this wins
   on specificity rather than by escalating. The model menus are left as
   they are: four sheets already fight over them. */
body[data-theme] #palette-backdrop .palette {
  -webkit-backdrop-filter: blur(var(--lq-blur-palette)) saturate(var(--lq-sat)) brightness(var(--lq-bright));
  backdrop-filter: blur(var(--lq-blur-palette)) saturate(var(--lq-sat)) brightness(var(--lq-bright));
  box-shadow: var(--lq-rim), var(--shadow-xl) !important;
}
/* The toast already draws its own top highlight, so only the frost moves. */
body[data-theme] #toast-wrap .toast {
  -webkit-backdrop-filter: blur(var(--lq-blur-toast)) saturate(var(--lq-sat)) brightness(var(--lq-bright));
  backdrop-filter: blur(var(--lq-blur-toast)) saturate(var(--lq-sat)) brightness(var(--lq-bright));
}
/* The account menu: the same 76% glass, the same lift, and the rim. */
body[data-theme] .account-popover {
  -webkit-backdrop-filter: blur(var(--lq-blur)) saturate(var(--lq-sat)) brightness(var(--lq-bright));
  backdrop-filter: blur(var(--lq-blur)) saturate(var(--lq-sat)) brightness(var(--lq-bright));
  box-shadow: var(--lq-rim), var(--shadow-xl);
}
/* Already solid (clean.css); the rim reads on the dark theme. */
body[data-theme] .notif-panel { box-shadow: var(--lq-rim), var(--shadow-xl); }
/* The phone's More sheet: an opaque panel (it stays one) on a dimmed page,
   which now also goes soft behind it, as the dialogs' scrim already does. */
body[data-theme] .mms-panel { box-shadow: var(--lq-rim), var(--shadow-xl); }
body[data-theme] .mobile-more-sheet:not(.hidden) {
  -webkit-backdrop-filter: blur(var(--lq-blur-scrim));
  backdrop-filter: blur(var(--lq-blur-scrim));
}
/* The dialog is opaque white on the light theme (nothing to add) and 76%
   glass on the dark one, where it gains the frost and the rim. styles.css
   sets every .modal's shadow with !important, so the rim has to say it as
   firmly; the shadow after it is the same token. */
body[data-theme="dark"] .modal-backdrop:not(.hidden) > .modal {
  -webkit-backdrop-filter: blur(var(--lq-blur)) saturate(var(--lq-sat)) brightness(var(--lq-bright));
  backdrop-filter: blur(var(--lq-blur)) saturate(var(--lq-sat)) brightness(var(--lq-bright));
  box-shadow: var(--lq-rim), var(--shadow-xl) !important;
}


/* ── 4. ARRIVALS ─────────────────────────────────────────────────────────── */
/* Standalone `scale` / `translate`, never transform, so a surface that is
   positioned with a transform keeps it; and fill BACKWARDS only (see the
   header). Each replaces an arrival the app already had (e-pop / f-pop,
   e-rise / f-rise, e-toast / f-toast), a little softer and a little longer:
   a menu drops out of its button, a dialog settles, a toast rises on a
   short spring. */
html[data-app] :is(.acct-menu, .sp-menu) {
  transform-origin: top right;
  animation: lq-pop var(--lq-d-pop) var(--lq-ease-out) backwards;
}
html[data-app] .modal {
  animation: lq-sheet var(--lq-d-sheet) var(--lq-ease-out) backwards;
}
html[data-app] .toast {
  animation: lq-toast var(--lq-d-toast) var(--lq-ease-spring) backwards;
}
/* The student's account menu had no arrival at all: it opens up from the
   account button at the foot of the sidebar. Its dialogs, palette, menus
   and toasts keep their own (alumaPanelIn, notif-drop, modalIn,
   toast-stagger-enter). */
body[data-theme] .account-popover:not(.hidden) {
  transform-origin: bottom left;
  animation: lq-rise var(--lq-d-pop) var(--lq-ease-out) backwards;
}
@keyframes lq-pop {
  from { opacity: 0; scale: var(--lq-pop-scale); translate: 0 var(--lq-drop); }
  to   { opacity: 1; scale: 1; translate: 0 0; }
}
@keyframes lq-rise {
  from { opacity: 0; scale: var(--lq-pop-scale); translate: 0 calc(var(--lq-drop) * -1); }
  to   { opacity: 1; scale: 1; translate: 0 0; }
}
@keyframes lq-sheet {
  from { opacity: 0; scale: var(--lq-sheet-scale); translate: 0 var(--lq-rise); }
  to   { opacity: 1; scale: 1; translate: 0 0; }
}
@keyframes lq-toast {
  from { opacity: 0; scale: var(--lq-pop-scale); translate: 0 var(--lq-rise); }
  to   { opacity: 1; scale: 1; translate: 0 0; }
}


/* ── 5. THE DONE RING (student; liquid.js mounts and plays it) ──────────── */
/* One quiet ring in the accent, spreading from the check that was just
   ticked and fading. Fixed to the viewport so the To-do row can re-render
   under it; the corner is copied inline from the button (see the header). */
.lq-done-ring {
  position: fixed;
  z-index: 2147483000;
  pointer-events: none;
  box-shadow: 0 0 0 2px var(--lq-done-ink);
  opacity: 0;
}


/* ── 6. REDUCED MOTION (by rule, as well as by token) ────────────────────── */
/* The tokens already zero every duration and amplitude, and both adult apps
   and the student app run a reduced-motion catch-all. These rules stop the
   arrivals outright, so a later selector war cannot leave one running. */
@media (prefers-reduced-motion: reduce) {
  html[data-app] :is(.acct-menu, .sp-menu, .modal, .toast),
  body[data-theme] .account-popover:not(.hidden) {
    animation: none !important;
  }
  html[data-app="family"] .topbar::before { transition: none; }
  .lq-done-ring { display: none; }
}


/* ── 7. LESS TRANSPARENCY, OR NO BLUR AT ALL: SOLID (LAST) ───────────────── */
/* `prefers-reduced-transparency` is an opt-out, and an opt-out a cosmetic
   rule can beat is not one (tests/test_beta_redesign.py says so of the
   bottom bar). These come last in the file and match every selector above
   at equal or higher strength. Each surface goes to the solid colour it is
   a glass of. The student toast and palette are included: redesign.css's
   own fallback never listed the toast, and this layer would otherwise
   out-rank its palette rule. */
@supports not ((backdrop-filter: blur(1px)) or (-webkit-backdrop-filter: blur(1px))) {
  html[data-app="family"] .topbar::before { background: var(--f-bg); }
  html[data-app="family"] :is(.acct-menu, .sp-menu, .toast) { background: var(--f-surface); }
  html[data-app="educators"] :is(.acct-menu, .sp-menu, .toast) { background: var(--e-surface); }
}
@media (prefers-reduced-transparency: reduce) {
  html[data-app="family"] .topbar::before {
    background: var(--f-bg);
    -webkit-backdrop-filter: none;
    backdrop-filter: none;
  }
  html[data-app] :is(.acct-menu, .sp-menu, .toast) {
    -webkit-backdrop-filter: none;
    backdrop-filter: none;
  }
  html[data-app="family"] :is(.acct-menu, .sp-menu, .toast) { background: var(--f-surface); }
  html[data-app="educators"] :is(.acct-menu, .sp-menu, .toast) { background: var(--e-surface); }
  html[data-app] .modal-backdrop,
  body[data-theme] .mobile-more-sheet:not(.hidden) {
    -webkit-backdrop-filter: none;
    backdrop-filter: none;
  }
  body[data-theme] #palette-backdrop .palette,
  body[data-theme] #toast-wrap .toast,
  body[data-theme] .account-popover,
  body[data-theme="dark"] .modal-backdrop:not(.hidden) > .modal {
    background: var(--surface-strong) !important;
    -webkit-backdrop-filter: none !important;
    backdrop-filter: none !important;
  }
}
