/* ============================================================
   LIQUID GLASS — an opt-in material for the chrome and panels.

   Loaded only when the material is chosen. The inline snippet in
   <head> appends this stylesheet before first paint when the stored
   preference asks for it, so nobody who has not chosen glass pays for
   it and nobody who has sees a flash of the flat material first.

   Technique follows samasante/liquid-glass. The load-bearing thing
   learned there: `backdrop-filter: url(#…)` is Chromium-only, so real
   refraction of the page behind a panel cannot be the baseline. What
   works everywhere is frost plus a lit edge, and that is what carries
   the look at UI scale. The refraction in glass.js is an enhancement
   on top, behind a support test.

   That library also warns that a single stretched displacement lens
   blooms an oval across a wide panel, and that lenses should stay
   content-sized. The nav here is a full-width bar, so it takes frost
   and an edge; only the menu panel and the controls get a lens.
   ============================================================ */

@layer utilities {
  /* ---- Tokens -------------------------------------------------
     The tint has a floor for a reason: text sits on these panels, and
     a panel transparent enough to read the page through is a panel
     you cannot read text on. 76% is the lowest that still holds AA
     for the type these surfaces carry. */
  /* Declared on body, not :root. Every mode override on this site sets
     --bg and --ink on body, so a tint resolved at :root only ever sees
     the product stylesheet's own (dark) value -- which painted a
     charcoal nav across the light face and made its buttons
     unreadable. */
  :root[data-material="glass"] body {
    /* One indirection, so the material asks each surface for its own
       ground and figure instead of guessing at token names.

       The tint used to read var(--bg, var(--paper, #fff)) inline, and
       on news, journal and genesis-fall -- which define neither, and
       carry private --nw-/--jr-/--qe- palettes -- it fell all the way
       through to #fff and painted white panels over a page that is not
       white. Measured before this change: a luminance gap of 0.993
       against their own ground, against 0.000 everywhere else.
       check-glass.mjs is what found it and now guards it. */
    --glass-ground: var(--bg, var(--paper, #fff));
    --glass-figure: var(--ink, #fff);

    --glass-tint: color-mix(in srgb, var(--glass-ground) 76%, transparent);
    --glass-tint-strong: color-mix(in srgb, var(--glass-ground) 88%, transparent);
    --glass-blur: 22px;
    --glass-saturate: 175%;

    /* The edge is the whole trick. Real glass catches light on the rim
       facing the source and goes dark on the rim facing away, so a
       single flat border reads as a rectangle while two opposed ones
       read as a solid with thickness. */
    --glass-rim-lit: color-mix(in srgb, var(--glass-figure) 4%, transparent);
    --glass-rim-shade: color-mix(in srgb, var(--glass-figure) 14%, transparent);
    --glass-sheen: color-mix(in srgb, var(--glass-figure) 5%, transparent);
  }

  /* The two editorial surfaces spell their ground privately. */
  :root[data-material="glass"] body.nw-page {
    --glass-ground: var(--nw-paper);
    --glass-figure: var(--nw-ink);
  }
  :root[data-material="glass"] body.jr-page {
    --glass-ground: var(--jr-paper);
    --glass-figure: var(--jr-ink);
  }
  :root[data-mode="dark"][data-material="glass"] body,
  :root[data-material="glass"] body.orrery-page,
  :root[data-material="glass"] body.vellum-page,
  :root[data-material="glass"] body.shelterix-page,
  :root[data-material="glass"] body.utility-page {
    /* On a dark ground the lit rim is light and does the work; on
       paper it is the shade that describes the edge. */
    --glass-rim-lit: rgba(255, 255, 255, 0.22);
    --glass-rim-shade: rgba(0, 0, 0, 0.34);
    --glass-sheen: rgba(255, 255, 255, 0.045);
  }

  /* ---- Something to be glass over ------------------------------
     This is the part that is easy to miss. Frost blurs what is behind
     it and refraction bends what is behind it, so over a flat
     near-black ground both do nothing at all: the panel reads as a
     slightly lighter box with a rim, which is how a glass treatment
     ends up looking cheap.

     iOS gets away with it because there is a photograph behind every
     panel. The equivalent here is to raise the page's own light when
     the material is on -- a wide, slow field the panels have something
     to catch. It is the material's backdrop, so it belongs to the
     material, not to the default page. */
  :root[data-material="glass"] body {
    background-image:
      radial-gradient(58% 44% at 18% 8%,
        color-mix(in srgb, var(--signal, var(--accent, transparent)) 20%, transparent),
        transparent 62%),
      radial-gradient(48% 40% at 88% 34%,
        color-mix(in srgb, var(--accent-2, var(--signal, transparent)) 13%, transparent),
        transparent 58%),
      radial-gradient(70% 50% at 46% 100%,
        color-mix(in srgb, var(--glass-figure, transparent) 7%, transparent),
        transparent 66%);
    background-attachment: fixed;
    background-repeat: no-repeat;
  }

  /* The heroes already carry a wash; under glass it works harder. */
  :root[data-material="glass"] .ehero,
  :root[data-material="glass"] .hero,
  :root[data-material="glass"] .vhero,
  :root[data-material="glass"] .sxhero,
  :root[data-material="glass"] .jr-hero { --atmosphere-gain: 1; }

  /* ---- The material ------------------------------------------- */
  :root[data-material="glass"] .nav,
  :root[data-material="glass"] .ebar,
  :root[data-material="glass"] .jr-header,
  :root[data-material="glass"] .nw-header,
  :root[data-material="glass"] .menu-panel,
  :root[data-material="glass"] .card,
  :root[data-material="glass"] .price-card,
  :root[data-material="glass"] .auth-card,
  :root[data-material="glass"] .mode-toggle,
  :root[data-material="glass"] .btn-ghost,
  :root[data-material="glass"] .ebtn:not(.ebtn-fill) {
    background: var(--glass-tint);
    border-color: transparent;
    box-shadow:
      inset 0 1px 0 0 var(--glass-rim-lit),
      inset 1px 0 0 0 var(--glass-rim-lit),
      inset 0 -1px 0 0 var(--glass-rim-shade),
      inset -1px 0 0 0 var(--glass-rim-shade),
      0 10px 34px -18px rgba(0, 0, 0, 0.5);
  }

  /* ---- Pressable glass ------------------------------------------
     The block above hands every ghost control a glass background from
     the utilities layer, which outranks the product layer where
     .ebtn:hover lives. So the material quietly ate the hover: six
     buttons on vespera and shelterix took their glass tint and then
     had nothing left to say when you pointed at them. It only showed
     up once glass became the default -- as an opt-in third look it was
     six controls almost nobody reached.

     A flat ghost button inverts on hover, ink for paper. Glass cannot
     do that without stopping being glass, so it does what glass does:
     the tint firms and the lit rim doubles, as though the panel caught
     more of the light. */
  :root[data-material="glass"] .ebtn:not(.ebtn-fill):hover,
  :root[data-material="glass"] .btn-ghost:hover,
  :root[data-material="glass"] .btn:not(.btn-primary):hover {
    background: var(--glass-tint-strong);
    box-shadow:
      inset 0 1px 0 0 var(--glass-rim-lit),
      inset 1px 0 0 0 var(--glass-rim-lit),
      inset 0 -1px 0 0 var(--glass-rim-shade),
      inset -1px 0 0 0 var(--glass-rim-shade),
      inset 0 0 0 1px var(--glass-rim-lit),
      0 14px 40px -18px rgba(0, 0, 0, 0.55);
  }
  :root[data-material="glass"] .ebtn:not(.ebtn-fill),
  :root[data-material="glass"] .btn-ghost,
  :root[data-material="glass"] .btn:not(.btn-primary) {
    transition:
      background var(--motion-control, 180ms) var(--ease, cubic-bezier(.22,1,.36,1)),
      box-shadow var(--motion-control, 180ms) var(--ease, cubic-bezier(.22,1,.36,1));
  }

  @supports (backdrop-filter: blur(1px)) or (-webkit-backdrop-filter: blur(1px)) {
    :root[data-material="glass"]:not(.no-heavy-motion) .nav,
    :root[data-material="glass"]:not(.no-heavy-motion) .ebar,
    :root[data-material="glass"]:not(.no-heavy-motion) .jr-header,
    :root[data-material="glass"]:not(.no-heavy-motion) .nw-header,
    :root[data-material="glass"]:not(.no-heavy-motion) .menu-panel,
    :root[data-material="glass"]:not(.no-heavy-motion) .card,
    :root[data-material="glass"]:not(.no-heavy-motion) .price-card,
    :root[data-material="glass"]:not(.no-heavy-motion) .auth-card,
    :root[data-material="glass"]:not(.no-heavy-motion) .mode-toggle {
      backdrop-filter: blur(var(--glass-blur)) saturate(var(--glass-saturate));
      -webkit-backdrop-filter: blur(var(--glass-blur)) saturate(var(--glass-saturate));
    }
  }

  /* ---- Sheen --------------------------------------------------
     A soft highlight that sits where the light would fall. glass.js
     moves --glass-x / --glass-y with the pointer on a fine-pointer
     device; without it the default puts the light top-left, which is
     where every other shadow on this site says it is. */
  :root[data-material="glass"] .nav,
  :root[data-material="glass"] .ebar,
  :root[data-material="glass"] .jr-header,
  :root[data-material="glass"] .menu-panel,
  :root[data-material="glass"] .card,
  :root[data-material="glass"] .price-card,
  :root[data-material="glass"] .auth-card {
    position: relative;
    isolation: isolate;
  }
  :root[data-material="glass"] .nav::after,
  :root[data-material="glass"] .ebar::after,
  :root[data-material="glass"] .jr-header::after,
  :root[data-material="glass"] .menu-panel::after,
  :root[data-material="glass"] .card::after,
  :root[data-material="glass"] .price-card::after,
  :root[data-material="glass"] .auth-card::after {
    content: "";
    position: absolute;
    inset: 0;
    z-index: -1;
    pointer-events: none;
    border-radius: inherit;
    /* Tight and faint. A large soft one does not read as light
       catching an edge, it reads as a grey smear across the panel. */
    background: radial-gradient(
      42% 60% at var(--glass-x, 18%) var(--glass-y, -10%),
      var(--glass-sheen), transparent 70%);
    transition: background var(--motion-glide) var(--ease);
  }

  /* Nav already sets its own background in tokens.css's utilities
     block; glass loads later in the same layer and takes over. */
  /* SSTR's nav backdrop is rgba(0,0,0,0.01) with blur(20px)
     saturate(180%) -- almost no tint, letting the blur carry it. That
     works there and works here for the chrome, which holds a handful
     of words over content that scrolls behind. A card holding body
     copy cannot afford it, so only the bars get the light tint. */
  :root[data-material="glass"] .nav,
  :root[data-material="glass"] .ebar,
  :root[data-material="glass"] .jr-header,
  :root[data-material="glass"] .nw-header {
    background: color-mix(in srgb, var(--bg, var(--paper)) 58%, transparent);
    --glass-blur: 28px;
    --glass-saturate: 185%;
  }
  :root[data-material="glass"] .nav.scrolled,
  :root[data-material="glass"] .ebar.scrolled {
    background: var(--glass-tint-strong);
  }

  /* ---- Chamfer -------------------------------------------------
     Hard corners are this site's signature and glass does not get to
     round them. The lens in glass.js reads the computed radius, so a
     3px corner refracts as a 3px corner rather than an iOS pill.

     What glass does get is a cut corner. SSTR uses clip-path polygons
     rather than radii throughout, and a chamfer suits a research
     instrument far better than a rounded rectangle: it reads as
     machined rather than as soft. It is also the one shape a radius
     cannot fake.

     Two layers, because a clip-path removes the corner rather than
     bending the border around it, so a bordered element loses its edge
     exactly where the cut is. The ::before carries the rim at the same
     shape, inset by a pixel, and the panel sits on top of it. */
  :root[data-material="glass"] .card,
  :root[data-material="glass"] .price-card,
  :root[data-material="glass"] .auth-card,
  :root[data-material="glass"] .menu-panel {
    --chamfer: 10px;
    border-radius: 0;
    clip-path: polygon(
      var(--chamfer) 0, calc(100% - var(--chamfer)) 0, 100% var(--chamfer),
      100% calc(100% - var(--chamfer)), calc(100% - var(--chamfer)) 100%,
      var(--chamfer) 100%, 0 calc(100% - var(--chamfer)), 0 var(--chamfer));
  }
  :root[data-material="glass"] .card::before,
  :root[data-material="glass"] .price-card::before,
  :root[data-material="glass"] .auth-card::before,
  :root[data-material="glass"] .menu-panel::before {
    content: "";
    position: absolute;
    inset: 0;
    z-index: -2;
    pointer-events: none;
    background: var(--glass-rim-lit);
    clip-path: inherit;
  }
  /* The mode control is small enough that a 10px cut would eat it. */
  :root[data-material="glass"] .mode-toggle { --chamfer: 5px; }
}

/* The blur and the pointer-tracked sheen are both continuous work for
   the compositor. Motion off means the material holds still: the frost
   stays, because it is not motion, but nothing follows the cursor. */
@media (prefers-reduced-motion: reduce) {
  :root[data-material="glass"] .nav::after,
  :root[data-material="glass"] .ebar::after,
  :root[data-material="glass"] .jr-header::after,
  :root[data-material="glass"] .menu-panel::after,
  :root[data-material="glass"] .card::after,
  :root[data-material="glass"] .price-card::after,
  :root[data-material="glass"] .auth-card::after {
    transition: none !important;
    background: radial-gradient(42% 60% at 18% -10%, var(--glass-sheen), transparent 70%) !important;
  }
}
