/* Tulpel design tokens — the one place a colour, a size or a duration is
 * decided (ZAB-475). The landing page (`site/`) is the design source; this
 * file is that design written down so every surface can use it:
 *
 *   site/site.css                 the landing page (light only: its <html>
 *                                 carries data-theme="light")
 *   apps/web/host.css             the host, the console, the gallery — and
 *                                 therefore every generated app, because a
 *                                 generated app is a `tulpel.ui` tree that
 *                                 the host renders
 *   crates/builder/ui/index.html  the builder page, via /host/tokens.css
 *
 * Read `docs/design/style-guide.md` for what each token means and which
 * pairs are checked for contrast. `crates/builder/tests/style.rs` parses
 * this file: it computes the WCAG ratio of every documented pair in both
 * themes and fails when one drops below AA.
 *
 * `site/tokens.css` is a byte-identical copy, because `site/` deploys as a
 * standalone static directory (Cloudflare Pages, and /app/site in the
 * image) and cannot reach `apps/web`. The same test fails when the two
 * drift; the style guide says how to re-sync.
 *
 * Nothing here is a component. Components live in `host.css`.
 */

/* The two faces, served from here (ZAB-475). `fonts/` sits beside this file
 * in both places it lives — `apps/web/fonts/` behind `/host/fonts/…`, and
 * `site/fonts/` beside the landing page — so one relative URL is correct on
 * every surface: the host, the console, the gallery, the builder page and
 * the landing site all resolve it against whichever copy of this file they
 * loaded.
 *
 * Nothing here reaches a third party. That is the whole point of vendoring
 * them: the host is offline-first and installable, and the landing page no
 * longer tells Google the address of everybody who reads it.
 *
 * Figtree is one variable file covering 400–700, which is smaller than the
 * four static weights it replaces and is why `font-weight` is a range. DM
 * Mono is static and ships as the two weights the product draws. Both are
 * the Latin subset only, which is every character these pages contain; a
 * word outside it falls through to the platform's own face rather than
 * pulling a second file.
 *
 * `font-display: swap` everywhere: a face that has not arrived is a page in
 * the fallback face, never a page with no words. `apps/web/fonts/README.md`
 * records where each file came from and its SHA-256, and
 * `crates/builder/tests/style.rs` fails if a rule here names a file that is
 * not in the repository, points at a URL, or loses its `swap`.
 */
@font-face {
  font-family: "Figtree";
  font-style: normal;
  font-weight: 400 700;
  font-display: swap;
  src: url("fonts/figtree-latin.woff2") format("woff2");
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
@font-face {
  font-family: "DM Mono";
  font-style: normal;
  font-weight: 400;
  font-display: swap;
  src: url("fonts/dm-mono-400-latin.woff2") format("woff2");
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
@font-face {
  font-family: "DM Mono";
  font-style: normal;
  font-weight: 500;
  font-display: swap;
  src: url("fonts/dm-mono-500-latin.woff2") format("woff2");
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}

:root {
  /* ---- brand constants: the same in both themes ---------------------- */
  --tp-brand-ink: #0F0F10;        /* the ink the landing page is drawn in */
  --tp-brand-paper: #FFFFFF;
  --tp-brand-lilac: #7C5CFF;      /* the mark's violet. Decoration only:  */
  --tp-brand-lavender: #B9A6FF;   /* under 4.5:1 on white, so never text  */
  --tp-brand-mist: #E9E2FF;
  --tp-brand-violet: #1B1140;     /* the dark panel at the foot of the page */
  --tp-brand-violet-2: #241A4F;
  --tp-brand-on-violet: #B8B8C2;       /* 8.89:1 on --tp-brand-violet */
  --tp-brand-on-violet-quiet: #9A9AA6; /* 6.28:1 on --tp-brand-violet */
  --tp-brand-violet-line: #5B4A8A;     /* a border drawn on the violet panel */
  --tp-brand-violet-quiet: #8F86B3;    /* a placeholder inside it — not a label */
  --tp-brand-ink-hover: #2A2A30;       /* the ink button, pressed */
  --tp-brand-tint: #F8F7FC;            /* the landing page's own panel wash */

  /* ---- semantic colour, light ---------------------------------------- */
  --tp-bg: #FFFFFF;               /* the page */
  --tp-surface: #FFFFFF;          /* bars, cards, sheets — paper on it */
  --tp-panel: #F5F5F7;            /* a quiet block inside a surface */
  --tp-ink: #0F0F10;              /* primary text */
  --tp-ink-2: #3A3A40;            /* secondary text, still full sentences */
  --tp-muted: #6E6E77;            /* labels, captions, timestamps */
  --tp-line: #E4E4E7;             /* hairlines between things */
  --tp-line-strong: #0F0F10;      /* the landing page's structural rules */
  --tp-accent: #7C5CFF;           /* decoration: indicators, dots, rules */
  --tp-accent-strong: #5B3FE0;    /* accent that carries text, either way */
  --tp-accent-hover: #4A31C4;
  --tp-accent-soft: #E9E2FF;
  --tp-on-accent: #FFFFFF;        /* text on --tp-accent-strong */
  --tp-ok: #136B3A;
  --tp-ok-soft: #E4F3EA;
  --tp-warn: #8A4B08;
  --tp-warn-soft: #FBF0E4;
  --tp-bad: #B3261E;
  --tp-bad-soft: #FBEAE9;
  --tp-focus: #5B3FE0;            /* the focus ring */
  --tp-overlay: rgba(15, 15, 16, 0.45);
  --tp-shadow-raised: 0 1px 2px rgba(15, 15, 16, 0.06);
  --tp-shadow-sheet: 0 12px 40px rgba(15, 15, 16, 0.18);

  /* ---- type: the two faces ---------------------------------------------
   * Figtree for everything, DM Mono for a label, a code, an id or a
   * coordinate — the landing page's faces, and therefore the product's.
   * Both are served from `fonts/` beside this file (the `@font-face` block
   * above), under the SIL Open Font License, and no page in this repository
   * fetches a font from anywhere else.
   *
   * The stacks still name the platform's own UI face after them — San
   * Francisco on iOS and macOS, Roboto on Android, Segoe on Windows —
   * because a face can always fail to arrive: a cold cache with no network,
   * a character outside the Latin subset, a proxy that strips `font/woff2`.
   * The scale is drawn to work in that fallback as much as in Figtree, so
   * nothing in it depends on a face's metrics. */
  --tp-font-sans: "Figtree", system-ui, -apple-system, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;
  --tp-font-mono: "DM Mono", ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;

  /* ---- type: one ladder -------------------------------------------------
   * Every size in the product is a step on this ladder, and the ladder is
   * in `rem`, never in `px`, so the person's own text size is what it is
   * measured against: `host.css` sets the root to 100%, and a device set to
   * large text draws every generated app larger with no program and no
   * stylesheet asked (ZAB-486). The comment on each line is the step at a
   * 16 px root, which is the design's own size.
   *
   * `--tp-font-size-base` is the floor for anything that carries a
   * sentence: 16 px is the size the landing page sets its body copy in, and
   * `crates/builder/tests/style.rs` fails if it is ever made smaller. The
   * steps below it are for a label, a caption, a coordinate or a badge —
   * words a person reads one of, not a paragraph of. */
  --tp-font-size-3xs: 0.625rem;    /* 10px — the window chrome in an illustration */
  --tp-font-size-2xs: 0.6875rem;   /* 11px — the mono label: uppercase, tracked */
  --tp-font-size-xs: 0.75rem;      /* 12px — badges, coordinates, hints */
  --tp-font-size-sm: 0.8125rem;    /* 13px — captions, subtitles, bar controls */
  --tp-font-size-md: 0.9375rem;    /* 15px — dense body: a card, a table cell */
  --tp-font-size-base: 1rem;       /* 16px — body. Never smaller than this. */
  --tp-font-size-lg: 1.0625rem;    /* 17px — a row's title, the brand */
  --tp-font-size-xl: 1.25rem;      /* 20px — a sheet's heading, a question */
  --tp-font-size-2xl: 1.625rem;    /* 26px — the builder's page title */
  --tp-font-size-3xl: 1.875rem;    /* 30px — a screen title in an illustration */
  /* Inline code, sized against the text it sits in rather than the root, so
   * a `code` inside a heading stays inside that heading. */
  --tp-font-size-code-inline: 0.875em;

  /* The fluid steps: a heading that answers to the width it is given. Each
   * one is `clamp(phone, viewport, desktop)` and nothing between them is
   * hand-tuned per breakpoint. */
  --tp-font-size-lede: clamp(1.125rem, 1.5vw, 1.375rem);      /* 18–22px */
  --tp-font-size-display-sm: clamp(1.5rem, 2.6vw, 1.875rem);  /* 24–30px — a document's h2 */
  --tp-font-size-display: clamp(1.625rem, 4vw, 2.125rem);     /* 26–34px — a screen's h1 */
  --tp-font-size-display-lg: clamp(2.125rem, 3.4vw, 3rem);    /* 34–48px — the violet panel */
  --tp-font-size-display-xl: clamp(2.25rem, 3.6vw, 3.25rem);  /* 36–52px — a section's h2 */
  --tp-font-size-hero: clamp(3.5rem, 7.2vw, 6.5rem);          /* 56–104px — the one h1 */

  /* The first pass's names (ZAB-475), kept as aliases so a rule written
   * against them still draws the same thing. New rules use the ladder. */
  --tp-text-label: var(--tp-font-size-2xs);
  --tp-text-xs: var(--tp-font-size-xs);
  --tp-text-sm: var(--tp-font-size-sm);
  --tp-text-base: var(--tp-font-size-base);
  --tp-text-md: var(--tp-font-size-lg);
  --tp-text-lg: var(--tp-font-size-xl);
  --tp-text-xl: var(--tp-font-size-2xl);
  --tp-text-display: var(--tp-font-size-display);

  /* ---- type: leading ----------------------------------------------------
   * Six, and each one belongs to a size band. The bigger the type, the
   * tighter it is set: a 104 px headline at 1.5 would be a ladder of
   * unrelated lines. */
  --tp-leading-hero: 0.95;      /* the hero's two words */
  --tp-leading-heading: 1.05;   /* a section's h2 */
  --tp-leading-tight: 1.15;     /* display, and a card's own title */
  --tp-leading-snug: 1.3;       /* a row's title, a sheet's heading */
  --tp-leading-lede: 1.45;      /* the lede, a pulled quote */
  --tp-leading: 1.5;            /* prose, and everything unqualified */

  /* ---- type: tracking ---------------------------------------------------
   * Positive on the mono label, which is uppercase and needs the air;
   * negative from 17 px up, and more of it the larger the type gets. */
  --tp-tracking-label: 0.12em;    /* the uppercase mono label */
  --tp-tracking-wide: 0.1em;      /* the same label at 10px, in an illustration */
  --tp-tracking-snug: -0.01em;    /* 17–20px */
  --tp-tracking-tight: -0.02em;   /* a title, the brand */
  --tp-tracking-display: -0.035em;/* display and section headings */
  --tp-tracking-hero: -0.045em;   /* the hero alone */

  --tp-weight-regular: 400;
  --tp-weight-medium: 500;
  --tp-weight-semibold: 600;
  --tp-weight-bold: 700;

  /* ---- space ------------------------------------------------------------
   * One ladder: 4 px steps to 32, then 8 px steps, then the three page-sized
   * ones. A gap, a padding or a margin anywhere in the product is a step on
   * it — `crates/builder/tests/style.rs` fails a literal of 4 px or more in
   * `host.css`, the builder page, `site.css` or `policy.css`.
   *
   * The half steps (`-4h`, `-5h`, `-6h`, `-7h`) sit between the whole ones
   * and carry the landing page's rhythm: 20, 28, 40 and 56. They are named
   * rather than renumbered because the whole steps are referenced by name
   * all over the product and a number that changed meaning would move a
   * layout silently. */
  --tp-space-1: 4px;
  --tp-space-2: 8px;
  --tp-space-3: 12px;
  --tp-space-4: 16px;
  --tp-space-4h: 20px;
  --tp-space-5: 24px;
  --tp-space-5h: 28px;
  --tp-space-6: 32px;
  --tp-space-6h: 40px;
  --tp-space-7: 48px;
  --tp-space-7h: 56px;
  --tp-space-8: 64px;
  --tp-space-9: 72px;
  --tp-space-10: 80px;
  --tp-space-11: 96px;
  /* Under a step: a hairline nudge, and the only sub-4 px values in the
   * product. A badge's own padding, a list marker's offset. */
  --tp-space-hair: 2px;
  --tp-space-hair-2: 3px;

  /* ---- layout -----------------------------------------------------------
   * How wide a thing is allowed to be, and how much air is beside it. A
   * screen and a sheet are measured for reading; the landing page is
   * full-bleed with a gutter that grows with the window, because it is a
   * poster and not a document. */
  --tp-measure: 720px;             /* a screen: the readable column */
  --tp-measure-wide: 1100px;       /* the console's table, the gallery */
  --tp-measure-builder: 820px;     /* the builder page: a form, not a table */
  --tp-measure-sheet: 420px;       /* a platform sheet */
  --tp-measure-lede: 600px;        /* a lede beside a headline */
  --tp-measure-prose: 60ch;        /* prose with no column to hold it */
  --tp-measure-doc: 76ch;          /* a policy document, which is all prose */
  --tp-cell-max: 280px;            /* a table cell before it ellipsises */
  --tp-map-h: 320px;               /* the map frame, above its pin list */
  --tp-editor-h: 420px;            /* the spec, open for editing */
  --tp-input-h-multi: 76px;        /* a sentence box, before it grows */
  --tp-measure-aside: 480px;       /* the landing page's second column */
  --tp-gutter: clamp(16px, 4vw, 32px);      /* beside a screen */
  --tp-gutter-page: clamp(16px, 6.6vw, 96px); /* beside the landing page */
  /* The three breakpoints. A media query cannot read a custom property, so
   * these are here to be read by a person: 1100px is where two columns
   * become two-up, 720px where everything becomes one column, and 560px is
   * the host's own phone width — where a form collapses and a sheet takes
   * the bottom edge. They are written out in the style guide. */

  /* ---- shape ------------------------------------------------------------ */
  --tp-radius-xs: 4px;            /* a checkbox in an illustration */
  --tp-radius-sm: 6px;            /* a chip inside a control */
  --tp-radius: 8px;               /* controls, the landing page's windows */
  --tp-radius-card: 12px;         /* rows, cards, panels */
  --tp-radius-sheet: 16px;        /* a sheet, and a phone's own cards */
  --tp-radius-pill: 999px;        /* badges, dots, avatars */
  --tp-radius-phone: 48px;        /* the phone illustration's shell */
  --tp-radius-phone-screen: 38px; /* and its glass */
  --tp-border: 1px;               /* every hairline in the product */
  --tp-border-strong: 3px;        /* an attempt's spine, a state's edge */

  /* ---- controls -------------------------------------------------------- */
  --tp-touch-min: 44px;           /* every tappable thing, both hosts */
  --tp-control-h: 44px;
  --tp-control-h-lg: 52px;        /* the landing page's calls to action */
  --tp-check: 1.375rem;           /* the paint of a checkbox, inside its 44px */
  --tp-focus-width: 2px;          /* the ring; wider under prefers-contrast */

  /* ---- motion ---------------------------------------------------------- */
  --tp-dur-fast: 120ms;
  --tp-dur: 200ms;
  --tp-dur-slow: 800ms;           /* one spinner, one revolution */
  --tp-ease: cubic-bezier(0.2, 0.8, 0.2, 1);
  --tp-ease-linear: linear;

  /* ---- the device's own edges (ADR 0006) -------------------------------- */
  /* Zero in a browser window; the notch, the home indicator and the rounded
   * corners in the Capacitor host, which sets viewport-fit=cover. */
  --tp-safe-top: env(safe-area-inset-top, 0px);
  --tp-safe-right: env(safe-area-inset-right, 0px);
  --tp-safe-bottom: env(safe-area-inset-bottom, 0px);
  --tp-safe-left: env(safe-area-inset-left, 0px);
}

/* Dark. The two blocks below declare the same tokens with the same values —
 * the first follows the device, the second is the gallery's toggle and
 * anything else that pins a theme. `crates/builder/tests/style.rs` fails
 * when they disagree.
 *
 * `site/` opts out by carrying data-theme="light": the landing page is a
 * light design that was approved as one. */
@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) {
    --tp-bg: #121215;
    --tp-surface: #1A1A1F;
    --tp-panel: #232329;
    --tp-ink: #F4F4F6;
    --tp-ink-2: #C9C9D3;
    --tp-muted: #9A9AA6;
    --tp-line: #2E2E36;
    --tp-line-strong: #4A4A57;
    --tp-accent: #A48BFF;
    --tp-accent-strong: #A48BFF;
    --tp-accent-hover: #BCA9FF;
    --tp-accent-soft: #241E3C;
    --tp-on-accent: #141220;
    --tp-ok: #5FE3A1;
    --tp-ok-soft: #142B1F;
    --tp-warn: #F0B267;
    --tp-warn-soft: #2E2312;
    --tp-bad: #FF9A93;
    --tp-bad-soft: #2E1817;
    --tp-focus: #A48BFF;
    --tp-overlay: rgba(5, 5, 7, 0.6);
    --tp-shadow-raised: 0 1px 2px rgba(0, 0, 0, 0.4);
    --tp-shadow-sheet: 0 12px 40px rgba(0, 0, 0, 0.55);
  }
}

:root[data-theme="dark"] {
  --tp-bg: #121215;
  --tp-surface: #1A1A1F;
  --tp-panel: #232329;
  --tp-ink: #F4F4F6;
  --tp-ink-2: #C9C9D3;
  --tp-muted: #9A9AA6;
  --tp-line: #2E2E36;
  --tp-line-strong: #4A4A57;
  --tp-accent: #A48BFF;
  --tp-accent-strong: #A48BFF;
  --tp-accent-hover: #BCA9FF;
  --tp-accent-soft: #241E3C;
  --tp-on-accent: #141220;
  --tp-ok: #5FE3A1;
  --tp-ok-soft: #142B1F;
  --tp-warn: #F0B267;
  --tp-warn-soft: #2E2312;
  --tp-bad: #FF9A93;
  --tp-bad-soft: #2E1817;
  --tp-focus: #A48BFF;
  --tp-overlay: rgba(5, 5, 7, 0.6);
  --tp-shadow-raised: 0 1px 2px rgba(0, 0, 0, 0.4);
  --tp-shadow-sheet: 0 12px 40px rgba(0, 0, 0, 0.55);
}

/* More contrast, when the device asks for it. Nothing here is a new colour:
 * hairlines become the structural rule the landing page draws with, quiet
 * text becomes secondary text, and the focus ring gets thicker. A device
 * that does not ask is untouched, so the approved design is still the
 * default (ZAB-486). */
@media (prefers-contrast: more) {
  :root {
    --tp-line: var(--tp-line-strong);
    --tp-muted: var(--tp-ink-2);
    --tp-focus-width: 3px;
  }
}
