/* ==========================================================================
   Kwiksend — the shipping manifest.
   Ink on paper, ruled and aligned, with one red stamp.

   Contrast ratios in comments are measured, not estimated. The lightest text
   token is --ink-3 at 6.0:1; there is deliberately nothing lighter, because
   this is used in direct sunlight on low-brightness Android screens.
   ========================================================================== */

/* ── Tokens ─────────────────────────────────────────────────────────────── */
:root {
  /* Paper & ink — every neutral is chroma 0, so the one red reads as loud as
     it should. No warm tint, no cream. */
  --bg:           oklch(1.000 0 0);      /* #ffffff — literally white       */
  --surface:      oklch(0.975 0 0);      /* #f7f7f7 — sidebar, table head   */
  --surface-2:    oklch(0.955 0 0);      /* #f0f0f0 — row hover, pressed    */
  --ink:          oklch(0.200 0 0);      /* #161616 — 18.1:1                */
  --ink-2:        oklch(0.400 0 0);      /* #484848 —  9.2:1                */
  --ink-3:        oklch(0.500 0 0);      /* #636363 —  6.0:1  ← floor       */
  --rule:         oklch(0.900 0 0);      /* #dedede — hairlines             */
  --rule-strong:  oklch(0.800 0 0);      /* #bebebe — dividers              */
  --field:        oklch(0.650 0 0);      /* #8f8f8f —  3.2:1  WCAG 1.4.11   */

  /* The stamp. If it is red, it costs money or it went wrong. */
  --brand:        oklch(0.550 0.200 26); /* #cc272b — 5.4:1 on white        */
  --brand-hover:  oklch(0.480 0.190 26); /* #b00b1a — 7.2:1                 */
  --brand-ink:    oklch(0.450 0.170 26); /* #9e151b — 7.4:1 on wash         */
  --brand-wash:   oklch(0.970 0.018 26); /* #fff1ef                         */
  --on-brand:     oklch(1.000 0 0);

  --ok:           oklch(0.500 0.130 155);/* #007840 — 5.6:1                 */
  --ok-wash:      oklch(0.970 0.020 155);
  --warn:         oklch(0.540 0.130 70); /* #9d5e00 — 5.2:1                 */
  --warn-ink:     oklch(0.430 0.110 70); /* on --warn-wash — 7.6:1          */
  --warn-wash:    oklch(0.975 0.020 70);

  /* Type */
  --font-ui:   system-ui, -apple-system, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;
  --font-mono: ui-monospace, SFMono-Regular, "SF Mono", Menlo, Consolas, "Liberation Mono", monospace;

  --t-xs:   0.75rem;    --t-sm:  0.8125rem;  --t-base: 0.875rem;
  --t-md:   1rem;       --t-lg:  1.125rem;   --t-xl:   1.375rem;
  --t-2xl:  1.75rem;    --t-3xl: 2.25rem;

  /* Space — 4px base */
  --s-1: 4px;  --s-2: 8px;  --s-3: 12px; --s-4: 16px;
  --s-5: 20px; --s-6: 24px; --s-8: 32px; --s-10: 40px; --s-12: 48px; --s-16: 64px;

  --radius:    8px;
  --radius-sm: 5px;

  /* Semantic z-scale — never arbitrary 999s */
  --z-dropdown: 100; --z-sticky: 200; --z-backdrop: 300; --z-modal: 400; --z-toast: 500;

  --ease: cubic-bezier(0.22, 1, 0.36, 1);   /* ease-out-quart */
  --dur:  180ms;

  --sidebar-w: 248px;
  --tabbar-h:  56px;
  --topbar-h:  69px;
}

/* Dark is the secondary environment (night desk work), not the default:
   the stated usage condition is direct sunlight, where dark is a mirror. */
@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) {
    --bg:          oklch(0.170 0 0);
    --surface:     oklch(0.215 0 0);
    --surface-2:   oklch(0.260 0 0);
    --ink:         oklch(0.970 0 0);
    --ink-2:       oklch(0.800 0 0);
    --ink-3:       oklch(0.700 0 0);
    --rule:        oklch(0.320 0 0);
    --rule-strong: oklch(0.420 0 0);
    --field:       oklch(0.520 0 0);
    --brand:       oklch(0.680 0.190 26);
    --brand-hover: oklch(0.750 0.170 26);
    --brand-ink:   oklch(0.780 0.150 26);
    --brand-wash:  oklch(0.270 0.060 26);
    --on-brand:    oklch(0.150 0 0);
    --ok:          oklch(0.720 0.150 155);
    --ok-wash:     oklch(0.260 0.050 155);
    --warn:        oklch(0.760 0.130 70);
    --warn-ink:    oklch(0.850 0.110 70);
    --warn-wash:   oklch(0.270 0.050 70);
  }
}
:root[data-theme="dark"] {
  --bg:          oklch(0.170 0 0);  --surface:     oklch(0.215 0 0);
  --surface-2:   oklch(0.260 0 0);  --ink:         oklch(0.970 0 0);
  --ink-2:       oklch(0.800 0 0);  --ink-3:       oklch(0.700 0 0);
  --rule:        oklch(0.320 0 0);  --rule-strong: oklch(0.420 0 0);
  --field:       oklch(0.520 0 0);  --brand:       oklch(0.680 0.190 26);
  --brand-hover: oklch(0.750 0.170 26); --brand-ink: oklch(0.780 0.150 26);
  --brand-wash:  oklch(0.270 0.060 26); --on-brand:  oklch(0.150 0 0);
  --ok:          oklch(0.720 0.150 155); --ok-wash:  oklch(0.260 0.050 155);
  --warn:        oklch(0.760 0.130 70);  --warn-wash:oklch(0.270 0.050 70);
  --warn-ink:    oklch(0.850 0.110 70);
}

/* ── Reset ──────────────────────────────────────────────────────────────── */
*, *::before, *::after { box-sizing: border-box; }
* { margin: 0; }
html { -webkit-text-size-adjust: 100%; }
body {
  font-family: var(--font-ui);
  font-size: var(--t-base);
  line-height: 1.55;
  color: var(--ink);
  background: var(--bg);
  -webkit-font-smoothing: antialiased;
}
img, svg { display: block; max-width: 100%; }
[hidden] { display: none !important; }

/* Baseline size for inline icons. An SVG with only a viewBox and no width
   expands to fill its container — that is how the landing page's Compose icon
   rendered at ~200px. Declared here in the reset so component rules override. */
svg[viewBox="0 0 16 16"] { width: 16px; height: 16px; flex: none; }
h1, h2, h3, h4 { line-height: 1.2; font-weight: 650; text-wrap: balance; }
h1 { font-size: var(--t-2xl); letter-spacing: -0.02em; }
h2 { font-size: var(--t-xl);  letter-spacing: -0.015em; }
h3 { font-size: var(--t-lg);  letter-spacing: -0.01em; }
p  { text-wrap: pretty; }
a  { color: var(--brand); text-underline-offset: 2px; }
a:hover { color: var(--brand-hover); }

/* Every figure is tabular so columns align and balances don't jitter. */
.num, .money, td.num, th.num {
  font-family: var(--font-mono);
  font-variant-numeric: tabular-nums;
  font-feature-settings: "tnum" 1;
}

:where(a, button, input, select, textarea, [tabindex]):focus-visible {
  outline: 2px solid var(--ink);
  outline-offset: 2px;
  border-radius: var(--radius-sm);
}

.sr-only {
  position: absolute; width: 1px; height: 1px; padding: 0;
  overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; border: 0;
}
.skip-link {
  position: absolute; left: var(--s-3); top: -60px; z-index: var(--z-toast);
  background: var(--ink); color: var(--bg); padding: var(--s-3) var(--s-4); min-height: 44px; display: flex; align-items: center;
  border-radius: var(--radius); transition: top var(--dur) var(--ease);
}
.skip-link:focus { top: var(--s-3); color: var(--bg); }

/* ── App shell ──────────────────────────────────────────────────────────── */
.shell { min-height: 100dvh; display: grid; grid-template-columns: 1fr; }
@media (min-width: 900px) {
  .shell { grid-template-columns: var(--sidebar-w) 1fr; }
}

.sidebar {
  display: none;
  background: var(--surface);
  border-right: 1px solid var(--rule);
  padding: var(--s-5) var(--s-3);
  position: sticky; top: 0; height: 100dvh; overflow-y: auto;
}
@media (min-width: 900px) { .sidebar { display: flex; flex-direction: column; gap: var(--s-6); } }

.brand-mark {
  display: flex; align-items: center; gap: var(--s-2);
  font-weight: 700; font-size: var(--t-md); letter-spacing: -0.02em;
  color: var(--ink); text-decoration: none; padding: 0 var(--s-2);
}
.brand-mark:hover { color: var(--ink); }
.brand-mark .dot {
  width: 10px; height: 10px; border-radius: 2px; background: var(--brand);
  flex: none;
}

.nav { display: flex; flex-direction: column; gap: 2px; }
.nav-label {
  font-size: var(--t-xs); font-weight: 600; color: var(--ink-3);
  text-transform: uppercase; letter-spacing: 0.06em;
  padding: var(--s-4) var(--s-2) var(--s-1);
}
.nav a {
  display: flex; align-items: center; gap: var(--s-3);
  padding: var(--s-2) var(--s-3); border-radius: var(--radius-sm);
  color: var(--ink-2); text-decoration: none; font-weight: 500;
  transition: background var(--dur) var(--ease), color var(--dur) var(--ease);
}
.nav a:hover { background: var(--surface-2); color: var(--ink); }
.nav a[aria-current="page"] { background: var(--bg); color: var(--ink); font-weight: 600; box-shadow: inset 0 0 0 1px var(--rule); }
.nav a[aria-current="page"] svg { color: var(--brand); }
.nav svg { width: 17px; height: 17px; flex: none; }

/* Wallet chip in the sidebar — always visible, because the balance is the
   thing that decides whether the next action is possible. */
.wallet-chip {
  margin-top: auto; border: 1px solid var(--rule); border-radius: var(--radius);
  padding: var(--s-3); background: var(--bg);
}
.wallet-chip .lbl { font-size: var(--t-xs); color: var(--ink-3); font-weight: 600; text-transform: uppercase; letter-spacing: 0.05em; }
.wallet-chip .amt { font-size: var(--t-xl); font-weight: 650; margin-top: 2px; }
.wallet-chip.is-low { border-color: var(--warn); background: var(--warn-wash); }
.wallet-chip.is-low .amt { color: var(--warn); }
.wallet-chip.is-empty { border-color: var(--brand); background: var(--brand-wash); }
.wallet-chip.is-empty .amt { color: var(--brand-ink); }

/* Mobile: bottom tab bar. Thumb reach beats a hamburger for a 5-item app. */
.tabbar {
  position: fixed; inset: auto 0 0 0; z-index: var(--z-sticky);
  display: grid; grid-auto-flow: column; grid-auto-columns: 1fr;
  background: var(--bg); border-top: 1px solid var(--rule);
  padding-bottom: env(safe-area-inset-bottom);
}
@media (min-width: 900px) { .tabbar { display: none; } }
.tabbar a {
  display: flex; flex-direction: column; align-items: center; justify-content: center;
  gap: 3px; padding: var(--s-2) var(--s-1); min-height: 56px;
  color: var(--ink-3); text-decoration: none; font-size: var(--t-xs); font-weight: 500;
}
.tabbar a[aria-current="page"] { color: var(--brand); font-weight: 650; }
.tabbar svg { width: 20px; height: 20px; }

.main { min-width: 0; padding-bottom: 76px; }
@media (min-width: 900px) { .main { padding-bottom: 0; } }

.topbar {
  display: flex; align-items: center; justify-content: space-between; gap: var(--s-4);
  padding: var(--s-4) var(--s-5); border-bottom: 1px solid var(--rule);
  background: var(--bg); position: sticky; top: 0; z-index: var(--z-sticky);
}
.topbar h1 { font-size: var(--t-lg); }

/* The sidebar wallet chip is display:none below 900px, which left the mobile
   app with no balance anywhere — you could compose a spend without knowing
   what you had. This is that number, in the one bar that is always on screen. */
.topbar-balance { display: flex; flex-direction: column; align-items: flex-end; line-height: 1.15; text-decoration: none; }
.topbar-balance .k { font-size: var(--t-xs); color: var(--ink-3); font-weight: 600; }
.topbar-balance .v { font-family: var(--font-mono); font-variant-numeric: tabular-nums; font-weight: 700; color: var(--ink); }
.topbar-balance.is-low .v   { color: var(--warn-ink); }
.topbar-balance.is-empty .v { color: var(--brand-ink); }
@media (min-width: 900px) { .topbar-balance { display: none; } }
.topbar .sub { color: var(--ink-3); font-size: var(--t-sm); margin-top: 1px; }
/* The topbar is sticky; on a 360x780 phone a two-line subtitle costs a fifth
   of the viewport on every screen. The page heading alone carries it there. */
@media (max-width: 719px) { .topbar .sub { display: none; } }
.content { padding: var(--s-6) var(--s-5); max-width: 1180px; }
@media (max-width: 599px) { .content, .topbar { padding-left: var(--s-4); padding-right: var(--s-4); } }

/* ── Buttons — one shape everywhere ─────────────────────────────────────── */
.btn {
  display: inline-flex; align-items: center; justify-content: center; gap: var(--s-2);
  min-height: 40px; padding: 0 var(--s-4);
  font: inherit; font-weight: 600; line-height: 1;
  border-radius: var(--radius); border: 1px solid transparent;
  cursor: pointer; text-decoration: none; white-space: nowrap;
  transition: background var(--dur) var(--ease), border-color var(--dur) var(--ease),
              color var(--dur) var(--ease), transform 80ms var(--ease);
}
@media (max-width: 899px) { .btn { min-height: 44px; } }   /* touch target */
.btn:active { transform: translateY(1px); }
.btn svg { width: 16px; height: 16px; }

/* Primary is red because pressing it spends the user's money. */
.btn-primary { background: var(--brand); color: var(--on-brand); border-color: var(--brand); }
.btn-primary:hover:not(:disabled) { background: var(--brand-hover); border-color: var(--brand-hover); color: var(--on-brand); }

.btn-default { background: var(--bg); color: var(--ink); border-color: var(--field); }
.btn-default:hover:not(:disabled) { background: var(--surface-2); color: var(--ink); }

.btn-ghost { background: transparent; color: var(--ink-2); border-color: transparent; }
.btn-ghost:hover:not(:disabled) { background: var(--surface-2); color: var(--ink); }

/* Disabled buttons carry the ENTIRE instruction on this app's primary action
   ("Add at least one recipient", "Top up GH₵1.50 to send"). A blanket
   opacity:0.5 composited #cc272b over white to rgb(229,146,148), putting the
   white label at a measured 2.37:1 — unreadable in the direct sunlight this
   product is used in. Disabled is a real token pair now, not a dimmer. */
.btn:disabled, .btn[aria-disabled="true"] {
  cursor: not-allowed; transform: none;
  background: var(--surface-2); color: var(--ink-2);
  border-color: var(--field); opacity: 1;
}
.btn-ghost:disabled { background: transparent; border-color: transparent; color: var(--ink-3); }
.btn.is-loading { pointer-events: none; position: relative; color: transparent; }
.btn.is-loading::after {
  content: ""; position: absolute; width: 15px; height: 15px;
  border: 2px solid currentColor; border-top-color: transparent; border-radius: 50%;
  color: var(--on-brand); animation: spin 700ms linear infinite;
}
.btn-primary:disabled.is-loading::after { color: var(--ink-2); }
.btn-default.is-loading::after, .btn-ghost.is-loading::after { color: var(--ink); }
@keyframes spin { to { transform: rotate(360deg); } }
.btn-sm { min-height: 32px; padding: 0 var(--s-3); font-size: var(--t-sm); }
/* Small buttons must still clear the touch-target floor on phones — the
   .btn-sm min-height would otherwise undo the 44px set above. */
@media (max-width: 899px) { .btn-sm { min-height: 40px; } }
.btn-block { width: 100%; }

/* ── Forms ──────────────────────────────────────────────────────────────── */
.field { display: flex; flex-direction: column; gap: var(--s-2); margin-bottom: var(--s-5); }
.field > label, .label {
  font-weight: 600; font-size: var(--t-sm); color: var(--ink);
}
.hint { font-size: var(--t-sm); color: var(--ink-3); }
.input, .select, .textarea {
  font: inherit; width: 100%; color: var(--ink); background: var(--bg);
  border: 1px solid var(--field); border-radius: var(--radius);
  padding: 10px var(--s-3); min-height: 40px;
  transition: border-color var(--dur) var(--ease), box-shadow var(--dur) var(--ease);
}
@media (max-width: 899px) {
  /* 16px prevents iOS Safari zooming the viewport on focus. */
  .input, .select, .textarea { font-size: 16px; min-height: 44px; }
}
.textarea { min-height: 132px; resize: vertical; line-height: 1.55; }
.input::placeholder, .textarea::placeholder { color: var(--ink-3); }  /* 6.0:1, not the default light gray */
.input:hover, .textarea:hover, .select:hover { border-color: var(--ink-3); }
/* Ink, not brand: a red ring on a valid field reads as an error, and red is
   reserved for money and failure. Ink at 18.1:1 is a stronger indicator anyway. */
.input:focus, .textarea:focus, .select:focus {
  outline: none; border-color: var(--ink);
  box-shadow: 0 0 0 3px color-mix(in oklch, var(--ink) 15%, transparent);
}
.input:disabled, .textarea:disabled, .select:disabled { background: var(--surface-2); color: var(--ink-3); cursor: not-allowed; }
.input[aria-invalid="true"], .textarea[aria-invalid="true"] { border-color: var(--brand); }
.field-error { font-size: var(--t-sm); color: var(--brand-ink); font-weight: 550; }
.select {
  appearance: none; padding-right: var(--s-8);
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' fill='none' stroke='%23636363' stroke-width='1.8' stroke-linecap='round'%3E%3Cpath d='M4 6l4 4 4-4'/%3E%3C/svg%3E");
  background-repeat: no-repeat; background-position: right var(--s-3) center; background-size: 15px;
}
fieldset { border: 0; padding: 0; }

/* ── Ruled data (not cards) ─────────────────────────────────────────────── */
.panel { border: 1px solid var(--rule); border-radius: var(--radius); background: var(--bg); overflow: hidden; }
.panel-head {
  display: flex; align-items: center; justify-content: space-between; gap: var(--s-3);
  padding: var(--s-3) var(--s-4); border-bottom: 1px solid var(--rule); background: var(--surface);
}
.panel-head h2, .panel-head h3 { font-size: var(--t-base); font-weight: 650; }
.panel-body { padding: var(--s-4); }

.table { width: 100%; border-collapse: collapse; }
.table th, .table td { padding: var(--s-3) var(--s-4); text-align: left; }
.table thead th {
  font-size: var(--t-xs); font-weight: 650; color: var(--ink-2);
  text-transform: uppercase; letter-spacing: 0.05em;
  background: var(--surface); border-bottom: 1px solid var(--rule); white-space: nowrap;
}
.table tbody tr { border-bottom: 1px solid var(--rule); }
.table tbody tr:last-child { border-bottom: 0; }
.table tbody tr:hover { background: var(--surface-2); }
.table .right { text-align: right; }
.table .num { white-space: nowrap; }

/* Under 720px a wide table becomes labelled stacked rows rather than a
   horizontal scroll the user has to discover. */
@media (max-width: 719px) {
  .table--stack thead { display: none; }
  .table--stack tbody tr { display: block; padding: var(--s-3) var(--s-4); border-bottom: 1px solid var(--rule); }
  .table--stack tbody td { display: flex; justify-content: space-between; gap: var(--s-4); padding: 3px 0; text-align: right; }
  .table--stack tbody td::before {
    content: attr(data-label); font-weight: 600; color: var(--ink-3);
    font-size: var(--t-sm); text-align: left; font-family: var(--font-ui);
  }
  .table--stack tbody td:first-child { font-weight: 650; }
}

/* ── Status pills — colour is never the only signal ─────────────────────── */
.pill {
  display: inline-flex; align-items: center; gap: 6px;
  font-size: var(--t-xs); font-weight: 650; letter-spacing: 0.01em;
  padding: 3px 9px 3px 7px; border-radius: 999px; white-space: nowrap;
  border: 1px solid transparent;
}
.pill::before { content: ""; width: 6px; height: 6px; flex: none; background: currentColor; }
.pill-ok      { color: var(--ok);        background: var(--ok-wash);    border-color: color-mix(in oklch, var(--ok) 30%, transparent); }
.pill-ok::before      { border-radius: 50%; }                                  /* circle  */
.pill-sent    { color: var(--ink-2);     background: var(--surface-2);  border-color: var(--rule); }
.pill-sent::before    { border-radius: 50%; }
.pill-queued  { color: var(--warn-ink);  background: var(--warn-wash);  border-color: color-mix(in oklch, var(--warn) 30%, transparent); }
.pill-queued::before  { border-radius: 1px; }                                  /* square  */
.pill-fail    { color: var(--brand-ink); background: var(--brand-wash); border-color: color-mix(in oklch, var(--brand) 30%, transparent); }
.pill-fail::before    { clip-path: polygon(50% 0, 100% 100%, 0 100%); }         /* triangle */

/* ── Alerts ─────────────────────────────────────────────────────────────── */
.alert {
  display: flex; gap: var(--s-3); align-items: flex-start;
  padding: var(--s-3) var(--s-4); border-radius: var(--radius);
  border: 1px solid var(--rule); background: var(--surface); margin-bottom: var(--s-5);
  font-size: var(--t-base);
}
.alert svg { width: 18px; height: 18px; flex: none; margin-top: 1px; }
.alert-error   { border-color: color-mix(in oklch, var(--brand) 40%, transparent); background: var(--brand-wash); color: var(--brand-ink); }
.alert-error a, .shortfall a:not(.btn) { color: var(--brand-ink); font-weight: 600; }
.alert-success { border-color: color-mix(in oklch, var(--ok) 40%, transparent);    background: var(--ok-wash);    color: var(--ok); }
.alert-warn    { border-color: color-mix(in oklch, var(--warn) 40%, transparent);  background: var(--warn-wash);  color: var(--warn-ink); }
.alert strong { font-weight: 700; }

/* ── Empty states — teach the next action ───────────────────────────────── */
.empty { padding: var(--s-12) var(--s-5); text-align: center; }
.empty svg { width: 32px; height: 32px; margin: 0 auto var(--s-3); color: var(--ink-3); }
.empty h3 { margin-bottom: var(--s-2); }
.empty p { color: var(--ink-2); max-width: 46ch; margin: 0 auto var(--s-5); }

/* ── Figure rows (the manifest look) ────────────────────────────────────── */
.figures { display: grid; gap: 0; }
.figure-row {
  display: flex; align-items: baseline; justify-content: space-between; gap: var(--s-4);
  padding: var(--s-3) 0; border-bottom: 1px dashed var(--rule);
}
.figure-row:last-child { border-bottom: 0; }
.figure-row dt { color: var(--ink-2); }
.figure-row dd { font-family: var(--font-mono); font-variant-numeric: tabular-nums; font-weight: 600; }
.figure-row.total { border-top: 2px solid var(--ink); border-bottom: 0; margin-top: var(--s-2); padding-top: var(--s-3); }
.figure-row.total dt { color: var(--ink); font-weight: 650; }
.figure-row.total dd { font-size: var(--t-xl); font-weight: 700; }

/* ── Utilities ──────────────────────────────────────────────────────────── */
.stack   { display: flex; flex-direction: column; }
.stack-4 { gap: var(--s-4); } .stack-6 { gap: var(--s-6); } .stack-8 { gap: var(--s-8); }
.row     { display: flex; align-items: center; gap: var(--s-3); flex-wrap: wrap; }
.row-between { display: flex; align-items: center; justify-content: space-between; gap: var(--s-3); flex-wrap: wrap; }
.grid-auto { display: grid; gap: var(--s-4); grid-template-columns: repeat(auto-fit, minmax(240px, 1fr)); }
.muted   { color: var(--ink-3); }
.dim     { color: var(--ink-2); }
.mono    { font-family: var(--font-mono); }
.small   { font-size: var(--t-sm); }
.xs      { font-size: var(--t-xs); }
.strong  { font-weight: 650; }
.mt-4 { margin-top: var(--s-4); } .mt-6 { margin-top: var(--s-6); } .mt-8 { margin-top: var(--s-8); }
.mb-4 { margin-bottom: var(--s-4); } .mb-6 { margin-bottom: var(--s-6); }
.nowrap { white-space: nowrap; }
.text-ok { color: var(--ok); } .text-brand { color: var(--brand-ink); } .text-warn { color: var(--warn); }

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

/* ==========================================================================
   The composer & cost meter — the signature surface.
   Design principle: the price is never a surprise. The meter is pinned, the
   figure updates as you type, and the send button states the exact amount.
   ========================================================================== */
.composer { display: grid; gap: var(--s-6); align-items: start; }
@media (min-width: 1024px) { .composer { grid-template-columns: minmax(0, 1fr) 340px; } }

.counter-line {
  display: flex; flex-wrap: wrap; gap: var(--s-4);
  font-size: var(--t-sm); color: var(--ink-2); margin-top: var(--s-2);
}
.counter-line b { font-family: var(--font-mono); font-variant-numeric: tabular-nums; color: var(--ink); font-weight: 650; }
.counter-line .warnable.is-warn { color: var(--warn); }
.counter-line .warnable.is-warn b { color: var(--warn); }

/* Encoding warning: a single pasted smart quote doubles the bill, so it is
   called out explicitly with the offending characters shown. */
.encoding-warn {
  display: none; margin-top: var(--s-3); font-size: var(--t-sm);
  padding: var(--s-3); border-radius: var(--radius);
  background: var(--warn-wash); color: var(--warn-ink);
  border: 1px solid color-mix(in oklch, var(--warn) 40%, transparent);
}
.encoding-warn.is-on { display: block; }
.encoding-warn code {
  font-family: var(--font-mono); background: var(--bg); color: var(--ink);
  padding: 1px 5px; border-radius: 3px; border: 1px solid var(--rule);
}

.meter { position: sticky; top: calc(var(--topbar-h) + var(--s-4)); }

/* ── Mobile: the meter is a fixed bottom bar, not the end of a long scroll ──
   Design Principle 1 says the cost is visible AS the message is typed. Stacked
   in flow at 360px the total landed ~1,170px below the message field — a full
   viewport of scrolling between cause and effect, on the exact device the
   product is used on at the port. Pinned to the bottom, the total and the send
   button are always on screen; the breakdown folds away behind a toggle. */
@media (max-width: 1023px) {
  .meter {
    position: fixed; inset: auto 0 var(--tabbar-h) 0; z-index: var(--z-sticky);
    background: var(--bg); border-top: 1px solid var(--rule-strong);
    box-shadow: 0 -6px 24px -12px rgba(0, 0, 0, 0.28);
    max-height: calc(100dvh - var(--tabbar-h) - var(--topbar-h));
    display: flex; flex-direction: column;
  }
  .meter .panel { border: 0; border-radius: 0; background: transparent; min-height: 0; display: flex; flex-direction: column; }
  .meter .panel-head { display: none; }              /* the toggle row replaces it */
  .meter .panel-body { padding: var(--s-3) var(--s-4) var(--s-4); overflow-y: auto; }
  .meter #sendBtn { margin-top: 0; }
  .meter .panel-body > .xs { margin-top: var(--s-3); }
  .meter-toggle { display: flex; }
  .meter-details[hidden] { display: none; }
  /* Content must clear the pinned meter + tab bar, or the last field is unreachable. */
  .main { padding-bottom: calc(var(--tabbar-h) + 148px); }
}
@media (min-width: 1024px) {
  .meter-toggle { display: none; }
  .meter-details[hidden] { display: block; }         /* never folded on desktop */
}

/* The fold control: a real button, states the two facts that matter. */
.meter-toggle {
  width: 100%; align-items: center; justify-content: space-between; gap: var(--s-3);
  font: inherit; font-weight: 600; text-align: left; cursor: pointer;
  background: var(--surface); border: 0; border-bottom: 1px solid var(--rule);
  padding: var(--s-3) var(--s-4); min-height: 48px; color: var(--ink);
}
.meter-toggle .mt-total { font-family: var(--font-mono); font-variant-numeric: tabular-nums; font-weight: 700; }
.meter-toggle .chev { transition: transform var(--dur) var(--ease); color: var(--ink-3); }
.meter-toggle[aria-expanded="true"] .chev { transform: rotate(180deg); }

/* ── Failure shouts ────────────────────────────────────────────────────────
   Previously the shortfall was --ink-3 at --t-sm while the unaffordable total
   was the largest thing on the panel: the blocking fact was the quietest
   element in the blocking state. Red already means "money or trouble" in this
   system, and a shortfall is both, so it gets full weight. */
.meter.is-short .figure-row.total dd { color: var(--brand-ink); }
.shortfall {
  display: none; margin-top: var(--s-4); padding: var(--s-3) var(--s-4);
  border: 1px solid var(--brand); border-radius: var(--radius);
  background: var(--brand-wash); color: var(--brand-ink);
}
.meter.is-short .shortfall { display: block; }
.shortfall .amt { font-size: var(--t-xl); font-weight: 700; font-family: var(--font-mono); display: block; line-height: 1.15; }
.shortfall .lbl { font-size: var(--t-sm); font-weight: 600; }
.shortfall .btn { margin-top: var(--s-3); }
/* Balance-after goes negative here — it is a failure, not an aside. */
.meter.is-short #mAfter { color: var(--brand-ink); font-weight: 700; }
.meter.is-short .figure-row dt.after-label { color: var(--brand-ink); font-weight: 600; }
.meter .panel-body { padding: var(--s-4) var(--s-5) var(--s-5); }

/* The figure cross-fades when it changes, so the eye is drawn to the number
   that just moved. This is the one deliberate moment of motion in the app. */
.meter-total { transition: opacity 140ms var(--ease); }
.meter-total.is-updating { opacity: 0.35; }

.meter .status {
  font-size: var(--t-sm); color: var(--ink-3); display: flex;
  align-items: center; gap: var(--s-2); margin-bottom: var(--s-3);
}
.meter .status .dot {
  width: 7px; height: 7px; border-radius: 50%; background: var(--ok); flex: none;
}
.meter .status.is-busy .dot { background: var(--warn); animation: pulse 900ms ease-in-out infinite; }
.meter .status.is-error .dot { background: var(--brand); }
@keyframes pulse { 0%, 100% { opacity: 1; } 50% { opacity: 0.3; } }

.breakdown-row {
  display: flex; justify-content: space-between; gap: var(--s-3);
  font-size: var(--t-sm); padding: var(--s-2) 0; border-bottom: 1px dashed var(--rule);
}
.breakdown-row:last-of-type { border-bottom: 0; }
.breakdown-row .c { color: var(--ink-2); }
.breakdown-row .v { font-family: var(--font-mono); font-variant-numeric: tabular-nums; }

.recipient-tally {
  display: flex; flex-wrap: wrap; gap: var(--s-2) var(--s-4); margin-top: var(--s-2);
  font-size: var(--t-sm);
}
.tally-ok   { color: var(--ok);        font-weight: 600; }
.tally-dupe { color: var(--ink-3); }
.tally-bad  { color: var(--brand-ink); font-weight: 600; }
.tally-bad button {
  font: inherit; background: none; border: 0; padding: 0; margin-left: 4px;
  color: inherit; text-decoration: underline; cursor: pointer;
}

.seg-note {
  font-size: var(--t-sm); color: var(--ink-2); background: var(--surface);
  border: 1px solid var(--rule); border-radius: var(--radius);
  padding: var(--s-3); margin-top: var(--s-4);
}

/* Source tabs for choosing recipients */
.tabs { display: flex; gap: var(--s-1); border-bottom: 1px solid var(--rule); margin-bottom: var(--s-4); }
.tabs button {
  font: inherit; font-weight: 600; font-size: var(--t-sm);
  background: none; border: 0; border-bottom: 2px solid transparent;
  padding: var(--s-2) var(--s-3); margin-bottom: -1px;
  color: var(--ink-3); cursor: pointer;
  transition: color var(--dur) var(--ease), border-color var(--dur) var(--ease);
}
.tabs button:hover { color: var(--ink); }
.tabs button[aria-selected="true"] { color: var(--ink); border-bottom-color: var(--brand); }
.tabpanel[hidden] { display: none; }

/* ── Auth pages ─────────────────────────────────────────────────────────── */
.auth-wrap { min-height: 100dvh; display: grid; place-items: center; padding: var(--s-6) var(--s-4); }
.auth-card { width: 100%; max-width: 400px; }
.auth-card .brand-mark { justify-content: center; margin-bottom: var(--s-6); font-size: var(--t-lg); }
.auth-card h1 { font-size: var(--t-xl); margin-bottom: var(--s-2); }
.auth-card .lede { color: var(--ink-2); margin-bottom: var(--s-6); }
.auth-foot { text-align: center; margin-top: var(--s-5); font-size: var(--t-sm); color: var(--ink-2); }

/* ── Stat rows (not hero-metric cards) ──────────────────────────────────── */
.stat-strip {
  display: grid; grid-template-columns: repeat(auto-fit, minmax(150px, 1fr));
  border: 1px solid var(--rule); border-radius: var(--radius); overflow: hidden;
}
.stat-strip > div { padding: var(--s-4); border-right: 1px solid var(--rule); border-bottom: 1px solid var(--rule); }
.stat-strip > div:last-child { border-right: 0; }
.stat-strip .k { font-size: var(--t-xs); font-weight: 650; color: var(--ink-3); text-transform: uppercase; letter-spacing: 0.05em; }
.stat-strip .v { font-family: var(--font-mono); font-variant-numeric: tabular-nums; font-size: var(--t-xl); font-weight: 650; margin-top: 3px; }
.stat-strip .n { font-size: var(--t-sm); color: var(--ink-3); }

/* ── Merge fields & scheduling ─────────────────────────────────────────────── */
.merge-bar { display: flex; flex-wrap: wrap; align-items: center; gap: var(--s-2); margin-top: var(--s-3); }
.chip {
  font: inherit; font-family: var(--font-mono); font-size: var(--t-xs); font-weight: 600;
  background: var(--surface); color: var(--ink-2); border: 1px solid var(--rule-strong);
  border-radius: 999px; padding: 4px 10px; cursor: pointer; min-height: 28px;
  transition: background var(--dur) var(--ease), color var(--dur) var(--ease), border-color var(--dur) var(--ease);
}
.chip:hover { background: var(--surface-2); color: var(--ink); border-color: var(--field); }
@media (max-width: 899px) { .chip { min-height: 34px; } }

/* A rendered sample beats trusting a placeholder, on a screen that spends money. */
.merge-preview {
  margin-top: var(--s-4); border: 1px solid var(--rule); border-radius: var(--radius);
  background: var(--surface); padding: var(--s-3) var(--s-4);
}
.merge-row {
  font-size: var(--t-sm); padding: var(--s-2) 0; border-bottom: 1px dashed var(--rule);
  display: flex; gap: var(--s-3); align-items: baseline;
}
.merge-row:last-child { border-bottom: 0; }
.merge-row .to { font-family: var(--font-mono); font-size: var(--t-xs); color: var(--ink-3); flex: none; min-width: 12ch; }
.merge-row .txt { color: var(--ink); }
.merge-row.is-fallback .txt { color: var(--warn-ink); }

.when-picker { display: flex; flex-wrap: wrap; gap: var(--s-2); margin-top: var(--s-5); }
.when-opt {
  display: flex; align-items: center; gap: var(--s-2); cursor: pointer;
  border: 1px solid var(--rule-strong); border-radius: var(--radius);
  padding: var(--s-2) var(--s-3); font-size: var(--t-sm); font-weight: 600; flex: 1;
  min-height: 40px; background: var(--bg);
}
.when-opt:has(input:checked) { border-color: var(--ink); box-shadow: inset 0 0 0 1px var(--ink); }
.when-opt input { accent-color: var(--ink); }
.when-at { width: 100%; margin-top: var(--s-3); }
.when-at[hidden] { display: none !important; }

/* One-time code entry: wide tracking so six digits are read as six digits. */
.code-input {
  font-family: var(--font-mono); font-size: 1.75rem; font-weight: 700;
  letter-spacing: 0.4em; text-align: center; padding-left: 0.4em;
}
