/* ==========================================================================
   NORVA — fluid-era design layer
   Loaded after app.css. Everything here is scoped to `#fluid` or to
   `body.has-fluid`, a class added only once the simulation reports that it
   actually runs. Remove this file and the site reverts to the flat dark theme
   with no dangling references.

   The governing constraint is readability. The fluid is not wallpaper — it is a
   live, high-chroma, moving surface that at any moment can put near-white teal
   directly behind body copy. So every text-bearing surface gets its own scrim,
   and the fluid is only ever allowed to be the loudest thing where there is
   nothing to read.
   ========================================================================== */

/* ------------------------------------------------------------ the ground --- */

/* Fixed, so viewport coordinates ARE canvas coordinates and the splash never
   compensates for scroll. Behind everything, and never a pointer target —
   drag is read from window instead. */
#fluid {
  position: fixed;
  inset: 0;
  z-index: -2;
  display: block;
  width: 100%;
  height: 100%;
  pointer-events: none;
  /* Visible before the first frame, and permanently if WebGL is unavailable. */
  background:
    radial-gradient(38% 44% at 18% 22%, color-mix(in oklab, var(--brass) 24%, transparent), transparent 70%),
    radial-gradient(32% 38% at 82% 16%, color-mix(in oklab, var(--indigo) 16%, transparent), transparent 70%),
    var(--coal);
}

/* The canvas paints its own ground in both themes, so the element's fallback
   gradient only shows before the first frame or when WebGL is unavailable. */
:root[data-theme='light'] #fluid {
  background:
    radial-gradient(38% 44% at 18% 22%, color-mix(in oklab, var(--brass-700) 16%, transparent), transparent 70%),
    radial-gradient(32% 38% at 82% 16%, color-mix(in oklab, var(--indigo-700) 12%, transparent), transparent 70%),
    var(--chalk-50);
}

body.has-fluid { background: transparent; }

/* The CSS aurora and grain were standing in for exactly this. */
body.has-fluid .aurora,
body.has-fluid .grain { display: none !important; }

/* ---------------------------------------------------------------- chrome --- */

body.has-fluid .marquee {
  background: color-mix(in oklab, var(--bg) 74%, transparent);
  -webkit-backdrop-filter: blur(14px) saturate(1.3);
          backdrop-filter: blur(14px) saturate(1.3);
}

body.has-fluid .nav {
  background: color-mix(in oklab, var(--bg) 56%, transparent);
  -webkit-backdrop-filter: blur(22px) saturate(1.5);
          backdrop-filter: blur(22px) saturate(1.5);
}
body.has-fluid .nav.is-stuck {
  background: color-mix(in oklab, var(--bg) 76%, transparent);
}

body.has-fluid .foot {
  background: color-mix(in oklab, var(--bg) 78%, transparent);
  -webkit-backdrop-filter: blur(18px);
          backdrop-filter: blur(18px);
  border-top-color: color-mix(in oklab, var(--accent) 16%, var(--line));
}

/* ----------------------------------------------------------------- hero --- */

/* No local glow — the fluid is the light source now. Competing radial
   gradients behind the device just muddy it. */
body.has-fluid .hero .stage__glow { opacity: 0.35; }

/* A vertical scrim under the hero copy only. The fluid can be brilliant behind
   the headline, and 820-weight Georgian at 5rem over moving teal is unreadable
   without it. Sized to the text column, not the viewport, so the right-hand
   side of the hero stays fully open. */
body.has-fluid .hero__grid > div:first-child {
  position: relative;
  isolation: isolate;
}
body.has-fluid .hero__grid > div:first-child::before {
  content: '';
  position: absolute;
  inset: -8% 0 -8% -50%;
  z-index: -1;
  pointer-events: none;
  background: radial-gradient(84% 70% at 30% 45%,
              color-mix(in oklab, var(--bg) 88%, transparent) 0%,
              color-mix(in oklab, var(--bg) 62%, transparent) 48%,
              transparent 80%);
}

body.has-fluid .hero__eyebrow {
  background: color-mix(in oklab, var(--bg) 70%, transparent);
  -webkit-backdrop-filter: blur(10px);
          backdrop-filter: blur(10px);
  border-color: color-mix(in oklab, var(--accent) 26%, var(--line-strong));
}

body.has-fluid .hero__stats { border-top-color: color-mix(in oklab, var(--accent) 20%, var(--line)); }

/* ---------------------------------------------------------------- glass --- */

/* Cards become glass so the ground reads through them, but the tint is heavy
   enough (82%) that a bright plume behind a card never reaches the price. This
   is the number to change if text ever becomes hard to read — not the fluid's
   brightness, which is what makes the page. */
body.has-fluid .card,
body.has-fluid .svc,
body.has-fluid .cat,
body.has-fluid .calc,
body.has-fluid .trust__item,
body.has-fluid .pdp__stage,
body.has-fluid .search-pop,
body.has-fluid .toast {
  background: color-mix(in oklab, var(--surface) 84%, transparent);
  -webkit-backdrop-filter: blur(16px) saturate(1.25);
          backdrop-filter: blur(16px) saturate(1.25);
  border-color: color-mix(in oklab, var(--accent) 12%, var(--line));
}

body.has-fluid .card:hover,
body.has-fluid .svc:hover,
body.has-fluid .cat:hover {
  border-color: color-mix(in oklab, var(--accent) 42%, var(--line-strong));
  box-shadow: 0 32px 80px -28px rgb(0 0 0 / .8),
              0 0 0 1px color-mix(in oklab, var(--accent) 18%, transparent);
}

/* The media well inside a card should read as a window onto the ground. */
body.has-fluid .card__media {
  background: linear-gradient(160deg,
              color-mix(in oklab, var(--bg) 46%, transparent),
              color-mix(in oklab, var(--bg-raised) 62%, transparent));
}

/* Artwork wells stay opaque over the fluid so a plume passing underneath does
   not wash through the product. The artwork is transparent SVG with its own
   ambient light, so this is a flat plate, not the light tile a photo needed. */
body.has-fluid .card__media--photo,
body.has-fluid .row__media,
body.has-fluid .pdp__stage--photo {
  background: var(--bg-raised);
  -webkit-backdrop-filter: none;
          backdrop-filter: none;
  border-color: var(--line);
}

/* Drawers sit above the fluid and must be fully legible — no transparency
   games where someone is reading a total. */
body.has-fluid dialog.drawer {
  background: color-mix(in oklab, var(--bg-raised) 96%, transparent);
  -webkit-backdrop-filter: blur(24px);
          backdrop-filter: blur(24px);
}

/* ------------------------------------------------------- pinned story --- */

/* The story panels float directly on the fluid with no card behind them, so
   they carry their own scrim. */
body.has-fluid .track__copy > .panel {
  padding: var(--sp-5);
  margin: calc(var(--sp-5) * -1);
  border-radius: var(--r-4);
  background: radial-gradient(88% 88% at 20% 40%,
              color-mix(in oklab, var(--bg) 84%, transparent),
              transparent 82%);
}

/* ---------------------------------------------------- text over the ground --- */

/* Anything that sits straight on the fluid with no card behind it needs its own
   scrim, or a bright plume passing underneath makes the secondary text
   (breadcrumbs, section ledes, eyebrows) unreadable for a second or two. The
   scrim is anchored to the text, bleeds well past it, and fades to nothing so
   there is never a visible rectangle. */
/* Section and page heads carry NO scrim: per-glyph adaptive contrast keeps
   this text readable now, so the fluid stays fully visible behind it. */

/* ------------------------------------------------- adaptive text contrast --- */

/* Primary mode: `ink-map`. contrast.js builds a small image of the ground's
   brightness — light text colour where the fluid is dark, ink where it is
   bright — stretches it over the viewport and clips it to the glyphs. The
   colour boundary therefore runs THROUGH the text: only the letters actually
   standing on a bright plume go dark. Inline styles carry the image and its
   viewport alignment; this rule does the clipping. */
body.has-fluid [data-adaptive].ink-map {
  -webkit-background-clip: text;
          background-clip: text;
  color: transparent !important;
  background-repeat: no-repeat;
  text-shadow: none;
  transition: none;
}
body.has-fluid [data-adaptive].ink-map .lume {
  -webkit-background-clip: text;
          background-clip: text;
  color: transparent !important;
  background-repeat: no-repeat;
}

/* Fallback mode, engines without background-clip:text only.
   `on-light` is toggled by contrast.js when the fluid behind an element gets
   bright enough that light text would stop being readable. The transition is
   deliberately slow and eased: the fluid moves continuously, so a hard swap
   reads as a glitch while a 400ms fade reads as the type responding to it.
   Colour is a token, not a literal, so this works in both themes — in dark mode
   the flip is white -> near-black, in light mode near-black -> white. */
body.has-fluid [data-adaptive] {
  transition: color var(--t-slow) var(--e-quiet);
}
body.has-fluid [data-adaptive].on-light,
body.has-fluid [data-adaptive].on-light .lume {
  color: var(--ink);
}
:root[data-theme='light'] body.has-fluid [data-adaptive].on-light,
:root[data-theme='light'] body.has-fluid [data-adaptive].on-light .lume {
  color: #FFFFFF;
  text-shadow: 0 1px 14px color-mix(in oklab, var(--ink) 45%, transparent);
}

/* The accent word in the hero keeps its colour until the ground actually
   threatens it, then goes solid so it never sits teal-on-teal. */
body.has-fluid .hero__title.on-light .lume { color: var(--brass-900); }
:root[data-theme='light'] body.has-fluid .hero__title.on-light .lume { color: var(--brass-100); }

@media (prefers-reduced-motion: reduce) {
  body.has-fluid [data-adaptive] { transition: none; }
}

/* Spec rows on a product page are long, thin and low-contrast by design. */
body.has-fluid .spec { border-bottom-color: color-mix(in oklab, var(--accent) 10%, var(--line)); }

/* --------------------------------------------------------------- accents --- */

/* The primary action has to out-glow the ground, or on a bright plume the CTA
   stops looking like the brightest thing on screen. */
body.has-fluid .btn--primary {
  box-shadow: 0 18px 50px -16px color-mix(in oklab, var(--accent) 72%, transparent),
              0 0 0 1px color-mix(in oklab, var(--accent) 26%, transparent);
}

/* ---------------------------------------------------------- degradation --- */

/* backdrop-filter is the one thing here without universal support. Without it
   the glass tints would be see-through over a live sim, so go opaque instead. */
@supports not ((backdrop-filter: blur(1px)) or (-webkit-backdrop-filter: blur(1px))) {
  body.has-fluid .card,
  body.has-fluid .svc,
  body.has-fluid .cat,
  body.has-fluid .calc,
  body.has-fluid .trust__item,
  body.has-fluid .pdp__stage,
  body.has-fluid .search-pop,
  body.has-fluid .toast,
  body.has-fluid .nav,
  body.has-fluid .marquee,
  body.has-fluid .foot { background: var(--surface); }
  body.has-fluid dialog.drawer { background: var(--bg-raised); }
}

@media (prefers-reduced-motion: reduce) {
  /* The sim never starts under reduce, so has-fluid is never added and the
     element's own background is what shows. Stated so it cannot regress. */
  #fluid { background: var(--bg); }
}
