/* ═══════════════════════════════════════════════════════════════════════════
   FPFX — AURORA DESIGN SYSTEM
   Approved 2026-08-25 from _design/dashboard-preview.html.

   This is the system layer: tokens, primitives and the two signature
   interactions. It sits ABOVE fpfx-theme.css, which remains the compatibility
   shim remapping the inherited stylesheets' tokens. Nothing here depends on
   that file — a page can load this alone.

   THE ONE RULE THAT MATTERS
   -------------------------
   The aurora has two intensities and they are not interchangeable:

     --aurora-strength: 1      marketing, auth, empty states, certificates
     --aurora-strength: .38    any screen carrying tabular figures

   Full strength puts moving light under small numbers, which is exhausting on
   a page someone opens several times a day. Set the calm value on <body> for
   every portal screen. That single variable is what lets one system serve both
   a landing page and a trading dashboard without becoming two designs.

   Requires: fpfx-aurora.js for the cursor-driven effects. Without it the
   components still render correctly, just without the sheen and edge light —
   they degrade to a plain hover, which is the intended fallback.
   ═══════════════════════════════════════════════════════════════════════════ */

@property --edge { syntax: '<angle>'; initial-value: 0deg; inherits: false; }

:root{
  /* ── ground and glass ────────────────────────────────────────────────────
     Glass is DARK-translucent, not white. White glass over a drifting aurora
     brightens as the light passes and small text disappears — that was a real
     legibility failure in the first pass, not a theoretical one.

     --au-bg WAS a near-black measuring hue 260 at 27% saturation — it was
     a VIOLET ground under a gold brand, and it is why the shadows on every
     Aurora surface read cool however warm the overlays were. Measured across
     one homepage viewport before this pass: lit areas hue 29, shadows hue
     255-270. The ground is now hue 30 at every rung, so no compositing path
     through this system can produce a cool pixel. That is a structural
     guarantee, not a colour preference. */
  --au-void:      #050403;   /* h30 — vignette terminus. Nothing is darker. */
  --au-bg:        #0A0806;   /* h30 — the ground */
  --au-bg-2:      #100C08;   /* h30 — section band / ground highlight */
  --au-bg-3:      #070504;   /* h20 — ground falloff, between bg and void */
  --au-glass:     rgba(13,11,10,.58);
  --au-glass-2:   rgba(18,15,13,.70);
  --au-line:      rgba(255,255,255,.10);
  --au-line-2:    rgba(255,255,255,.06);
  --au-line-hi:   rgba(243,196,106,.42);

  /* ── type colours ────────────────────────────────────────────────────────
     THE RATIOS BELOW ARE WORST CASES, NOT BEST CASES, AND THAT IS THE POINT.

     The old comment on --au-dim read "6.4:1". It was wrong twice over: the
     measured value of #948C82 on the old --au-bg is 6.024:1, not 6.4, and
     --au-bg was never the backdrop that mattered. Text lands on whatever the
     atmosphere has painted. With the aurora at its old .42 opacity the
     rendered ground measured #4B3119, and #948C82 on that is 3.61:1 — a
     WCAG AA failure that was live on every Aurora surface. It is the same
     failure as the rejected #635E58, reached by a different route: the floor
     passed against the token and failed against the pixel.

     So the atmosphere now has a computed ceiling (see .au-aurora) and every
     value here is proven against the WORST backdrop it can land on — all six
     ladder rungs, the ceiling itself, and --au-glass resolved over the
     ceiling (#18130D), which is the true worst case for text on a panel that
     happens to be sitting over a lit band.

     --au-dim is the FLOOR: never introduce a dimmer value, never use it
     above 11px. */
  --au-text:      #F5F3F0;   /* 18.05:1 on --au-bg · 14.09:1 worst case */
  --au-muted:     #BCB5AB;   /*  9.84:1 on --au-bg ·  7.68:1 worst case */
  --au-dim:       #AEA8A1;   /*  8.49:1 on --au-bg ·  6.63:1 worst — THE FLOOR */

  /* ── gold, warm only ─────────────────────────────────────────────────────
     There is no cool value in this system except --au-up on a winning trade.
     Do not add blue, violet or teal accents. */
  --au-gold:      #F3C46A;
  --au-gold-hi:   #FFF0CE;
  --au-gold-lo:   #B4823A;
  --au-amber:     #E0913C;
  --au-copper:    #9A5A2E;
  --au-grad:      linear-gradient(100deg,#FFF0CE,#F3C46A 46%,#C08C2E);
  --au-glow:      rgba(243,196,106,.32);

  /* ── semantic ── */
  --au-up:        #5BE49B;
  --au-down:      #FF8A73;

  /* ── type ── */
  --au-disp: "Outfit", system-ui, -apple-system, sans-serif;
  --au-body: "DM Sans", system-ui, -apple-system, sans-serif;
  --au-mono: "JetBrains Mono", ui-monospace, SFMono-Regular, monospace;

  /* ── geometry and motion ─────────────────────────────────────────────────
     ONE easing curve across the entire system. Consistency of easing is most
     of what makes an interface feel authored rather than assembled. */
  --au-rad:    18px;
  --au-rad-sm: 12px;
  --au-pill:   100px;
  --au-ease:   cubic-bezier(.2,.8,.3,1);
  --au-fast:   .18s;
  --au-base:   .35s;
  --au-slow:   .55s;

  --aurora-strength: 1;
}

/* ── host ────────────────────────────────────────────────────────────────
   Retrofitting a fixed background into pages that were not built for one has a
   trap: a positioned element at z-index 0 paints ABOVE static block boxes, so
   the aurora would cover the page rather than sit behind it. Lifting every
   direct child of the host onto its own layer fixes it in one rule, without
   touching 82 templates individually.

   Put `class="au-host au-grain"` on <body> and make the two background divs its
   first children. */
.au-host{
  position:relative;
  /* THE PAGE GROUND, AND THE DITHER THAT MAKES IT SHIP-ABLE.

     Four stops, hue 30 at every one of them, so the ramp never crosses a
     neutral on its way down. Worth being precise about why that matters,
     because the usual explanation is wrong: CSS gradients interpolate in
     PREMULTIPLIED ALPHA per css-images-4, so `gradient(gold, transparent)`
     does not desaturate through grey in any current engine. The mud on this
     platform never came from interpolation. It came from stacking four warm
     radials over an opaque base measuring hue 255 at 89% saturation — which
     gave hue 29 where the overlays were thick and hue 255-270 wherever they
     thinned. Warm veneer, violet base, cool shadows. Every stop in this
     stack is now hue 20-30, so no compositing path can produce a cool pixel.

     THE SECOND BACKGROUND LAYER IS THE DITHER.
     Measured, taking a visible band as quantisation edges more than 3px apart:

         OLD  aurora blob @ .42     98 steps / 1000px -> an edge every  10.2px
         OLD  body::before radial   72 steps / 1200px -> an edge every  16.7px
         OLD  scrim vignette         6 steps / 1000px -> an edge every 166.7px
         NEW  strata band @ .12     28 steps / 1100px -> an edge every  39.3px
         NEW  vignette to void       5 steps / 1000px -> an edge every 200.0px

     The new ramps band 3.9x less often AND THEY STILL BAND. So would any
     other stop count: a shallow dark ramp bands by geometry, because 8-bit
     sRGB does not have enough codes between #0A0806 and #261F12. Dither is
     the only thing that removes it.

     WHICH LAYER ACTUALLY DOES THE WORK — measured, and NOT what an earlier
     version of this comment asserted. Isolated ground, no content, 1280x900,
     longest run of one identical 8-bit value down three columns:

         this overlay dither + .au-grain::after     7px /  9px /  6px
         this overlay dither ALONE, grain removed  20px / 33px / 26px

     So .au-grain::after is the layer that breaks the plateaus, and this one
     is a contributor rather than the whole fix. The claim that used to stand
     here — that a single octave is "the only thing" that dithers and that
     .au-grain's three octaves "dither nothing" — is false, and it is the kind
     of false claim that gets the working layer weakened by the next person
     reading it. Both layers ship. Do not lower .au-grain::after's opacity
     without re-running the A/B above; at .022 it is already the quiet end of
     what still clears the plateaus, and it sits ABOVE the content, so raising
     it grains 10-11px type.

     background-blend-mode:overlay, not source-over. Source-over can only
     ADD light, so it lifts the ground as much as it dithers it. Measured
     over #0A0806, for the same 3.4 LSB of dither amplitude:

         source-over @ .022   mean lift 1.95 LSB   ground luminance x1.24
         overlay     @ .5     mean lift 1.16 LSB   ground luminance x1.12

     Overlay is bidirectional and, on a dark backdrop, scales with it — so it
     dithers a bright band peak harder than it dithers the void. Hue measured
     at 30.0 after blending, unchanged.

     The alpha is halved inside the SVG (opacity='.5' on the rect) rather than
     with a wrapper element, because background-blend-mode weights by the
     source layer's own alpha and this keeps it to one paint.

     It lives HERE, on the body background, rather than on a fixed overlay
     div, for a reason that is easy to get wrong: overlay needs an opaque
     backdrop to blend against, and a fixed div at z-index:-1 creates its own
     stacking context, so its blend would only ever reach its own background
     and not the ground. Everything above this — five strata at alpha <=.12
     and a vignette — is translucent, so the dither propagates up through the
     whole stack from the bottom. One layer, dithering all of it.

     background-attachment:fixed is load-bearing on the gradient. Without it
     the gradient box is the whole document — over 12,000px on the homepage —
     and the radial smears into a flat wash. Fixed sizes it to the viewport,
     which is the only place a ground gradient means anything. --au-void
     underneath guarantees the darkest possible pixel before the vignette. */
  background-color:var(--au-void);
  background-image:
    url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='180' height='180'%3E%3Cfilter id='d'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='.9' numOctaves='1' stitchTiles='stitch'/%3E%3C/filter%3E%3Crect width='100%25' height='100%25' filter='url(%23d)' opacity='.5'/%3E%3C/svg%3E"),
    radial-gradient(150% 105% at 50% -12%,
      var(--au-bg-2) 0%, var(--au-bg) 42%, var(--au-bg-3) 74%, var(--au-void) 100%);
  background-size:180px 180px, cover;
  background-repeat:repeat, no-repeat;
  background-attachment:fixed, fixed;
  background-blend-mode:overlay, normal;
}

/* The backdrop sits on its own negative layer instead of lifting every sibling.
   The earlier version of this rule set `position:relative` on EVERY direct child
   of the host, which silently overrode `position:fixed` on the mobile nav sheet,
   the nebulae, the grain, the cursor glow, the preloader, the auth field and the
   reading-progress bar — they all dropped into the flow and pushed the page
   down. Keep the aurora behind the content by lowering the aurora, never by
   raising the content. */
.au-host > .au-aurora,
.au-host > .au-scrim{ z-index:-1; }

/* ── the atmosphere: LIQUIDITY STRATA ─────────────────────────────────────
   WHAT THIS REPLACED, AND WHY IT IS A DIFFERENT IDEA RATHER THAN A REPAINT.

   Until this pass the ground of the marketing site was the upstream release's
   star field — a <canvas> painter that was byte-identical to its origin across
   121 lines, whose tint table was three-fifths cool (hue 260 violet, hue 313
   magenta, hue 218 blue). Measured on one homepage viewport at 1280x900:
   31,186 lit canvas pixels, of which 27,553 were cool — 88.35%, with the
   single largest bucket at hue 270. A warm gold brand was painted on top of a
   competitor's violet canvas, which is why the page read as a clone AND why it
   did not read as premium. One cause, both complaints.

   Recolouring that canvas would have left the same object doing the same
   thing. So the object changed:

       points  -> bands          scattered      -> stratified
       omni    -> horizontal     discrete       -> continuous
       canvas  -> compositor     JS every frame -> zero JS

   Horizontality is the visual grammar of a market — depth of book, the tape.
   A star field says outer space. Nothing here scatters, twinkles or streaks.

   THREE PROPERTIES THAT MADE THIS THE RIGHT CALL

   1. At rest it is already finished. The static composition IS the design and
      the drift is a two-to-four-minute modulation on top of it. There is no
      state where the page waits for an animation to become interesting — which
      is also why prefers-reduced-motion and a hidden-tab first paint are
      solved structurally rather than patched. The 0x0-canvas bug cannot recur,
      because there is no canvas: this paints on the first style pass with no
      script involved at all.

   2. It survives --aurora-strength:.38 under figures better than blobs do.
      A drifting radial sweeps a luminance GRADIENT ACROSS a table, so a
      figure's backdrop changes while the eye is on it. A horizontal band
      drifting horizontally under a horizontal table row changes that row's
      backdrop UNIFORMLY — no gradient ever crosses a column of numbers.

   3. It costs less than what it replaced. The star field ran 5.1-8.8ms of
      main-thread work per frame with ~23 createRadialGradient allocations per
      frame and was gated by nothing — not visibility, not intersection. This
      is five composited layers animating transform only: zero main-thread
      cost per frame, zero JavaScript, zero allocations.

   THE CEILING — the rule that stops the inversion recurring.

   The whole failure was that the ground exceeded the panels. The old aurora
   ran at opacity calc(.42 * strength); the approved reference
   (_design/dashboard-preview.html) specifies .16. At marketing strength the
   shipped value was .42 — 2.6x the approved luminance — and it made the
   rendered ground 7.22x BRIGHTER than a resting panel. Panels stopped reading
   as surfaces lifted off a ground and started reading as holes punched in a
   lit field. That inversion is the "not premium".

   So the atmosphere now has a hard, computed ceiling. The brightest pixel any
   band can produce is the .12 stop of the shared gradient below, and the
   per-band opacities are set so the composite of overlapping bands stays at or
   under it:

       ceiling = #F3C46A at effective alpha .12 over --au-bg = #261F12, L=.01436
       old rendered ground #4B3119  L=.03763  =  7.22x a resting panel
       new ceiling         #261F12  L=.01436  =  2.38x a resting panel

   Every type token in this file is proven against that ceiling. Raise the .12
   and you invalidate the contrast matrix — it is a budget, not a taste knob.
   --aurora-strength scales it linearly and is still the one variable that
   turns a landing page into a dashboard. */
.au-aurora{
  position:fixed; inset:0; z-index:0; pointer-events:none;
  overflow:hidden;
  opacity:var(--aurora-strength);
}

/* Five bands: ::before is the top stratum, the three <i> are the middle, and
   ::after is the floor. No markup changes on any of the nine surfaces that
   already carry `<div class="au-aurora"><i></i><i></i><i></i></div>`.

   R9 — THE BLUR GOES ON EACH BAND, NEVER ON THE CONTAINER. The old rule put
   filter:blur(86px) on .au-aurora and animated the children inside it. A
   filter makes its subtree rasterise as ONE layer, so animating anything
   within it forces the blurred result to be re-rasterised every frame, on the
   main thread — the single most expensive way to move a blurred shape. With
   the blur on each band, each band is a self-contained blurred layer whose
   transform the compositor moves without repainting anything. */
.au-aurora::before,
.au-aurora::after,
.au-aurora i{
  content:""; position:absolute; display:block;
  left:-34%; width:168%;
  /* One shared gradient, hue carried THROUGH the midpoint rather than allowed
     to desaturate: gold h39 -> amber h31 -> copper h24, alpha falling to zero
     at both ends. Per-band brightness is the element's own opacity, which
     keeps the ceiling arithmetic to one number. */
  background:linear-gradient(90deg,
    rgba(243,196,106,0)    0%,
    rgba(243,196,106,.12) 34%,
    rgba(224,145,60,.095) 58%,
    rgba(154,90,46,.055)  82%,
    rgba(154,90,46,0)    100%);
  /* Vertical falloff. The blur alone leaves a plateau in the middle of a tall
     band and a defined edge at top and bottom; the mask turns each band into a
     stratum with soft boundaries, and — the part that matters numerically —
     cuts the tails that overlap the neighbouring band, which is what keeps the
     composite under the ceiling. */
  -webkit-mask-image:linear-gradient(180deg,transparent 0%,#000 36%,#000 64%,transparent 100%);
          mask-image:linear-gradient(180deg,transparent 0%,#000 36%,#000 64%,transparent 100%);
  filter:blur(78px);
  will-change:transform;
  transform:translate3d(0,0,0);
  animation-name:au-strata;
  animation-timing-function:var(--au-ease);
  animation-iteration-count:infinite;
  animation-direction:alternate;
}
/* Drift amplitude scales off the ONE variable, so the calm intensity is calmer
   in motion as well as in luminance. The keyframes are symmetric about zero,
   which is what makes translate3d(0,0,0) — the resting transform above — the
   exact mid-frame of the animation. That is why reduced motion below can stop
   the animation and be left with a correct composed frame rather than a
   frozen half-drawn one. */
@keyframes au-strata{
  from{ transform:translate3d(calc(-1 * var(--au-amp)),0,0) }
  to  { transform:translate3d(var(--au-amp),0,0) }
}
.au-aurora::before   { top:-8%;    height:32%; opacity:1;   --au-amp:calc(7%   * var(--aurora-strength)); animation-duration:110s }
.au-aurora i:nth-child(1){ top:19%; height:26%; opacity:.70; --au-amp:calc(-6%  * var(--aurora-strength)); animation-duration:140s }
.au-aurora i:nth-child(2){ top:41%; height:28%; opacity:.55; --au-amp:calc(5.5% * var(--aurora-strength)); animation-duration:165s }
.au-aurora i:nth-child(3){ top:63%; height:26%; opacity:.40; --au-amp:calc(-4.5%* var(--aurora-strength)); animation-duration:190s }
.au-aurora::after    { bottom:-8%; height:32%; opacity:.28; --au-amp:calc(4%   * var(--aurora-strength)); animation-duration:230s }

/* Vignette. Corners are where text washed out worst, so they stay dark.
   FOUR STOPS, NOT TWO. The old two-stop ramp produced six quantisation steps
   across 1000px — a visible contour plateau every 167px, which is the single
   most obvious "cheap dark UI" artefact there is. Four stops cut that to a
   step every 200px on a much shorter visible run; the dither below is what
   actually removes it. rgba(5,4,3) is --au-void: the vignette terminates on
   the darkest rung of the ladder, at hue 30. It used to terminate on
   rgba(9,8,11) — hue 260, violet — which is how a warm page ended up with
   cool corners. */
.au-scrim{
  position:fixed; inset:0; z-index:0; pointer-events:none;
  background:radial-gradient(132% 96% at 50% 34%,
    rgba(5,4,3,0) 26%, rgba(5,4,3,.42) 62%, rgba(5,4,3,.72) 84%, rgba(5,4,3,.90) 100%);
}

/* Grain, above the content, for filmic quality only. It does NOT dither —
   numOctaves='3' is correlated low-frequency noise, which is texture. The
   dither is the single-octave layer on .au-host. Conflating the two is why
   this platform was running two full-viewport grain layers and still banding.
   .038 -> .022. At .038 this layer was measurably graining 10-11px type — it
   sits at z-index:98, ABOVE everything, so it lands on the smallest text on
   the page, which is exactly the text with the least contrast to spare. The
   ground now has its own dither underneath, so this layer no longer has to
   carry a job it was never the right texture for. */
.au-grain::after{
  content:""; position:fixed; inset:0; pointer-events:none; z-index:98; opacity:.022;
  background-image:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='200' height='200'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='.85' numOctaves='3'/%3E%3C/filter%3E%3Crect width='100%25' height='100%25' filter='url(%23n)'/%3E%3C/svg%3E");
}

/* ── panel ───────────────────────────────────────────────────────────────── */
.au-panel{
  position:relative; isolation:isolate; overflow:hidden;
  border-radius:var(--au-rad); padding:20px;
  background:var(--au-glass); border:1px solid var(--au-line);
  backdrop-filter:blur(22px) saturate(150%);
  -webkit-backdrop-filter:blur(22px) saturate(150%);
  transition:transform var(--au-base) var(--au-ease), background var(--au-base);
}
/* Specular sheen tracking the cursor across the glass. */
.au-panel::before{
  content:""; position:absolute; inset:0; z-index:-1; pointer-events:none; opacity:0;
  background:radial-gradient(320px circle at var(--mx,50%) var(--my,50%),
    rgba(255,240,206,.12), rgba(243,196,106,.04) 42%, transparent 68%);
  transition:opacity var(--au-base);
}
/* The edge lights on the side the cursor is on. A conic gradient rotated to
   face the pointer, masked to 1px — not a uniform border glow. */
.au-panel::after{
  content:""; position:absolute; inset:-1px; border-radius:inherit; padding:1px;
  pointer-events:none; opacity:0; transition:opacity var(--au-base);
  background:conic-gradient(from var(--edge,0deg), transparent 0deg,
    var(--au-line-hi) 26deg, rgba(255,240,206,.85) 46deg,
    var(--au-line-hi) 66deg, transparent 96deg);
  -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;
}
.au-panel:hover{ transform:translateY(-2px); background:var(--au-glass-2); }
.au-panel:hover::before,.au-panel:hover::after{ opacity:1; }

/* ── buttons ─────────────────────────────────────────────────────────────── */
.au-btn{
  --fx:50%; --fy:50%; --lift:0px;
  appearance:none; border:0; cursor:pointer;
  position:relative; overflow:hidden; isolation:isolate;
  display:inline-flex; align-items:center;
  font-family:var(--au-body); font-weight:600; font-size:14px; letter-spacing:-.01em;
  padding:13px 26px; border-radius:var(--au-pill); color:#150F05;
  background:linear-gradient(180deg,#FFE9BC,#F3C46A 42%,#D09E3F);
  box-shadow:inset 0 1px 0 rgba(255,255,255,.82),
             inset 0 -1px 0 rgba(120,80,10,.32),
             0 5px 16px -8px var(--au-glow);
  transform:translateY(var(--lift));
  transition:transform var(--au-base) var(--au-ease), box-shadow var(--au-base),
             color .3s .05s;
}
/* The fill grows from the point the cursor crossed the edge, so entering from
   the left fills left-to-right. fpfx-aurora.js sets --fx/--fy. */
.au-btn::before{
  content:""; position:absolute; left:var(--fx); top:var(--fy); z-index:-1;
  width:0; height:0; border-radius:50%; transform:translate(-50%,-50%);
  background:radial-gradient(circle,#FFFBF0,#FFE9BC 55%,#F3C46A);
  transition:width var(--au-slow) var(--au-ease), height var(--au-slow) var(--au-ease);
}
.au-btn:hover::before{ width:260%; height:260%; }
.au-btn:hover{ --lift:-2px;
  box-shadow:inset 0 1px 0 rgba(255,255,255,.9),
             inset 0 -1px 0 rgba(120,80,10,.32),
             0 16px 40px -12px var(--au-glow); }
.au-btn:active{ --lift:0px; }
.au-btn:focus-visible{ outline:2px solid var(--au-gold); outline-offset:3px; }
/* The label must sit above the fill. */
.au-btn__lb{ position:relative; z-index:1; display:inline-flex; align-items:center; gap:9px; }
.au-btn__arw{ transition:transform .4s var(--au-ease); }
.au-btn:hover .au-btn__arw{ transform:translateX(5px); }

.au-btn--ghost{
  background:rgba(255,255,255,.05); color:var(--au-gold-hi);
  border:1px solid var(--au-line);
  box-shadow:inset 0 1px 0 rgba(255,255,255,.09);
  backdrop-filter:blur(18px); -webkit-backdrop-filter:blur(18px);
}
.au-btn--ghost::before{ background:radial-gradient(circle,#FFF6E2,#F3C46A 60%,#D9A548); }
.au-btn--ghost:hover{ color:#150F05; }

.au-btn--text{
  background:none; color:var(--au-gold-hi); padding:10px 4px;
  box-shadow:none; border:0; border-radius:0;
}
.au-btn--text::before{ display:none; }
.au-btn--text::after{
  content:""; position:absolute; left:4px; right:4px; bottom:4px; height:1px;
  background:var(--au-grad); transform:scaleX(0); transform-origin:left;
  transition:transform .45s var(--au-ease);
}
.au-btn--text:hover::after{ transform:scaleX(1); }

/* ── figures ─────────────────────────────────────────────────────────────── */
.au-stat__k{ font-family:var(--au-mono); font-size:10.5px; letter-spacing:.16em;
  text-transform:uppercase; color:var(--au-muted); }
.au-stat__v{ display:block; margin-top:11px; font-family:var(--au-mono);
  font-weight:700; font-size:30px; line-height:1; font-variant-numeric:tabular-nums;
  background:var(--au-grad); -webkit-background-clip:text; background-clip:text;
  color:transparent; }
.au-stat__d{ display:flex; align-items:center; gap:6px; margin-top:9px;
  font-family:var(--au-mono); font-size:11.5px; color:var(--au-muted); }
.au-stat__d--up{ color:var(--au-up); }
.au-stat__d--dn{ color:var(--au-down); }

/* ── progress, with meaning in the colour ────────────────────────────────── */
.au-track{ height:7px; border-radius:var(--au-pill);
  background:rgba(255,255,255,.07); overflow:hidden; }
.au-fill{ height:100%; border-radius:var(--au-pill); position:relative;
  background:var(--au-grad); transform-origin:left; transform:scaleX(var(--p,0));
  animation:au-fill 1.3s var(--au-ease) forwards; }
.au-fill::after{ content:""; position:absolute; right:0; top:0; bottom:0; width:14px;
  border-radius:var(--au-pill);
  background:linear-gradient(90deg,transparent,var(--au-gold-hi)); }
/* Gold = progress toward a goal. Green = a limit with room. Red = a limit
   being consumed. The colour is the message; the label is confirmation. */
.au-fill--ok{ background:linear-gradient(100deg,#9BF0C4,#5BE49B 60%,#2FA771); }
.au-fill--ok::after{ background:linear-gradient(90deg,transparent,#CFFCE4); }
.au-fill--risk{ background:linear-gradient(100deg,#FFC9BC,#FF8A73 60%,#C4503B); }
.au-fill--risk::after{ background:linear-gradient(90deg,transparent,#FFD9CF); }
@keyframes au-fill{ from{ transform:scaleX(0) } to{ transform:scaleX(var(--p,1)) } }

/* ── chart crosshair ─────────────────────────────────────────────────────── */
.au-chart{ position:relative; touch-action:none; }
.au-chart__cross{ stroke:rgba(255,240,206,.34); stroke-width:1; stroke-dasharray:3 4;
  opacity:0; transition:opacity var(--au-fast); }
.au-chart__halo{ fill:rgba(243,196,106,.2); opacity:0; transition:opacity var(--au-fast); }
.au-chart__dot{ fill:#FFF0CE; stroke:var(--au-bg); stroke-width:2.5; opacity:0;
  transition:opacity var(--au-fast); }
.au-chart.is-live .au-chart__cross,
.au-chart.is-live .au-chart__halo,
.au-chart.is-live .au-chart__dot{ opacity:1; }
.au-tip{
  position:absolute; pointer-events:none; z-index:5; opacity:0; white-space:nowrap;
  transform:translate(-50%,-124%) scale(.96); transform-origin:50% 100%;
  transition:opacity var(--au-fast), transform var(--au-fast) var(--au-ease);
  background:rgba(14,12,10,.94); border:1px solid var(--au-line);
  border-radius:10px; padding:9px 12px; backdrop-filter:blur(14px);
  box-shadow:0 14px 34px -14px rgba(0,0,0,.9);
}
.au-chart.is-live .au-tip{ opacity:1; transform:translate(-50%,-134%) scale(1); }

/* ── reduced motion ────────────────────────────────────────────────────────
   A CORRECT STATIC FRAME, NOT A FROZEN ONE.

   The old rule stopped the blob animations and left whatever transform the
   element happened to hold. It was survivable there and it was NOT survivable
   on the homepage, where the ground was a canvas: fpfx.js had no else branch
   for reduced motion and draw() was the only thing that ever painted, so a
   visitor with the preference set got no background at all.

   Here the resting state is the design. The strata keyframes run symmetrically
   from calc(-1 * --au-amp) to --au-amp, so translate3d(0,0,0) — the transform
   already declared on every band — is the exact mid-frame. Cancelling the
   animation lands on a composed frame by construction rather than by luck, and
   the same property is what makes the first paint correct in a hidden or
   backgrounded tab: there is no script, no rAF and no canvas in this
   background, so the first style pass paints the finished composition whether
   or not a frame is ever scheduled. */
@media (prefers-reduced-motion:reduce){
  .au-aurora::before,
  .au-aurora::after,
  .au-aurora i{ animation:none; transform:translate3d(0,0,0); will-change:auto; }
  .au-panel,.au-btn,.au-btn__arw,.au-btn--text::after{ transition-duration:.01ms; }
  .au-fill{ animation:none; transform:scaleX(var(--p,1)); }
}
