/* bankstatements-csv shared chrome -- the single source of truth for anything that appears on
   both the marketing site and the app: design tokens, the header bar, the brand
   lockup, buttons, and the footer.

   Loaded twice, by two different consumers:
     - the static pages, via <link rel="stylesheet" href="/chrome.css">
     - the React app, via `@import` in src/index.css (Vite bundles it)

   INVARIANT: no bare-element selectors (no html, body, a, img, svg, *). Only :root
   and class selectors. The app loads this alongside Tailwind's preflight, and an
   element selector here would silently restyle app internals that never opted in.
   Marketing-page resets and blocks live in styles.css, which only the site loads. */

/* ---------- typeface ---------- */

/* IBM Plex, self-hosted. No page in this product may fire a third-party request -- that is
   the same promise the parser makes about your statements, and a webfont CDN would quietly
   break it. Latin subset, five files, ~104KB total.

   The URLs are absolute, and have to be. A relative ./assets/... would be correct for the
   static pages but wrong for the app: @tailwindcss/postcss inlines this file into
   src/index.css before Vite's url() rewriting runs, so Vite never sees these as paths to
   resolve and emits them verbatim into dist/app/assets/, where ./assets/... would resolve
   one directory too deep. /assets/fonts/ is served by the Worker for both surfaces; the
   siteAssets() plugin in vite.config.ts mounts the same path under `vite dev`. */

@font-face {
  font-family: 'IBM Plex Sans';
  src: url('/assets/fonts/ibm-plex-sans-latin-400-normal.woff2') format('woff2');
  font-weight: 400;
  font-style: normal;
  font-display: swap;
}
@font-face {
  font-family: 'IBM Plex Sans';
  src: url('/assets/fonts/ibm-plex-sans-latin-500-normal.woff2') format('woff2');
  font-weight: 500;
  font-style: normal;
  font-display: swap;
}
@font-face {
  font-family: 'IBM Plex Sans';
  src: url('/assets/fonts/ibm-plex-sans-latin-600-normal.woff2') format('woff2');
  font-weight: 600;
  font-style: normal;
  font-display: swap;
}
@font-face {
  font-family: 'IBM Plex Mono';
  src: url('/assets/fonts/ibm-plex-mono-latin-400-normal.woff2') format('woff2');
  font-weight: 400;
  font-style: normal;
  font-display: swap;
}
@font-face {
  font-family: 'IBM Plex Mono';
  src: url('/assets/fonts/ibm-plex-mono-latin-500-normal.woff2') format('woff2');
  font-weight: 500;
  font-style: normal;
  font-display: swap;
}

:root {
  --font-sans: 'IBM Plex Sans', ui-sans-serif, system-ui, -apple-system, 'Segoe UI', Roboto, sans-serif;
  --font-mono: 'IBM Plex Mono', ui-monospace, SFMono-Regular, Menlo, monospace;

  /* ---------- the two scales ---------- */

  /* Type. Every font-size in this product comes from here; nothing declares a raw value.
     Before this existed there were twelve distinct sizes inside a 4px range (0.68, 0.7,
     0.72, 0.75, 0.78, 0.8, 0.82, 0.85, 0.875, 0.9, 0.925, 0.95rem), which is not a scale,
     it is a series of individually-tuned guesses -- and several of them (0.925rem =
     14.8px, 0.68rem = 10.88px) landed between pixels.

     Steps are 1px below the body floor, where the mono micro-labels need fine control and
     a 2px jump is a third of the glyph height, then 2px and 4px above it, so every value
     is a whole pixel at a 16px root. --fs-base is a floor for anything that is prose:
     body copy never goes below it. Sizes under it are for labels, figures and chrome,
     which are scanned rather than read. */
  --fs-2xs: 0.6875rem; /* 11px -- uppercase mono micro-labels */
  --fs-xs: 0.75rem;    /* 12px */
  --fs-sm: 0.8125rem;  /* 13px */
  --fs-md: 0.875rem;   /* 14px -- table figures, captions, chrome */
  --fs-base: 1rem;     /* 16px -- body copy floor */
  --fs-lg: 1.125rem;   /* 18px */
  --fs-xl: 1.25rem;    /* 20px */
  --fs-2xl: 1.5rem;    /* 24px */
  --fs-3xl: 1.75rem;   /* 28px */
  --fs-4xl: 2rem;      /* 32px */
  --fs-5xl: 3.5rem;    /* 56px -- the price figure, and nothing else */

  /* Space. A 0.25rem (4px) grid, the same one Tailwind uses, so the static pages and the
     app land on identical stops and a value can be moved between them unchanged. Half
     steps (0.125rem) exist only below 1rem, where 4px is a visible jump in a button or a
     table cell. Nothing here should carry a value that is not on the grid. */
  --sp-05: 0.125rem;
  --sp-1: 0.25rem;
  --sp-1h: 0.375rem;
  --sp-2: 0.5rem;
  --sp-2h: 0.625rem;
  --sp-3: 0.75rem;
  --sp-3h: 0.875rem;
  --sp-4: 1rem;
  --sp-4h: 1.125rem;
  --sp-5: 1.25rem;
  --sp-6: 1.5rem;
  --sp-7: 1.75rem;
  --sp-8: 2rem;
  --sp-10: 2.5rem;
  --sp-12: 3rem;
  --sp-14: 3.5rem;
  --sp-16: 4rem;
  --sp-20: 5rem;

  /* Ink, not black. Statements are printed in a blue-black, and it stops large type
     from vibrating against the paper the way #000 does.
     All three clear 4.5:1 on both --surface and --bg, so any of them may be used at any
     size on either ground. --text-3 in particular is load-bearing: it sets nearly every
     small label on the site (eyebrows, column heads, captions, footer links), and a
     value that only passed on white would fail across the whole muted layer. */
  --text: #131f2b;   /* 16.7:1 on white */
  --text-2: #45566a; /* 7.7:1 */
  --text-3: #5d6e80; /* 5.2:1 on white, 4.7:1 on --bg */

  --surface: #ffffff;
  --bg: #eef1f5;

  /* Two rule weights, because a ledger has two: the hairline between rows and the
     heavier one that closes a column off. */
  --border: #dbe2e9;
  --border-strong: #bcc7d3;

  /* Structural colour is ink. Saturated colour is spent only where it carries meaning
     on a statement -- credit green, debit red -- so a figure is never coloured for
     decoration. The accent is a deep institutional navy chosen so it never competes
     with them for attention. */
  --accent: #1b4571;
  --accent-hover: #123253;
  --accent-soft: #e4ecf5;  /* the selected-row wash, matching the app's accent-100 */

  /* The one dark ground on the site. A page of ink on paper needs a single place where it
     inverts, and the honest place is the privacy claim, which is the argument the product
     is actually making.

     A slate navy, not the near-black it started as: at #0e2942 the band hit 14.8:1 against
     its own white type and stopped reading as a section, more as a hole cut in the page.
     This is the same family as --accent, so it reads as the site turning up its voice
     rather than as a different site.

     The body tint is nearly white on purpose. An earlier #bed0e2 measured 7.1:1 -- past
     AAA -- and still read as hard work, because it was a blue tint on a blue ground: the
     luminance contrast was fine but the hues sat on top of each other, so the type had
     nothing but brightness separating it. Pulling the tint toward neutral white gives the
     eye the second cue and takes it to 9.8:1. Worth remembering that a passing ratio and
     a comfortable read are not the same measurement. */
  --ink-deep: #173650;
  --on-ink: #ffffff;     /* 12.5:1 on --ink-deep */
  --on-ink-2: #dbe5ef;   /* 9.8:1  -- body copy */
  --on-ink-3: #a9c0d8;   /* 6.7:1  -- eyebrows, diagram annotations */
  --ink-rule: #355a80;

  /* Credit, and 'this reconciles'.

     Darker than the #146c43 it started as, and the reason is the colour it sits next to
     rather than anything about the green itself. At #146c43 it measured L* 40.0 against
     --debit's L* 39.6: the two colours that carry the most meaning on this page were the
     same brightness, so the only thing separating a debit from a credit was hue. Simulate
     deuteranopia and both collapse to the same olive -- #746826 and #615b46, four L*
     apart -- which is to say the colour coding stopped working entirely for roughly one
     man in twelve. Nothing is *lost* there, because the column header and position still
     say which is which, but the cue was doing no work.

     At #0f5132 the pair is 10 L* apart sighted and 15 L* apart simulated, so brightness
     carries the distinction on its own and hue is the second cue rather than the only
     one. It also takes the green from 6.5:1 to 9.4:1 on white, which the small tabular
     figures were the better for. */
  --green: #0f5132;
  --green-soft: #d9ecdf;
  --green-bright: #5fd396; /* credit green legible on --ink-deep only */
  /* The fill for a solid green pill, where white type sits on the green instead of the
     green sitting on white. --green is dark because it has to out-darken --debit next to
     it in a column of figures; a pill has no debit beside it, so that constraint doesn't
     apply and all the darkness bought was a pill that read as a serious warning rather
     than as good news. This is the same hue turned up: 15 L* brighter and a little more
     saturated, still 5.5:1 under white type so the 0.75rem label clears AA comfortably.
     Only ever a background -- as a figure colour it would collide with --debit again. */
  --green-solid: #12784a;
  /* Saturated rather than the darker, browner red it started as: at 0.875rem in mono, a
     desaturated red reads as brown ink, not as "money left the account". Same 6.6:1 as
     the value it replaces, so nothing was traded for the legibility. */
  --debit: #b3242c;
  --debit-soft: #f7e6e6;
  /* The em-dash standing in for an empty debit/credit cell. The palest ink on the site,
     and the one value that does not clear 4.5:1 -- it is a typographic 'intentionally
     blank', exempt as decoration, and what it conveys (no amount in this column) is
     already conveyed by the cell being empty. It is aria-hidden wherever it appears, so
     nothing depends on reading it. Set at 3.4:1 rather than the ~2:1 a placeholder would
     normally get, so it stays visible to low-vision users without competing with the
     figures either side of it. */
  --nil: #7f8d9d;

  --radius: 0.375rem;
  --radius-sm: 0.25rem;

  /* Elevation is rationed. Structure comes from rules and alignment; this is spent in
     exactly one place, the screenshot frame, where the app really is floating above
     the page. */
  --shadow: 0 1px 2px rgba(19, 31, 43, 0.05);
  --header-h: 4rem;
}

/* ---------- layout ---------- */

/* padding-inline, not the `padding` shorthand: this file is unlayered, so a shorthand
   would beat Tailwind's layered py-* utilities and zero out vertical padding wherever
   the app puts .container on a padded element. */
.container { max-width: 68rem; margin: 0 auto; padding-inline: var(--sp-5); }

/* The app is a two-pane document tool and needs the room; the header bar it sits
   under is otherwise identical to the site's. */
.container--wide { max-width: 1800px; }

/* ---------- header ---------- */

.site-header {
  background: var(--surface);
  border-bottom: 1px solid var(--border);
  position: sticky;
  top: 0;
  z-index: 10;
  flex: none; /* the app mounts this in a flex column; never let it compress */
}

.nav-row {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--sp-4);
  min-height: var(--header-h);
  flex-wrap: wrap;
  padding-top: var(--sp-2);
  padding-bottom: var(--sp-2);
}

/* The identity slot. Byte-identical on every surface, signed in or out -- it is the
   only element that survives the crossing from / to /app, so it must not move. */
.brand {
  display: flex;
  align-items: center;
  gap: var(--sp-2);
  font-weight: 600;
  font-size: var(--fs-xl);
  color: var(--text);
  text-decoration: none;
}
.brand:hover { text-decoration: none; }
.brand:hover .brand-name { color: var(--accent); }
.brand svg { width: var(--sp-7); height: var(--sp-7); flex: none; }
/* Monospace wordmark: the name is kebab-case like a domain/CLI tool, so this is the one
   place it's styled as a logo rather than a sentence -- inline prose mentions elsewhere
   stay in the body font, or they'd read as stray code formatting mid-paragraph. */
.brand-name {
  margin: 0;
  font-size: var(--fs-lg); /* = the old 0.9em of the 1.25rem .brand, now stated outright */
  font-weight: 500;
  line-height: 1;
  letter-spacing: -0.02em;
  font-family: var(--font-mono);
  transition: color 0.15s ease;
}

/* The context slot. Contents differ per surface; the box does not. */
.site-nav { display: flex; align-items: center; gap: var(--sp-6); flex-wrap: wrap; }
.site-nav a:not(.btn) { color: var(--text-2); font-weight: 500; font-size: var(--fs-md); text-decoration: none; }
.site-nav a:not(.btn):hover { color: var(--text); text-decoration: none; }

/* App toolbars pack more controls into the same bar than the site's 3-link nav. */
.site-nav--tight { gap: var(--sp-3); }

/* Mono, because it is a statement of fact about the software rather than a slogan --
   the same voice the column labels and figures are set in. */
.header-tagline {
  color: var(--text-3);
  font-family: var(--font-mono);
  font-size: var(--fs-xs);
  letter-spacing: -0.01em;
  border-left: 1px solid var(--border);
  padding-left: var(--sp-3h);
  margin: 0;
}
@media (max-width: 60rem) { .header-tagline { display: none; } }

/* ---------- signed-in / signed-out CTA swap ---------- */

/* auth-cta.js sets data-auth on <html> before first paint. With no JS and no cookie
   the signed-out label is what renders, which is the safe default: /app authenticates
   for real regardless of what this button says. */
[data-when="in"] { display: none; }
:root[data-auth="in"] [data-when="in"] { display: inline; }
:root[data-auth="in"] [data-when="out"] { display: none; }

/* ---------- buttons ---------- */

/* Horizontal padding is exactly twice the vertical on all three sizes. That ratio is what
   makes a box read as a button rather than as a tag or an input, and it survives the
   label getting longer or shorter. Conveniently every stop is also on the space grid, so
   the two constraints agree: 0.625/1.25, 0.4375/0.875, 0.75/1.5. */
.btn {
  display: inline-block;
  border-radius: var(--radius-sm);
  font-family: inherit;
  font-weight: 500;
  font-size: var(--fs-md);
  letter-spacing: -0.005em;
  padding: var(--sp-2h) var(--sp-5);
  border: 1px solid transparent;
  transition: background-color 0.15s ease, border-color 0.15s ease, color 0.15s ease;
  text-decoration: none;
  cursor: pointer;
}
.btn:hover { text-decoration: none; }
.btn:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }

.btn-primary { background: var(--accent); border-color: var(--accent); color: #fff; }
.btn-primary:hover { background: var(--accent-hover); border-color: var(--accent-hover); }

.btn-secondary {
  background: var(--surface);
  color: var(--text);
  border-color: var(--border-strong);
}
.btn-secondary:hover { border-color: var(--text-3); background: var(--surface); }

/* The tertiary action. Reads as a link with a rule under it rather than a third box --
   two adjacent outlined buttons make neither look like the thing to press.
   The rule is the accent at full strength, not a pale grey: sitting beside a solid
   filled button, a grey underline read as a decorative flourish on a piece of static
   text rather than as the second thing you can click. The arrow says which way it goes. */
.btn-quiet {
  display: inline-flex;
  align-items: center;
  gap: var(--sp-1h);
  font-weight: 500;
  color: var(--accent);
  border-bottom: 2px solid var(--accent);
  padding-bottom: var(--sp-05);
  text-decoration: none;
  transition: gap 0.15s ease, color 0.15s ease;
}
/* The arrow is set back from the label it follows. An icon beside text at the same
   strength reads heavier than the text does -- solid ink, no counters, no x-height to
   soften it -- so matching the colour exactly makes the glyph the loud half of the pair.
   Three-quarter strength puts them at the same apparent weight. */
.btn-quiet::after {
  content: "→";
  font-size: 0.9em;
  color: color-mix(in srgb, currentColor 75%, transparent);
}
.btn-quiet:hover { text-decoration: none; color: var(--accent-hover); gap: var(--sp-2h); }
@media (prefers-reduced-motion: reduce) {
  .btn-quiet { transition: none; }
  .btn-quiet:hover { gap: var(--sp-1h); }
}

.btn-sm { padding: 0.4375rem var(--sp-3h); font-size: var(--fs-sm); }
.btn-lg { padding: var(--sp-3) var(--sp-6); font-size: var(--fs-base); }

/* ---------- footer ---------- */

/* Full 4-column footer: marketing pages only. The app is overflow-hidden with no
   scroll to reach a footer, and every pixel it took would come out of the PDF pane. */
.site-footer {
  border-top: 1px solid var(--border);
  background: var(--surface);
  margin-top: var(--sp-16);
  padding: var(--sp-12) 0 var(--sp-8);
  font-size: var(--fs-md);
}
.footer-grid {
  display: grid;
  grid-template-columns: 2fr 1fr 1fr 1fr;
  gap: var(--sp-8) var(--sp-6);
}
@media (max-width: 46rem) { .footer-grid { grid-template-columns: 1fr 1fr; } }
.site-footer h4 {
  margin: 0 0 var(--sp-3h);
  font-family: var(--font-mono);
  font-size: var(--fs-2xs);
  font-weight: 500;
  text-transform: uppercase;
  letter-spacing: 0.09em;
  color: var(--text-3);
}
.site-footer ul { list-style: none; margin: 0; padding: 0; }
.site-footer li { margin-bottom: var(--sp-2); }
.site-footer a { color: var(--text-2); text-decoration: none; }
.site-footer a:hover { color: var(--text); text-decoration: underline; text-underline-offset: 0.2em; }
.footer-blurb { color: var(--text-3); max-width: 20rem; }

.footer-fine {
  color: var(--text-3);
  font-size: var(--fs-sm);
  border-top: 1px solid var(--border);
  margin-top: var(--sp-10);
  padding-top: var(--sp-4h);
}

/* The auth screens (sign-in, paywall, reconnect) get the fine-print line with no grid
   above it. Those are the surfaces where someone decides whether to trust us with
   money, so the legal links have to be one glance away, not behind the login wall. */
.footer-minimal {
  border-top: 1px solid var(--border);
  background: var(--surface);
  padding: var(--sp-4) 0;
  text-align: center;
  color: var(--text-3);
  font-size: var(--fs-sm);
  flex: none;
}
.footer-minimal a { color: var(--text-3); text-decoration: none; }
.footer-minimal a:hover { color: var(--text-2); text-decoration: underline; }
