/* Life OS · the Nova Orb, and the material that is hers to share
   ==============================================================

   `design-system.css` already carries §2 of NOVA_ORB_DESIGN_LANGUAGE.md — the
   token table, in a dark-first arrangement the document explicitly sanctions
   ("for a deliberately dark-first design, swap light and dark consistently
   through this whole pattern"). What it never carried is §3: the orb itself,
   and the material of §3.3. The site cited the design language and shipped
   none of its two defining objects.

   This file is that half. It is loaded AFTER the design system so it can lean
   on those tokens; it defines no colours of its own beyond the tint a caller
   passes in.

   **The orb is not a logo, and this file will not let it become one.** §1 is
   explicit: she appears where she is speaking or waiting to, and nowhere else
   — not a header, not a favicon, not a bullet. On a marketing site that means
   exactly one place: the section that is actually about her. Everything else
   on the page that wants to feel like the same product uses `.material`
   (§3.3), which is the shareable half by design. */

/* ------------------------------------------------- the §2 names, as aliases */

/* **The document and this stylesheet call the same colours different things.**
   NOVA_ORB_DESIGN_LANGUAGE.md §2 names them `well`, `good`, `warn`;
   `design-system.css` shipped them as `--card-raised`, `--sage`, `--amber`.
   Both are the same values — but a rule written against the document's
   vocabulary resolves `var(--good)` to nothing, and an invalid custom
   property does not fall back to something sensible, it drops the whole
   declaration. That is not hypothetical: the "Ready to pay" figure below was
   written as `var(--good)` and rendered plain white, and the earnings tiles
   asked for `var(--well)` and got no background at all. Both looked like a
   design decision rather than a typo, which is precisely why this is worth a
   permanent fix rather than a search-and-replace.

   Aliases rather than new values, so there is still exactly one definition of
   each colour. `var()` resolves at use time, so these follow the light/dark
   overrides in `design-system.css` automatically. */
:root {
  --well: var(--card-raised);
  --good: var(--sage);
  --warn: var(--amber);
  --text-secondary: var(--text-2);
  --text-tertiary: var(--text-3);
}

/* ---------------------------------------------------------------- the orb */

/* §3.1. Three layers and a shadow, in order, inside a square of side S.
   `--orb` is that side; `--tint` is the subject colour. Both are set by the
   caller, so one class draws her at 24px in a row and 220px in a hero. */
.orb {
  --orb: 120px;
  --tint: var(--accent);
  width: var(--orb);
  height: var(--orb);
  border-radius: 50%;
  position: relative;
  flex: none;

  /* 1. The sphere. This gradient IS the roundness — the document's own words.
        A flat fill at any size reads as a sticker. */
  background: radial-gradient(circle at 33% 28%,
    color-mix(in srgb, var(--tint) 95%, transparent) 0%,
    color-mix(in srgb, var(--tint) 55%, transparent) 55%,
    color-mix(in srgb, var(--tint) 14%, transparent) 100%);

  /* 4. The cast shadow. */
  box-shadow: 0 calc(var(--orb) * 0.06) calc(var(--orb) * 0.22)
              color-mix(in srgb, var(--tint) 35%, transparent);
}

.orb::before,
.orb::after {
  content: "";
  position: absolute;
  inset: 0;
  border-radius: 50%;
  pointer-events: none;
}

/* 2. The highlight. Small, high, soft, and FIXED — she is lit from one place
      and stays lit from it. A hard white dot reads as gloss, not as a lit
      surface, and a tilt-tracked one is the glass-icon treatment (§7), which
      is deliberately the opposite of hers. */
.orb::before {
  background: radial-gradient(circle at 32% 26%,
    rgb(255 255 255 / 55%) 0%, transparent 34%);
  mix-blend-mode: plus-lighter;
}

/* 3. The contact shadow, centred BELOW the box at 105%. This is what gives
      the sphere a near side instead of leaving it floating. */
.orb::after {
  background: radial-gradient(circle at 50% 105%,
    transparent 18%, rgb(0 0 0 / 42%) 62%);
}

/* §3.2.3 — the breathing. Period 3.4s at rest, amplitude 0.018. This is the
   only motion she has at rest, and it is what makes her look present rather
   than pasted on. The speaking variant (0.7s / 0.05) has no meaning on a
   marketing page, so it is not defined here rather than defined and unused. */
@keyframes orb-breathe {
  0%, 100% { transform: scale(1); }
  50%      { transform: scale(1.018); }
}

.orb--breathing {
  animation: orb-breathe 3.4s ease-in-out infinite;
  will-change: transform;
}

/* §3.4 — a still orb is a legitimate orb. Same object, paid for once, for a
   surface with a render budget. Nothing to do but not animate. */

/* ----------------------------------------------------------- the material */

/* §3.3. Four coloured blooms drifting on independent timers, blurred. This is
   what makes a hero or a card feel like the same product WITHOUT pretending
   to be her — and the document is firm that it is never shaped into a circle,
   which is why this is a rectangle that clips its own children. */
.material {
  position: absolute;
  inset: 0;
  overflow: hidden;
  pointer-events: none;
  border-radius: inherit;
  /* Behind content, above the surface's own ground. */
  z-index: 0;
}

.material > i {
  position: absolute;
  display: block;
  width: 62%;
  aspect-ratio: 1;
  border-radius: 50%;
  /* Scaled to the object: the document's `scale = size / 300` expressed as a
     percentage of the container, so one class works at any hero size. */
  filter: blur(calc(var(--bloom-blur, 60px)));
  /* **Weather, not a light show.** This was 0.5, which put a strong red and a
     strong green at either end of the hero and left the lede fighting the
     field behind it. The material's job is to make a surface feel like the
     product, and it does that by being the thing you notice second. */
  opacity: 0.36;
  will-change: transform;
}

/* Four blooms, four independent timers. The periods are deliberately
   coprime-ish so the set never visibly loops as a unit — the moment two of
   them share a period the whole field pulses, which reads as a loading
   animation rather than as weather. Speed 1.8 from §3.3 is the relationship
   between them, not a literal duration. */
.material > i:nth-child(1) { background: var(--bloom-1, #de7368); top: -14%; left: -8%;  animation: bloom-a 19s ease-in-out infinite; }
.material > i:nth-child(2) { background: var(--bloom-2, #669dde); top:  18%; left: 46%;  animation: bloom-b 23s ease-in-out infinite; }
.material > i:nth-child(3) { background: var(--bloom-3, #ad99d6); top:  40%; left:  4%;  animation: bloom-c 31s ease-in-out infinite; }
.material > i:nth-child(4) { background: var(--bloom-4, #53ca75); top: -10%; left: 62%;  animation: bloom-d 27s ease-in-out infinite; }

@keyframes bloom-a {
  0%, 100% { transform: translate3d(0, 0, 0) scale(1); }
  50%      { transform: translate3d(14%, 18%, 0) scale(1.14); }
}
@keyframes bloom-b {
  0%, 100% { transform: translate3d(0, 0, 0) scale(1.08); }
  50%      { transform: translate3d(-18%, 12%, 0) scale(0.92); }
}
@keyframes bloom-c {
  0%, 100% { transform: translate3d(0, 0, 0) scale(0.96); }
  50%      { transform: translate3d(20%, -14%, 0) scale(1.18); }
}
@keyframes bloom-d {
  0%, 100% { transform: translate3d(0, 0, 0) scale(1.1); }
  50%      { transform: translate3d(-12%, 20%, 0) scale(0.94); }
}

/* **Dimmer on a narrow screen, and that rule is inherited, not invented.**
   The hero it replaced carried `.hero-3d canvas { opacity: 0.28 }` under 720px
   for a real reason: the copy occupies most of a phone's viewport, so text and
   field overlap almost everywhere instead of sitting side by side. Removing
   the canvas would have quietly dropped that protection along with the rule
   that expressed it. */
@media (max-width: 720px) {
  .material > i { opacity: 0.22; }
}

/* A surface carrying the material needs to be a positioning context for it.

   **Deliberately only that.** The first version of this also did
   `.material-host > *:not(.material) { position: relative; z-index: 1 }` to
   lift the content clear — which at (0,2,0) outranks a plain `.hero-3d-fade`
   at (0,1,0) and would have quietly reset that element from `absolute` to
   `relative`, collapsing a full-bleed scrim into a zero-height box. A host
   lays out its own children; this only supplies the frame they sit in. The
   material carries `z-index: 0`, so any positioned sibling declared after it
   paints above it without being told to. */
.material-host { position: relative; }

/* -------------------------------------------------- a page head, on material */

/* A section head carrying the material needs room for it and an edge that
   resolves into the page, rather than a hard line where the blooms stop. The
   fade is a mask, not a gradient to the ground colour: §5 is explicit that
   anything whose job is to hide or blend an edge is a blur or a mask, and a
   gradient painted in `--ground` is exactly the thing that breaks when the
   theme flips. */
.page-head--material {
  padding-top: 104px;
  /* 72px here plus the following `.section`'s own 96px left a dead 168px
     band between the headline and the first thing you can actually do. */
  padding-bottom: 32px;
  overflow: hidden;
}
.page-head--material .material {
  -webkit-mask-image: linear-gradient(to bottom, #000 40%, transparent 100%);
          mask-image: linear-gradient(to bottom, #000 40%, transparent 100%);
}

/* **Quieter than the home hero, because this headline has no scrim.**
   `.hero-3d-copy h1` carries a text-shadow; `.h-page` does not, and at the
   hero's 0.36 the last words of "Get paid for the people you send." crossed
   the bright green bloom and dropped from near-white to a grey-green. The
   figure that matters on this page is a balance, not the wallpaper. */
.page-head--material .material > i { opacity: 0.24; }

/* ------------------------------------------------------- earnings figures */

/* **The numbers are the subject.** The app's own widget spec puts a small
   caps label over a big figure and lets the figure carry the tile; this is
   that, so the balance on the website and the balance in the app read as the
   same object. Each one sits in a `--well` recess (§2.1: "a recess: a ring's
   unfilled track, an input") rather than floating on the card, which is what
   separates four figures from one paragraph of numbers. */
.money-grid > div {
  background: var(--well);
  border-radius: var(--r-tile);
  padding: var(--s-card);
}

/* "Ready to pay" is money that is actually yours to take, which is the one
   figure here with a state rather than just a value — `good` in §2.1's
   sense, "a number that is where it should be".

   §2.2 is satisfied without doing anything extra: the label above it already
   reads "Ready to pay", so the colour is the second channel and not the only
   one. Somebody who cannot separate green from the rest loses nothing. */
.money--good { color: var(--good) !important; }

/* ------------------------------------------------------------ the brand */

/* **The site's mark is the orb now, on instruction.**

   Worth recording that this is a deliberate departure from §1, which says in
   as many words: "She is not a logo. Do not put the orb on a widget, a
   favicon, a marketing header or an empty state unless that surface is
   actually about her." That line exists because an earlier pass turned every
   Space icon into a tinted copy of her and it read as ten copies of the one
   shape that means her specifically. The owner has asked for the orb as the
   website's mark anyway, and the rule is his to revise — but the reason
   behind it is worth keeping visible, so: on this site she is the mark, and
   she is still not repeated as decoration anywhere else.

   Tinted blue rather than the neutral accent, because that is what she
   actually looks like on the app's own first run — a grey sphere in the
   header beside a full-colour one at launch would read as two objects. */
.brand .orb {
  --orb: 20px;
  --tint: #669dde;
}

/* The mark keeps its three layers at 20px — §3.4's "a still orb is a
   legitimate orb". What it does NOT keep is the breathing: a logo that
   pulses in the corner of every page is the kind of always-on motion §4
   exists to prevent, and it would be running on all forty-seven pages. */

/* -------------------------------------------------------------- formulas */

/* An inline formula, on the algorithms page. Nothing in the site styled
   `code` before, so it fell to the browser default: a monospace face at
   whatever size the UA picks, which next to this type ramp reads as a
   rendering fault rather than as a deliberate change of voice.

   The size is `0.92em` rather than a fixed pixel value — monospace faces
   have a larger apparent size than the body face at the same nominal size,
   and a fixed size here would also ignore the reader's own text setting,
   which §6 of the design language rules out. */
code {
  font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
  font-size: 0.92em;
  background: var(--well);
  border-radius: 6px;
  padding: 0.12em 0.4em;
  /* Formulas contain digits that should line up if two sit near each other. */
  font-variant-numeric: tabular-nums;
  /* A long formula on a 320px screen wraps rather than widening the page —
     §9.8, the rule the whole site is checked against. */
  overflow-wrap: anywhere;
}

/* --------------------------------------------------------------- controls */

/* **A disabled button that looks exactly like an enabled one is a trap.**
   `.btn` had no disabled state at all, so every control this site disables
   mid-request — "Sending…", "Checking…", and now an Apple button waiting on a
   Services ID — stayed at full contrast with a live hover lift under the
   cursor. Colour is not the only channel here either: the pointer changes too. */
.btn:disabled,
.btn[disabled] {
  opacity: 0.45;
  cursor: not-allowed;
}
.btn:disabled:hover,
.btn[disabled]:hover {
  transform: none;
  opacity: 0.45;
}

/* Sign in with Apple takes the primary ground: it is the same identity the
   app's own first run offers, so on this page it is the main door and the
   emailed code is the alternative beside it. */
.btn--apple {
  width: 100%;
  justify-content: center;
}

/* ---------------------------------------------------------------- motion */

/* §4. Motion is a setting, ALWAYS — and it stops, it does not slow down. An
   always-running loop that ignores the preference keeps the device from
   idling and costs battery for something nobody asked to see. */
@media (prefers-reduced-motion: reduce) {
  .orb--breathing,
  .material > i {
    animation: none !important;
  }
}

/* ================================================================
   Asked for 9 Sept 2026, in one pass:
     · the glow across every page, moving, not cut off
     · the orb blue, real, and moving like it does in the app
     · the download button carrying the same signature, in mono
     · the header laid out so the tabs stop being messy
   ================================================================ */

/* ------------------------------------------------- the glow, page-wide */

/* **Why this is `fixed` and not another `.material` host.**
   `.material` is `position: absolute; inset: 0; overflow: hidden` — it clips
   to whatever box it is inside. That is right for a card and wrong for a page:
   it is precisely why the glow "gets cut off", because each section drew its
   own little rectangle of weather and stopped at the edge. One fixed layer
   behind the whole document has no seams to cut at, costs four elements for
   the entire site rather than four per section, and keeps drifting while you
   scroll instead of scrolling away with the block it belonged to.

   Behind everything: `z-index: -1` against a `body` that paints no opaque
   ground of its own. */
.page-glow {
  position: fixed;
  inset: -10vmax;              /* over-sized so a bloom never shows an edge */
  z-index: -1;
  pointer-events: none;
  overflow: hidden;
  contain: strict;             /* the blur cannot force layout on the page */
}

.page-glow > i {
  position: absolute;
  display: block;
  width: 46vmax;
  aspect-ratio: 1;
  border-radius: 50%;
  filter: blur(90px);
  /* Fainter than a card's material. It is behind every word on the site now,
     not behind one hero, so it has to be the thing you notice second at most. */
  opacity: 0.30;
  will-change: transform;
}

/* Blue-led, because she is blue. The other three are the same family the
   material already uses, kept so the site reads as one product. */
.page-glow > i:nth-child(1) { background: #669dde; top: -6%;  left: -8%; animation: bloom-a 37s ease-in-out infinite; }
.page-glow > i:nth-child(2) { background: #7fb0e8; top: 34%;  left: 52%; animation: bloom-b 43s ease-in-out infinite; }
.page-glow > i:nth-child(3) { background: #ad99d6; top: 62%;  left: 10%; animation: bloom-c 53s ease-in-out infinite; }
.page-glow > i:nth-child(4) { background: #53ca75; top: -2%;  left: 64%; animation: bloom-d 47s ease-in-out infinite; }

/* Slower than a card's blooms on purpose: a full-viewport shape moving at the
   hero's speed reads as the page sliding. */

@media (max-width: 720px) {
  .page-glow > i { opacity: 0.20; filter: blur(70px); }
}

/* ------------------------------------------------------- the orb, moving */

/* "Make the Nova orb now just blue. Make it the real Nova orb, and make it
   also move, just like the app."
   The header mark was deliberately still — a logo that pulses on all
   forty-seven pages was the thing §4 exists to prevent. He has asked for it
   anyway, and the rule is his; it is one small object with a 5.5s period,
   which is slower than the app's 3.4s so it reads as present rather than
   busy. */
.brand .orb {
  animation: orb-breathe 5.5s ease-in-out infinite;
}

/* ----------------------------------------- the download button's signature */

/* The same object as the orb, flattened into a control: a lit surface with
   something moving under it. Monochrome, as asked — white ground, black type,
   and the drift shows as a soft grey travelling across it rather than as
   colour, so it never competes with the orb for being the coloured thing. */
.btn--nova {
  position: relative;
  overflow: hidden;
  isolation: isolate;
  background: #f4f4f5;
  color: #101013;
  border: 0;
}

.btn--nova::before {
  content: "";
  position: absolute;
  inset: -40%;
  z-index: -1;
  background:
    radial-gradient(circle at 30% 28%, rgb(255 255 255 / 95%) 0%, transparent 46%),
    radial-gradient(circle at 68% 74%, rgb(0 0 0 / 26%) 0%, transparent 52%);
  animation: nova-sheen 7s ease-in-out infinite;
  will-change: transform;
}

@keyframes nova-sheen {
  0%, 100% { transform: translate3d(-6%, -4%, 0) scale(1.04); }
  50%      { transform: translate3d(7%, 5%, 0) scale(1.12); }
}

/* ------------------------------------------------------------- the header */

/* Brand hard left, the tabs in the middle, Download hard right.
   `.nav` carried `margin-left: auto`, which shoved all eleven links up against
   the Download button and left the whole right-hand half of the bar crowded
   while the left was empty. `margin-inline: auto` centres the tabs between the
   two fixed ends instead, which is the arrangement being asked for. */
.header-inner > .nav { margin-left: auto; margin-right: auto; }
.header-inner > .nav-cta { margin-left: 0; }

@media (prefers-reduced-motion: reduce) {
  .page-glow > i,
  .brand .orb,
  .btn--nova::before {
    animation: none !important;
  }
}

/* **Quiet, 28 Sept 2026.** "Minimise the design, less colour, less tacky, more
   premium", and the same for the website. The four drifting coloured blooms
   behind every page and the coloured halo behind the hero phone are off: black
   ground, colour only where it means something. The markup stays so the
   layers can come back from one place. */
.page-glow, .ah-halo { display: none !important; }
.btn--nova::before { animation: none !important; }
