/* Generated by build.sh — edit site.src.css or ../tokens/, never this. */
/* Geist and Geist Mono, subsetted for the SDK.

   The design system ships 24 OTF faces totalling 3.9 MB. That is right for a
   design tool and impossible for a package that has to load inside a Chrome
   extension popup, so only the faces the product rules can actually reach are
   shipped here, as latin-subset WOFF2 — 41 KB for all three.

   Which three follows from the rules rather than taste: "product UI uses only
   400 and 500", and Geist Mono carries everything numeric. Weights 300 and
   700 are reserved for editorial and are deliberately absent — a marketing
   page should link the design system's own stylesheet, not this one. A
   product component asking for 600 or 700 gets a synthesised bold, which is a
   visible signal that it has left the product type scale.

   Self-hosted, never a CDN: Manifest V3 blocks remote font loads, so an
   extension could not use these otherwise. */

@font-face {
  font-family: "Geist";
  font-style: normal;
  font-weight: 400;
  font-display: swap;
  src: url("/fonts/Geist-Regular.woff2") format("woff2");
}

@font-face {
  font-family: "Geist";
  font-style: normal;
  font-weight: 500;
  font-display: swap;
  src: url("/fonts/Geist-Medium.woff2") format("woff2");
}

@font-face {
  font-family: "Geist Mono";
  font-style: normal;
  font-weight: 400;
  font-display: swap;
  src: url("/fonts/GeistMono-Regular.woff2") format("woff2");
}
/* OpenApps color system.
   Layer 1 — the brand palette, verbatim from the supplied brand CSS (hex values unchanged).
   Layer 2 — ramps derived from those hues with color-mix (no new hues invented).
   Layer 3 — semantic aliases. Product code should only ever use layer 3. */
:root{
  /* ---- 1. Brand palette (verbatim) ---- */
  --black:#000000;
  --white:#ffffff;
  --blue:#15b9eb;
  --green:#00c896;
  --red:#ff4d4d;
  --yellow:#f2e863;
  --orange:#ff8c42;
  --violet:#8c6cff;
  --gray-050:#f8f8f7;
  --gray-100:#f0f0ee;
  --gray-200:#deded9;
  --gray-300:#c1c0b8;
  --gray-400:#989898;
  --gray-500:#7c7c7c;
  --gray-600:#656565;
  --gray-700:#464646;
  --gray-800:#3d3d3d;
  --gray-900:#282828;
  --gray-950:#1a1a1a;

  --white-90:rgba(255,255,255,0.9);
  --white-80:rgba(255,255,255,0.8);
  --white-70:rgba(255,255,255,0.7);
  --white-60:rgba(255,255,255,0.6);
  --white-50:rgba(255,255,255,0.5);
  --white-40:rgba(255,255,255,0.4);
  --white-30:rgba(255,255,255,0.3);
  --white-20:rgba(255,255,255,0.2);
  --white-10:rgba(255,255,255,0.1);
  --white-04:rgba(255,255,255,0.04);
  --white-00:rgba(255,255,255,0);
  --black-90:rgba(0,0,0,0.9);
  --black-80:rgba(0,0,0,0.8);
  --black-70:rgba(0,0,0,0.7);
  --black-60:rgba(0,0,0,0.6);
  --black-50:rgba(0,0,0,0.5);
  --black-40:rgba(0,0,0,0.4);
  --black-30:rgba(0,0,0,0.3);
  --black-20:rgba(0,0,0,0.2);
  --black-10:rgba(0,0,0,0.1);
  --black-04:rgba(0,0,0,0.04);
  --black-00:rgba(0,0,0,0);

  --color-font:#020202;
  --color-background:#f2f2f2;
  --color-background-alt:#111111;
  --color-primary-focus:#e0e0e0;
  --color-filter-background:rgb(42,42,42);
  --color-nav:#ffffff;

  /* ---- 2. Derived ramps ---- */
  --gray-0:var(--white);
  --gray-1000:var(--black);
  --gray-025:#fcfcfb;
  --gray-150:#e7e7e3;

  --green-50:color-mix(in oklab,var(--green) 10%,var(--white));
  --green-100:color-mix(in oklab,var(--green) 18%,var(--white));
  --green-200:color-mix(in oklab,var(--green) 34%,var(--white));
  --green-300:color-mix(in oklab,var(--green) 55%,var(--white));
  --green-400:color-mix(in oklab,var(--green) 78%,var(--white));
  --green-500:var(--green);
  --green-600:color-mix(in oklab,var(--green) 88%,var(--black));
  --green-700:color-mix(in oklab,var(--green) 72%,var(--black));
  --green-800:color-mix(in oklab,var(--green) 55%,var(--black));
  --green-900:color-mix(in oklab,var(--green) 38%,var(--black));

  --blue-100:color-mix(in oklab,var(--blue) 14%,var(--white));
  --blue-500:var(--blue);
  --blue-600:color-mix(in oklab,var(--blue) 74%,var(--black));

  --red-100:color-mix(in oklab,var(--red) 14%,var(--white));
  --red-500:var(--red);
  --red-600:color-mix(in oklab,var(--red) 78%,var(--black));

  --yellow-100:color-mix(in oklab,var(--yellow) 30%,var(--white));
  --yellow-500:var(--yellow);

  --orange-100:color-mix(in oklab,var(--orange) 16%,var(--white));
  --orange-500:var(--orange);
  --orange-600:color-mix(in oklab,var(--orange) 68%,var(--black));
  /* legacy alias — warning states used to be amber */
  --amber-100:var(--orange-100);
  --amber-500:var(--orange-500);
  --amber-600:var(--orange-600);

  /* ---- Extended hues ----
     --violet is a first-class brand hue (it replaces the source CSS's --purple, which
     was an off-white and almost certainly a mistake). --teal and --lime are MIXED FROM
     the brand hues, never sampled, so they sit at the same chroma level. */
  --violet-100:color-mix(in oklab,var(--violet) 14%,var(--white));
  --violet-500:var(--violet);
  --violet-600:color-mix(in oklab,var(--violet) 76%,var(--black));
  --teal:color-mix(in oklab,var(--green) 62%,var(--blue) 38%);
  --lime:color-mix(in oklab,var(--yellow) 52%,var(--green) 48%);
  /* Five more hues, minted the same way and for the same reason teal and lime
     were: the suite outgrew eight accent slots. Twelve applications were sharing
     them by September 2026, four of them doubled up and one slot claimed three
     times, which is not a naming problem — it is a palette that ran out. Each
     of these sits between two adjacent brand hues at the same chroma level, so
     nothing here is a new colour, only a new position on a wheel the brand
     already defines. */
  --amber:color-mix(in oklab,var(--orange) 55%,var(--yellow) 45%);
  --coral:color-mix(in oklab,var(--red) 58%,var(--orange) 42%);
  --rose:color-mix(in oklab,var(--red) 55%,var(--violet) 45%);
  --indigo:color-mix(in oklab,var(--violet) 60%,var(--blue) 40%);
  --cyan:color-mix(in oklab,var(--blue) 62%,var(--teal) 38%);
  --plum:color-mix(in oklab,var(--violet) 66%,var(--red) 34%);
  /* Slot 15, minted 6 September 2026 for opentagged. By then every adjacent
     pair of brand hues already had a midpoint, so this one is a midpoint of two
     minted slots — blue and indigo, the widest gap left on the wheel at 44.7°.
     Measured: 6.5 ΔE from blue and 6.3 from indigo (OKLab ×100), which is the
     spacing the ring already has elsewhere (rose and plum sit 6.9 apart). That
     is fine for product accents, which are never seen side by side, and it does
     mean the ring is now at its practical limit: a sixteenth hue at this chroma
     would land under 5 ΔE from both neighbours. Separate by lightness or chroma
     instead, or let two products share a hue deliberately. */
  --azure:color-mix(in oklab,var(--blue) 50%,var(--indigo) 50%);
  /* Slot 16. The hue ring is full; this one separates by lightness and
     chroma instead, per ACCENT-SLOTS.md. Ledger ink: a deep blue that cannot
     be mistaken for a red or green result state. */
  --ink:color-mix(in oklab,var(--blue) 54%,var(--black) 46%);
  /* Slot 17, minted the same way: a hue deepened, not the ring bisected. */
  --mulberry:color-mix(in oklab,var(--violet) 58%,var(--black) 42%);
  --petrol:color-mix(in oklab,var(--cyan) 62%,var(--black) 38%);
  /* Slot 19. The first deepening on the warm side — ink, mulberry and petrol
     are all cool, and a fourth cool one would have crowded them. Deep enough
     to carry white type, because this one is used as a key fill. */
  --rust:color-mix(in oklab,var(--orange) 56%,var(--black) 44%);
  /* Slot 20. Yellow deepened — the only brand hue still undeepened that is not
     a result state. Green and red were unavailable to opendocscan for the
     reason recorded three times in ACCENT-SLOTS.md: a scanner reports "read"
     in green and "failed" in red, so an accent near either says the wrong
     thing about the row it tints. 31.1 dE from its nearest neighbour (rust),
     which is the widest separation any deepened slot has. */
  --brass:color-mix(in oklab,var(--yellow) 56%,var(--black) 44%);
  /* Slot 21. Not a deepening and not a bisection of the ring — by September
     2026 both were exhausted: every candidate deepened from the green/teal
     family landed under 5 dE from petrol, which is the crowding limit stated
     below. This one was found by searching the ring's own chroma band
     (0.06–0.15 C) at usable accent lightness for the largest gap, then
     expressed as a mix rather than kept as the sampled hex. 12.3 dE from its
     nearest neighbour (rust) and 14.1 from the mulberry it is built on.
     opennotetaker paints success green, failure red and warnings orange, so
     all three of those families were excluded before separation was
     considered at all. Carries white type at 6.2:1; as *text* on a dark page
     it is only 3.4:1, so small type in the accent lifts toward white the way
     brass does. */
  --wine:color-mix(in oklab,var(--mulberry) 62%,var(--red) 38%);
  /* Slot 22, minted 8 September 2026 for opentabs — the first *lift* in the
     ring, where 16-21 are all deepenings. Deepening was unavailable: opentabs
     paints success green, failure red and warnings orange, and every deepening
     of what remained landed in the lane ink, petrol and mulberry already hold.
     Lifting petrol instead moves along lightness in the other direction and
     opens ground nothing occupies: 14.3 dE from petrol itself, 14.5 from brass
     and azure, and 18.4 from the nearest of the product's own result colours.
     Chosen over the ring's widest gap (an olive at H 106, 16.3 dE) because
     opentabs' mark has been a blue-cyan since it was drawn, and the register
     asks for separation, not for a product to stop looking like itself.
     It is a fill before it is anything else — the mark is a rounded square
     with white bars on it — and it carries those bars at 3.90:1, where the
     slot-2 blue it replaces managed 2.29:1. The icon got more legible at 16px
     by being moved off a colour it was never entitled to. */
  --slate:color-mix(in oklab,var(--petrol) 74%,var(--white));
  /* Slot 23, minted 11 September 2026 for opensync — the sync engine's own
     relay service. This is the olive the slot-22 note measured and set aside:
     the ring's widest remaining gap, 15.1 dE from its nearest neighbours
     (green and orange) where the crowding threshold is 5. It was set aside for
     opentabs because that product's mark was already blue-cyan; opensync has
     no drawn mark, and what it *was* using — a private #0C6E77 in its client
     stylesheet, never tokenised — sits 3.8 dE from petrol, inside the
     threshold and squarely in openfilesend's lane. It was not entitled to it.
     The usual elimination does not bind here: opensync's surfaces paint no
     success or failure colour at all, only an accent, so the warm families
     that were unavailable to opendocscan and opentabs are available to this
     one. 7.01:1 as accent text on the dark page; 2.81:1 on white, so on a
     light ground it deepens toward brass rather than being used raw. As a fill
     it takes black glyphs at 7.48:1, not white at 2.81:1. */
  --olive:color-mix(in oklab,var(--brass) 57%,var(--yellow) 43%);
  /* Slot 24, iris: minted 14 Sep 2026 for openphotoshop. The ring had no
     room at full chroma; violet deepened by ink lands 13.4 dE from its nearest
     slot (violet) and 20.1 from every result colour. See ACCENT-SLOTS.md. */
  --iris:color-mix(in oklab,var(--violet) 56%,var(--ink));

  /* ---- Accent slots ----
     Generic, product-neutral names. Any application built on this system claims a slot
     rather than a colour, so palettes can be re-pointed centrally without touching code.
     Rule: one slot per application, never two in a single surface.
     Who holds which is recorded in tokens/ACCENT-SLOTS.md — check it before
     claiming one. No single product can see the collision it is about to make. */
  --accent-1:var(--green);
  --accent-2:var(--blue);
  --accent-3:var(--violet);
  --accent-4:var(--orange);
  --accent-5:var(--red);
  --accent-6:var(--yellow);
  --accent-7:var(--teal);
  --accent-8:var(--lime);
  --accent-9:var(--amber);
  --accent-10:var(--coral);
  --accent-11:var(--rose);
  --accent-12:var(--indigo);
  --accent-13:var(--cyan);
  --accent-14:var(--plum);
  --accent-15:var(--azure);
  --accent-16:var(--ink);
  --accent-17:var(--mulberry);
  --accent-18:var(--petrol);
  --accent-19:var(--rust);
  --accent-20:var(--brass);
  --accent-21:var(--wine);
  --accent-22:var(--slate);
  --accent-23:var(--olive);
  --accent-24:var(--iris);

  /* ---- App accents ----
     The OpenApps suite's own assignment. These are aliases: consuming products should
     either re-point them or use the --accent-N slots directly. */
  --app-notes:var(--accent-6);
  --app-tasks:var(--accent-1);
  --app-calendar:var(--accent-5);
  --app-mail:var(--accent-4);
  --app-files:var(--accent-7);
  --app-draw:var(--accent-3);
  --app-terminal:var(--accent-8);
  --app-vault:var(--gray-600);
  /* OpenScreenCapture — a product in the family, with its own mobile app and extension. */
  --app-screenshot:var(--accent-2);
  /* Shipped products. These were defined only in downstream copies of this
     file until 7 September 2026, which is why --app-pdfedit resolved to
     nothing wherever the local copy was not the one in use. Promoted here so
     the source of truth actually holds them. */
  --app-pdfedit:var(--accent-5);
  --app-frame:var(--accent-3);
  /* OpenSubs was OpenVidSub. The product renamed; the token had not, and
     opensubs' local copy carried a note asking for exactly this rename.
     --app-vidsub stays as a legacy alias so no existing surface breaks. */
  --app-opensubs:var(--accent-4);
  --app-vidsub:var(--app-opensubs);
  /* OpenDocScan. Slot 20; see the note on --brass. */
  --app-docscan:var(--accent-20);
  /* opentabs held slot 2 verbatim until 8 September 2026 — blue, which
     openinbox claims and the shipped opencapture also uses. See ACCENT-SLOTS.md. */
  --app-opentabs:var(--accent-22);

  /* ---- 3. Semantic aliases (use these) ---- */
  --brand:var(--green);
  --brand-strong:var(--green-600);
  --brand-soft:var(--green-50);
  --brand-contrast:var(--black);

  --bg-page:var(--color-background);
  --bg-subtle:var(--gray-050);
  --bg-sunken:var(--gray-100);
  --surface-card:var(--white);
  --surface-raised:var(--white);
  --surface-inverse:var(--color-background-alt);
  --surface-hover:var(--black-04);
  --surface-active:var(--gray-100);
  --surface-selected:var(--green-50);

  --text-strong:var(--color-font);
  --text-body:var(--gray-800);
  --text-muted:var(--gray-600);
  --text-faint:color-mix(in oklab, var(--gray-500) 30%, var(--gray-600));
  --text-inverse:var(--white);
  --text-brand:var(--green-700);
  --text-link:var(--color-font);
  --text-link-hover:var(--green-700);

  --border-hairline:var(--gray-200);
  --border-strong:var(--gray-300);
  --border-inverse:var(--white-10);
  --border-focus:var(--color-font);

  --success-fg:var(--green-700);
  --success-bg:var(--green-50);
  --warning-fg:var(--orange-600);
  --warning-bg:var(--orange-100);
  --danger-fg:var(--red-600);
  --danger-bg:var(--red-100);
  --info-fg:var(--blue-600);
  --info-bg:var(--blue-100);

  --focus-ring:0 0 0 2px var(--bg-page),0 0 0 4px var(--color-font);
}

/* ---- Brand mark ----
   The mark is typographic: the wordmark sets in the display font, the monogram is a
   numeral in a squircle. These tokens let a host re-skin it without editing the SVGs.
   The BrandMark component reads them, so re-pointing these re-skins every surface.
   Dark overrides live in the shared dark block below, never here. */
:root{
  --logo-fg:var(--color-font);
  --logo-dot:var(--brand);
  --logo-tile-bg:var(--color-font);
  --logo-tile-fg:var(--brand);
  --logo-tile-radius:0.22em;
  --logo-tracking:-0.04em;
}

/* ---- Dark scope ----
   Declared ONCE and emitted into both dark selectors below. If you add a dark override,
   add it to both blocks or neither — they are required to be character-identical.
   .oa-dark  = opt in on any container.
   .oa-auto  = on <html>, follows the OS/browser setting. */
.oa-dark{
  --bg-page:var(--color-background-alt);
  --bg-subtle:var(--gray-950);
  --bg-sunken:var(--black);
  --surface-card:var(--gray-950);
  --surface-raised:var(--gray-900);
  --surface-inverse:var(--white);
  --surface-hover:var(--white-04);
  --surface-active:var(--white-10);
  --surface-selected:color-mix(in oklab,var(--green) 20%,transparent);
  --text-strong:var(--white);
  --text-body:var(--white-70);
  --text-muted:var(--white-60);
  --text-faint:var(--white-50);
  --text-inverse:var(--black);
  --text-brand:var(--green);
  --text-link:var(--white);
  --text-link-hover:var(--green);
  --border-hairline:var(--white-10);
  --border-strong:var(--white-20);
  --border-focus:var(--green);
  --brand-soft:color-mix(in oklab,var(--green) 20%,transparent);
  --brand-contrast:var(--black);
  --success-bg:color-mix(in oklab,var(--green) 18%,transparent);
  --success-fg:var(--green);
  --danger-bg:color-mix(in oklab,var(--red) 20%,transparent);
  --danger-fg:var(--red);
  --warning-bg:color-mix(in oklab,var(--orange) 20%,transparent);
  --warning-fg:var(--orange);
  --info-bg:color-mix(in oklab,var(--blue) 20%,transparent);
  --info-fg:var(--blue);
  --focus-ring:0 0 0 2px var(--bg-page),0 0 0 4px var(--green);
  --logo-fg:var(--white);
  --logo-tile-bg:var(--white);
  --logo-tile-fg:var(--color-font);
}
@media (prefers-color-scheme:dark){
  .oa-auto{
    --bg-page:var(--color-background-alt);
    --bg-subtle:var(--gray-950);
    --bg-sunken:var(--black);
    --surface-card:var(--gray-950);
    --surface-raised:var(--gray-900);
    --surface-inverse:var(--white);
    --surface-hover:var(--white-04);
    --surface-active:var(--white-10);
    --surface-selected:color-mix(in oklab,var(--green) 20%,transparent);
    --text-strong:var(--white);
    --text-body:var(--white-70);
    --text-muted:var(--white-60);
    --text-faint:var(--white-50);
    --text-inverse:var(--black);
    --text-brand:var(--green);
    --text-link:var(--white);
    --text-link-hover:var(--green);
    --border-hairline:var(--white-10);
    --border-strong:var(--white-20);
    --border-focus:var(--green);
    --brand-soft:color-mix(in oklab,var(--green) 20%,transparent);
    --brand-contrast:var(--black);
    --success-bg:color-mix(in oklab,var(--green) 18%,transparent);
    --success-fg:var(--green);
    --danger-bg:color-mix(in oklab,var(--red) 20%,transparent);
    --danger-fg:var(--red);
    --warning-bg:color-mix(in oklab,var(--orange) 20%,transparent);
    --warning-fg:var(--orange);
    --info-bg:color-mix(in oklab,var(--blue) 20%,transparent);
    --info-fg:var(--blue);
    --focus-ring:0 0 0 2px var(--bg-page),0 0 0 4px var(--green);
    --logo-fg:var(--white);
    --logo-tile-bg:var(--white);
    --logo-tile-fg:var(--color-font);
  }
}
:root{
  --radius-xs:4px;
  --radius-sm:6px;
  --radius-md:8px;
  --radius-lg:12px;
  --radius-xl:16px;
  --radius-2xl:24px;
  --radius-full:999px;
  --border-width:1px;
  --border-width-strong:1.5px;
}
/* 4px base grid. Product density is tight: most UI gaps are 8–16. */
:root{
  --space-0:0px;
  --space-1:4px;
  --space-2:8px;
  --space-3:12px;
  --space-4:16px;
  --space-5:20px;
  --space-6:24px;
  --space-7:28px;
  --space-8:32px;
  --space-10:40px;
  --space-12:48px;
  --space-14:56px;
  --space-16:64px;
  --space-20:80px;
  --space-24:96px;
  --space-32:128px;

  /* Layout */
  --container-max:1200px;
  --container-prose:680px;
  --gutter-page:24px;
  --gutter-page-lg:48px;
  --section-y:96px;
  --control-h-sm:28px;
  --control-h-md:34px;
  --control-h-lg:42px;
  --sidebar-w:248px;
  --rail-w:56px;
  --topbar-h:52px;
  --header-height:64px;
  --footer-height:64px;
  /* Browser-extension geometry. Chrome caps popups at 800×600. */
  --popup-w:380px;
  --popup-h:560px;
  --options-max:720px;
}
:root{
  --font-sans:"Geist","Helvetica Neue",Arial,sans-serif;
  --font-display:"Geist","Helvetica Neue",Arial,sans-serif;
  --font-mono:"Geist Mono",ui-monospace,SFMono-Regular,Menlo,monospace;

  --weight-light:300;
  --weight-regular:400;
  --weight-medium:500;
  --weight-bold:700;

  /* Sizes — 15px body. Product UI lives between 12 and 20. */
  --text-2xs:11px;
  --text-xs:12px;
  --text-sm:13px;
  --text-base:15px;
  --text-md:17px;
  --text-lg:20px;
  --text-xl:24px;
  --text-2xl:32px;
  --text-3xl:40px;
  --text-4xl:56px;
  --text-5xl:72px;
  --text-6xl:96px;

  --leading-tight:1.05;
  --leading-snug:1.2;
  --leading-normal:1.45;
  --leading-relaxed:1.6;

  /* Optical tracking: the bigger the type, the tighter it sets */
  --tracking-display:-0.035em;
  --tracking-heading:-0.02em;
  --tracking-body:-0.005em;
  --tracking-wide:0.02em;
  --tracking-caps:0.08em;

  /* Composed roles */
  --type-display:var(--weight-medium) var(--text-4xl)/var(--leading-tight) var(--font-display);
  --type-h1:var(--weight-medium) var(--text-3xl)/1.1 var(--font-display);
  --type-h2:var(--weight-medium) var(--text-2xl)/1.15 var(--font-display);
  --type-h3:var(--weight-medium) var(--text-xl)/1.25 var(--font-sans);
  --type-h4:var(--weight-medium) var(--text-lg)/1.3 var(--font-sans);
  --type-body-lg:var(--weight-regular) var(--text-md)/var(--leading-relaxed) var(--font-sans);
  --type-body:var(--weight-regular) var(--text-base)/var(--leading-normal) var(--font-sans);
  --type-ui:var(--weight-medium) var(--text-sm)/1.3 var(--font-sans);
  --type-caption:var(--weight-regular) var(--text-xs)/1.35 var(--font-sans);
  --type-mono:var(--weight-regular) var(--text-sm)/1.5 var(--font-mono);
  --type-eyebrow:var(--weight-medium) var(--text-xs)/1.2 var(--font-sans);
}
/* Shadows are quiet. Hierarchy comes from hairlines first, elevation second.
   All shadows are pure black at low alpha, from the brand's black-XX ramp. */
:root{
  --shadow-none:none;
  --shadow-xs:0 1px 1px var(--black-04);
  --shadow-sm:0 1px 2px rgba(0,0,0,0.06),0 1px 1px var(--black-04);
  --shadow-md:0 2px 4px rgba(0,0,0,0.06),0 4px 12px rgba(0,0,0,0.06);
  --shadow-lg:0 4px 8px rgba(0,0,0,0.05),0 12px 32px rgba(0,0,0,0.09);
  --shadow-xl:0 8px 16px rgba(0,0,0,0.06),0 24px 64px rgba(0,0,0,0.12);
  --shadow-inset-top:inset 0 1px 0 var(--white-10);
  --shadow-key:0 1px 0 var(--black-10);
  --blur-panel:saturate(180%) blur(20px); /* @kind other */
  --scrim:var(--black-50);
}
/* Motion is short and mechanical. Nothing bounces, nothing overshoots. */
:root{
  --duration-instant:80ms; /* @kind other */
  --duration-fast:120ms; /* @kind other */
  --duration-base:180ms; /* @kind other */
  --duration-slow:260ms; /* @kind other */
  --duration-page:400ms; /* @kind other */
  --ease-standard:cubic-bezier(0.2,0,0,1); /* @kind other */
  --ease-out:cubic-bezier(0.16,1,0.3,1); /* @kind other */
  --ease-in:cubic-bezier(0.4,0,1,1); /* @kind other */
  --ease-linear:linear; /* @kind other */
  --transition-control:background-color var(--duration-fast) var(--ease-standard),border-color var(--duration-fast) var(--ease-standard),color var(--duration-fast) var(--ease-standard),box-shadow var(--duration-fast) var(--ease-standard),opacity var(--duration-fast) var(--ease-standard);
}
@media (prefers-reduced-motion:reduce){
  :root{--duration-instant:0ms; /* @kind other */ --duration-fast:0ms; /* @kind other */ --duration-base:0ms; /* @kind other */ --duration-slow:0ms; /* @kind other */ --duration-page:0ms /* @kind other */}
}
/* Platform foundations — one system, five surfaces:
   local app, web app, mobile app, browser extension, embedded account SDK.
   Everything here is geometry. Colour, type and motion never change per platform. */
:root{
  /* ---- Touch ----
     44px is the floor for anything tappable. Never ship a smaller hit target. */
  --touch-min:44px;
  --control-h-touch-sm:40px;
  --control-h-touch-md:48px;
  --control-h-touch-lg:56px;

  /* ---- Mobile layout ---- */
  --mobile-gutter:16px;
  --navbar-h:52px;
  --tabbar-h:56px;
  --safe-top:env(safe-area-inset-top,0px);
  --safe-bottom:env(safe-area-inset-bottom,0px);
  --sheet-radius:var(--radius-2xl);
  --list-row-h:52px;

  /* ---- Local (desktop) app window ---- */
  --titlebar-h:38px;
  --window-min-w:880px;
  --window-min-h:560px;
  --window-radius:10px;

  /* ---- Embedded SDK ----
     The account/credits surfaces render inside a host app, so they size to a card,
     not a page. --sdk-max is the widest an embedded panel ever gets. */
  --sdk-max:420px;
  --sdk-wide:560px;

  /* ---- Breakpoints ----
     Declared for reference; media queries must use the literal px values. */
  --bp-sm:600px;
  --bp-md:900px;
  --bp-lg:1200px;
  --bp-xl:1440px;
}

/* Touch devices get bigger controls and one step more text, automatically.
   Every component reads --control-h-*, so nothing needs a mobile variant. */
@media (pointer:coarse){
  :root{
    --control-h-sm:var(--control-h-touch-sm);
    --control-h-md:var(--control-h-touch-md);
    --control-h-lg:var(--control-h-touch-lg);
    --text-xs:13px;
    --text-sm:14px;
    --list-row-h:56px;
  }
}
/* Base layer: resets + the handful of element defaults every OpenApps surface assumes. */
*,*::before,*::after{box-sizing:border-box}
html{-webkit-text-size-adjust:100%}
body{margin:0;background:var(--bg-page);color:var(--text-body);font:var(--type-body);letter-spacing:var(--tracking-body);-webkit-font-smoothing:antialiased;text-rendering:optimizeLegibility}
h1,h2,h3,h4,h5,h6{margin:0;color:var(--text-strong);font-weight:var(--weight-medium);letter-spacing:var(--tracking-heading)}
h1{font:var(--type-h1);letter-spacing:var(--tracking-display)}
h2{font:var(--type-h2)}
h3{font:var(--type-h3)}
h4{font:var(--type-h4)}
p{margin:0;text-wrap:pretty}
a{color:var(--text-link);text-decoration:none;text-underline-offset:3px;text-decoration-thickness:1px;transition:color var(--duration-fast) var(--ease-standard)}
a:hover{color:var(--text-link-hover);text-decoration:underline}
code,kbd,pre,samp{font-family:var(--font-mono);font-size:0.92em}
hr{border:0;border-top:var(--border-width) solid var(--border-hairline);margin:var(--space-6) 0}
button,input,select,textarea{font:inherit;letter-spacing:inherit;color:inherit}
:focus-visible{outline:none;box-shadow:var(--focus-ring)}
::selection{background:var(--green-200);color:var(--gray-950)}
img,svg,video{display:block;max-width:100%}
ul,ol{margin:0;padding-left:1.15em}

@keyframes oa-spin{to{transform:rotate(360deg)}}
@keyframes oa-fade-up{from{opacity:0;transform:translateY(4px)}to{opacity:1;transform:none}}
@keyframes oa-marquee{from{transform:translateX(0)}to{transform:translateX(-50%)}}
@media (prefers-reduced-motion:reduce){[style*="oa-marquee"]{animation:none!important}}

/* Type role helpers — for prose and specimen surfaces, not a utility framework. */
.oa-display{font:var(--type-display);letter-spacing:var(--tracking-display);color:var(--text-strong)}
.oa-eyebrow{font:var(--type-eyebrow);letter-spacing:var(--tracking-caps);text-transform:uppercase;color:var(--text-muted)}
.oa-lead{font:var(--type-body-lg);color:var(--text-muted);text-wrap:pretty}
.oa-caption{font:var(--type-caption);color:var(--text-faint)}
.oa-mono{font:var(--type-mono);color:var(--text-muted)}
.oa-hairline{border:var(--border-width) solid var(--border-hairline)}
.oa-grid-bg{background-image:linear-gradient(var(--border-hairline) 1px,transparent 1px),linear-gradient(90deg,var(--border-hairline) 1px,transparent 1px);background-size:24px 24px}
/* ===================================================================
   opensync.network — the site layer.

   Type B: no web app. The product is four applications you install and a
   relay they talk to, so the page's job is to explain the relay and hand
   over two ways to use one. Dark ground, per the type.
   =================================================================== */

/* ---- The product's accent -------------------------------------------
   Slot 23, olive. `--brand` is green in the system — slot 1 verbatim,
   held by openreveal — so a page that leaves it alone paints somebody
   else's colour. Re-pointing the semantic aliases is the whole of what a
   product is supposed to do here; nothing below reads a layer-1 hue. */
:root,
.oa-dark {
  --brand: var(--olive);
  --brand-strong: var(--brass);
  --brand-soft: color-mix(in oklab, var(--olive) 16%, transparent);
  /* Black on olive is 7.48:1; white on it is 2.81:1 and is wrong. */
  --brand-contrast: var(--black);
  --text-brand: var(--olive);
  --text-link-hover: var(--olive);
  --border-focus: var(--olive);
  --focus-ring: 0 0 0 2px var(--bg-page), 0 0 0 4px var(--olive);
  --logo-dot: var(--olive);
  --logo-tile-bg: var(--olive);
  --logo-tile-fg: var(--black);
}

/* ---- Page frame ---------------------------------------------------- */
.container {
  max-width: var(--container-max);
  margin: 0 auto;
  padding-inline: var(--gutter-page-lg);
}
@media (max-width: 900px) {
  .container { padding-inline: var(--space-6); }
}

/* ---- Header -------------------------------------------------------- */
.site-header {
  position: sticky;
  top: 0;
  z-index: 40;
  background: color-mix(in oklab, var(--bg-page) 82%, transparent);
  backdrop-filter: var(--blur-panel);
  -webkit-backdrop-filter: var(--blur-panel);
  border-bottom: var(--border-width) solid var(--border-hairline);
}
.site-header .container {
  display: flex;
  align-items: center;
  gap: var(--space-8);
  height: var(--header-height);
}
.brand {
  display: inline-flex;
  align-items: center;
  gap: var(--space-3);
  color: var(--logo-fg);
  text-decoration: none;
  font: var(--weight-medium) var(--text-md)/1 var(--font-display);
  letter-spacing: var(--logo-tracking);
}
.brand:hover { text-decoration: none; color: var(--logo-fg); }
.brand-tile {
  display: grid;
  place-items: center;
  width: 26px;
  height: 26px;
  border-radius: var(--logo-tile-radius);
  background: var(--logo-tile-bg);
  color: var(--logo-tile-fg);
  font-size: 26px; /* so the em-based radius resolves against the tile */
}
.brand-tile svg { width: 14px; height: 14px; }
.brand .muted { color: var(--text-muted); }
.brand .dot { color: var(--logo-dot); }

.site-nav { display: flex; gap: var(--space-6); flex: 1; }
.site-nav a {
  font: var(--type-ui);
  color: var(--text-muted);
  transition: color var(--duration-fast) var(--ease-standard);
}
.site-nav a:hover { color: var(--text-strong); text-decoration: none; }

.header-actions { display: flex; align-items: center; gap: var(--space-5); }
/* `:not(.btn)` is load-bearing: `.header-actions > a` outranks `.btn-primary`,
   and the button's label turns muted-on-olive at 2.1:1 without it. */
.header-actions > a:not(.btn) { font: var(--type-ui); color: var(--text-muted); }
.header-actions > a:not(.btn):hover { color: var(--text-strong); text-decoration: none; }

@media (max-width: 760px) {
  /* The tabs move to a second row. They do not disappear — hiding them
     leaves a phone with a brand, a button, and no way to reach Pricing. */
  .site-header .container {
    flex-wrap: wrap; height: auto; gap: var(--space-4);
    padding-block: var(--space-3);
  }
  .brand { margin-right: auto; }
  .header-actions { order: 2; }
  /* `flex: 0 0 100%`, not `width: 100%`: the base rule sets `flex: 1`, whose
     `flex-basis: 0%` wins over the width and leaves the tabs sharing a line
     with the button, clipped to about a word each. */
  .site-nav {
    order: 3; flex: 0 0 100%;
    overflow-x: auto; gap: var(--space-5); padding-bottom: var(--space-1);
  }
}

/* ---- Buttons ------------------------------------------------------- */
.btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: var(--space-2);
  height: var(--control-h-lg);
  padding-inline: var(--space-5);
  border: var(--border-width) solid transparent;
  border-radius: var(--radius-md);
  font: var(--weight-medium) var(--text-sm)/1 var(--font-sans);
  cursor: pointer;
  white-space: nowrap;
  transition: background var(--duration-fast) var(--ease-standard),
              border-color var(--duration-fast) var(--ease-standard),
              color var(--duration-fast) var(--ease-standard);
}
.btn:hover { text-decoration: none; }
.btn-sm { height: var(--control-h-md); padding-inline: var(--space-4); }
.btn-primary { background: var(--brand); color: var(--brand-contrast); border-color: var(--brand); }
.btn-primary:hover { background: var(--brass); border-color: var(--brass); color: var(--brand-contrast); }
.btn-secondary { background: transparent; color: var(--text-strong); border-color: var(--border-strong); }
.btn-secondary:hover { background: var(--surface-hover); color: var(--text-strong); }

/* ---- Sections ------------------------------------------------------ */
section {
  border-bottom: var(--border-width) solid var(--border-hairline);
  /* The header is sticky, so an anchor without this lands the heading
     *underneath* it — the reader clicks "Pricing" and arrives at the middle
     of a sentence. It has to follow the header's real height, which doubles
     on a phone when the tabs take their own row. */
  scroll-margin-top: calc(var(--header-height) + var(--space-2));
}
@media (max-width: 760px) { section { scroll-margin-top: 104px; } }
section > .container { padding-block: var(--space-24); }
@media (max-width: 760px) { section > .container { padding-block: var(--space-14); } }

.section-head { max-width: var(--container-prose); }
.eyebrow {
  font: var(--type-eyebrow);
  letter-spacing: var(--tracking-caps);
  text-transform: uppercase;
  color: var(--text-muted);
  margin-bottom: var(--space-3);
}
.section-head h2 { text-wrap: balance; }
.lede {
  font: var(--type-body-lg);
  color: var(--text-muted);
  max-width: var(--container-prose);
  margin-top: var(--space-4);
  text-wrap: pretty;
}

/* ---- Hero ---------------------------------------------------------- */
#hero > .container { padding-block: var(--space-32) var(--space-24); }
@media (max-width: 760px) { #hero > .container { padding-block: var(--space-16) var(--space-14); } }
#hero h1 { font: var(--type-display); letter-spacing: var(--tracking-display); max-width: 17ch; text-wrap: balance; }
@media (max-width: 600px) { #hero h1 { font-size: var(--text-2xl); } }
.hero-actions { display: flex; flex-wrap: wrap; gap: var(--space-3); margin-top: var(--space-8); }
.hero-endpoint {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: var(--space-3);
  margin-top: var(--space-10);
  padding: var(--space-4) var(--space-5);
  border: var(--border-width) solid var(--border-hairline);
  border-radius: var(--radius-md);
  background: var(--surface-card);
  max-width: 640px;
}
.hero-endpoint code { font: var(--type-mono); color: var(--text-strong); font-size: var(--text-base); }
.hero-endpoint .oa-caption { flex-basis: 100%; }

/* Captions that carry a fact, not a whisper. The system's `.oa-caption`
   helper is --text-faint, which is white-40 on this ground and under AA at
   12px; these three say what the endpoint is for, which platform each app
   runs on, and what is inside the sealed field, so they read as text. */
.hero-endpoint .oa-caption,
.surface .oa-caption,
.wire-note { color: var(--text-muted); }

/* ---- What is actually on the wire ----------------------------------
   Type B shows no running product, so the hero has to show *something* the
   product does. The honest visual here is the payload: one real pointer
   event, with the part the relay cannot read left as what it actually is. */
.wire {
  /* All four sides: a <figure> carries `margin: 1em 40px` from the user agent,
     so the mock sat indented against the card above it on every page. */
  margin: var(--space-10) 0 0;
  border: var(--border-width) solid var(--border-hairline);
  border-radius: var(--radius-lg);
  background: var(--surface-card);
  overflow: hidden;
  max-width: 620px;
}
.wire-bar {
  display: flex; align-items: center; justify-content: space-between; gap: var(--space-4);
  padding: var(--space-3) var(--space-4);
  border-bottom: var(--border-width) solid var(--border-hairline);
  font: var(--type-eyebrow); text-transform: uppercase; letter-spacing: var(--tracking-caps);
  color: var(--text-muted);
}
.wire pre {
  margin: 0; padding: var(--space-4);
  overflow-x: auto;
  font: var(--type-mono); color: var(--text-muted);
}
.wire .k { color: var(--text-strong); }
.wire .sealed { color: var(--brand); word-break: break-all; }
.wire-note { padding: 0 var(--space-4) var(--space-4); }

/* ---- Cards --------------------------------------------------------- */
.grid { display: grid; gap: var(--space-5); margin-top: var(--space-12); }
.grid-3 { grid-template-columns: repeat(3, 1fr); }
.grid-2 { grid-template-columns: repeat(2, 1fr); }
.grid-4 { grid-template-columns: repeat(4, 1fr); }
@media (max-width: 900px) { .grid-3, .grid-4 { grid-template-columns: repeat(2, 1fr); } }
@media (max-width: 600px) { .grid-2, .grid-3, .grid-4 { grid-template-columns: 1fr; } }

.card {
  display: flex;
  flex-direction: column;
  gap: var(--space-3);
  padding: var(--space-6);
  border: var(--border-width) solid var(--border-hairline);
  border-radius: var(--radius-lg);
  background: var(--surface-card);
}
.card h3 { font: var(--type-h4); }
.card p { color: var(--text-muted); }
.chip {
  display: grid;
  place-items: center;
  width: 34px;
  height: 34px;
  border-radius: var(--radius-md);
  background: var(--brand-soft);
  color: var(--brand);
}
.chip svg { width: 17px; height: 17px; }

/* ---- A card that is a link ----------------------------------------- */
.sibling { text-decoration: none; transition: border-color var(--duration-fast) var(--ease-standard); }
.sibling:hover { border-color: var(--brand); text-decoration: none; }
.sibling h3 { color: var(--text-strong); }
.sibling p { color: var(--text-muted); }

/* ---- Steps --------------------------------------------------------- */
.steps { counter-reset: step; }
.steps .card { position: relative; }
.step-n {
  font: var(--type-mono);
  /* Not --text-faint: it is white-40 on this ground, which is under AA, and
     the step number is the thing that says these four are in an order. */
  color: var(--text-muted);
  font-variant-numeric: tabular-nums;
}

/* ---- Surfaces list (where it runs) --------------------------------- */
.surface { display: flex; gap: var(--space-4); align-items: flex-start; }
.surface .chip { flex: none; }
.surface h3 { font: var(--type-h4); margin-bottom: var(--space-1); }
.surface p { color: var(--text-muted); font-size: var(--text-sm); }
.surface .oa-caption { margin-top: var(--space-2); }

/* ---- What the relay sees ------------------------------------------- */
.ledger { width: 100%; border-collapse: collapse; margin-top: var(--space-12); }
.ledger th, .ledger td {
  text-align: left;
  padding: var(--space-4) var(--space-4) var(--space-4) 0;
  border-bottom: var(--border-width) solid var(--border-hairline);
  vertical-align: top;
}
.ledger th { font: var(--type-eyebrow); text-transform: uppercase; letter-spacing: var(--tracking-caps); color: var(--text-muted); }
.ledger td:first-child { color: var(--text-strong); width: 38%; }
.ledger td:last-child { color: var(--text-muted); }
.ledger .yes { color: var(--text-strong); }
.ledger .no { color: var(--text-faint); }
.ledger-wrap { overflow-x: auto; }
.ledger-wrap table { min-width: 520px; }

/* ---- Pricing / access ---------------------------------------------- */
.plans { display: grid; grid-template-columns: repeat(2, 1fr); gap: var(--space-5); margin-top: var(--space-12); }
@media (max-width: 760px) { .plans { grid-template-columns: 1fr; } }
.plan { display: flex; flex-direction: column; gap: var(--space-4); padding: var(--space-8);
        border: var(--border-width) solid var(--border-hairline); border-radius: var(--radius-lg); }
.plan--lead { border-color: var(--brand); }
.plan h3 { font: var(--type-h3); }
.plan .price { font: var(--type-h2); color: var(--text-strong); }
.plan ul { list-style: none; padding: 0; display: flex; flex-direction: column; gap: var(--space-3); color: var(--text-muted); }
.plan li { display: flex; gap: var(--space-3); align-items: flex-start; }
.plan li svg { flex: none; width: 15px; height: 15px; margin-top: 3px; color: var(--brand); }
.plan .btn { margin-top: auto; align-self: flex-start; }

/* ---- FAQ ----------------------------------------------------------- */
.faq { margin-top: var(--space-12); max-width: 820px; }
.faq details { border-bottom: var(--border-width) solid var(--border-hairline); }
.faq summary {
  display: flex; justify-content: space-between; align-items: center; gap: var(--space-4);
  padding: var(--space-5) 0; cursor: pointer; list-style: none;
  font: var(--weight-medium) var(--text-md)/1.4 var(--font-sans); color: var(--text-strong);
}
.faq summary::-webkit-details-marker { display: none; }
.faq summary::after { content: "+"; color: var(--text-faint); font: var(--type-mono); font-size: var(--text-lg); }
.faq details[open] summary::after { content: "\2212"; }
.faq details p { color: var(--text-muted); padding-bottom: var(--space-5); max-width: var(--container-prose); }

/* ---- Closing CTA — the one filled panel on the page ---------------- */
#cta > .container { padding-block: var(--space-20); }
.cta-panel {
  display: flex; flex-wrap: wrap; align-items: center; justify-content: space-between;
  gap: var(--space-6); padding: var(--space-10);
  border-radius: var(--radius-lg); background: var(--bg-subtle);
  border: var(--border-width) solid var(--border-hairline);
}
.cta-panel h2 { font: var(--type-h2); max-width: 22ch; text-wrap: balance; }
.cta-panel .hero-actions { margin-top: 0; }

/* ---- Footer -------------------------------------------------------- */
.site-footer { border-top: var(--border-width) solid var(--border-hairline); }
.site-footer .container {
  padding: var(--space-14) var(--gutter-page-lg) var(--space-8);
  display: grid; grid-template-columns: 1.4fr repeat(3, 1fr); gap: var(--space-7);
}
@media (max-width: 900px) { .site-footer .container { grid-template-columns: repeat(3, 1fr); } }
@media (max-width: 600px) { .site-footer .container { grid-template-columns: repeat(2, 1fr); } }
.footer-about p { color: var(--text-muted); font-size: var(--text-sm); margin-top: var(--space-3); max-width: 34ch; }
.footer-group { display: flex; flex-direction: column; gap: var(--space-3); }
.footer-group a { color: var(--text-muted); font: var(--type-ui); }
.footer-group a:hover { color: var(--text-strong); text-decoration: none; }
.footer-base { border-top: var(--border-width) solid var(--border-hairline); }
.footer-base .container {
  padding: var(--space-5) var(--gutter-page-lg) var(--space-10);
  display: flex; align-items: center; justify-content: space-between;
  gap: var(--space-4); flex-wrap: wrap;
}
.footer-base span { color: var(--text-muted); font-size: var(--text-xs); }

/* ---- Prose sub-pages ----------------------------------------------- */
.doc { max-width: 44rem; padding-block: var(--space-10) var(--space-16); }
.doc h1 { margin-bottom: var(--space-4); }
.doc h2 { margin-top: var(--space-12); margin-bottom: var(--space-4); font: var(--type-h3); }
.doc h3 { margin-top: var(--space-8); margin-bottom: var(--space-3); font: var(--type-h4); }
.doc p, .doc li { color: var(--text-body); margin-bottom: var(--space-4); }
.doc ul, .doc ol { margin-bottom: var(--space-4); }
.doc code { background: var(--surface-card); border: var(--border-width) solid var(--border-hairline);
            border-radius: var(--radius-sm); padding: 1px 5px; }
.doc pre {
  background: var(--surface-card); border: var(--border-width) solid var(--border-hairline);
  border-radius: var(--radius-md); padding: var(--space-4); overflow-x: auto;
  margin-bottom: var(--space-6);
}
.doc pre code { background: none; border: 0; padding: 0; }
.doc table { width: 100%; border-collapse: collapse; margin-bottom: var(--space-6); min-width: 480px; }
.doc .table-wrap { overflow-x: auto; }
.doc th, .doc td { text-align: left; padding: var(--space-3) var(--space-4) var(--space-3) 0;
                   border-bottom: var(--border-width) solid var(--border-hairline); }
.doc th { font: var(--type-eyebrow); text-transform: uppercase; letter-spacing: var(--tracking-caps); color: var(--text-muted); }

/* ---- Skip link ----------------------------------------------------- */
.skip { position: absolute; left: -9999px; }
.skip:focus { left: var(--space-4); top: var(--space-4); z-index: 50; background: var(--surface-card);
              padding: var(--space-3) var(--space-4); border-radius: var(--radius-md); }
