/* NOTE ON THE TOKEN NAMES
 *
 * Every custom property here is `--lg-*`, for the same reason the classes are
 * `.lg*`: css/style.css already owns a `--glass-*` namespace.
 *
 * This is not hypothetical. `--glass-edge` was originally declared here with
 * the same name style.css uses, and because this file loads second it won this
 * cascade — turning the game's inset edge-rim SHADOW LIST into a bare colour.
 * `box-shadow: var(--glass-edge), 0 12px 34px ...` then parsed as invalid and
 * computed to `none`, which silently removed every drop shadow and every lit
 * edge in the game. Nothing errored; the whole UI just went flat.
 */

/* NOTE ON THE CLASS NAMES
 *
 * These are `.lg*`, not `.glass*`, because VoidBox Cyberverse ALREADY has a
 * `.glass` system in css/style.css — one that deliberately runs "Frost 0 — NO
 * blur", suggesting refraction with saturate/contrast/brightness instead,
 * because blurring those panels re-ran a backdrop filter under an animating
 * title and tanked lobby FPS. That decision is documented there and it is
 * correct.
 *
 * So this material does not replace it and must never be applied wholesale to
 * the game. It is for surfaces that genuinely sit OVER moving content and need
 * to separate themselves from it — the shop's Exit pill, the bot dashboard's
 * panels and drawer. Everywhere else, the existing `.glass` stays.
 */

/* ============================================================================
   Liquid Glass — a shared material for VoidBox Cyberverse and the bot panel.
   ============================================================================

   ---- WHAT THIS IS, AND WHAT IT IS NOT ----

   Apple's Liquid Glass refracts what is behind it: the backdrop is warped
   through a lens, not merely blurred. The web cannot do that cheaply. The only
   way to get true refraction in a browser is an SVG feDisplacementMap per
   element, which forces a filter pass over the backdrop on every frame — on a
   phone that is the difference between 60fps and a slideshow, and this is a
   game and a settings panel, not a demo.

   So this is the honest subset: blur + saturation + a specular highlight + a
   lit edge. It reads as glass, and it costs one compositor operation.

   ---- THE PERFORMANCE RULES THIS FILE OBEYS ----

   1. `backdrop-filter` is the expensive part, so the number of elements
      carrying it is BOUNDED. Panels, bars, dialogs and the odd floating
      control — never rows of a scrolling list, never anything there can be
      fifty of on screen.
   2. Nothing animates `backdrop-filter`, ever. Animating it re-runs the blur
      every frame over a changing region. Hover states move opacity, borders
      and transforms instead, which stay on the compositor.
   3. Every glass surface declares its own opaque background FIRST. Browsers
      without `backdrop-filter` get a solid panel that still has contrast, and
      so does anybody who has asked the OS to reduce transparency.
   4. `.lg` never nests. Two stacked backdrop filters double the cost and
      look muddy — a child inside glass uses `.lg-inset`, which is paint
      only.

   ---- WHY THE OPACITY IS SO LOW ----

   Text over a blurred backdrop is a contrast problem before it is a taste
   problem. Every surface here keeps a dark base under the translucency so
   body text stays legible over a bright background image, which both of these
   products have.
*/

:root {
  /* The base under the glass. Dark, because both products are dark. */
  --lg-base: 14, 22, 52;
  --lg-tint: .40;          /* how much of that base shows through */
  --lg-tint-strong: .62;   /* dialogs, and anything holding a form */
  --lg-blur: 18px;
  --lg-sat: 165%;
  --lg-edge: rgba(255, 255, 255, .16);
  --lg-edge-lit: rgba(255, 255, 255, .34);
  --lg-shadow: 0 8px 32px rgba(0, 0, 0, .38);
}

/* ---- the material ---- */
.lg,
.lg-strong {
  /* Needed so the ::before sheen has a containing block.
     See the override below — this line is a footgun. */
  position: relative;
  /* Rule 3: the opaque fallback comes first and is overridden below only when
     the browser can actually blur. */
  background: rgb(var(--lg-base));
  border: 1px solid var(--lg-edge);
  box-shadow: var(--lg-shadow);
  /* Glass has to clip its own highlight. */
  overflow: hidden;
}

@supports ((backdrop-filter: blur(1px)) or (-webkit-backdrop-filter: blur(1px))) {
  .lg {
    background: rgba(var(--lg-base), var(--lg-tint));
    -webkit-backdrop-filter: blur(var(--lg-blur)) saturate(var(--lg-sat));
    backdrop-filter: blur(var(--lg-blur)) saturate(var(--lg-sat));
  }
  .lg-strong {
    background: rgba(var(--lg-base), var(--lg-tint-strong));
    -webkit-backdrop-filter: blur(calc(var(--lg-blur) * 1.4)) saturate(var(--lg-sat));
    backdrop-filter: blur(calc(var(--lg-blur) * 1.4)) saturate(var(--lg-sat));
  }
}

/* The specular sheen: a light source above-left, and a lit top edge.
   Pure paint — a gradient and a border — so it costs nothing per frame. */
.lg::before,
.lg-strong::before {
  content: '';
  position: absolute;
  inset: 0;
  pointer-events: none;
  border-radius: inherit;
  background:
    linear-gradient(160deg, rgba(255,255,255,.13) 0%, rgba(255,255,255,.04) 34%, transparent 62%);
  /* The lit rim. inset so it draws inside the border, like light caught on an
     edge rather than an outline around it. */
  box-shadow: inset 0 1px 0 0 var(--lg-edge-lit);
}

/* ---- do not clobber an element that positions itself ----
 *
 * `position: relative` above is a utility class overwriting an author's own
 * layout, and this file loads after style.css, so it wins on source order.
 *
 * Every current `.lg` user is a `.corner-link`, which is `position: absolute`
 * with a `bottom` offset. Forcing those to `relative` turned that offset into
 * a RELATIVE shift — moving each one UP by its own `bottom` value, straight
 * into the content above it. That is the overlap on the Blitz mode-select
 * screen, and it applied to all nine of them.
 *
 * Two classes, so this beats the single-class rule above no matter which file
 * loads first. Anything else given `.lg` keeps the relative default it needs.
 */
.corner-link.lg,
.corner-link.lg-strong { position: absolute; }

/* A child of a glass surface. Rule 4: paint only, no second blur. */
.lg-inset {
  background: rgba(255, 255, 255, .055);
  border: 1px solid rgba(255, 255, 255, .10);
  box-shadow: inset 0 1px 0 0 rgba(255, 255, 255, .07);
}

/* An interactive glass surface. Everything that moves here is compositor-only:
   opacity, transform, border-colour. */
.lg-btn {
  transition: transform .16s cubic-bezier(.34, 1.4, .64, 1),
              border-color .16s ease,
              filter .16s ease;
  will-change: transform;
}
.lg-btn:hover { transform: translateY(-1px); border-color: var(--lg-edge-lit); filter: brightness(1.12); }
.lg-btn:active { transform: translateY(0) scale(.985); }

/* ---- opting out ------------------------------------------------------------
 *
 * Three ways this turns itself off, in order of authority:
 *
 *   1. The OS transparency setting. Somebody who has asked for less
 *      translucency has usually asked because it is hard to read.
 *   2. The OS contrast setting, for the same reason but louder.
 *   3. `html.no-lg`, set by the runtime probe in js/glass.js when the
 *      device cannot afford it.
 *
 * All three land on the same opaque surface, so there is one fallback to keep
 * looking right rather than three.
 */
@media (prefers-reduced-transparency: reduce) {
  .lg, .lg-strong {
    background: rgb(var(--lg-base)) !important;
    -webkit-backdrop-filter: none !important;
    backdrop-filter: none !important;
  }
}
@media (prefers-contrast: more) {
  .lg, .lg-strong {
    background: rgb(var(--lg-base)) !important;
    -webkit-backdrop-filter: none !important;
    backdrop-filter: none !important;
    border-color: rgba(255,255,255,.5);
  }
}
html.no-lg .lg,
html.no-lg .lg-strong {
  background: rgb(var(--lg-base)) !important;
  -webkit-backdrop-filter: none !important;
  backdrop-filter: none !important;
}
html.no-lg .lg::before,
html.no-lg .lg-strong::before { display: none; }

@media (prefers-reduced-motion: reduce) {
  .lg-btn { transition: none; }
  .lg-btn:hover, .lg-btn:active { transform: none; }
}

/* ============================================================================
   Integration with VoidBox Cyberverse's graphics tiers
   ============================================================================

   The game already decides how much glass a device can afford, via
   html[data-gfx="low|medium|high"] — and it already measured the answer:
   backdrop filtering under the animating lobby title was "the root cause of
   ~30fps on medium". Adding a second, independent opinion about the same
   question is how two systems end up fighting and the slower one wins.

   So `.lg` obeys that tier, exactly the way `.glass` does. Inside the game the
   probe in js/glass.js is not loaded at all; the player's own graphics setting
   is the authority, because they chose it.
*/
html[data-gfx="medium"] .lg,
html[data-gfx="medium"] .lg-strong {
  /* Halved. Still reads as glass, costs meaningfully less to composite. */
  -webkit-backdrop-filter: blur(9px) saturate(140%);
  backdrop-filter: blur(9px) saturate(140%);
}
/* The lobby/menu screens drop it entirely, matching the existing rule for
   `.glass` — same reason, same place. The four game screens keep it. */
html[data-gfx="medium"] .screen:not(.game-screen) .lg,
html[data-gfx="medium"] .screen:not(.game-screen) .lg-strong {
  -webkit-backdrop-filter: none !important;
  backdrop-filter: none !important;
  background: rgba(var(--lg-base), .74);
}
html[data-gfx="low"] .lg,
html[data-gfx="low"] .lg-strong {
  -webkit-backdrop-filter: none !important;
  backdrop-filter: none !important;
  /* Denser tint so text over a bright nebula still reads — the same trade the
     existing low tier makes. */
  background: rgba(var(--lg-base), .80);
}
html[data-gfx="low"] .lg-btn { transition: none; }
html[data-gfx="low"] .lg-btn:hover,
html[data-gfx="low"] .lg-btn:active { transform: none; }
