/* ═══════════════════════════════════════════════════════════════════
   glass.css — the real wabi.ai glass button.

   PORTED VERBATIM from `wabi mini apps/era/css/wabi.css`, which itself was
   ported from production (sotabot/website-v1,
   src/components/ui/glassmorphic-button + header.tsx). Everything between
   the rules below is wabi.ai's numbers — the conic edge, the travelling
   shine, the five-layer inset shadow, the 400ms ease.

   ⚠ RE-PORT, NEVER NUDGE. The whole point of this file is that a pill here
   is indistinguishable from a pill on wabi.ai; a value tuned by eye to look
   right on this page is the moment that stops being true. Site-specific
   sizing goes in the joonas.wtf section at the FOOT of this file, which
   only ever sets geometry.
   ═══════════════════════════════════════════════════════════════════ */

/* ⚠ THE TOKENS COME WITH IT. The ported rules say `transition: all 400ms
   var(--ease-wabi)` and `color: var(--grey-500)`, and an undeclared custom
   property makes the WHOLE declaration invalid at computed-value time — so the
   pill silently had no transition at all: no 400ms settle, no shine travel, no
   conic-edge turn on hover. It rendered perfectly and did nothing, and the only
   thing that said so was reading transitionTimingFunction back and finding
   `ease`. These are wabi.ai's values, from the same file as the rules below. */
:root {
  --ease-wabi: cubic-bezier(0.25, 1, 0.5, 1);
  --ease-expo: cubic-bezier(0.22, 1, 0.36, 1);
  --grey-500: #737373;
}

@property --angle-1 { syntax: '<angle>'; inherits: false; initial-value: -75deg; }
@property --angle-2 { syntax: '<angle>'; inherits: false; initial-value: -45deg; }
/* ── the glass button ─────────────────────────────────────────────── */
.glass-btn-wrap{
  position:relative; z-index:2; border-radius:999vw; background:transparent;
  pointer-events:none; transition:all 400ms var(--ease-wabi);
}
.glass-btn-shadow{
  position:absolute;
  width:calc(100% + 0.5em); height:calc(100% + 0.5em);
  top:calc(0% - 0.25em); left:calc(0% - 0.25em);
  filter:blur(clamp(0.5px, 0.0313em, 3px));
  overflow:visible; pointer-events:none; z-index:1;
}
.glass-btn-shadow::after{
  content:''; position:absolute; z-index:0; inset:0; border-radius:999vw;
  background:linear-gradient(180deg, rgba(0,0,0,.015), rgba(0,0,0,.0075));
  width:calc(100% - 0.5em - 0.0625em); height:calc(100% - 0.5em - 0.0625em);
  top:calc(0.5em - 0.125em); left:calc(0.5em - 0.21875em);
  padding:.125em; box-sizing:border-box;
  -webkit-mask:linear-gradient(#000 0 0) content-box, linear-gradient(#000 0 0);
          mask:linear-gradient(#000 0 0) content-box, linear-gradient(#000 0 0);
  -webkit-mask-composite:xor; mask-composite:exclude;
  transition:all 400ms var(--ease-wabi); opacity:1;
}
.glass-btn{
  all:unset; cursor:pointer; position:relative;
  -webkit-tap-highlight-color:rgba(0,0,0,0);
  pointer-events:auto; z-index:3;
  background:linear-gradient(-75deg, rgba(255,255,255,.05), rgba(255,255,255,.2), rgba(255,255,255,.05));
  border-radius:999vw;
  box-shadow:
    inset 0 .125em .125em rgba(0,0,0,.035),
    inset 0 -.125em .125em rgba(255,255,255,.5),
    0 .25em .125em -.125em rgba(0,0,0,.1),
    0 0 .1em .25em inset rgba(255,255,255,.2),
    0 0 0 0 rgba(255,255,255,1);
  backdrop-filter:blur(clamp(1px, .125em, 4px));
  -webkit-backdrop-filter:blur(clamp(1px, .125em, 4px));
  transition:all 400ms var(--ease-wabi);
  display:flex; align-items:center; justify-content:center;
  height:46px; font-family:inherit;
}
.glass-btn:hover{
  transform:scale(.975);
  box-shadow:
    inset 0 .125em .125em rgba(0,0,0,.035),
    inset 0 -.125em .125em rgba(255,255,255,.5),
    0 .075em .025em -.1em rgba(0,0,0,.25),
    0 0 .05em .1em inset rgba(255,255,255,.5),
    0 0 0 0 rgba(255,255,255,1);
}
.glass-btn span{
  position:relative; width:100%; height:100%;
  display:flex; align-items:center; justify-content:center; user-select:none;
  color:rgba(50,50,50,1); text-shadow:0 .05em .05em rgba(0,0,0,.05);
  transition:all 400ms var(--ease-wabi);
  padding-inline:1.75em; font-size:16px; font-weight:500;
  letter-spacing:-0.18px; white-space:nowrap;
}
.glass-btn span::after{                       /* the travelling shine */
  content:''; display:block; position:absolute; z-index:3;
  width:calc(100% - clamp(1px,.0625em,4px)); height:calc(100% - clamp(1px,.0625em,4px));
  top:calc(0% + clamp(1px,.0625em,4px) / 2); left:calc(0% + clamp(1px,.0625em,4px) / 2);
  border-radius:999vw; overflow:clip;
  background:linear-gradient(var(--angle-2),
    rgba(255,255,255,0) 0%, rgba(255,255,255,.5) 40% 50%, rgba(255,255,255,0) 55%);
  mix-blend-mode:screen; pointer-events:none;
  background-size:200% 200%; background-position:0% 50%; background-repeat:no-repeat;
  transition:background-position 500ms var(--ease-wabi), --angle-2 500ms var(--ease-wabi);
}
.glass-btn:hover span::after{ background-position:25% 50%; }
.glass-btn::after{                            /* the conic edge */
  content:''; position:absolute; z-index:1; inset:0; border-radius:999vw;
  width:calc(100% + clamp(1px,.0625em,4px)); height:calc(100% + clamp(1px,.0625em,4px));
  top:calc(0% - clamp(1px,.0625em,4px) / 2); left:calc(0% - clamp(1px,.0625em,4px) / 2);
  padding:clamp(1px,.0625em,4px); box-sizing:border-box;
  background:
    conic-gradient(from var(--angle-1) at 50% 50%,
      rgba(0,0,0,.5), rgba(0,0,0,0) 5% 40%, rgba(0,0,0,.5) 50%, rgba(0,0,0,0) 60% 95%, rgba(0,0,0,.5)),
    linear-gradient(180deg, rgba(255,255,255,.5), rgba(255,255,255,.5));
  -webkit-mask:linear-gradient(#000 0 0) content-box, linear-gradient(#000 0 0);
          mask:linear-gradient(#000 0 0) content-box, linear-gradient(#000 0 0);
  -webkit-mask-composite:xor; mask-composite:exclude;
  transition:all 400ms var(--ease-wabi), --angle-1 500ms ease;
  box-shadow:inset 0 0 0 calc(clamp(1px,.0625em,4px) / 2) rgba(255,255,255,.35);
}
.glass-btn:hover::after{ --angle-1:-125deg; }
.glass-btn-wrap:has(button:hover) .glass-btn-shadow,
.glass-btn-wrap:has(a:hover) .glass-btn-shadow{ filter:blur(clamp(.25px,.0156em,1.5px)); }
.glass-btn-wrap:has(button:active) { transform:rotate3d(1,0,0,25deg); }

/* header variant — the bright pill */
.glass-btn-wrap--header{ width:140px; }
.glass-btn-wrap--header .glass-btn{
  width:140px; height:40px;
  background:
    linear-gradient(-75deg, rgba(255,255,255,.10), rgba(255,255,255,.30), rgba(255,255,255,.10)),
    rgba(255,255,255,.55);
  border:1px solid rgba(255,255,255,.9);
  box-shadow:
    inset 0 1px 4px rgba(255,255,255,.665),
    inset 0 -4px 4px rgba(255,255,255,.85),
    inset 0 0 14px rgba(229,229,229,.55),
    0 11px 28px rgba(0,0,0,.07),
    0 23px 31px rgba(0,0,0,.05);
  backdrop-filter:blur(clamp(4px,.5em,14px));
  -webkit-backdrop-filter:blur(clamp(4px,.5em,14px));
}
.glass-btn-wrap--header .glass-btn span{
  font-size:13px; font-weight:400; letter-spacing:-0.14px; color:#363636;
  padding-inline:.65em; display:inline-flex; align-items:center; gap:5px;
}
.glass-btn-wrap--header .glass-btn span svg{ display:none; }
@media (min-width:1025px){
  .glass-btn-wrap--header{ width:200px; }
  .glass-btn-wrap--header .glass-btn{ width:200px; height:56px; }
  .glass-btn-wrap--header .glass-btn span{
    font-size:19px; letter-spacing:-0.19px; padding-inline:.7em; gap:7px;
  }
  .glass-btn-shadow{
    width:calc(100% + 1em); height:calc(100% + 1em);
    top:calc(0% - .5em); left:calc(0% - .5em);
    filter:blur(clamp(1px,.0625em,6px));
  }
  .glass-btn-shadow::after{
    background:linear-gradient(180deg, rgba(0,0,0,.03), rgba(0,0,0,.015));
    width:calc(100% - 1em - .125em); height:calc(100% - 1em - .125em);
    top:calc(1em - .25em); left:calc(1em - .4375em);
  }
}
/* icon variant — the footer's social buttons */
.glass-btn-wrap--icon{ width:32px; }
.glass-btn-wrap--icon .glass-btn{ width:32px; height:32px; }
.glass-btn-wrap--icon .glass-btn span{ padding-inline:0; }
.glass-btn-wrap--icon svg{ width:16px; height:16px; color:var(--grey-500); }

/* ═══ joonas.wtf ═══════════════════════════════════════════════════════
   Geometry only — nothing here touches the material above.

   The site's paper is #ffffff where wabi.ai's is #f0f0f0, so the pill has
   less to stand against; what carries it is its own drop shadow, which is
   why the header variant's two-layer shadow is the one used and not the
   plain .glass-btn's. */
.chrome-pills {
  display: flex;
  align-items: center;
  gap: 10px;
  /* ⚠ FIXED, not a flex item in the header. These are controls, not
     masthead — the wind pill switches an effect that covers the whole page
     and the Playground pill is the way out of it, and both scrolled away
     the moment you started reading. They stay put. */
  position: fixed;
  top: 24px;
  right: var(--pad, 40px);
  z-index: 60;
}
/* ── the header's two fixed corners ────────────────────────────────────
   The mark is pinned on the same terms as the pills, and for the same
   reason: it is the way HOME, and a way home that scrolls away is only a
   way home for the first screenful. The two corners are one decision, so
   they share their offsets. */
.mark {
  position: fixed;
  top: 24px;
  left: var(--pad, 40px);
  z-index: 60;
  /* it grows rightward into "Joonas Virtanen" on hover, so it is anchored by
     its left edge and never by a centre that would drift */
  transform-origin: left center;
}
/* the header keeps its height with both corners out of the flow */
.topbar { min-height: 46px; }
/* a pill sized for this topbar: the header variant's material, this site's
   dimensions, and width driven by its own content rather than fixed at the
   140/200px wabi.ai uses for one known word */
.chrome-pills .glass-btn-wrap--header { width: auto; }
/* ⚠ THE HEIGHT IS THE PADDING HERE. The pill is a flex box with a FIXED
   height and its label centred in it, so adding vertical padding to the span
   moves nothing — the box does not grow and the text stays exactly where the
   centring put it. "One more pixel above and below" is +2 on the height.
   The round pill's width tracks it, or it stops being a circle. */
.chrome-pills .glass-btn-wrap--header .glass-btn {
  width: auto; height: 40px;
}
.chrome-pills .glass-btn-wrap--header .glass-btn span {
  font-family: 'Inter', sans-serif;
  font-size: 13px; font-weight: 500; letter-spacing: -0.005em;
  padding-inline: 19px; gap: 7px;
}
/* ⚠ the ported rule hides any svg inside a header pill (wabi.ai's carries a
   decorative one it does not show); ours IS the icon, so it must come back */
.chrome-pills .glass-btn-wrap--header .glass-btn span svg { display: block; }

/* the icon-only pill — a circle, so the width is the height */
.chrome-pills .glass-btn-wrap--round .glass-btn { width: 40px; }
.chrome-pills .glass-btn-wrap--round .glass-btn span { padding-inline: 0; }
.chrome-pills .glass-btn svg { width: 17px; height: 17px; display: block; }

/* ── the wind pill's ON state ──────────────────────────────────────────
   The komorebi is a thing you switch on, so the pill has to say whether it
   is. It stays the same material and gains the ink — a second, louder
   material for one state would read as a different control. */
.chrome-pills .glass-btn.is-on span { color: #191919; }
.chrome-pills .glass-btn.is-on svg { opacity: 1; }
.chrome-pills .glass-btn svg { opacity: .62; transition: opacity 400ms var(--ease-wabi); }
.chrome-pills .glass-btn:hover svg { opacity: .9; }
/* the wind strokes drift while it is on — the effect it opens is about
   moving air, and a still icon over a moving scene is the only thing on
   screen not answering the wind */
.chrome-pills .glass-btn.is-on .wind-a { animation: wind-drift 2.6s ease-in-out infinite; }
.chrome-pills .glass-btn.is-on .wind-b { animation: wind-drift 2.6s ease-in-out infinite .35s; }
.chrome-pills .glass-btn.is-on .wind-c { animation: wind-drift 2.6s ease-in-out infinite .7s; }
@keyframes wind-drift {
  0%, 100% { transform: translateX(0); opacity: 1; }
  50%      { transform: translateX(1.1px); opacity: .72; }
}
@media (prefers-reduced-motion: reduce) {
  .chrome-pills .glass-btn.is-on .wind-a,
  .chrome-pills .glass-btn.is-on .wind-b,
  .chrome-pills .glass-btn.is-on .wind-c { animation: none; }
}

@media (max-width: 800px) {
  .chrome-pills { gap: 8px; top: 16px; }
  .mark { top: 16px; }
  .chrome-pills .glass-btn-wrap--header .glass-btn { height: 38px; }
  .chrome-pills .glass-btn-wrap--round .glass-btn { width: 38px; }
  .chrome-pills .glass-btn-wrap--header .glass-btn span { padding-inline: 17px; }
}


/* ═══ ONE OUTLINE, EVERYWHERE, AT HALF STRENGTH ════════════════════════
   The ported material is wabi.ai's, and wabi.ai uses it for ONE bright CTA
   on a page. Used for every control on this site it is too silvery — the
   conic edge and the white insets are doing a lot of work for a button that
   is not the point of the screen.

   ⚠ So the strength is a KNOB, not a re-tune: `--gl` scales exactly the
   three declarations that make the silver (the conic edge, the inset
   highlights, the travelling shine) and leaves every other ported value
   alone. 0.5 is half of wabi.ai's. Set it to 1 and this file is the live
   site again, which is the only way a port stays a port.

   Everything below is applied by SELECTOR to controls that are not
   .glass-btn — the komorebi panel's, the game's — so there is still exactly
   one definition of the material on the page. */
:root {
  --gl: 0.5;

  /* ── the shadow a piece of glass casts ──────────────────────────────
     ⚠ EVERY SURFACE WAS CASTING A DIFFERENT ONE. The header pills laid a
     soft plate under their whole footprint (0 11px 28px at zero spread)
     while the komorebi pills wore a single rim-hugging 0 4px 12px -6px —
     so the two matched at the border and nowhere else, which reads as two
     objects made of the same stuff standing on two different tables.

     ⚠ AND A SLAB OF GLASS DOES NOT CAST A SOLID SHADOW. Light goes THROUGH
     the middle of it; what stops light is the rim, where the bevel turns
     the ray far enough sideways to leave. So every layer here carries a
     NEGATIVE SPREAD — the shape is shrunk before it is blurred, so the
     shadow only ever peeks out past the edges and never lays a grey plate
     under the centre. That single sign is most of the difference between
     reading as glass and reading as a plastic chip.

     Three layers, because a real one has three jobs: a CONTACT line that
     says the thing is resting on the page at all (without it everything
     floats, however soft the rest is), a near ambient that gives it its
     weight, and a far one that puts it in a room. */
  /* ⚠ 40% LIGHTER than it was (.13/.15/.10), on Joonas's note about the
     Playground pill — and taken off the SHARED token rather than off that one
     button, because the whole argument of this cast is that every glass
     surface on the page is the same material standing on the same table. One
     pill with a lighter shadow than its neighbour is two materials again. */
  --glass-cast:
    0 1px 1.5px -1px rgba(0, 0, 0, 0.078),
    0 7px 14px -7px rgba(0, 0, 0, 0.09),
    0 20px 34px -16px rgba(0, 0, 0, 0.06);
  /* pressed: the object moves TOWARD the page, so the shadow tightens and
     darkens rather than simply fading — a lifted-looking press is the
     commonest tell of a shadow that was chosen rather than derived */
  --glass-cast-press:
    0 1px 1px -1px rgba(0, 0, 0, 0.096),
    0 3px 7px -4px rgba(0, 0, 0, 0.102),
    0 9px 16px -10px rgba(0, 0, 0, 0.06);
}

/* ⚠ THE EDGE IS ONE TREATMENT, WORN BY EVERY GLASS SURFACE. The corner pills
   carried the conic rim and the travelling shine and the weather pills did
   not — so two things that are meant to be the same material had visibly
   different edges, which is the first thing the eye checks. The rule is
   shared by selector and every surface keeps its OWN radius (`inherit`),
   because the dropdown is a rounded card and the pills are pills. */
.komo-wx-pill::after,
.komo-wx-menu::after {
  content: '';
  position: absolute;
  z-index: 1;
  inset: 0;
  border-radius: inherit;
  pointer-events: none;
  width: calc(100% + clamp(1px,.0625em,4px));
  height: calc(100% + clamp(1px,.0625em,4px));
  top: calc(0% - clamp(1px,.0625em,4px) / 2);
  left: calc(0% - clamp(1px,.0625em,4px) / 2);
  padding: clamp(1px,.0625em,4px);
  box-sizing: border-box;
  -webkit-mask: linear-gradient(#000 0 0) content-box, linear-gradient(#000 0 0);
          mask: linear-gradient(#000 0 0) content-box, linear-gradient(#000 0 0);
  -webkit-mask-composite: xor; mask-composite: exclude;
  box-shadow: inset 0 0 0 calc(clamp(1px,.0625em,4px) / 2) rgba(255,255,255,calc(.35 * var(--gl)));
  transition: all 400ms var(--ease-wabi), --angle-1 500ms ease;
}
.komo-wx-pill, .komo-wx-menu { position: relative; }
.komo-wx-pill:hover::after { --angle-1: -125deg; }

.glass-btn::after,
.komo-wx-pill::after,
.komo-wx-menu::after {
  background:
    conic-gradient(from var(--angle-1) at 50% 50%,
      rgba(0,0,0,calc(.5 * var(--gl))), rgba(0,0,0,0) 5% 40%,
      rgba(0,0,0,calc(.5 * var(--gl))) 50%, rgba(0,0,0,0) 60% 95%,
      rgba(0,0,0,calc(.5 * var(--gl)))),
    linear-gradient(180deg, rgba(255,255,255,calc(.5 * var(--gl))), rgba(255,255,255,calc(.5 * var(--gl))));
  box-shadow: inset 0 0 0 calc(clamp(1px,.0625em,4px) / 2) rgba(255,255,255,calc(.35 * var(--gl)));
}
.glass-btn span::after {
  background: linear-gradient(var(--angle-2),
    rgba(255,255,255,0) 0%, rgba(255,255,255,calc(.5 * var(--gl))) 40% 50%, rgba(255,255,255,0) 55%);
}
.glass-btn-wrap--header .glass-btn {
  box-shadow:
    inset 0 1px 4px rgba(255,255,255,calc(.665 * var(--gl))),
    inset 0 -4px 4px rgba(255,255,255,calc(.85 * var(--gl))),
    inset 0 0 14px rgba(229,229,229,calc(.55 * var(--gl))),
    var(--glass-cast);
  border-color: rgba(255,255,255,calc(.9 * var(--gl) + .1));
}
/* ⚠ THE PORTED :hover NEVER REACHED THESE. `.glass-btn:hover` and
   `.glass-btn-wrap--header .glass-btn` are the same specificity (0,2,0), and
   the header rule comes later in the file — so it won even while the pointer
   was on the button, and the press was a scale with no shadow behind it. */
.glass-btn-wrap--header .glass-btn:hover,
.glass-btn-wrap--header .glass-btn:active {
  box-shadow:
    inset 0 1px 4px rgba(255,255,255,calc(.665 * var(--gl))),
    inset 0 -4px 4px rgba(255,255,255,calc(.85 * var(--gl))),
    inset 0 0 14px rgba(229,229,229,calc(.55 * var(--gl))),
    var(--glass-cast-press);
}

/* ── the same outline on every other control ───────────────────────────
   The komorebi lab's buttons and the game's, which each had their own
   1px border and their own idea of a corner. Only the OUTLINE and the
   surface are shared — size, type and colour stay each app's own. */
.komo-presets button,
.komo-leaf button,
.komo-wind button,
.komo-copy,
.komo-weather,
.komo-wx-pill,
.komo-wx-menu,
.dash-btn,
.dash-go-again,
.dash-name-save {
  border: 1px solid rgba(255,255,255,calc(.9 * var(--gl) + .1)) !important;
  background:
    linear-gradient(-75deg, rgba(255,255,255,.10), rgba(255,255,255,.30), rgba(255,255,255,.10)),
    rgba(255,255,255,.55) !important;
  -webkit-backdrop-filter: blur(clamp(4px,.5em,14px));
  backdrop-filter: blur(clamp(4px,.5em,14px));
  box-shadow:
    inset 0 1px 4px rgba(255,255,255,calc(.665 * var(--gl))),
    inset 0 -4px 4px rgba(255,255,255,calc(.85 * var(--gl))),
    inset 0 0 14px rgba(229,229,229,calc(.55 * var(--gl))),
    var(--glass-cast) !important;
  transition: all 400ms var(--ease-wabi);
}
.komo-presets button:hover,
.komo-leaf button:hover,
.komo-wind button:hover,
.komo-copy:hover,
.komo-weather:hover,
.komo-wx-pill:hover,
.dash-btn:hover,
.dash-go-again:hover,
.dash-name-save:hover {
  box-shadow:
    inset 0 1px 4px rgba(255,255,255,calc(.665 * var(--gl))),
    inset 0 -4px 4px rgba(255,255,255,calc(.85 * var(--gl))),
    inset 0 0 14px rgba(229,229,229,calc(.55 * var(--gl))),
    var(--glass-cast-press) !important;
}
/* ⚠ the radius is per-surface, not shared. A 999vw pill radius forced onto
   the dropdown would turn a rounded card into a lozenge. */
.komo-presets button,
.komo-leaf button,
.komo-wind button,
.komo-copy,
.komo-weather,
.komo-wx-pill,
.dash-btn,
.dash-go-again,
.dash-name-save { border-radius: 999vw !important; }
.komo-wx-menu { border-radius: 18px !important; }
/* the SELECTED state stays the app's own — a chosen preset is ink, not
   another material */
.komo-presets button.on,
.komo-leaf button.on,
.komo-wind button.on {
  background: var(--ink, #1a1a1a) !important;
  color: var(--paper, #fbf9f5) !important;
  border-color: rgba(255,255,255,calc(.35 * var(--gl))) !important;
}

/* ── the cleared view ──────────────────────────────────────────────────
   Chrome out of the way, but never GONE: a control that removes the only
   way to undo itself is a trap. The pills go to a ghost and come back
   under the pointer. */
html.komo-cleared .chrome-pills { opacity: .10; }
/* ⚠ :has(:focus-visible), NOT :focus-within. Clicking the Clear button
   leaves it FOCUSED, so :focus-within held the pills lit for ever and the
   button defeated itself — the one thing it exists to do never happened
   after a mouse click. :focus-visible keeps them up for a keyboard user,
   which is who needs them, and lets go for a mouse. */
html.komo-cleared .chrome-pills:hover,
html.komo-cleared .chrome-pills:has(:focus-visible) { opacity: 1; }
.chrome-pills { transition: opacity 420ms var(--ease-wabi); }
html.komo-cleared .topbar .mark { opacity: .12; transition: opacity 420ms var(--ease-wabi); }


/* ═══ LIQUID GLASS ═════════════════════════════════════════════════════
   The refraction layer, on top of the ported wabi material. See
   liquid-glass.js for how the displacement map is derived (Snell's law over
   a convex-squircle bezel profile, written along the rounded-rect SDF's
   outward normal).

   ⚠ THE FALLBACK IS A CASCADE FALLBACK, NOT AN @supports ONE. The plain
   blur is declared FIRST and the url() version second: an engine that
   cannot reference an SVG filter from backdrop-filter drops the second
   declaration as invalid and keeps the first. `@supports (backdrop-filter:
   url(#x))` answers TRUE in Safari and Firefox, which parse the value and
   never render it — so it is exactly the wrong test.
   ⚠ backdrop-filter makes a containing block AND a stacking context, so
   anything at z-index:-1 inside needs `isolation` on the parent or it
   escapes behind the page. */
[data-liquid-glass] {
  /* every engine gets this */
  -webkit-backdrop-filter: blur(7px) saturate(180%) brightness(1.04);
  backdrop-filter: blur(7px) saturate(180%) brightness(1.04);
  isolation: isolate;
}
/* Chromium only: the blur drops right down, because the REFRACTION is now
   doing the work the blur was standing in for. A heavy blur over a
   displacement map just smears the thing it bent. */
[data-liquid-glass].lg-on {
  -webkit-backdrop-filter: blur(1.5px) var(--lg-filter) saturate(185%) brightness(1.05);
  backdrop-filter: blur(1.5px) var(--lg-filter) saturate(185%) brightness(1.05);
}

/* the rim light — this is most of what makes it read as glass, and it works
   in every browser whether or not the refraction does */
[data-liquid-glass] {
  position: relative;
  border: 1px solid rgba(255,255,255,calc(.34 * var(--gl) + .06));
  box-shadow:
    0 8px 30px -10px rgba(25,25,25,.22),
    0 2px 6px -3px rgba(25,25,25,.12),
    inset 0  1px 1px rgba(255,255,255,calc(.85 * var(--gl))),
    inset 0 -1px 1px rgba(255,255,255,calc(.45 * var(--gl))),
    inset  1px 0 1px rgba(255,255,255,calc(.30 * var(--gl))),
    inset -1px 0 1px rgba(255,255,255,calc(.30 * var(--gl)));
}
/* the diagonal sheen, and a highlight that follows the pointer */
[data-liquid-glass]::before {
  content: ''; position: absolute; inset: 0; z-index: 0;
  border-radius: inherit; pointer-events: none;
  background:
    radial-gradient(140px 90px at var(--lg-mx, 50%) var(--lg-my, 0%),
      rgba(255,255,255,calc(.40 * var(--gl))), transparent 68%),
    linear-gradient(135deg,
      rgba(255,255,255,calc(.55 * var(--gl))),
      rgba(255,255,255,calc(.10 * var(--gl))) 26%,
      transparent 56%);
  mix-blend-mode: screen;
  transition: --lg-mx .2s ease-out, --lg-my .2s ease-out;
}
[data-liquid-glass] > * { position: relative; z-index: 1; }

@property --lg-mx { syntax: '<percentage>'; inherits: true; initial-value: 50%; }
@property --lg-my { syntax: '<percentage>'; inherits: true; initial-value: 0%; }


/* ── the WebGL glass stage ─────────────────────────────────────────────
   One canvas over the viewport, one lens drawn per pill. It sits ABOVE the
   komorebi it samples and BELOW the pills' own text, so a pill is: refracted
   backdrop (canvas) → rim and sheen (CSS) → its own label (DOM). */
.lg-stage {
  position: fixed;
  inset: 0;
  z-index: 55;              /* komorebi is -1; the pills are 60 */
  pointer-events: none;
}
/* ⚠ WHEN THE WEBGL GLASS IS LIVE THE CSS BACKDROP MUST GET OUT OF ITS WAY.
   Both would be sampling the same thing — the blur would frost the very
   backdrop the refraction is bending, and the pill's own fill would paint
   over the lens drawn behind it. `lg-live` is set by the engine only when
   there is really something behind the glass to refract. */
html.lg-live [data-liquid-glass] {
  -webkit-backdrop-filter: none;
  backdrop-filter: none;
  background: transparent;
}
html.lg-live .komo-wx-pill,
html.lg-live .komo-wx-menu { background: transparent; }


/* ── the komorebi keeps to itself ──────────────────────────────────────
   Its pill is hidden until the effect is on, so the page says nothing about
   it and K is the only way in. It was briefly unhidden — a shortcut is not
   an affordance, and nobody presses a key a page has not told them about —
   and Joonas's call is that the front page stays quiet about it anyway.
   Once it IS on, the pill appears as the way back out, and Clear beside it. */
html:not(.komorebi-on) #komo-pill,
html:not(.komorebi-on) #komo-pill + *,
html:not(.komorebi-on) .glass-btn-wrap:has(#komo-pill) { display: none; }


/* ── the header stands down for the game ───────────────────────────────
   Donkey Dash takes the whole screen, and the two pinned corners floated
   over its HUD. They fade rather than vanish, so leaving the game does not
   feel like the page rebuilding itself. */
html.dash-on .chrome-pills,
html.dash-on .mark {
  opacity: 0;
  pointer-events: none;
  transition: opacity 260ms var(--ease-wabi);
}
