/* ═══════════════════════════════════════════════════════════════════════════
   Todah — foundation. Loaded by every page, site and app alike.

   The system is specified in DESIGN.md at the repo root, reverse-engineered
   from spaceship.com's live design system and adapted for bookkeeping. Read
   that before changing anything here. Four rules carry most of it:

   1. ONE MOTION CURVE. --ease, everywhere, almost always at --base. The
      consistency IS the smoothness; a single stray easing is felt even when
      it cannot be named.

   2. TWO RADII. 8px or a full pill. Containers may take 24 or 32. Nothing
      else. The old scale had six steps and read as indecision.

   3. TWO WEIGHTS. 400 and 700. If it needs emphasis it is bold; if not it is
      regular. 500 and 600 are where hierarchy goes to die.

   4. DEPTH IS A HAIRLINE, NOT A BORDER. A raised surface catches light along
      its top edge — a 1px inset highlight in dark, an inset shade in light —
      over a soft ambient drop. Cards do not get borders.

   THE MARK STAYS. The identity is still an abacus: beads on rods, colour
   positional, never reordered. The beads now count in the one accent rather
   than in three unrelated hues, so the mark belongs to the system instead of
   arguing with it.

   The tenant still owns the app. --brand and --accent are written at runtime
   from each company's white-label settings and outrank every colour here.
   ═══════════════════════════════════════════════════════════════════════════ */

/* One family, two weights. Figtree stands in for Spaceship's proprietary
   Proxima Nova, and it carries real tabular figures, without which money
   columns do not align.

   SELF-HOSTED, ON PURPOSE. Until 26 Sept this was an @import from Google
   Fonts, and the Content-Security-Policy (style-src 'self', font-src falling
   back to default-src 'self') blocked both the stylesheet and the font files.
   Figtree never loaded on todah.ca: every page rendered in the visitor's
   system font, which is most of why the product read as generic. Serving the
   files from /fonts/ needs no CSP change and no third party. Licence: SIL OFL
   1.1, in /fonts/OFL.txt. Subsets and ranges are fontsource's. */
@font-face {
  font-family:'Figtree'; font-style:normal; font-weight:400; font-display:swap;
  src:url(/fonts/figtree-latin-400-normal.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:'Figtree'; font-style:normal; font-weight:700; font-display:swap;
  src:url(/fonts/figtree-latin-700-normal.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:'Figtree'; font-style:normal; font-weight:400; font-display:swap;
  src:url(/fonts/figtree-latin-ext-400-normal.woff2) format('woff2');
  unicode-range:U+0100-02BA,U+02BD-02C5,U+02C7-02CC,U+02CE-02D7,U+02DD-02FF,U+0304,U+0308,U+0329,U+1D00-1DBF,U+1E00-1E9F,U+1EF2-1EFF,U+2020,U+20A0-20AB,U+20AD-20C0,U+2113,U+2C60-2C7F,U+A720-A7FF;
}
@font-face {
  font-family:'Figtree'; font-style:normal; font-weight:700; font-display:swap;
  src:url(/fonts/figtree-latin-ext-700-normal.woff2) format('woff2');
  unicode-range:U+0100-02BA,U+02BD-02C5,U+02C7-02CC,U+02CE-02D7,U+02DD-02FF,U+0304,U+0308,U+0329,U+1D00-1DBF,U+1E00-1E9F,U+1EF2-1EFF,U+2020,U+20A0-20AB,U+20AD-20C0,U+2113,U+2C60-2C7F,U+A720-A7FF;
}

:root {
  /* ── Neutrals. Near-neutral, faintly cool. ── */
  --p0:#ffffff;  --p25:#fafafa; --p50:#f4f4f5; --p100:#ededf0; --p150:#e4e4e7;
  --p200:#d6d6da; --p300:#bcbcc1; --p400:#9a999d; --p500:#706f71; --p600:#5b5a5c;
  --p700:#444346; --p800:#2b2b2d; --p900:#1d1d1f; --p950:#111112;

  /* ── The one accent. Everything that is "the action" is this colour. ── */
  --accent-base:#475bff; --accent-hover:#394eff; --accent-deep:#000fbd;
  --accent-text:#5d8bff;                    /* accent AS TEXT on dark only */
  --on-accent:#f9f9f9;

  /* ── The three beads: one accent, three steps, counting up the rods.
        Order is fixed and never swapped for variety. Where a bead carries
        meaning it is always paired with a word — colour alone never says it. ── */
  --bead-1:#7b8dff; --bead-1-lit:#98a6ff; --bead-1-wash:#eef0ff;
  --bead-2:#475bff; --bead-2-lit:#6675ff; --bead-2-wash:#e7eaff;
  --bead-3:#1e2a99; --bead-3-lit:#2c3bc4; --bead-3-wash:#e3e5f5;

  /* Todah's own accent on public surfaces. The second bead — the true accent. */
  --td:var(--bead-2); --td-hi:var(--accent-hover); --td-lo:var(--accent-deep);
  --td-wash:var(--bead-2-wash);

  /* ── Surfaces. Re-pointed wholesale by [data-theme="dark"]. ── */
  --bg:var(--p50);      --card:var(--p0);     --panel:var(--p100);
  --raised:var(--p0);   --sidebar:var(--p25); --overlay:rgba(23,24,26,.5);
  --border:var(--p150); --border-strong:var(--p200); --border-soft:var(--p100);
  --hair:rgba(23,24,26,.09);
  --ghost:rgba(23,24,26,.06); --ghost-hover:rgba(23,24,26,.10);
  --text:var(--p800);   --text-2:var(--p500); --text-3:var(--p500); --text-4:var(--p400);

  /* ── State. A ledger has four outcomes and they must survive colour
        blindness, so every one of these ships with a word beside it. ── */
  --paid:#166b4a;    --paid-bg:#e6f2ec;
  --partial:#8a6100; --partial-bg:#fbf1dc;
  --over:#c4381f;    --over-bg:#fbe9e6;
  --info:var(--bead-2); --info-bg:var(--bead-2-wash);
  --success:var(--paid); --danger:var(--over); --warn:var(--partial);

  /* ── Tenant colour. Overwritten at runtime; a safe start only. ── */
  --brand:var(--p900); --accent:var(--accent-base);

  /* ── Type. One face. ── */
  --display:'Figtree',-apple-system,BlinkMacSystemFont,'Segoe UI',Helvetica,Arial,sans-serif;
  --sans:var(--display);
  --mono:ui-monospace,SFMono-Regular,'SF Mono',Menlo,Consolas,monospace;

  /* One body size does almost all the work, then a hard jump to display.
     There is no gentle ramp; the drama comes from the gap.

     The two smallest steps exist for the app, not the site. A marketing page
     has one column and room to breathe; a ledger has eleven columns and a
     figure in each. Shopify's Polaris — the closest public reference for an
     admin this dense — defines 11px and 13px for exactly this reason, so the
     dense steps are a normal part of a real scale rather than a concession.
     Without them the app was inventing 12.5px at call sites, a size that
     appears on no scale anywhere. */
  --t-2xs:11px; --t-xs:12px; --t-dense:13px; --t-sm:14px; --t-base:16px; --t-md:20px;
  --t-lg:22px; --t-xl:32px; --t-2xl:40px; --t-3xl:56px; --t-4xl:72px; --t-5xl:88px;

  /* ── Spacing. 8px base. ── */
  --s1:4px; --s2:8px; --s3:12px; --s4:16px; --s5:20px; --s6:24px;
  --s8:32px; --s10:40px; --s12:48px; --s16:64px; --s20:80px; --s24:96px; --s32:128px;

  /* ── Radius. Two, plus a container size. ── */
  --r-sm:6px; --r:8px; --r-md:8px; --r-lg:24px; --r-xl:32px; --r-full:9999px;

  /* ── Depth. A hairline along the top edge, then a soft ambient drop. ── */
  --sh-1:rgba(23,24,26,.06) 0 1px 0 0 inset, rgba(23,24,26,.07) 0 1px 2px;
  --sh-2:rgba(23,24,26,.06) 0 1px 0 0 inset, rgba(23,24,26,.09) 0 1.5px 3px,
         rgba(23,24,26,.07) 0 3px 8px;
  --sh-3:rgba(23,24,26,.06) 0 1px 0 0 inset, rgba(23,24,26,.10) 0 4px 10px,
         rgba(23,24,26,.08) 0 12px 28px;
  --sh-4:rgba(23,24,26,.06) 0 1px 0 0 inset, rgba(23,24,26,.12) 0 8px 20px,
         rgba(23,24,26,.14) 0 28px 64px;
  --lift:var(--sh-2);

  /* ── Motion. One curve. If you are unsure, it is var(--base) var(--ease). ── */
  --ease:cubic-bezier(.55,0,.35,1);
  --ease-io:var(--ease);
  /* The one sanctioned exception: a bead settling against its stop. The mark
     is the identity, and a bead that does not settle is not a bead. */
  --ease-bead:cubic-bezier(.32,1.24,.6,1);
  --fast:100ms; --base:200ms; --slow:300ms; --slower:400ms;

  --focus:0 0 0 2px var(--card), 0 0 0 4px color-mix(in srgb, var(--accent) 62%, transparent);
}

[data-theme="dark"] {
  /* The ramp inverts as a whole. It carries two roles — low numbers are
     surfaces, high numbers are borders then text — and 98 rules across the
     stylesheets reach for a raw step rather than a semantic name. Re-pointing
     only the semantic tokens left every one of those rules light: a light page
     behind a dark sign-in card, and most of the pricing page. Inverting the
     ramp fixes all 98 at once and keeps each step's ROLE identical. */
  --p0:#252628;  --p25:#1e1f21; --p50:#0f1012; --p100:#141517;
  --p150:#333438; --p200:#3f4043; --p300:#4d4e52; --p400:#727378;
  --p500:#909091; --p600:#b3b3b4; --p700:#d2d2d3; --p800:#f0f0f1;
  --p900:#f9f9f9; --p950:#ffffff;

  --bg:#18191b;      --card:#252628;     --panel:#2e2f31;
  --raised:#363739;  --sidebar:#1d1e20;  --overlay:rgba(0,0,0,.66);
  --border:#333437;  --border-strong:#414245; --border-soft:#2b2c2e;
  --hair:rgba(249,249,249,.08);
  --ghost:rgba(237,238,240,.08); --ghost-hover:rgba(255,255,255,.12);
  --text:#f9f9f9;    --text-2:#b3b3b4;   --text-3:#909091; --text-4:#7a7a7b;

  /* The ramp lightens so all three beads stay legible on ink, and the steps
     stay far enough apart to be told from one another. */
  --bead-1:#c2cfff; --bead-1-lit:#d8e0ff; --bead-1-wash:#1e2233;
  --bead-2:#7d93ff; --bead-2-lit:#9aabff; --bead-2-wash:#1c2033;
  --bead-3:#475bff; --bead-3-lit:#6675ff; --bead-3-wash:#191c2e;

  --paid:#52c795;    --paid-bg:#142a21;
  --partial:#e0a526; --partial-bg:#2b2113;
  --over:#f0796a;    --over-bg:#2e1917;
  --info:var(--bead-2); --info-bg:var(--bead-2-wash);
  --td:var(--bead-2); --td-hi:var(--bead-2-lit); --td-lo:var(--bead-3);
  --td-wash:var(--bead-2-wash);

  /* Inverted: in dark the top edge catches light rather than taking a shade. */
  --sh-1:rgba(249,249,249,.08) 0 0 .5px 1px inset, rgba(0,0,0,.30) 0 1px 2px;
  --sh-2:rgba(249,249,249,.08) 0 0 .5px 1px inset, rgba(0,0,0,.18) 0 1.5px 3px,
         rgba(0,0,0,.14) 0 3px 8px;
  --sh-3:rgba(249,249,249,.08) 0 0 .5px 1px inset, rgba(0,0,0,.34) 0 4px 10px,
         rgba(0,0,0,.30) 0 12px 28px;
  --sh-4:rgba(249,249,249,.08) 0 0 .5px 1px inset, rgba(0,0,0,.44) 0 8px 20px,
         rgba(0,0,0,.58) 0 28px 64px;
}

/* ═══ Reset ═══════════════════════════════════════════════════════════════ */
*,*::before,*::after { box-sizing:border-box; margin:0; padding:0; }
html { -webkit-text-size-adjust:100%; }

body {
  font-family:var(--sans);
  font-size:var(--t-base);
  line-height:1.5;
  color:var(--text);
  background:var(--bg);
  -webkit-font-smoothing:antialiased;
  -moz-osx-font-smoothing:grayscale;
}

/* Display type is heavy and only lightly tracked. Spaceship measures -0.4px
   at 32px and -0.6px at 88px; the -.025em to -.055em this used to carry is
   the crushed, over-designed look of a generated page. */
h1,h2,h3 {
  font-weight:700; line-height:1.1; letter-spacing:-.01em; text-wrap:balance;
}
h4,h5 { font-weight:700; line-height:1.25; letter-spacing:0; }
p { text-wrap:pretty; }
img,svg { display:block; max-width:100%; }
button,input,select,textarea { font:inherit; color:inherit; }
button { background:none; border:none; cursor:pointer; }
a { color:inherit; }

/* Money and dates align in columns or they cannot be read. */
table, .num, .amt, input[type="number"], td, th, time, .tabular {
  font-variant-numeric:tabular-nums;
  font-feature-settings:'tnum' 1;
}

/* ═══ Browser surfaces ════════════════════════════════════════════════════ */

::selection { background:color-mix(in srgb, var(--accent) 26%, transparent); color:var(--text); }
input,textarea,[contenteditable] { caret-color:var(--accent); }

:focus { outline:none; }
:focus-visible { outline:none; box-shadow:var(--focus); border-radius:var(--r-sm); }

* { scrollbar-width:thin; scrollbar-color:var(--p300) transparent; }
::-webkit-scrollbar { width:11px; height:11px; }
::-webkit-scrollbar-track { background:transparent; }
::-webkit-scrollbar-thumb {
  background:var(--p300); border-radius:var(--r-full);
  border:3px solid transparent; background-clip:content-box;
}
::-webkit-scrollbar-thumb:hover { background:var(--p400); background-clip:content-box; }
::-webkit-scrollbar-corner { background:transparent; }

a { text-underline-offset:.18em; text-decoration-thickness:from-font; }

/* ═══ Shared primitives ═══════════════════════════════════════════════════ */

.sr-only {
  position:absolute; width:1px; height:1px; padding:0; margin:-1px;
  overflow:hidden; clip:rect(0,0,0,0); white-space:nowrap; border:0;
}

.skip-link {
  position:absolute; left:var(--s4); top:-60px; z-index:200;
  background:var(--card); color:var(--text); padding:var(--s3) var(--s5);
  border-radius:var(--r); box-shadow:var(--sh-3);
  font-weight:700; font-size:var(--t-sm);
  transition:top var(--base) var(--ease);
}
.skip-link:focus { top:var(--s4); }

/* ── The bead. A status pill is a bead on its rod: the dot is the bead, the
      label is what it counts. The label is not optional — it is what makes
      the status readable without colour. ── */
.pill {
  display:inline-flex; align-items:center; gap:6px;
  padding:3px 10px 3px 8px; border-radius:var(--r-full);
  font-size:var(--t-xs); font-weight:700;
  white-space:nowrap; line-height:1.5;
}
.pill::before {
  content:''; width:7px; height:7px; border-radius:50%;
  background:currentColor; flex:none;
}
.pill.paid     { color:var(--paid);    background:var(--paid-bg); }
.pill.partial  { color:var(--partial); background:var(--partial-bg); }
.pill.over     { color:var(--over);    background:var(--over-bg); }
.pill.draft,
.pill.neutral  { color:var(--text-2);  background:var(--ghost); }
.pill.info     { color:var(--info);    background:var(--info-bg); }

/* ── The rod. A hairline with a bead sitting on it. ── */
.rod {
  display:flex; align-items:center; gap:0;
  height:1px; background:var(--border); position:relative;
}
.rod::before {
  content:''; position:absolute; left:0; top:50%;
  width:9px; height:9px; border-radius:50%;
  background:var(--bead-1); transform:translateY(-50%);
}
.rod.two::before  { background:var(--bead-2); }
.rod.three::before{ background:var(--bead-3); }

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