/* GENERATED FILE. DO NOT EDIT.
 * Copied verbatim by scripts/sync-tokens.mjs from ../site/design-system/fonts.css.
 * The checkout must load the same faces at the same weights as the site.
 */
/* SELF-HOSTED TYPE.
 *
 * Two reasons, and the second one is not about speed.
 *
 * 1. Performance. Third-party font CSS costs a DNS lookup, a TLS handshake and
 *    a round trip before the browser even learns which font files it needs, and
 *    then repeats most of that for the files themselves. Served from our own
 *    origin the files come down on a connection that is already open, and they
 *    cache across every page.
 *
 * 2. Privacy. Google Fonts and Fontshare received the IP address of every
 *    visitor to this site. Our own privacy policy had to list them as third
 *    parties who see your data, and it promised we were working on stopping it.
 *    This is that.
 *
 * Only the weights the site actually uses. Clash Display is the display face,
 * Satoshi is the text face, Satoshi is the mono-ish label face.
 * font-display:swap so text is readable before the file lands.
 */

/* ---- TRACKING TOKENS LIVE HERE, AND THE REASON IS NOT TASTE --------------
 * They were in index.html's :root, which build.mjs slices into tokens.css.
 * contact.html does not link tokens.css. It links this file. So after 514
 * label declarations were collapsed onto var(--ls-uc), every label on the
 * contact page rendered with NO tracking at all, silently, and the build
 * reported success. fonts.css is the ONLY stylesheet all five shipped pages
 * link, and a typography token belongs with the typography anyway.
 * No var() fallbacks on the usages, deliberately: a fallback would let a page
 * that is missing this file look almost right while quietly ignoring the
 * system, which is the failure mode that caused this in the first place.
 * ------------------------------------------------------------------------ */
:root{
/* .045em, chosen 13 Aug from /lab-eyebrows.html. The tell that reads as a
   generic AI landing page is the PAIR: a monospace face plus a tiny, widely
   tracked uppercase kicker. Dropping Space Grotesk removed one half; .13em was
   the other half still sitting there. Caps stay, because a label has to read as
   a label rather than as a short sentence somebody forgot to finish. It is the
   tracking that comes out. This one value moves every eyebrow, tag, table
   header and chip on the site AND in the checkout, in step. */
--ls-uc:.045em;    /* every uppercase label, tag, table header, chip */
--ls-d1:-.036em;   /* display, 64px and up */
--ls-d2:-.028em;   /* display, 34 to 63px */
--ls-d3:-.019em;   /* display, 20 to 33px */
--ls-t1:-.008em;   /* Satoshi, 18px and up */
--ls-t2:0em;       /* Satoshi, under 18px. Body copy does not want tracking */

/* ---- THE SIZE RAMP, RECUT 13 AUG -----------------------------------------
   The first cut of this ramp had eleven steps and was chosen by taste. It did
   not work, and the way it failed is worth writing down, because it is the
   failure mode of most design systems.

   MEASURED: of 477 font-size declarations in the shipped source, 33 used the
   ramp. Not because anyone disagreed with it. Because it was cut coarser than
   the site speaks, so an author reaching for "the size just under the section
   heading" found a 26% gap and wrote a raw px value instead. A ramp smaller
   than the reality it describes does not get adopted. It gets stepped around,
   and then it is decoration.

   SO THIS CUT WAS FITTED, NOT CHOSEN. Every hardcoded size was weighted by how
   often it is used, and a geometric ladder was searched across anchor, ratio
   and step count for the one that moves the least ink. The winner: anchor
   107.5, ratio 1.18 through the display range, 1.15 through text. Then each
   rung was nudged up to 4.5% onto the sizes it actually carries, weighted by
   the SQUARE of the drift, so a rung carrying 87 declarations refuses to
   travel to spare one. Result across 420 declarations: mean drift 2.9%, worst
   9.7%, and the two workhorse rungs (--t5, --t6) move 1.7% and 1.5%.
   The fitters are .ramp-fit.mjs and .ramp-settle.mjs, kept out of the build.

   SIXTEEN STEPS IS NOT A LOT. It reads like a lot next to a blog post about
   type scales; it is fewer than Carbon and about the same as Material. The
   site has a hero watermark and a 9.75px chip and it needs the rungs in
   between. Every one of these carries real declarations, which is the test:
   a rung with nothing on it is a rung somebody will misuse.

   WHY THE RATIOS WOBBLE (1.10 to 1.28). They follow the content rather than a
   spreadsheet. Jumps are wide at the top, because at display size the eye
   reads relative difference and needs it to be unmistakable, and tight at the
   bottom, because below 20px the job is information density and a 25% jump
   throws away a whole level of hierarchy.

   THE STEPS ARE NOT ALL FOR EVERY SURFACE, and that is the point of naming
   them. The checkout is the site continued and uses the whole ramp: a stranger
   is deciding whether to trust it with money. The console is a dense tool used
   daily by two people who already trust it, and it uses --t1 down only. A 54px
   heading is right above a price and wrong above next week's sessions.

   FLUID ABOVE 29px, FIXED AT AND BELOW 26px. Display type has to survive a
   360px phone, so --d1..--d8 clamp. Body and label type must NOT shrink on a
   phone: it is already at the readable floor, and a "responsive" 12px caption
   becomes a 9px one on the device that most needs it legible.
   Each vw is size/14.4, so the preferred value equals the ceiling at 1440.

   Display steps carry their own tracking band, so use them as a pair:
   font-size:var(--d5) with letter-spacing:var(--ls-d2).
   ------------------------------------------------------------------------ */
--d1:clamp(42px,7.22vw,104px); /* the hero watermark, the statement. site only */
--d2:clamp(38px,5.76vw,83px);  /* the closing word, a page h1. site only */
--d3:clamp(36px,4.51vw,65px);  /* the ticker figure. site only */
--d4:clamp(34px,3.75vw,54px);  /* THE section heading. site + checkout */
--d5:clamp(30px,3.13vw,45px);  /* sub-section heading. site + checkout */
--d6:clamp(27px,2.71vw,39px);  /* a price, a big number. site + checkout */
--d7:clamp(25px,2.36vw,34px);  /* card heading. site + checkout */
--d8:clamp(23px,2.01vw,29px);  /* h3. everywhere */
--t1:26px;                     /* a pull quote, a lead statement */
--t2:22px;                     /* the intro lead */
--t3:19.5px;                   /* lead paragraph */
--t4:16.25px;                  /* body large */
--t5:14.75px;                  /* body. the default, 87 declarations */
--t6:12.75px;                  /* small print, meta, 88 declarations */
--l1:10.75px;                  /* the eyebrow, tag, table header */
--l2:9.75px;                   /* chip, micro label */

/* ---- RADIUS, 13 Aug --------------------------------------------------------
   Counted before cutting: the site sets a corner radius 101 times across 14
   values and the checkout 59 times across 11. Two of those are not really
   radii at all and carry most of the uses. 99px is the PILL, a shape, not a
   number, and it must never be scaled with the others or a button stops being
   a button. 50% is a CIRCLE, same argument. Those two are named for what they
   are; the rest is a five-step scale on roughly a 1.5 ratio, which is wide
   because a corner reads as "the same" over a far bigger range than type does.
   Fitted the same way as the sizes: 5->r1, 8/10->r2, 12/14->r3, 16/18->r4,
   20/22/24->r5. Nothing moves more than 2px.
   ------------------------------------------------------------------------ */
--r1:4px;      /* an inner chip, a tag, a swatch */
--r2:8px;      /* an input, a small control */
--r3:12px;     /* a tile, a slot */
--r4:18px;     /* a card */
--r5:24px;     /* a panel, a sheet */
--r-pill:99px; /* a button, a chip. a SHAPE, never scaled */

/* ---- ELEVATION, 13 Aug -----------------------------------------------------
   64 shadows on the site, 81 in the checkout, 21 distinct in the checkout
   alone. A shadow is the most expensive thing to have many of, because the eye
   reads inconsistent elevation as "unfinished" long before it can say why:
   two cards on the same plane casting different shadows is the whole tell.
   THREE PLANES, and that is all a flat page needs.
     e1  resting on the page. A card that is simply there.
     e2  lifted by the pointer, or lifted because it wants reading first.
     e3  floating over the page. A nav, a sheet, a menu.
   The colour is the ink at low alpha rather than black, so the shadow belongs
   to the palette instead of greying the sand toward blue.
   --ring is here and not with the colours because it is elevation's sibling:
   it is drawn OUTSIDE the box like a shadow, and every control needs the same
   one or keyboard users get a different affordance per component.
   ------------------------------------------------------------------------ */
--e1:0 1px 2px rgba(22,21,15,.04), 0 2px 8px rgba(22,21,15,.04);
--e2:0 2px 4px rgba(22,21,15,.05), 0 8px 22px rgba(22,21,15,.07);
--e3:0 4px 10px rgba(22,21,15,.07), 0 18px 46px rgba(22,21,15,.12);
/* --ring, NOT --focus, AND THE NAME IS THE WHOLE POINT.
   I called this --focus when I added it. buttons.css has declared --focus for
   months, as a COLOUR, and eighteen files on this site read it that way in
   `outline:2px solid var(--focus)`. Nothing broke, purely because buttons.css
   is linked after fonts.css and won the cascade. Change the link order and
   every focus ring on the site becomes `outline:2.5px solid <a shorthand>`,
   which is invalid and therefore no ring at all, on every interactive element.
   A token name is an API. Two files declaring the same name with different
   TYPES is not a conflict you notice, it is one you inherit.

   AN OUTLINE, NOT A SHADOW, and that took two attempts to get right.
   The first version was a two-stop box-shadow whose inner stop had to be the
   PAGE colour, to fake a gap. The page colour is --sand-100 on the website and
   --sand in the checkout, so the token could not name it without naming a
   variable that does not exist on one of the two surfaces. apa-booking's style
   contract test caught exactly that and refused the build, which is the test
   doing its job.
   outline-offset draws the gap out of whatever is actually behind the control,
   so it is correct on sand, on iron, on a card and on the lime band without
   knowing which. It also follows border-radius in every current browser and is
   never clipped by an ancestor's overflow, which a box-shadow ring is.
   USE AS A PAIR: outline:var(--ring); outline-offset:var(--ring-off). */
--ring:2.5px solid rgba(22,21,15,.72);
--ring-off:2px;
}

/* ---- WHAT THE OLD NAMES BECAME ------------------------------------------
 * The VALUES barely moved; the NAMES shifted by a step or two because eight
 * display rungs replaced five. apa-booking reads this file by name, so this
 * table is the migration and scripts/sync-tokens.mjs enforces it.
 *   old --d1 104   -> --d1 104        old --t1 20    -> --t3 19.5
 *   old --d2  72   -> --d3 65         old --t2 16.5  -> --t4 16.25
 *   old --d3  54   -> --d4 54  (same) old --t3 14.5  -> --t5 14.75
 *   old --d4  40   -> --d6 39         old --t4 13    -> --t6 12.75
 *   old --d5  28   -> --d8 29         old --l1 11.5  -> --l1 10.75
 *                                     old --l2 10.5  -> --l1 10.75
 * The one real merge is at the bottom: 11.5 and 10.5 were never two things,
 * they were one label size written twice. --l2 is now the genuinely smaller
 * chip at 9.75, which the site was already setting by hand 29 times.
 * ------------------------------------------------------------------------ */

/* ---- Clash Display: headings and display only, never body ---------------- */
@font-face{font-family:'Clash Display';src:url('/fonts/clash-500.woff2') format('woff2');
  font-weight:500;font-style:normal;font-display:swap}
@font-face{font-family:'Clash Display';src:url('/fonts/clash-600.woff2') format('woff2');
  font-weight:600;font-style:normal;font-display:swap}
@font-face{font-family:'Clash Display';src:url('/fonts/clash-700.woff2') format('woff2');
  font-weight:700;font-style:normal;font-display:swap}

/* ---- Satoshi: body ------------------------------------------------------- */
/* THE WEIGHT RANGES ARE NOT DECORATION, THEY ARE A BUG FIX, 13 Aug.
 * The site's CSS declares FIVE Satoshi weights (400, 500, 600, 700, 900) and
 * three files exist. Measured in the browser: font-weight:400 and 500 render
 * at identical widths, and so do 600 and 700, because the CSS font-matching
 * algorithm silently substitutes the nearest available face. So every author
 * who wrote 400 for body copy got 500, and every 600 on a button got 700, and
 * nothing anywhere said so. That is most of the "inconsistent weights" feeling:
 * the stylesheet describes a four-weight system that cannot exist.
 * Declaring a RANGE per file makes the substitution explicit and permanent
 * instead of implicit and version-dependent. Nothing changes visually; what
 * changes is that the stylesheet now tells the truth about what it can render.
 */
@font-face{font-family:'Satoshi';src:url('/fonts/satoshi-500.woff2') format('woff2');
  font-weight:400 500;font-style:normal;font-display:swap}
@font-face{font-family:'Satoshi';src:url('/fonts/satoshi-700.woff2') format('woff2');
  font-weight:600 700;font-style:normal;font-display:swap}
@font-face{font-family:'Satoshi';src:url('/fonts/satoshi-900.woff2') format('woff2');
  font-weight:800 900;font-style:normal;font-display:swap}

/* ---- AND THAT IS THE WHOLE LIST, 13 AUG.
 * Space Grotesk is gone. It carried every eyebrow, every small label and every
 * number on the site, and it was the third voice in a design that only ever
 * needed two. The labels are Satoshi now, at 700 and tracked, which is the same
 * job done by the face that was already doing the body copy.
 * TWO FILES LEFT THE REPO WITH IT: grotesk-500-latin.woff2 and its -ext pair.
 * Nothing references them, so keeping them would only mean shipping bytes that
 * can never be read.
 */
