/* ==========================================================================
   JTPay design tokens, v2 — the single source of truth for colour, type,
   space and form on every JTPay surface (merchant app, back office, hosted
   checkout). Synced into src/web/projects/shared/src/lib/brand/tokens.css by
   scripts/sync-brand.mjs. Never hard-code a hex value downstream; a test fails
   the build if one appears outside this file. See brand/BRAND.md.
   ========================================================================== */

/* ------------------------------------------------------------------- fonts
   Inter, self-hosted. A payment surface has no business fetching fonts from a
   third-party CDN: it is an availability dependency in the page load and a
   privacy leak on every request. The woff2 files live in src/web/public/fonts
   (Latin subset, OFL licence alongside) and the container's CSP keeps
   font-src 'self'. */



/* ================================================================= light
   The full palette lives here. The dark blocks below redefine colour tokens
   only; nothing about form or type changes with the theme. */

:root {
  color-scheme: light;

  /* --- brand -------------------------------------------------------- */
  --jt-color-forest: #0f2e24; /* hero surface, primary button, links */
  --jt-color-forest-hover: #163d30;
  --jt-color-forest-deep: #1f4c3a;
  --jt-color-mint: #8ef0b4; /* the one bright accent */
  --jt-color-mint-hover: #6fe29e;
  --jt-color-mint-soft: #ddf5e6;
  --jt-color-on-forest: #ffffff;
  --jt-color-on-mint: #0f2e24;
  /* Foreground for "selected on a tinted surface" — the mint-soft pill behind an
     active nav link, tab icon or badge. Plain forest reads fine here in light
     (12.72:1) but is nearly invisible against dark's mint-soft (1.10:1, review
     round 2), so this gets its own token rather than reusing forest. */
  --jt-color-on-mint-soft: #0f2e24;

  /* --- surfaces ----------------------------------------------------- */
  --jt-color-ground: #f7f9f8;
  --jt-color-card: #ffffff;
  --jt-color-sunk: #eef2f0;
  --jt-color-hairline: #e4eae7;
  --jt-color-input-border: #d3dcd8;
  --jt-color-scrim: rgb(11 21 18 / 0.5);

  /* --- text ----------------------------------------------------------
     Neutrals carry a cool green bias so they sit with forest and mint.
     Faint fails AA on purpose: dividers and decorative icons only, never
     running text — placeholders included, which is why they use muted. */
  --jt-color-ink: #0b1512;
  --jt-color-secondary: #2a3833;
  --jt-color-muted: #5f6f6a;
  --jt-color-faint: #93a19b;

  /* --- semantic ------------------------------------------------------
     Pending is amber, never red. On the payout rail, UnionBank's transfer
     response code NC maps to PDNG ("we do not know yet"); colouring that
     as a failure teaches operators to treat unknown as dead, which is how
     a gateway pays twice. Blue is money confirmed and on its way: real
     time on InstaPay and intrabank, the next banking day on PESONet. */
  --jt-color-success: #166b40;
  --jt-color-success-bg: #ddf5e6;
  --jt-color-success-dot: #22a05f;
  --jt-color-warning: #8a5a00;
  --jt-color-warning-bg: #fff1d6;
  --jt-color-warning-dot: #e8a317;
  --jt-color-error: #a3341e;
  --jt-color-error-bg: #fce8e3;
  --jt-color-error-dot: #d9553a;
  --jt-color-arriving: #1e4fa3;
  --jt-color-arriving-bg: #e3ecfb;
  --jt-color-arriving-dot: #3b72d9;
  --jt-color-neutral: var(--jt-color-muted);
  --jt-color-neutral-bg: var(--jt-color-sunk);
  --jt-color-neutral-dot: var(--jt-color-faint);

  /* --- controls ------------------------------------------------------ */
  --jt-button-primary-bg: var(--jt-color-forest);
  --jt-button-primary-fg: var(--jt-color-on-forest);
  --jt-button-primary-hover: var(--jt-color-forest-hover);
  --jt-button-accent-bg: var(--jt-color-mint);
  --jt-button-accent-fg: var(--jt-color-on-mint);
  --jt-button-accent-hover: var(--jt-color-mint-hover);
  --jt-button-secondary-bg: var(--jt-color-card);
  --jt-button-secondary-fg: var(--jt-color-ink);
  --jt-button-secondary-border: var(--jt-color-input-border);
  --jt-button-secondary-hover: var(--jt-color-sunk);
  --jt-button-ghost-fg: var(--jt-color-forest);
  --jt-button-ghost-hover: var(--jt-color-sunk);
  --jt-button-destructive-bg: var(--jt-color-error);
  --jt-button-destructive-fg: #ffffff;
  --jt-button-destructive-hover: #8a2a16;
  --jt-color-focus: #3b72d9;
  --jt-color-link: var(--jt-color-forest);
  --jt-color-selection: var(--jt-color-mint-soft);

  /* --- QR ------------------------------------------------------------
     A scanner needs dark modules on a light quiet zone whatever the theme,
     so these two never change with it. */
  --jt-qr-dark: #0b1512;
  --jt-qr-light: #ffffff;

  /* --- form ----------------------------------------------------------- */
  --jt-radius-sm: 8px; /* chips, small controls */
  --jt-radius-md: 12px; /* inputs, buttons */
  --jt-radius-lg: 16px; /* cards */
  --jt-radius-xl: 20px; /* hero cards, sheets */
  --jt-radius-2xl: 24px; /* dialogs */
  --jt-radius-pill: 999px;

  --jt-space-1: 4px;
  --jt-space-2: 8px;
  --jt-space-3: 12px;
  --jt-space-4: 16px;
  --jt-space-5: 20px;
  --jt-space-6: 24px;
  --jt-space-7: 28px;
  --jt-space-8: 32px;
  --jt-space-9: 36px;
  --jt-space-10: 40px;
  --jt-space-11: 44px;
  --jt-space-12: 48px;
  --jt-space-13: 52px;
  --jt-space-14: 56px;
  --jt-space-15: 60px;
  --jt-space-16: 64px;

  --jt-shadow-sm: 0 1px 2px rgb(11 21 18 / 0.05);
  --jt-shadow-md: 0 4px 12px rgb(11 21 18 / 0.08);
  --jt-shadow-lg: 0 12px 32px rgb(11 21 18 / 0.1);

  --jt-page-width: 68rem;
  --jt-nav-height: 64px;
  --jt-tabbar-height: 64px;

  /* --- type -----------------------------------------------------------
     One family. Each role is a font shorthand: font: var(--jt-text-h1).
     Tabular numerals on amounts and references come from .jt-numeric. */
  --jt-font-sans:
    'Inter', system-ui, -apple-system, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, sans-serif;
  --jt-font-mono: ui-monospace, 'Cascadia Code', 'SF Mono', Menlo, Consolas, monospace;

  --jt-text-display: 800 3rem/1.05 var(--jt-font-sans);
  --jt-text-h1: 700 2rem/1.15 var(--jt-font-sans);
  --jt-text-h2: 700 1.5rem/1.2 var(--jt-font-sans);
  --jt-text-h3: 600 1.125rem/1.3 var(--jt-font-sans);
  --jt-text-body: 400 1rem/1.5 var(--jt-font-sans);
  --jt-text-body-strong: 600 1rem/1.5 var(--jt-font-sans);
  --jt-text-small: 400 0.875rem/1.45 var(--jt-font-sans);
  --jt-text-caption: 500 0.75rem/1.4 var(--jt-font-sans);

  --jt-tracking-display: -0.02em;
  --jt-tracking-heading: -0.01em;
  --jt-tracking-label: 0.06em; /* uppercase captions only */

  --jt-size-xs: 0.75rem;
  --jt-size-sm: 0.875rem;
  --jt-size-base: 1rem;
  --jt-size-lg: 1.125rem;
  --jt-size-xl: 1.5rem;
  --jt-size-2xl: 2rem;
  --jt-size-3xl: 3rem;
  --jt-leading-tight: 1.15;
  --jt-leading-body: 1.5;

  --jt-motion-fast: 120ms;
  --jt-motion-base: 200ms;
  --jt-ease: cubic-bezier(0.2, 0, 0, 1);

  /* --- v1 aliases -----------------------------------------------------
     The merchant and admin pages built on the first brand still reference
     these. They resolve to the v2 values so those pages render in the new
     palette until merchant-app and back-office rebuild them, after which
     this block goes. Do not use these names in new code. */
  --jt-bg: var(--jt-color-ground);
  --jt-surface: var(--jt-color-card);
  --jt-surface-sunk: var(--jt-color-sunk);
  --jt-border: var(--jt-color-hairline);
  --jt-border-strong: var(--jt-color-input-border);
  --jt-text: var(--jt-color-ink);
  --jt-text-muted: var(--jt-color-muted);
  --jt-text-inverse: var(--jt-color-card);
  --jt-ink: var(--jt-color-ink);
  --jt-ink-muted: var(--jt-color-muted);
  --jt-paper: var(--jt-color-card);
  --jt-primary: var(--jt-color-forest);
  --jt-primary-hover: var(--jt-color-forest-hover);
  --jt-primary-soft: var(--jt-color-mint-soft);
  --jt-on-primary: var(--jt-color-on-forest);
  --jt-jade: var(--jt-color-forest);
  --jt-jade-50: var(--jt-color-mint-soft);
  --jt-jade-100: var(--jt-color-mint-soft);
  --jt-jade-400: var(--jt-color-success-dot);
  --jt-jade-600: var(--jt-color-forest);
  --jt-jade-800: var(--jt-color-forest);
  --jt-clay-50: var(--jt-color-error-bg);
  --jt-clay-400: var(--jt-color-error-dot);
  --jt-clay-600: var(--jt-color-error);
  --jt-clay-800: var(--jt-color-error);
  --jt-gold: var(--jt-color-warning);
  --jt-gold-50: var(--jt-color-warning-bg);
  --jt-gold-400: var(--jt-color-warning-dot);
  --jt-gold-600: var(--jt-color-warning-dot);
  --jt-gold-800: var(--jt-color-warning);
  --jt-slate-50: var(--jt-color-ground);
  --jt-slate-100: var(--jt-color-sunk);
  --jt-slate-200: var(--jt-color-hairline);
  --jt-slate-300: var(--jt-color-input-border);
  --jt-slate-500: var(--jt-color-muted);
  --jt-slate-700: var(--jt-color-secondary);
  --jt-slate-900: var(--jt-color-ink);
  --jt-danger-ink: var(--jt-color-error);
  --jt-danger-bg: var(--jt-color-error-bg);
  --jt-danger-border: var(--jt-color-error-dot);
  --jt-status-settled: var(--jt-color-success);
  --jt-status-settled-bg: var(--jt-color-success-bg);
  --jt-status-open: var(--jt-color-neutral);
  --jt-status-open-bg: var(--jt-color-neutral-bg);
  --jt-status-pending: var(--jt-color-warning);
  --jt-status-pending-bg: var(--jt-color-warning-bg);
  --jt-status-rejected: var(--jt-color-error);
  --jt-status-rejected-bg: var(--jt-color-error-bg);
  --jt-debit: var(--jt-color-ink);
  --jt-credit: var(--jt-color-success);
  --jt-font-display: var(--jt-font-sans);
  --jt-font-body: var(--jt-font-sans);
  --jt-radius: var(--jt-radius-sm);
  --jt-shadow: var(--jt-shadow-md);
  --jt-focus-ring: 2px solid var(--jt-color-focus);
  --jt-focus-offset: 2px;
}

/* ================================================================== dark
   A designed set, not an inversion: the primary button flips to pale ink
   with dark text, chips keep their hue on deep tints, and mint stays mint.
   The block is written twice on purpose (system preference, explicit
   toggle) and a test asserts the two copies are identical. */

@media (prefers-color-scheme: dark) {
  :root:not([data-theme='light']) {
    color-scheme: dark;

    --jt-color-forest: #163d30;
    --jt-color-forest-hover: #1f4c3a;
    --jt-color-forest-deep: #26594a;
    --jt-color-mint: #8ef0b4;
    --jt-color-mint-hover: #a9f5c7;
    --jt-color-mint-soft: #17352a;
    --jt-color-on-forest: #eaf1ec;
    --jt-color-on-mint: #0b1512;
    --jt-color-on-mint-soft: #8ef0b4;

    --jt-color-ground: #0b1512;
    --jt-color-card: #132019;
    --jt-color-sunk: #0e1914;
    --jt-color-hairline: #223129;
    --jt-color-input-border: #2e4038;
    --jt-color-scrim: rgb(0 0 0 / 0.6);

    --jt-color-ink: #eaf1ec;
    --jt-color-secondary: #c9d4ce;
    --jt-color-muted: #9baba4;
    --jt-color-faint: #6e7d77;

    --jt-color-success: #7fe0a8;
    --jt-color-success-bg: #12352a;
    --jt-color-success-dot: #7fe0a8;
    --jt-color-warning: #f0be5a;
    --jt-color-warning-bg: #3a2a0c;
    --jt-color-warning-dot: #f0be5a;
    --jt-color-error: #f0917a;
    --jt-color-error-bg: #3d1a12;
    --jt-color-error-dot: #f0917a;
    --jt-color-arriving: #8fb4f5;
    --jt-color-arriving-bg: #15294d;
    --jt-color-arriving-dot: #8fb4f5;
    --jt-color-neutral: #9baba4;
    --jt-color-neutral-bg: #1c2923;
    --jt-color-neutral-dot: #6e7d77;

    --jt-button-primary-bg: #eaf1ec;
    --jt-button-primary-fg: #0b1512;
    --jt-button-primary-hover: #ffffff;
    --jt-button-accent-bg: #8ef0b4;
    --jt-button-accent-fg: #0b1512;
    --jt-button-accent-hover: #a9f5c7;
    --jt-button-secondary-bg: #132019;
    --jt-button-secondary-fg: #eaf1ec;
    --jt-button-secondary-border: #2e4038;
    --jt-button-secondary-hover: #1c2923;
    --jt-button-ghost-fg: #8ef0b4;
    --jt-button-ghost-hover: #1c2923;
    --jt-button-destructive-bg: #f0917a;
    --jt-button-destructive-fg: #0b1512;
    --jt-button-destructive-hover: #f5a894;
    --jt-color-focus: #8fb4f5;
    --jt-color-link: #8ef0b4;
    --jt-color-selection: #1f4c3a;

    --jt-shadow-sm: 0 1px 2px rgb(0 0 0 / 0.4);
    --jt-shadow-md: 0 4px 12px rgb(0 0 0 / 0.45);
    --jt-shadow-lg: 0 12px 32px rgb(0 0 0 / 0.55);

    --jt-text-inverse: #0b1512;
  }
}

:root[data-theme='dark'] {
  color-scheme: dark;

  --jt-color-forest: #163d30;
  --jt-color-forest-hover: #1f4c3a;
  --jt-color-forest-deep: #26594a;
  --jt-color-mint: #8ef0b4;
  --jt-color-mint-hover: #a9f5c7;
  --jt-color-mint-soft: #17352a;
  --jt-color-on-forest: #eaf1ec;
  --jt-color-on-mint: #0b1512;
  --jt-color-on-mint-soft: #8ef0b4;

  --jt-color-ground: #0b1512;
  --jt-color-card: #132019;
  --jt-color-sunk: #0e1914;
  --jt-color-hairline: #223129;
  --jt-color-input-border: #2e4038;
  --jt-color-scrim: rgb(0 0 0 / 0.6);

  --jt-color-ink: #eaf1ec;
  --jt-color-secondary: #c9d4ce;
  --jt-color-muted: #9baba4;
  --jt-color-faint: #6e7d77;

  --jt-color-success: #7fe0a8;
  --jt-color-success-bg: #12352a;
  --jt-color-success-dot: #7fe0a8;
  --jt-color-warning: #f0be5a;
  --jt-color-warning-bg: #3a2a0c;
  --jt-color-warning-dot: #f0be5a;
  --jt-color-error: #f0917a;
  --jt-color-error-bg: #3d1a12;
  --jt-color-error-dot: #f0917a;
  --jt-color-arriving: #8fb4f5;
  --jt-color-arriving-bg: #15294d;
  --jt-color-arriving-dot: #8fb4f5;
  --jt-color-neutral: #9baba4;
  --jt-color-neutral-bg: #1c2923;
  --jt-color-neutral-dot: #6e7d77;

  --jt-button-primary-bg: #eaf1ec;
  --jt-button-primary-fg: #0b1512;
  --jt-button-primary-hover: #ffffff;
  --jt-button-accent-bg: #8ef0b4;
  --jt-button-accent-fg: #0b1512;
  --jt-button-accent-hover: #a9f5c7;
  --jt-button-secondary-bg: #132019;
  --jt-button-secondary-fg: #eaf1ec;
  --jt-button-secondary-border: #2e4038;
  --jt-button-secondary-hover: #1c2923;
  --jt-button-ghost-fg: #8ef0b4;
  --jt-button-ghost-hover: #1c2923;
  --jt-button-destructive-bg: #f0917a;
  --jt-button-destructive-fg: #0b1512;
  --jt-button-destructive-hover: #f5a894;
  --jt-color-focus: #8fb4f5;
  --jt-color-link: #8ef0b4;
  --jt-color-selection: #1f4c3a;

  --jt-shadow-sm: 0 1px 2px rgb(0 0 0 / 0.4);
  --jt-shadow-md: 0 4px 12px rgb(0 0 0 / 0.45);
  --jt-shadow-lg: 0 12px 32px rgb(0 0 0 / 0.55);

  --jt-text-inverse: #0b1512;
}

/* ============================================================ primitives
   The few global rules every surface needs. Anything beyond this belongs to
   a component in projects/shared/src/lib/ui. */

*,
*::before,
*::after {
  box-sizing: border-box;
}

html {
  background: var(--jt-color-ground);
  -webkit-text-size-adjust: 100%;
}

body {
  margin: 0;
  background: var(--jt-color-ground);
  color: var(--jt-color-ink);
  font: var(--jt-text-body);
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
}

::selection {
  background: var(--jt-color-selection);
}

h1,
h2,
h3,
h4 {
  margin: 0;
  color: var(--jt-color-ink);
  text-wrap: balance;
}

h1,
.jt-h1 {
  font: var(--jt-text-h1);
  letter-spacing: var(--jt-tracking-heading);
}

h2,
.jt-h2 {
  font: var(--jt-text-h2);
  letter-spacing: var(--jt-tracking-heading);
}

h3,
.jt-h3 {
  font: var(--jt-text-h3);
}

.jt-display {
  font: var(--jt-text-display);
  letter-spacing: var(--jt-tracking-display);
}

.jt-body {
  font: var(--jt-text-body);
}

.jt-small {
  font: var(--jt-text-small);
}

.jt-caption {
  font: var(--jt-text-caption);
  color: var(--jt-color-muted);
}

/* Uppercase labels get tracking; nothing else does. */
.jt-label {
  font: var(--jt-text-caption);
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: var(--jt-tracking-label);
  color: var(--jt-color-muted);
}

.jt-muted {
  color: var(--jt-color-muted);
}

/* Every figure a person might compare against another figure. Amounts in a
   column that do not line up turn a reconciliation screen into a puzzle. */
.jt-numeric,
.jt-amount,
.jt-reference {
  font-variant-numeric: tabular-nums;
  font-feature-settings: 'tnum' 1;
}

.jt-numeric,
td.jt-numeric,
.jt-amount {
  text-align: right;
}

/* References get read aloud and compared by eye. */
.jt-reference {
  font-size: var(--jt-size-sm);
  letter-spacing: 0.02em;
  word-break: break-all;
}

a {
  color: var(--jt-color-link);
  text-decoration-thickness: 1px;
  text-underline-offset: 0.15em;
}

:where(a, button, input, select, textarea, summary, [tabindex]):focus-visible {
  outline: 2px solid var(--jt-color-focus);
  outline-offset: 2px;
}

/* Wide content scrolls inside its own box; the page body never moves sideways. */
.jt-scroll-x,
.jt-table-container {
  overflow-x: auto;
  max-width: 100%;
  -webkit-overflow-scrolling: touch;
}

/* Generic flex layout, used anywhere a page needs a vertical or horizontal run without
   reaching for its own CSS. Kept here (rather than as a component) for the same reason
   .jt-scroll-x is: a page composes it directly in markup. */
.jt-stack {
  display: flex;
  flex-direction: column;
  gap: var(--jt-space-4);
}

.jt-row {
  display: flex;
  align-items: center;
  gap: var(--jt-space-3);
}

.jt-visually-hidden {
  position: absolute;
  width: 1px;
  height: 1px;
  overflow: hidden;
  clip: rect(0 0 0 0);
  clip-path: inset(50%);
  white-space: nowrap;
}

/* ----------------------------------------------------------------- table
   Applied by the jtTable directive (or the class directly). Rows are quiet
   hairlines, headers are captions, and amounts right-align through
   .jt-numeric. jt-table--sticky pins the header inside a jt-table-container.

   Every selector below repeats its .jt-table class (".jt-table.jt-table") on purpose.
   Both merchant/src/styles.css and admin/src/styles.css still carry the old v1
   ".jt-table" block at the same specificity, imported *after* this file, so on source
   order alone the app's rule would win and silently turn border-collapse back to
   collapse — which breaks .jt-table--sticky, since a sticky thead cell cannot stick
   under border-collapse: collapse. Doubling the class raises this block to a
   specificity the single-class v1 rule can never match, independent of import order,
   without editing files this task does not own. AC7 removes the v1 block entirely in
   a later slice; once that lands the doubling stops mattering but stays harmless. */
.jt-table-container {
  border: 1px solid var(--jt-color-hairline);
  border-radius: var(--jt-radius-lg);
  background: var(--jt-color-card);
}

.jt-table.jt-table {
  width: 100%;
  border-collapse: separate;
  border-spacing: 0;
  font: var(--jt-text-small);
  color: var(--jt-color-ink);
}

.jt-table.jt-table th {
  font: var(--jt-text-caption);
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: var(--jt-tracking-label);
  color: var(--jt-color-muted);
  text-align: left;
  padding: var(--jt-space-3) var(--jt-space-4);
  border-bottom: 1px solid var(--jt-color-hairline);
  background: var(--jt-color-card);
  white-space: nowrap;
}

.jt-table.jt-table td {
  padding: var(--jt-space-3) var(--jt-space-4);
  border-bottom: 1px solid var(--jt-color-hairline);
  vertical-align: middle;
}

.jt-table.jt-table tbody tr:last-child td {
  border-bottom: 0;
}

.jt-table.jt-table tbody tr:hover td {
  background: var(--jt-color-ground);
}

.jt-table--sticky.jt-table--sticky thead th {
  position: sticky;
  top: 0;
  z-index: 1;
}

.jt-table.jt-table td.jt-numeric,
.jt-table.jt-table th.jt-numeric {
  text-align: right;
}

@media (prefers-reduced-motion: reduce) {
  /*
   * Every animation goes to effectively-instant EXCEPT the two that are the only
   * evidence something is happening rather than nothing: the loading spinner and the
   * "still confirming" status dot's pulse. On a payments product the one thing a user
   * must never be left guessing about is whether their money moved (see jt-loading's
   * doc comment) — freezing that spinner for a reduced-motion user removes the only
   * signal a payout is in flight, which is worse than the motion it was meant to spare.
   * They are exempt from the kill rule below, but not left to run at full speed either:
   * they slow down instead, the same trade the old system made.
   */
  *:not(.spinner):not(.dot--pulse),
  *::before,
  *::after {
    animation-duration: 0.01ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 0.01ms !important;
    scroll-behavior: auto !important;
  }

  .spinner,
  .dot--pulse {
    animation-duration: 3s !important;
  }
}
