/* ⛔ GENERATED FILE — DO NOT EDIT.
   Source: design/tokens.css · regenerate with `npm run tokens`.
   It exists only because marketing/site/ deploys as its own root. */
/* ═════════════════════════════════════════════════════════════════════════════
   MentionChecker — THE DESIGN SYSTEM. One file, two surfaces.

   ⛔ THIS FILE IS THE SOURCE OF TRUTH FOR BOTH THE SITE AND THE APP.
   `marketing/site/tokens.css` is a GENERATED COPY — never edit it. Run
   `npm run tokens` after changing this file; `npm run check` fails if they drift.
   The copy exists only because `marketing/site/` deploys as its own root and
   cannot reach up out of it.

   ⭐ THE REFERENCES, and they were actually looked at: calendly.com and xero.com.
   What they have in common is the whole brief:

     · Ink is DEEP NAVY, never black.        Calendly #071A31 · Xero #000856
     · Ground is TINTED PAPER, not white.    Calendly #FCFBF8 · Xero #ECF2F6
     · Buttons are SOFT RECTANGLES.          Calendly 8px · Xero 6px — neither uses pills
     · ONE dominant weight (500) under a very large, tightly-tracked display
     · Cards are FLAT FILLS. Barely a border, barely a shadow

   ⚠️ WHERE THE TWO REFERENCES DISAGREE, AND HOW IT WAS SETTLED. Calendly's paper is
   WARM cream. `marketing/site/README.md` forbids that: the brand is cool neutrals +
   the accent specifically so it cannot be mistaken for Redwatcher's warm paper + #FF4500.
   ⛔ So the GROUND is Xero's (cool blue-grey) and the CALM is Calendly's. Do not
   "improve" the paper toward cream — that constraint is older than this file.

   ⛔ THE SIX RULES. Break one and the two surfaces stop being one product.

     1. SPACE SEPARATES, NOT LINES. A hairline is a last resort. There is no border
        on a card — a card is a tinted plane. Sections breathe at 96–128px.
     2. INK IS NAVY. #0B1B36, not #000. Black is a screen default; navy is a decision,
        and it is the single strongest signal both references share.
     3. TYPE CARRIES THE HIERARCHY. The scale is wide on purpose — 11px micro-labels
        beside 64px numbers. A narrow scale needs boxes and chips to compensate.
     4. ONE ACCENT, RARELY. --accent is the primary action and the number the customer
        came for. Not active nav, not section headers, not a chip that only needs to be quiet.
     5. PILLS ARE FOR CHIPS, NOT BUTTONS. Buttons are --r-sm rectangles. A pill CTA is
        the single most generic shape on the web and neither reference uses one.
     6. SHADOWS ARE ATMOSPHERE. Large, soft, near-invisible. If you can see where a
        shadow stops, it is too strong.

   ⭐ THE ONE PRODUCT-SPECIFIC PAIR: --you and --rival. This product exists to answer
   "do they name me, or someone else." That dichotomy is a token, not a per-chart choice.
   ═════════════════════════════════════════════════════════════════════════════ */

:root {
  color-scheme: light;

  /* ── ink. Navy, four steps. Wide, because type carries the hierarchy. ─────── */
  --ink:   #0B1B36;   /* display, headings, numbers, the answer */
  --ink-2: #3B475F;   /* body prose */
  --ink-3: #626E86;   /* secondary — still fully readable, 4.7:1 on white */
  --ink-4: #939CAF;   /* micro-labels and units. ⚠️ NEVER for anything you must read */
  --on-ink: #FFFFFF;
  --on-accent: #0B1B36;   /* ⭐ navy, because the accent is light. 6.6:1 */

  /* ── grounds. Cool, tinted, four and no more. ──────────────────────────────
     ⛔ A fifth surface is the first step back to boxes-inside-boxes. */
  --page:  #FFFFFF;   /* the ground */
  --band:  #F1F5FA;   /* ⭐ a FULL-WIDTH section ground. The Xero move: the page changes
                          colour instead of growing another box */
  --plane: #F7F9FC;   /* a card. This tint IS the card — it has no border */
  --well:  #E9EEF6;   /* a recess: tracks, quiet inputs, the un-filled part of a bar */

  /* ⭐ THE CHEVRON ON A SELECT, as a token — because `.field` sets `appearance: none` and
     that removes the native one, leaving a control that looks like a text box and ignores
     typing. ⛔ It is a TOKEN and not a rule in components.css because the colour has to
     change with the theme and a data: URI cannot read a custom property.
     ⚠️ THE HEX IS `--ink-3`, WRITTEN OUT. That is the one duplication in this file and it is
     forced: change one, change both. */
  --caret: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 12 12' fill='none' stroke='%23626E86' stroke-width='1.5' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M3 4.75 6 7.75 9 4.75'/%3E%3C/svg%3E");
  /* ⭐ WARM PAPER — the MARKETING SITE only, and it is a deliberate reversal.
     This file used to say the ground must be cool so the brand could not be mistaken for
     Redwatcher's warm paper + #FF4500. Pavel overruled that on 2026-09-03: the site takes
     calendly.com's warm ground after all. The two tones are sampled from calendly.com, not
     guessed — #FCFBF8 is its page and nav, #F5F3EE its hero panel.
     ⚠️ The APP stays cool. It is a data surface with a dark theme, and warm paper under a
     navy-and-blue report reads muddy. `marketing/site/styles.css` is the only place these
     are bound to --page / --band / --plane / --well. */
  --paper:        #FCFBF8;
  --paper-2:      #F5F3EE;
  --paper-3:      #EDEAE3;
  --paper-hair:   #EAE6DD;
  --paper-hair-2: #DCD7CB;

  /* ⛔⛔ NOT A SURFACE — the plate under a THIRD PARTY's favicon or wordmark, artwork drawn for
     a light background. ⭐ IT STAYS LIGHT IN BOTH THEMES, and that is the entire reason it is its
     own token rather than one of the four grounds above.
     ⚠️ Use it ONLY behind artwork this product did not draw and cannot recolour. Anything of ours
     that merely needs to LOOK raised — a popover, a menu — takes `--raised` below. */
  --plate: #FFFFFF;

  /* ⭐ THE RAISED SURFACE: a popover, a menu, a sheet that floats over the page. Unlike `--plate`
     this one FOLLOWS THE THEME, because it is our own surface with our own text on it.
     ⚠️ Identical to `--plate` in the light theme TODAY and deliberately separate — they part
     company in dark, which is the whole point of the split. */
  --raised: #FFFFFF;
  --scrim: rgba(11, 27, 54, .05);
  /* ⭐ THE MODAL BACKDROP, and it is NOT `--scrim`. A 5% wash is a hover state — it says "this
     row is under the cursor". A backdrop has to say "the page behind is out of play", which is a
     different job and an order of magnitude more ink. ⚠️ It DARKENS IN BOTH THEMES: a backdrop
     that lightened in dark would read as a flare, not as depth. */
  --veil: rgba(11, 27, 54, .44);

  /* ── hairlines. Two, both nearly invisible. The second is used ~twice a page. ── */
  --hair:   #E4EAF2;
  --hair-2: #D3DBE7;

  /* ── the accent. ⭐ TWO BLUES, and the split is not decoration. ──────────────
     The brand blue is #5BA4F6 (Pavel, 2026-09-03, replacing #1B4DE4). It is LIGHT:
     2.6:1 on white. That is fine for a fill, artwork or a bar, and it fails outright
     for text — white on it is 2.6:1 against the 4.5:1 floor.

       --accent      the brand blue. FILLS ONLY: buttons, washes, the mark, chips.
       --accent-ink  the same blue, dark enough to read. TEXT, links, focus rings, and
                     every bar in the report. #206ECE is the
                     BRIGHTEST step that still clears 4.5:1 on every ground this system has —
                     white 5.0, warm page 4.9, warm panel 4.5, cool plane 4.8. Solved, not picked.

     ⭐ --on-accent is NAVY, not white — navy on #5BA4F6 is 6.6:1, and it is exactly what
     xero.com does with its mint button. ⛔ Do not "fix" it back to white: that is the
     2.6:1 failure this split exists to prevent. */
  --accent: #5BA4F6;
  --accent-press: #4A96EE;
  --accent-ink: #206ECE;
  --accent-wash: rgba(91, 164, 246, .16);
  --accent-ring: rgba(91, 164, 246, .55);
  /* ⭐ The highlighter. Xero's treatment on "cash flow." — a phrase in the display stays
     navy and sits on a wash, so emphasis costs no accent (rule 4). */
  /* ⭐ THE BRAND BLUE, ON DISPLAY-SIZE NUMBERS ONLY (Pavel, 2026-09-03). The report's headline
     and its accent figure are the two places the customer's own number is set at 40px+, and
     Pavel's call is that the number wears the BRAND blue rather than the readable one — at that
     size it reads as the mark, not as text.
     ⛔ THE EXCEPTION IS THE SIZE, AND NOTHING ELSE. #5BA4F6 is 2.6:1 on white, under even the
     3:1 large-text floor, so ANYTHING you can call prose keeps --accent-ink: links, labels, the
     1rem percentages beside a bar, every bar in the report, focus rings, chips.
     ⚠️ It follows the theme for free — dark redefines --accent, where the same blue is high
     contrast on a near-black page rather than low contrast on white. */
  --accent-display: var(--accent);
  --accent-mark: rgba(91, 164, 246, .34);
  --accent-rgb: 91, 164, 246;
  --ink-rgb: 11, 27, 54;

  /* ── ⭐ THE CTA. A DIFFERENT COLOUR FROM THE BRAND ACCENT, on purpose. ───────
     xero.com's model: a blue brand, a green call to action. The accent identifies the
     product; the CTA is the one thing on the page you are meant to press.

     ⭐ #50DCAA is Xero's own value, read off xero.com/uk's RENDERED buttons — not sampled
     from a screenshot, not approximated. Navy #000856 on it is exactly what they ship.
     Navy on ours is 9.9:1.

     ⚠️ WHAT THE PROBE ALSO SHOWED, and it is the part worth keeping: on xero.com/uk the
     mint is NOT the workhorse. 10 of 13 filled buttons are #1F65D6 deep blue with WHITE
     text; the mint appears 3 times, always on white or #ECF2F6, never on their navy. It
     is the OFFER colour, and it reads as a highlight because a deep blue anchors
     everything around it.
     ⛔ This product has no such anchor — our brand blue #5BA4F6 is light, and Xero has no
     light blue anywhere. So here the green is the most saturated thing on the page and
     carries the CTA alone. A deliberate departure, not an imitation.
     ⛔ Navy text, never white — same rule as the accent button. */
  --cta: #50DCAA;
  --cta-press: #3FCE9B;
  /* ⛔ A LITERAL NAVY, NOT var(--ink) — and it must stay literal.
     ⚠️ MEASURED 2026-09-03: it was `var(--ink)`, which is #0B1B36 in light and #EEF1F7 in dark. The
     CTA green does NOT flip between themes (--cta is #50DCAA in both), so the label followed --ink
     to near-white and "Run now" went white-on-mint — legible at about 1.4:1, which is to say not.
     ⭐ The rule this encodes: a token named `--on-X` belongs to X's colour, not to the theme. If
     the background is fixed, the foreground on it is fixed too. Navy on #50DCAA is 10.4:1. */
  --on-cta: #0B1B36;

  /* ── ⭐ the product's dichotomy. You, and whoever got named instead. ───────── */
  --you: var(--accent-ink);   /* ⛔ the READABLE blue — a light bar cannot out-read a grey rival */
  --rival: #6B7790;
  --rival-wash: #DDE3ED;

  /* ── semantic pairs — a wash and an ink, ALWAYS redefined together. Flipping
        only the page leaves a bright chip painted onto a dark screen. ────────── */
  --ok:   #0F7A52; --ok-wash:   rgba(15, 122, 82, .09);
  --warn: #8A6212; --warn-wash: rgba(138, 98, 18, .10);
  --bad:  #D4353D; --bad-wash:  rgba(212, 53, 61, .08);

  /* ── shadows: atmosphere, not edges. ───────────────────────────────────────── */
  --lift:   0 1px 2px rgba(11, 27, 54, .04), 0 8px 24px -14px rgba(11, 27, 54, .10);
  --lift-2: 0 1px 3px rgba(11, 27, 54, .05), 0 24px 60px -24px rgba(11, 27, 54, .16);
  --lift-3: 0 2px 6px rgba(11, 27, 54, .06), 0 48px 90px -32px rgba(11, 27, 54, .26);

  /* ── radii. Radius follows padding: a 20px corner on a 28px-padded plane reads
        soft; the same corner on a 12px-padded one reads like a bubble. ────────── */
  --r-xs: 6px;
  --r-sm: 10px;   /* ⭐ THE BUTTON RADIUS (rule 5) */
  --r:    14px;
  --r-lg: 20px;
  --r-xl: 28px;
  --r-pill: 999px; /* chips and dots ONLY */

  /* ── space. ⭐ This is what "airy" actually means. ─────────────────────────── */
  --s1: 4px;  --s2: 8px;  --s3: 12px; --s4: 16px; --s5: 24px;
  --s6: 32px; --s7: 48px; --s8: 64px; --s9: 96px; --s10: 128px;

  /* ── type. ⭐ Geist — the face calendly.com actually ships, open-source and on
        Google Fonts, so the reference is the implementation and not an impression
        of one. -apple-system is the fallback, never the intent. ───────────────── */
  /* ⚠️ --font-geist-* is set by next/font in the APP (self-hosted, no render-blocking
     request — see app/layout.js). The static SITE has no bundler, so it falls through to
     the "Geist" family its <link> loads. One stack, two ways of getting the file. */
  --font-sans: var(--font-geist, "Geist"), -apple-system, BlinkMacSystemFont, "Segoe UI", Inter, Roboto, sans-serif;
  /* ⭐ THE DISPLAY FACE — Urbanist, and it is a SECOND face on purpose. Geist is a neutral
     grotesk; the wordmark is a geometric sans with circular bowls, a tall x-height and short
     ascenders, and Urbanist is the closest open face to it. Set the two side by side and the
     headline now looks related to the logo instead of merely near it.
     ⛔ DISPLAY ONLY — the headline and the one number. Body, UI, controls and every table
     stay --font-sans. Two faces is a pairing; three is a mess.
     ⚠️ Urbanist reads lighter than Geist at the same weight, so the display weight is 700
     against the body's 600. Do not "harmonise" them back to one number. */
  --font-display: var(--font-urbanist, "Urbanist"), var(--font-sans);
  --w-display: 700;
  /* ⭐ THE ACCENT FACE — Fraunces 500 at optical size 144. A THIRD face, which the rule
     below says is a mess, so it earns its place only by being almost never used.
     calendly.com runs three too and the proportion is the whole point: ~2870 elements in
     Geist, ~90 in their UI sans, ~40 in Wulkan Text. The serif is about 1% of the page.
     ⛔ THREE PLACES ONLY. Two are the ones calendly.com uses it for: THE PRICE, and the
     closing headline. The third is THE REPORT'S HEADLINE FIGURES — mention rate, share of
     voice, average rank (Pavel, 2026-09-08) — which is the same job as the price: the one
     number on the screen somebody came to read.
     ⚠️ THE LIST GREW BY ONE AND THAT IS THE POINT OF WRITING IT DOWN. Not section heads,
     not the hero, not a pull quote, not "just this once". A face used anywhere beyond this
     list stops being an accent and becomes noise — the proportion below is the test.
     ⚠️ Wulkan Text — Calendly's actual serif — is Fontfabric retail and not free. Newsreader
     is SIL OFL, verified against Google's repo.
     ⭐ Newsreader is VARIABLE on two axes: wght 200–800 and opsz 6–72. It is loaded across
     the full optical range and left on `font-optical-sizing: auto`, so a 40px price gets a
     finer, higher-contrast cut than a 15px one would — for free, from one file.
     ⚠️ Instrument Serif was tried here first and ships ONE weight (400). Any face put in
     this slot must be checked for the weight it actually has: ask for a weight a font does
     not ship and the browser synthesises a faux bold, which on a high-contrast serif looks
     broken in a way readers notice without knowing why. */
  --font-accent: var(--font-newsreader, "Newsreader"), Georgia, "Times New Roman", serif;
  --w-accent: 500;
  --font-mono: var(--font-geist-mono, "Geist Mono"), ui-monospace, SFMono-Regular, Menlo, monospace;
  --w-body: 400;
  --w-med:  500;   /* ⭐ the dominant weight on both references */
  --w-semi: 600;   /* display, headings, and a number */

  /* ── motion. One curve, three durations. ──────────────────────────────────── */
  --ease: cubic-bezier(.16, 1, .3, 1);
  --t-fast: .16s;
  --t: .28s;
  --t-slow: .6s;
}

/* ⛔ LIGHT IS THE PRODUCT, and it does NOT follow the system. This block is an explicit
   opt-in, never `prefers-color-scheme` — a light-first product that silently turns dark
   on two thirds of machines is not a light product, it is a coin flip.
   ⛔ AND IT IS A SCREEN DECISION. `@media screen` because paper is white: MEASURED 2026-09-11
   (PLAN.md P14.2), a dark-theme customer's PDF came out as pale grey text on a white sheet inside
   a navy page — every token here is the wrong way round for print. Printing falls back to the
   light set above, whichever theme is on screen. */
@media screen {
:root[data-theme="dark"] {
  color-scheme: dark;

  --on-accent: #06101F;
  --ink:   #EEF1F7;
  --ink-2: #B4BDCC;
  --ink-3: #8A93A5;
  --ink-4: #6A7387;
  --on-ink: #080D18;

  --page:  #080D18;
  --band:  #0C1322;
  --plane: #111928;
  --well:  #182134;
  /* ⛔⛔⛔ `--plate` IS DELIBERATELY NOT REDEFINED HERE. It is #FFFFFF in both themes.
     🪤 THIS BLOCK USED TO SAY `--plate: #182134`, added to stop every popover rendering as a white
     sheet in dark mode. It fixed the popovers and broke the thing the token exists for: ⛔ a
     THIRD PARTY's favicon is artwork drawn for a white background, and on navy the ones drawn in
     black or dark grey simply disappear. MEASURED ON SCREEN 2026-09-09 on the JBL report — Bose's
     white-on-black roundel and Sennheiser's dark mark were both illegible in the standings.
     ⭐ ONE TOKEN WAS DOING TWO JOBS. The popovers were never asking for "the plate", they were
     asking for "a surface that reads as raised" — which is `--raised`, right below, and which is
     free to follow the theme because everything on it is ours.
     ⛔ Do not add `--plate` back to this block. If a popover looks white in dark mode, it is
     reading the wrong token. */

  /* ⭐ THE LIGHTEST SURFACE, because a raised sheet floats above everything: it lands on the
     canvas, on a card, or over the top bar, and must read as raised on all three. ⚠️ Same hex as
     `--well` TODAY and deliberately its own token — a raised sheet is ABOVE and a well is INSET,
     so if either moves the other has no reason to follow. */
  --raised: #182134;
  /* ⚠️ Same SVG, `--ink-3`'s DARK hex. See the note beside the light one. */
  --caret: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 12 12' fill='none' stroke='%238A93A5' stroke-width='1.5' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M3 4.75 6 7.75 9 4.75'/%3E%3C/svg%3E");
  --scrim: rgba(255, 255, 255, .05);
  /* ⚠️ Deeper than the light one, and black rather than the ink navy: the dark page is already
     near-black, so a navy veil at 34% would barely separate the panel from what is behind it. */
  --veil: rgba(0, 0, 0, .68);

  --hair:   #1B2437;
  --hair-2: #263148;

  --accent: #5C8CFF;
  --accent-press: #7CA3FF;
  /* ⛔ THE TWO BLUES COLLAPSE INTO ONE HERE, and that is the correct answer rather than a
     shortcut. The light theme splits them because the brand blue is too LIGHT to read on a white
     ground; on a near-black ground the constraint inverts — the readable blue is the light one, and
     #206ECE (which this block did not override until 2026-09-03) was inheriting from `:root` and
     sitting at 3.87:1 on the page. That passes for a big number and FAILS 4.5:1 for the small
     accent text actually using it: the percentage beside every bar, links, chips.
     ⭐ SOLVED, NOT PICKED, the same way #206ECE was. The binding case is a chip — accent text on
     an --accent-wash over --plane, the lightest ground this token ever meets — and #5C8CFF clears
     it at 4.7:1 (5.1 on --well, 6.2 on --page). Every deeper blue tried fails that chip. */
  --accent-ink: #5C8CFF;
  --accent-wash: rgba(92, 140, 255, .12);
  --accent-ring: rgba(92, 140, 255, .28);
  --accent-mark: rgba(92, 140, 255, .22);
  --accent-rgb: 92, 140, 255;
  --ink-rgb: 0, 0, 0;

  --cta: #50DCAA;
  --cta-press: #6BE6BB;
  --rival: #7E89A1;
  --rival-wash: #253048;

  --ok:   #35C88A; --ok-wash:   rgba(53, 200, 138, .12);
  --warn: #E3AE4A; --warn-wash: rgba(227, 174, 74, .12);
  --bad:  #FF6B72; --bad-wash:  rgba(255, 107, 114, .12);

  --lift:   0 1px 2px rgba(0, 0, 0, .30), 0 8px 24px -14px rgba(0, 0, 0, .55);
  --lift-2: 0 1px 3px rgba(0, 0, 0, .36), 0 24px 60px -24px rgba(0, 0, 0, .70);
  --lift-3: 0 2px 6px rgba(0, 0, 0, .40), 0 48px 90px -32px rgba(0, 0, 0, .80);
}
}

/* ═══ base ═══════════════════════════════════════════════════════════════════
   Element defaults, shared. ⛔ Neither surface redeclares a font stack or a heading
   size of its own — that is how the site and the app drifted apart in the first place. */
* { box-sizing: border-box; }
html { -webkit-text-size-adjust: 100%; background: var(--page); }
body {
  margin: 0;
  background: var(--page);
  color: var(--ink);
  font-family: var(--font-sans);
  font-size: 15px;
  /* ⚠️ 1.6, not 1.5. Line-height is the cheapest airiness there is, and the one most
     often left at a console default. */
  line-height: 1.6;
  letter-spacing: -.011em;
  font-weight: var(--w-body);
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
}
h1, h2, h3, h4, h5, h6 { margin: 0; color: var(--ink); font-weight: var(--w-semi); line-height: 1.14; }
p { margin: 0; }
a { color: inherit; text-decoration: none; }
button { font: inherit; color: inherit; background: none; border: 0; padding: 0; cursor: pointer; }
input, textarea, select { font: inherit; color: inherit; }
::selection { background: var(--accent-wash); }

/* ⛔ Never `outline:none` without a replacement — a keyboard user who cannot see where
   they are has no way back. Offset so it reads as a halo, not a box. */
:focus-visible { outline: 2px solid var(--accent-ink); outline-offset: 3px; border-radius: var(--r-xs); }
input:focus-visible, textarea:focus-visible { outline: none; }

/* ═══ the type scale ══════════════════════════════════════════════════════════
   ⚠️ WIDE on purpose: an 11px micro-label beside a 64px number. Both references run
   a display two to three times the size of anything near it — that gap IS the
   hierarchy, and it is what a page full of 24px headings can never buy back. */
.t-display {
  font-family: var(--font-display);
  font-size: clamp(2.6rem, 5.2vw, 4rem);
  line-height: 1.04; letter-spacing: -.035em; font-weight: var(--w-display);
  text-wrap: balance;
}
.t-h1   { font-family: var(--font-display); font-size: clamp(1.9rem, 3.2vw, 2.6rem); line-height: 1.1;  letter-spacing: -.03em; font-weight: var(--w-display); text-wrap: balance; }
.t-h2   { font-size: 1.375rem; line-height: 1.25; letter-spacing: -.026em; font-weight: var(--w-semi); }
.t-h3   { font-size: 1rem;     line-height: 1.4;  letter-spacing: -.018em; font-weight: var(--w-med); }
/* ⭐ `pretty` on PROSE, `balance` on HEADINGS (above) — they are not interchangeable.
   `balance` evens every line and is right for two or three words; on a paragraph it is expensive
   and can look contrived. `pretty` only stops the last line being a single orphaned word, which is
   the actual defect: "…is measured over." was leaving "over." alone on its own line.
   ⚠️ Progressive — a browser without it simply wraps as before. Nothing depends on it. */
.t-lede { font-size: 1.125rem; line-height: 1.55; letter-spacing: -.014em; color: var(--ink-2); text-wrap: pretty; }
.t-body { font-size: .9375rem; line-height: 1.62; color: var(--ink-2); }
.t-sm   { font-size: .8125rem; line-height: 1.55; color: var(--ink-3); text-wrap: pretty; }
.t-xs   { font-size: .75rem;   line-height: 1.5;  color: var(--ink-4); }
/* The micro-label. Its whole job is to be quiet, so it is never bold and never dark. */
.t-label {
  font-size: .6875rem; font-weight: var(--w-med); letter-spacing: .085em;
  text-transform: uppercase; color: var(--ink-4);
}
/* ⭐ The number the customer paid for. Tabular, tight, and the only other place
   --accent is allowed (rule 4). */
.t-num { font-variant-numeric: tabular-nums; letter-spacing: -.035em; font-weight: var(--w-semi); line-height: 1; }
.tnum  { font-variant-numeric: tabular-nums; font-feature-settings: "tnum"; }
.mono  { font-family: var(--font-mono); font-feature-settings: "tnum"; letter-spacing: -.01em; }

/* ═══ motion ═════════════════════════════════════════════════════════════════
   Everything carries meaning: something arrived, or something is working. */
@keyframes rise { from { opacity: 0; transform: translateY(10px); } to { opacity: 1; transform: none; } }
@keyframes fade { from { opacity: 0; } to { opacity: 1; } }
@keyframes spin { to { transform: rotate(360deg); } }
@keyframes breathe { 0%, 100% { opacity: 1; } 50% { opacity: .4; } }
.rise    { animation: rise var(--t-slow) var(--ease) both; }
.fade    { animation: fade .4s ease both; }
.spin    { animation: spin .7s linear infinite; }
.breathe { animation: breathe 1.8s ease-in-out infinite; }

@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after {
    animation-duration: .01ms !important; animation-iteration-count: 1 !important;
    transition-duration: .01ms !important;
  }
}
