/* lv-auth.css — THE ACCOUNT DIALOG, one design for every website surface (v7 lane B, AUTH_SPEC.md).
   Served from FastAPI's own /static mount (generate/api/static/lv-auth.css) beside lv-capsule.css, and
   linked by the seven static_v2 pages and the Studio. The flagship draws the same dialog from
   web/components/auth-dialog.tsx with these numbers copied into globals.css (a parity test reads both).

   ONE <dialog class="lva">, FOUR KINDS: the sign-in / create-account form, the "save it to your account"
   prompt a guest meets at a 401 account_required, the plan prompt at a 402 plus_required, and the
   confirm that replaces window.confirm() (delete, sign out). One material, one motion, one close.

   THE MATERIAL IS CRYSTAL — the house anatomy (HOME_GLASS_SPEC §2.3: translucent fill, 1px hairline on
   what is behind, lit top edge, faint inner rim, top-lit sheen, neutral shadow cast down) with three
   things more, because the owner asked for MORE glass in the pop-ups:
     · a stronger blur (36px fine / 24px coarse, the sheet's 28 + 8) over a page that is itself dimmed
       and blurred by ::backdrop, so the card reads as a thick pane rather than a white slab;
     · a SPECULAR RIM — a 1px gradient edge, bright at the lit top-left and bottom-right corners and
       nearly clear between, drawn by ::before with a mask so it sits exactly on the border;
     · a LIGHT POOL — a soft white radial in the top-left of the sheen, the reflection of the room.
   Every tint is black or white alpha (palette MAX_SPREAD 3); every saturate() is <= 1, so the glass can
   only MUTE what it covers.

   AA IS ARITHMETIC, MEASURED OVER BLACK (test_glass_system's photo-independent bound). The fill is
   .72 white: over a black backdrop the pane composites to 184, where --lva-ink is 9.48:1 and --lva-ink-2
   4.89:1 — the quiet line is the binding one, so the fill is not lower. --lva-danger on bare .72 glass
   would be 3.96:1, so the error line never sits on bare glass: it has its own well (.62 white → 228
   over black, danger 6.21:1). A first cut at .78 with a bare error line measured 4.72:1 (pixels,
   run.mjs) and read as a white slab; this is the most glass AA allows. The sheen only ever adds white,
   so it can only raise those numbers. The phone sheet is .80 (204 over black: ink-2 6.06). Re-run
   proto_auth/run.mjs `contrast` before changing either.

   THE BLUR WAITS FOR THE CARD TO STOP. While the card travels (entrance, exit, a drag) --g-fx is none
   and a FROST VEIL (::after, .86 near-white, under the content) covers the fill, so the pane is
   effectively 97% opaque and nothing sharp shows through. At rest (.is-settled) the blur switches on
   and the veil fades out over 280ms: the frost "clears" instead of popping. Never animated: the blur
   radius, backdrop-filter itself, the pane's size. */

/* Same family and source as leorve.css / lv-capsule.css; a browser loads it once. */
@font-face {
  font-family: "Leorve Wordmark";
  src: url("/app/brand/julius-leorve.woff2") format("woff2");
  font-weight: 400; font-style: normal; font-display: swap;
  unicode-range: U+45, U+4C, U+4F, U+52, U+56;
}

/* ── TOKENS, NAMESPACED ON THE DIALOG ───────────────────────────────────────────────────────────
   The Studio, the static pages and the legal shell each have their own :root, and not all of them
   define --ink-2 or --danger (the Studio's names differ). The dialog carries its own copies of the
   house values so it renders the same on all of them; the page's fonts win when it has them.
   .lv-crystal carries them too: it is the same material for any other modal card on these surfaces
   (critic C16 — the plan sheet, the occasion planner, a page's own confirm), see the end of the
   material block below. */
.lva, .lv-crystal {
  --lva-ink: #111111;
  --lva-ink-2: #444444;
  --lva-danger: #9E1B33;          /* the documented exception to the neutral palette */
  --lva-on-ink: #FFFFFF;
  --lva-display: var(--font-display, "Bricolage Grotesque", system-ui, -apple-system, "Segoe UI", sans-serif);
  --lva-body: var(--font-body, "Instrument Sans", system-ui, -apple-system, "Segoe UI", sans-serif);
  --lva-ease: cubic-bezier(.16, 1, .3, 1);          /* state */
  --lva-glide: cubic-bezier(.32, .72, 0, 1);        /* travel */
  --lva-exit: cubic-bezier(.4, 0, 1, 1);
  /* CRYSTAL */
  --crystal-fill: rgba(255, 255, 255, .72);
  --crystal-fx: blur(36px) saturate(.7);
  --crystal-line: rgba(255, 255, 255, .58);
  --crystal-sheen: radial-gradient(120% 70% at 0% 0%, rgba(255, 255, 255, .42) 0%, rgba(255, 255, 255, 0) 58%),
                   linear-gradient(180deg, rgba(255, 255, 255, .55) 0%, rgba(255, 255, 255, .08) 38%, rgba(255, 255, 255, 0) 62%);
  --crystal-hi: inset 0 1px 0 rgba(255, 255, 255, .98), inset 0 0 0 1px rgba(255, 255, 255, .34),
                inset 0 -1px 0 rgba(17, 17, 17, .06);
  --crystal-shadow: 0 1px 2px rgba(0, 0, 0, .10), 0 24px 48px -24px rgba(0, 0, 0, .42),
                    0 60px 120px -48px rgba(0, 0, 0, .40);
  --crystal-rim: linear-gradient(150deg, rgba(255, 255, 255, .95) 0%, rgba(255, 255, 255, .20) 28%,
                 rgba(255, 255, 255, .04) 55%, rgba(255, 255, 255, .24) 82%, rgba(255, 255, 255, .80) 100%);
  --crystal-veil: rgba(250, 250, 250, .86);
  --crystal-solid: #FAFAFA;
  /* the wells (inputs) are frosted look, NO blur: a field is never a live pane.
     NON-TEXT CONTRAST, MEASURED AND RECORDED (critic C20, WCAG 1.4.11): a field's boundary is this
     well (.66 white → 231 over the black bound) plus a .14 ink line, against the card at 184 — about
     1.3–1.6:1, under 3:1. The label above every field and the 2px ink focus ring carry the field's
     identity, and every house input today draws the same line. An AA-strict edge would be
     --well-line: rgba(17,17,17,.42); it reads heavier, so it is the owner's call (AUTH_SPEC §9). */
  --well-fill: rgba(255, 255, 255, .66);
  --well-line: rgba(17, 17, 17, .14);
  --well-hi: inset 0 1px 2px rgba(17, 17, 17, .06), 0 1px 0 rgba(255, 255, 255, .75);
}
@media (pointer: coarse) { .lva, .lv-crystal { --crystal-fx: blur(24px) saturate(.7); } }

/* ── THE DIALOG BOX: a transparent full-viewport layer in the top layer ──────────────────────────
   The card is centred inside it, so a press on the dialog itself (never on the card) is a press on
   the dimmed page — that is how "click the backdrop to close" is detected without a second element. */
.lva {
  position: fixed; inset: 0; width: 100%; height: 100%; max-width: none; max-height: none;
  margin: 0; padding: 24px; border: 0; background: transparent; overflow: hidden;
  color: var(--lva-ink); font: 400 15px/1.5 var(--lva-body); letter-spacing: 0; text-align: left;
  overscroll-behavior: contain;
}
.lva[open] { display: flex; align-items: center; justify-content: center; }
/* THE HOST PAGE'S OWN ELEMENT RULES MUST NOT REACH IN. The Studio styles every input:focus and button,
   static_v2 every h2 and p; each property the dialog depends on is set on its own classes, and the box
   model is fixed here (the Studio's inked focus border leaked into the first 390 shot). */
.lva, .lva *, .lva *::before, .lva *::after { box-sizing: border-box; }
/* LITERALS, NOT var() (HOME_GLASS_SPEC [R8]): ::backdrop inherits custom properties only from
   Chromium 122 / Safari 17.4 / Firefox 120, and on an older engine the page would not even dim. */
.lva::backdrop {
  background: rgba(8, 8, 8, .32);
  -webkit-backdrop-filter: blur(10px) saturate(.9); backdrop-filter: blur(10px) saturate(.9);
  opacity: 0; transition: opacity 240ms linear;
}
.lva.is-in::backdrop { opacity: 1; }

/* ── THE CARD — the one live pane ────────────────────────────────────────────────────────────── */
.lva-card {
  --g-fill: var(--crystal-fill); --g-fx: var(--crystal-fx); --g-line: var(--crystal-line);
  --g-sheen: var(--crystal-sheen); --g-hi: var(--crystal-hi); --g-shadow: var(--crystal-shadow);
  position: relative; isolation: isolate;
  display: flex; flex-direction: column;
  width: min(440px, 100%); max-height: 100%;
  border-radius: 28px;
  border: 1px solid var(--g-line);
  background: var(--g-sheen), var(--g-fill);
  background-clip: padding-box;
  -webkit-backdrop-filter: var(--g-fx); backdrop-filter: var(--g-fx);
  box-shadow: var(--g-hi), var(--g-shadow);
  opacity: 0; transform: translateY(14px) scale(.97);
  transition: opacity 260ms var(--lva-ease), transform 420ms var(--lva-glide);
}
.lva.is-in .lva-card { opacity: 1; transform: none; }
.lva.is-out .lva-card { opacity: 0; transform: translateY(8px) scale(.985);
  transition: opacity 180ms var(--lva-exit), transform 200ms var(--lva-exit); }
/* The blur is ON only at rest. */
.lva:not(.is-settled) .lva-card, .lva.is-drag .lva-card, .lva.is-out .lva-card { --g-fx: none; }
/* THE SPECULAR RIM, exactly on the 1px border (inset -1px), masked to the ring. Above the content
   only in paint order; it never takes a pointer. */
.lva-card::before {
  content: ""; position: absolute; inset: -1px; border-radius: inherit; padding: 1px;
  background: var(--crystal-rim); pointer-events: none; z-index: 2;
  -webkit-mask: linear-gradient(#000 0 0) content-box, linear-gradient(#000 0 0);
  -webkit-mask-composite: xor;
  mask: linear-gradient(#000 0 0) content-box, linear-gradient(#000 0 0);
  mask-composite: exclude;
}
/* THE FROST VEIL: under the content (z -1 inside the card's own isolated stacking context), over the
   fill. Opaque enough while travelling that the un-blurred page never shows as ghost text. */
.lva-card::after {
  content: ""; position: absolute; inset: 0; border-radius: inherit; z-index: -1; pointer-events: none;
  background: var(--crystal-veil); opacity: 1; transition: opacity 280ms var(--lva-ease);
}
.lva.is-settled:not(.is-drag):not(.is-out) .lva-card::after { opacity: 0; }

/* ── CRYSTAL FOR ANY OTHER MODAL CARD (critic C16: "more glass in pop-ups" is plural) ────────────
   The same anatomy, rim and veil as .lva-card, for a vanilla card that is not this dialog: lv-plus.js's
   plan sheet, occasion.js's planner, a page's own <dialog> (membership's pay confirm, shop's size
   guide). The card wears class="lv-crystal"; its script calls lvAuth.crystal(card) as it opens, which
   adds .is-settled once the card has stopped moving — the blur is ON only at rest, and the veil hides
   the switch, exactly as above. A card nobody settles stays a 97%-opaque frosted plate: legible, just
   not see-through. Only #111 and #444 ink belong on it (9.48 and 4.89:1 over black at .72); anything
   red stands on a well, as .lva-err does. */
.lv-crystal {
  --g-fill: var(--crystal-fill); --g-fx: var(--crystal-fx); --g-line: var(--crystal-line);
  --g-sheen: var(--crystal-sheen); --g-hi: var(--crystal-hi); --g-shadow: var(--crystal-shadow);
  isolation: isolate;
  border: 1px solid var(--g-line);
  background: var(--g-sheen), var(--g-fill);
  background-clip: padding-box;
  -webkit-backdrop-filter: var(--g-fx); backdrop-filter: var(--g-fx);
  box-shadow: var(--g-hi), var(--g-shadow);
  color: var(--lva-ink);
}
/* The rim and the veil are absolutely placed, so a card needs a containing block — but NOT a
   <dialog> that wears the class itself (shop's size guide): a modal dialog is position:fixed by the
   UA sheet, and an author `relative` here would have beaten it and un-pinned the sheet. */
.lv-crystal:not(dialog) { position: relative; }
.lv-crystal:not(.is-settled) { --g-fx: none; }
.lv-crystal::before {
  content: ""; position: absolute; inset: -1px; border-radius: inherit; padding: 1px;
  background: var(--crystal-rim); pointer-events: none; z-index: 2;
  -webkit-mask: linear-gradient(#000 0 0) content-box, linear-gradient(#000 0 0);
  -webkit-mask-composite: xor;
  mask: linear-gradient(#000 0 0) content-box, linear-gradient(#000 0 0);
  mask-composite: exclude;
}
.lv-crystal::after {
  content: ""; position: absolute; inset: 0; border-radius: inherit; z-index: -1; pointer-events: none;
  background: var(--crystal-veil); opacity: 1; transition: opacity 280ms var(--lva-ease);
}
.lv-crystal.is-settled::after { opacity: 0; }
/* NO MASKS, NO RIM (critic C19). Without mask-composite the z-index:2 rim is not a ring but a full
   gradient plate, painted over the whole card and the words on it. */
@supports not ((mask-composite: exclude) or (-webkit-mask-composite: xor)) {
  .lva-card::before, .lv-crystal::before { display: none; }
}

/* The scroller is INSIDE the card, so the rim (::before, inset -1px) is never clipped by overflow. */
.lva-scroll { position: relative; overflow-y: auto; overscroll-behavior: contain; padding: 22px 32px 26px;
  border-radius: inherit; scrollbar-width: thin; }

/* ── the busy hairline (transient: only while a request is out) ── */
.lva-busy { position: absolute; left: 28px; right: 28px; top: 0; height: 2px; overflow: hidden; border-radius: 2px;
  opacity: 0; transition: opacity 160ms linear; pointer-events: none; z-index: 3; }
.lva.is-busy .lva-busy { opacity: 1; }
.lva-busy::before { content: ""; position: absolute; top: 0; bottom: 0; left: 0; width: 38%;
  background: linear-gradient(90deg, rgba(17, 17, 17, 0), rgba(17, 17, 17, .55), rgba(17, 17, 17, 0)); }
/* THE SWEEP RUNS ONLY WHILE A REQUEST IS OUT. It ran from the moment the dialog opened, invisible at
   opacity 0 — an infinite animation under a glass pane (measured: document.getAnimations() listed it
   on every open dialog), which the blur budget forbids even when nobody can see it. */
.lva.is-busy .lva-busy::before { animation: lva-sweep 1.1s var(--lva-ease) infinite; }
@keyframes lva-sweep { from { transform: translateX(-100%); } to { transform: translateX(265%); } }

/* ── head: the lockup and the close ── */
.lva-grab { display: none; }
.lva-top { display: flex; align-items: center; justify-content: space-between; min-height: 44px; }
.lva-lock { display: inline-flex; align-items: center; gap: 10px; color: var(--lva-ink); }
.lva-mark { display: block; height: 20px; aspect-ratio: 595 / 308; background: currentColor;
  -webkit-mask: center / contain no-repeat url("/app/brand/leorve-script.svg");
  mask: center / contain no-repeat url("/app/brand/leorve-script.svg"); }
.lva-word { font: 400 10px/1 "Leorve Wordmark", Georgia, serif; letter-spacing: .44em; margin-right: -.44em;
  color: var(--lva-ink); white-space: nowrap; }
.lva-x { width: 44px; height: 44px; margin: 0 -10px 0 0; flex: none; display: grid; place-items: center;
  border-radius: 999px; border: 1px solid rgba(17, 17, 17, .10); background: rgba(255, 255, 255, .55);
  box-shadow: inset 0 1px 0 rgba(255, 255, 255, .9); color: var(--lva-ink); cursor: pointer; padding: 0; }
.lva-x svg { width: 18px; height: 18px; fill: none; stroke: currentColor; stroke-width: 1.7; stroke-linecap: round; }

/* ── words ── */
.lva-eyebrow { margin: 18px 0 8px; font: 700 11px/1.2 var(--lva-body); letter-spacing: .2em; text-transform: uppercase;
  color: var(--lva-ink-2); }
.lva-title { margin: 0; font: 600 30px/1.08 var(--lva-display); letter-spacing: -.02em; color: var(--lva-ink);
  text-wrap: balance; outline: none; }
.lva-lede { margin: 10px 0 0; font-size: 15px; line-height: 1.55; color: var(--lva-ink-2); text-wrap: pretty; }
.lva-lede:empty { display: none; }
.lva-body { margin-top: 22px; }
.lva-body.is-swap { animation: lva-swap 200ms var(--lva-ease) both; }
@keyframes lva-swap { from { opacity: 0; transform: translateY(4px); } to { opacity: 1; transform: none; } }

/* ── the form ── */
.lva-form { display: grid; gap: 14px; }
.lva-field { display: grid; gap: 6px; }
.lva-field[hidden] { display: none; }
.lva-label { font: 600 13px/1.3 var(--lva-body); color: var(--lva-ink); }
.lva-opt { font-weight: 400; color: var(--lva-ink-2); }
.lva-input { appearance: none; -webkit-appearance: none; width: 100%; height: 52px; margin: 0; padding: 0 16px;
  border-radius: 14px; border: 1px solid var(--well-line); background: var(--well-fill); box-shadow: var(--well-hi);
  font: 400 16px/1.2 var(--lva-body); color: var(--lva-ink); /* 16px: iOS zooms the page under anything smaller */
  transition: border-color 160ms var(--lva-ease), background-color 160ms var(--lva-ease); }
.lva-input::placeholder { color: var(--lva-ink-2); opacity: 1; }
/* ONE RING, not a ring around an inked border (the first cut drew both, which read as a double line
   at 390). The well brightens so the field being typed in is the lightest thing on the card. */
.lva-input:focus { outline: 2px solid var(--lva-ink); outline-offset: 2px; border-color: var(--well-line);
  background-color: rgba(255, 255, 255, .92); }
.lva-input[aria-invalid="true"] { border-color: var(--lva-danger); }
.lva-input:-webkit-autofill { -webkit-box-shadow: 0 0 0 40px #FAFAFA inset; -webkit-text-fill-color: var(--lva-ink); }
.lva-pw { position: relative; }
.lva-pw .lva-input { padding-right: 84px; }
.lva-show { position: absolute; top: 4px; right: 4px; height: 44px; min-width: 64px; padding: 0 12px; margin: 0;
  border: 0; border-radius: 10px; background: transparent; cursor: pointer;
  font: 600 13px/1 var(--lva-body); color: var(--lva-ink); text-decoration: underline; text-underline-offset: 3px; }
.lva-hint { margin: 0; font-size: 12.5px; line-height: 1.45; color: var(--lva-ink-2); }
.lva-hint[hidden] { display: none; }
/* THE ERROR HAS ITS OWN WELL: --lva-danger on bare .72 glass over black is 3.96:1, on this 6.21:1. */
.lva-err { display: flex; gap: 8px; align-items: flex-start; margin: 0; padding: 10px 12px; border-radius: 12px;
  border: 1px solid rgba(17, 17, 17, .08); background: rgba(255, 255, 255, .62); background-clip: padding-box;
  font: 500 13.5px/1.45 var(--lva-body); color: var(--lva-danger); animation: lva-swap 200ms var(--lva-ease) both; }
.lva-err[hidden] { display: none; }
.lva-err svg { flex: none; width: 16px; height: 16px; margin-top: 2px; fill: none; stroke: currentColor;
  stroke-width: 1.8; stroke-linecap: round; }

/* ── buttons ── */
.lva-btn { position: relative; display: inline-flex; align-items: center; justify-content: center; gap: 10px;
  width: 100%; min-height: 52px; margin: 0; padding: 0 22px; border-radius: 999px; cursor: pointer;
  border: 1px solid #111111; background: #111111; color: var(--lva-on-ink);
  box-shadow: inset 0 1px 0 rgba(255, 255, 255, .16), inset 0 -1px 0 rgba(0, 0, 0, .40),
              0 1px 2px rgba(17, 17, 17, .22), 0 10px 22px -12px rgba(17, 17, 17, .55);
  font: 600 15px/1 var(--lva-body); letter-spacing: .01em; text-decoration: none;
  transition: transform 160ms var(--lva-ease), background-color 160ms var(--lva-ease); }
.lva-btn:active { transform: scale(.985); }
.lva-btn[aria-disabled="true"] { cursor: progress; }
.lva-btn.is-ghost { background: rgba(255, 255, 255, .55); color: var(--lva-ink); border-color: rgba(17, 17, 17, .16);
  box-shadow: inset 0 1px 0 rgba(255, 255, 255, .9); }
.lva-btn.is-quiet { background: transparent; border-color: transparent; box-shadow: none; color: var(--lva-ink);
  text-decoration: underline; text-underline-offset: 3px; min-height: 44px; }
/* THE DESTRUCTIVE ANSWER IS QUIET, BUT NEVER ON BARE GLASS: danger text on .72 over black is 3.96:1, so
   it stands on the same well as the error line (6.21:1) — an outline pill, not an underlined word. */
.lva-btn.is-quiet.is-danger { color: var(--lva-danger); text-decoration: none; border-color: rgba(17, 17, 17, .10);
  background: rgba(255, 255, 255, .62); box-shadow: inset 0 1px 0 rgba(255, 255, 255, .9); min-height: 52px; }
.lva-btn .lva-ck { width: 18px; height: 18px; fill: none; stroke: currentColor; stroke-width: 2; stroke-linecap: round;
  stroke-linejoin: round; }
.lva-acts { display: grid; gap: 10px; margin-top: 22px; }
.lva-switch { margin: 14px 0 0; text-align: center; font-size: 14px; color: var(--lva-ink-2); }
.lva-link { display: inline-flex; align-items: center; min-height: 44px; margin: 0; padding: 0 4px; border: 0;
  background: none; cursor: pointer; font: 600 14px/1 var(--lva-body); color: var(--lva-ink);
  text-decoration: underline; text-underline-offset: 3px; }

/* ── "everything you made comes with you", the list, the price, the fine print ── */
.lva-keep { display: flex; gap: 10px; align-items: flex-start; margin: 16px 0 0; padding: 12px 14px;
  border-radius: 14px; border: 1px solid rgba(17, 17, 17, .08); background: rgba(255, 255, 255, .50);
  font-size: 13.5px; line-height: 1.45; color: var(--lva-ink); }
.lva-keep[hidden] { display: none; }
.lva-keep svg, .lva-list svg { flex: none; width: 18px; height: 18px; margin-top: 1px; fill: none; stroke: currentColor;
  stroke-width: 1.7; stroke-linecap: round; stroke-linejoin: round; }
.lva-list { list-style: none; margin: 0; padding: 0; display: grid; gap: 10px; }
/* margin 0: the legal shell sets li{margin-bottom:6px} for its clauses, which reached in here. */
.lva-list li { display: flex; gap: 10px; align-items: flex-start; margin: 0; font-size: 14.5px; line-height: 1.45; color: var(--lva-ink); }
.lva-list[hidden] { display: none; }
.lva-price { margin: 16px 0 0; font: 600 15px/1.4 var(--lva-body); color: var(--lva-ink); }
.lva-price[hidden] { display: none; }
.lva-fine { margin: 18px 0 0; font-size: 12.5px; line-height: 1.5; color: var(--lva-ink-2); }
.lva-fine:empty { display: none; }
.lva-fine a { color: var(--lva-ink); text-decoration: underline; text-underline-offset: 2px; }

/* the house focus ring, on everything in the card */
.lva :focus-visible { outline: 2px solid var(--lva-ink); outline-offset: 3px; }
.lva .lva-btn:focus-visible { outline-offset: 3px; }

/* ── the toast (a sentence after the dialog has gone): SMOKE, transient ── */
.lva-toast { position: fixed; left: 50%; bottom: calc(24px + env(safe-area-inset-bottom)); z-index: 2147483000;
  max-width: min(520px, calc(100vw - 32px)); padding: 12px 18px; border-radius: 18px;
  transform: translate(-50%, 12px); opacity: 0; pointer-events: none;
  border: 1px solid rgba(255, 255, 255, .14); color: #FFFFFF;
  background: linear-gradient(180deg, rgba(255, 255, 255, .10), rgba(255, 255, 255, .02) 50%, rgba(255, 255, 255, 0)), rgba(18, 18, 18, .86);
  background-clip: padding-box;
  -webkit-backdrop-filter: blur(16px) saturate(.9); backdrop-filter: blur(16px) saturate(.9);
  box-shadow: inset 0 1px 0 rgba(255, 255, 255, .20), 0 22px 44px -22px rgba(0, 0, 0, .55);
  font: 500 14px/1.45 var(--font-body, "Instrument Sans", system-ui, sans-serif);
  transition: opacity 220ms cubic-bezier(.16, 1, .3, 1), transform 320ms cubic-bezier(.32, .72, 0, 1); }
.lva-toast.is-in { opacity: 1; transform: translate(-50%, 0); }

/* ── hover: fine pointers only (a tap leaves :hover stuck on a phone) ── */
@media (hover: hover) and (pointer: fine) {
  .lva-btn:not(.is-ghost):not(.is-quiet):hover { background: #2B2B2B; border-color: #2B2B2B; }
  .lva-btn.is-ghost:hover { background: rgba(255, 255, 255, .80); }
  .lva-btn.is-quiet:hover, .lva-link:hover, .lva-show:hover { text-decoration-thickness: 2px; }
  .lva-btn.is-quiet.is-danger:hover { background: rgba(255, 255, 255, .86); }
  .lva-x:hover { background: rgba(255, 255, 255, .85); }
  .lva-fine a:hover { text-decoration-thickness: 2px; }
}

/* ── PHONE: a bottom sheet with a drag handle, above the home indicator ─────────────────────────── */
/* THE PHONE FILL IS .80, AND NOT FOR AA (critic C19): .72 already holds --lva-ink-2 at 4.89:1 over
   black on a phone too. It is for the GHOST — the page read through a 20px blur, which smears far
   less than 36px, so at .72 a line of the page's own type showed through as legible words behind the
   form. .80 keeps the pane glass and the words under it a texture. */
@media (max-width: 767.98px) {
  .lv-crystal { --crystal-fill: rgba(255, 255, 255, .80); --crystal-fx: blur(20px) saturate(.7); }
  .lva { padding: 0 0 var(--lva-kb, 0px); }
  .lva[open] { align-items: flex-end; }
  .lva-card { --crystal-fill: rgba(255, 255, 255, .80); --crystal-fx: blur(20px) saturate(.7);
    width: 100%; max-height: calc(100% - 12px); border-radius: 28px 28px 0 0;
    opacity: 1; transform: translateY(100%);
    transition: transform 420ms var(--lva-glide); }
  .lva.is-in .lva-card { transform: translateY(var(--lva-dy, 0px)); }
  .lva.is-out .lva-card { opacity: 1; transform: translateY(100%); transition: transform 220ms var(--lva-exit); }
  .lva.is-drag .lva-card { transition: none; }
  .lva-scroll { padding: 8px 20px calc(18px + env(safe-area-inset-bottom)); }
  .lva-grab { display: block; width: 36px; height: 5px; margin: 0 auto 6px; border-radius: 3px; background: rgba(17, 17, 17, .18); }
  .lva-top { touch-action: none; }
  .lva-title { font-size: 26px; }
  .lva-eyebrow { margin-top: 12px; }
  .lva-body { margin-top: 18px; }
  .lva-busy { left: 20px; right: 20px; }
  .lva::backdrop { -webkit-backdrop-filter: none; backdrop-filter: none; background: rgba(8, 8, 8, .36); }
}
@media (max-width: 767.98px) and (max-height: 560px) {
  .lva-eyebrow, .lva-fine { display: none; }
}

/* ── FALLBACKS ─────────────────────────────────────────────────────────────────────────────────── */
/* No backdrop-filter: never see-through without a blur. Solid, and the hierarchy (hairline, highlight,
   shadow, rim) survives. */
@supports not ((backdrop-filter: blur(1px)) or (-webkit-backdrop-filter: blur(1px))) {
  .lva .lva-card, html .lv-crystal { --g-fill: var(--crystal-solid); }
  .lva .lva-card::after, html .lv-crystal::after { display: none; }
  .lva::backdrop { background: rgba(8, 8, 8, .40); }
}
@media (prefers-reduced-transparency: reduce) {
  .lva .lva-card, html .lv-crystal { --g-fill: var(--crystal-solid); --g-fx: none; }
  .lva .lva-card::after, html .lv-crystal::after { display: none; }
  .lva::backdrop { -webkit-backdrop-filter: none; backdrop-filter: none; background: rgba(8, 8, 8, .40); }
  .lva-toast { -webkit-backdrop-filter: none; backdrop-filter: none; background: rgba(20, 20, 20, .94); }
}
@media (prefers-contrast: more) {
  .lva .lva-card, html .lv-crystal { --g-fill: var(--crystal-solid); --g-line: rgba(17, 17, 17, .45); }
  .lva-input { border-color: rgba(17, 17, 17, .45); }
}
@media (forced-colors: active) {
  .lva .lva-card, html .lv-crystal { background: Canvas; color: CanvasText; border: 1px solid CanvasText; box-shadow: none;
    -webkit-backdrop-filter: none; backdrop-filter: none; }
  .lva .lva-card::before, .lva .lva-card::after, html .lv-crystal::before, html .lv-crystal::after { display: none; }
  .lva-input, .lva-btn, .lva-x { border: 1px solid CanvasText; }
}
@media (prefers-reduced-motion: reduce) {
  .lva-card, .lva.is-out .lva-card, .lva::backdrop, .lva-card::after, .lv-crystal::after, .lva-input, .lva-btn, .lva-toast { transition: none; }
  .lva-card, .lva.is-out .lva-card { transform: none; }
  /* A drag is direct manipulation, not an animation: the phone sheet still follows the finger. */
  .lva.is-in .lva-card { transform: translateY(var(--lva-dy, 0px)); }
  .lva-body.is-swap, .lva-err { animation: none; }
  .lva.is-busy .lva-busy::before { animation: none; width: 100%; opacity: .35; }
  .lva-btn:active { transform: none; }
}
@media print { .lva, .lva-toast { display: none !important; } }
/* UNDER THE DIALOG NOTHING ELSE BLURS (critic C17, the phone budget: the card is the one live layer).
   The capsule stands down in lv-capsule.css; a crystal card under this dialog — a page sheet left
   open beneath it — stands down here. Keyed on the dialog itself, not on html.is-modal, because the
   Studio may set is-modal for its own overlays, whose crystal cards are then the top layer and must
   keep their glass. A browser without :has() drops this rule only (one more blur, never a wrong look). */
html:has(dialog.lva[open]) .lv-crystal { -webkit-backdrop-filter: none; backdrop-filter: none; }
/* AND EVERY OTHER PANE ON THE PAGE, whoever drew it (measured: /wardrobe's own Vellum pane kept a live
   blur at 390 and 360 with this dialog open — two layers where the budget allows one). Under a modal
   the page is inert and still and sits under ::backdrop, so no pane on it can show its glass; standing
   them all down here holds the budget by construction rather than page by page. Zero specificity with
   !important, so it beats a page's own blur without out-ranking anything else; the dialog and its card
   are excluded. A browser without :has() or complex :not() drops this rule only. */
html:has(dialog.lva[open]) body :where(:not(dialog.lva):not(dialog.lva *)) {
  -webkit-backdrop-filter: none !important; backdrop-filter: none !important;
}
