/* ═══════════════════════════════════════════════════════════════════════════════
   Texture — the light, grain and rim vocabulary, extracted so the marketing page
   can speak it too.

   WHERE THIS CAME FROM. trial.html's brand tile already had the whole language:
   a bloom, a light pool, a conic ray fan, fractal-noise grain, a gloss streak and
   a masked rim. It was written inline for one 96px tile and nothing else could
   reach it, so index.html stayed flat while every surface behind the login looked
   lit. These are the same effects, parameterised and given light-mode behaviour.

   THE ONE RULE THAT MATTERS: `screen` blend is invisible on white.
   The tile could hardcode `mix-blend-mode:screen` because it was always a near-black
   square. A marketing page is white half the time, and a screen-blended white glow on
   #f5f6f8 renders as absolutely nothing — which is exactly how "we added glimmer" turns
   into "nothing happened" on a light monitor. So every layer here flips: `screen` +
   white light on dark, `multiply` + a tinted shadow on light. Same geometry, inverted
   ink. Nothing is hardcoded to one scheme.

   Layers are pointer-events:none and aria-hidden by construction (::before/::after or
   an explicit .tx-layer div). None of them ever intercept a click.
   ═══════════════════════════════════════════════════════════════════════════════ */

:root{
  /* Strength knobs. Kept low on purpose: the moment texture is visible AS texture it
     reads as a filter rather than as light. */
  --tx-grain:0.055;          /* fractal noise opacity, dark scheme */
  --tx-grain-light:0.035;    /* less on white — noise on pale surfaces is much louder */
  --tx-rim:0.42;             /* masked border light */
  --tx-bloom:0.30;
  --tx-hue:var(--accent, #5b8def);
}
@media (prefers-color-scheme: light){
  :root{ --tx-grain:0.035; --tx-rim:0.30; --tx-bloom:0.16; }
}

/* ── Grain ────────────────────────────────────────────────────────────────────
   One inline SVG turbulence, tiled. baseFrequency 0.8 gives chunky speckle that
   survives being scaled down; a finer value dissolves into flat grey the moment the
   browser resamples it. */
.tx-grain,
.tx::after{
  content:"";
  position:absolute; inset:0; pointer-events:none; border-radius:inherit;
  background-image:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='160' height='160'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.8' numOctaves='2' stitchTiles='stitch'/%3E%3C/filter%3E%3Crect width='100%25' height='100%25' filter='url(%23n)'/%3E%3C/svg%3E");
  background-size:96px 96px;
  mix-blend-mode:screen;          /* light speckle lifting a dark surface */
  opacity:var(--tx-grain);
  z-index:1;
}
@media (prefers-color-scheme: light){
  /* On white the speckle has to REMOVE light instead of adding it, or it is invisible. */
  .tx-grain, .tx::after{ mix-blend-mode:multiply; opacity:var(--tx-grain-light); }
}

/* ── Rim ──────────────────────────────────────────────────────────────────────
   A border-only gradient. The two-layer mask (padding-box XOR border-box) punches the
   middle out, so the gradient survives only in the 1px ring — this is what makes it a
   lit EDGE rather than a glowing card.

   This replaces the rotating conic sweep. A rotation announces itself and never stops;
   a fixed rim reads as a light source somewhere above-left of the page, which is the
   same lie every other surface in the console tells. Soft edges come from the gradient
   fading to transparent well before the corners rather than from a blur, which would
   bleed the ring outward and lose the hairline. */
.tx-rim{
  position:absolute; inset:0; pointer-events:none; border-radius:inherit;
  padding:1px;
  background:
    radial-gradient(58% 44% at 84% 6%, color-mix(in oklab, white 82%, transparent) 0%, transparent 52%),
    linear-gradient(180deg,
      color-mix(in oklab, white 34%, transparent) 0%,
      color-mix(in oklab, white 6%, transparent) 18%,
      transparent 46%,
      transparent 74%,
      color-mix(in oklab, var(--tx-hue) 70%, transparent) 100%);
  -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;
  opacity:var(--tx-rim);
  z-index:2;
}
@media (prefers-color-scheme: light){
  /* White-on-white is nothing. On light the rim becomes a soft SHADOW edge with the
     accent still pooling at the bottom, so the card keeps its direction. */
  .tx-rim{
    background:
      radial-gradient(58% 44% at 84% 6%, color-mix(in oklab, white 95%, transparent) 0%, transparent 52%),
      linear-gradient(180deg,
        color-mix(in oklab, white 90%, transparent) 0%,
        transparent 40%,
        transparent 70%,
        color-mix(in oklab, var(--tx-hue) 42%, transparent) 100%);
  }
}

/* Accent-tinted rim, for the one card that is meant to be chosen. */
.tx-rim.is-accent{ --tx-hue:var(--accent); opacity:calc(var(--tx-rim) + 0.22); }
.tx-rim.is-gold{ --tx-hue:var(--gold, #e0b341); opacity:calc(var(--tx-rim) + 0.28); }

/* ── Bloom, pool, rays, gloss ─────────────────────────────────────────────────
   The atmospheric set. Used together at the top of the page; individually on cards. */
.tx-bloom{
  position:absolute; pointer-events:none; border-radius:inherit;
  background:radial-gradient(46% 42% at 50% 47%, var(--tx-hue) 0%, transparent 70%);
  filter:blur(30px); mix-blend-mode:screen; opacity:var(--tx-bloom); z-index:0;
}
.tx-pool{
  position:absolute; inset:auto 0 0 0; height:60%; pointer-events:none;
  background:radial-gradient(72% 100% at 50% 118%, var(--tx-hue) 0%, transparent 72%);
  filter:blur(14px); mix-blend-mode:screen; opacity:.45; z-index:0;
}
/* ── The ray fan ──────────────────────────────────────────────────────────────
   Two elements, and both are load-bearing.

   .tx-rays is the FRAME: it occupies only the top band, clips, and carries the mask.
   .tx-rays-spin is the gradient, and it is the only thing that rotates. Rotating the
   masked element itself would drag the mask around with it, so the soft edge would
   sweep across the page instead of staying put — the fan has to turn inside a window
   that doesn't.

   THE MOIRE. A conic gradient with a 1.1deg period is finer than the pixel grid once
   the spokes are more than a few hundred px from the origin, so the rasteriser beats
   against itself and produces those interference bands. The period is now ~3x wider
   (3.4deg of ink in an 11deg cycle), which puts the spokes comfortably above the
   sampling limit, and the blur is up from 0.4px to 1.4px so what aliasing remains is
   averaged away rather than sharpened.

   A conic gradient radiates through the full 360deg, so the old one fanned DOWNWARD
   out of the same origin as well — that was the second band at the bottom of the hero.
   The frame now stops at the top band, so there is nowhere for the lower half to draw. */
.tx-rays{
  position:absolute; inset:0 0 auto 0; height:56%;
  pointer-events:none; overflow:hidden;
  -webkit-mask-image:radial-gradient(78% 88% at 50% 0%, #000 6%, transparent 78%);
          mask-image:radial-gradient(78% 88% at 50% 0%, #000 6%, transparent 78%);
  mix-blend-mode:screen; opacity:.30; z-index:0;
}
.tx-rays-spin{
  position:absolute;
  /* Oversized and centred on the origin so the corners stay covered as it turns. */
  left:50%; top:0; width:260%; aspect-ratio:1; transform-origin:50% 0;
  translate:-50% 0;
  background:repeating-conic-gradient(from -12deg at 50% 0%,
    transparent 0deg,
    color-mix(in oklab, white 40%, transparent) 3.4deg,
    transparent 11deg);
  filter:blur(1.4px);
  animation:tx-ray-turn 150s linear infinite;
}
@keyframes tx-ray-turn{ to{ rotate:360deg; } }
.tx-gloss{
  position:absolute; inset:0; pointer-events:none; border-radius:inherit;
  background:linear-gradient(150deg, color-mix(in oklab, white 13%, transparent) 0%, transparent 36%);
  mix-blend-mode:screen; opacity:.55; z-index:1;
}
.tx-vignette{
  position:absolute; inset:0; pointer-events:none; border-radius:inherit;
  background:radial-gradient(120% 120% at 50% 26%, transparent 54%, rgba(0,0,0,.42) 100%);
  mix-blend-mode:multiply; z-index:1;
}
@media (prefers-color-scheme: light){
  /* Screen-blended light does nothing on white: the atmosphere has to be built from
     tinted shadow instead, at much lower strength or it turns into smog. */
  .tx-bloom{ mix-blend-mode:normal; opacity:calc(var(--tx-bloom) * 0.9);
    background:radial-gradient(46% 42% at 50% 47%, color-mix(in oklab, var(--tx-hue) 26%, transparent) 0%, transparent 70%); }
  .tx-pool{ mix-blend-mode:normal; opacity:.28;
    background:radial-gradient(72% 100% at 50% 118%, color-mix(in oklab, var(--tx-hue) 22%, transparent) 0%, transparent 72%); }
  .tx-rays{ mix-blend-mode:multiply; opacity:.12; }
  .tx-rays-spin{ background:repeating-conic-gradient(from -12deg at 50% 0%,
      transparent 0deg, color-mix(in oklab, var(--tx-hue) 34%, transparent) 3.4deg, transparent 11deg); }
  .tx-gloss{ mix-blend-mode:normal; opacity:.5;
    background:linear-gradient(150deg, rgba(255,255,255,.85) 0%, transparent 40%); }
  .tx-vignette{ mix-blend-mode:multiply; opacity:.5;
    background:radial-gradient(120% 120% at 50% 26%, transparent 58%, rgba(20,24,33,.10) 100%); }
}

/* Anything carrying texture layers needs a stacking context of its own, or the
   screen-blended layers composite against the PAGE instead of the card and wash out. */
.tx, .tx-host{ position:relative; isolation:isolate; }
.tx > *:not([class^="tx-"]){ position:relative; z-index:3; }

@media (prefers-reduced-motion: reduce){
  .tx-rays-spin{ animation:none; }
}
@media (prefers-reduced-transparency: reduce){
  .tx-grain, .tx::after, .tx-rays, .tx-bloom, .tx-pool{ display:none; }
}
