/* ─────────────────────────────────────────────────────────────
   Sales Tracker — stylesheet

   One file, no framework, no CDN. Everything is driven by the custom
   properties below, so changing the brand colour is a one-line edit.

   Written mobile-first in the places that matter. The app is installed
   to a phone home screen, so it spends most of its life in a 390px
   standalone window with no browser chrome and a thumb for a mouse:
   navigation sits at the bottom where a thumb reaches, tap targets are
   44px, and the wide tables fold into cards rather than a side-scroll.

   Dark mode is selected, not flipped. The chart and status colours are
   stepped separately for the dark surface and validated against it.
   ───────────────────────────────────────────────────────────── */

:root {
  color-scheme: light;

  --bg:          #f4f6fa;
  --bg-accent:   #e9eefb;   /* the tint the page fades from, behind the header */
  --surface:     #ffffff;
  --surface-alt: #f8fafc;
  --surface-sunk:#f1f5f9;
  --border:      #e2e8f0;
  --border-firm: #cbd5e1;

  --text:        #16202e;
  --text-soft:   #5a6b81;
  --text-faint:  #8b9ab0;

  --brand:       #1d4ed8;
  --brand-dark:  #1e3a8a;
  --brand-soft:  #3b6fe0;
  --brand-tint:  #eef4ff;

  /* ── Data colours ──────────────────────────────────────────
     Five hues, validated as a set against the white card surface:
     every pair clears the normal-vision floor and the contrast gate.
     Amber and green sit in the 6–8 CVD band, which is permitted only
     because every bar, badge and legend entry in this app carries a
     visible text label — colour never carries meaning on its own. */
  --c-blue:      #1d4ed8;
  --c-green:     #059669;
  --c-amber:     #d97706;
  --c-red:       #be123c;
  --c-violet:    #7c3aed;

  --green:       #047857;
  --green-tint:  #ecfdf5;
  --amber:       #b45309;
  --amber-tint:  #fffbeb;
  --red:         #be123c;
  --red-tint:    #fff1f2;
  --violet-tint: #f5f3ff;
  --slate-tint:  #f1f5f9;

  /* Chart chrome. Recessive on purpose — the data is the ink. */
  --grid:        #eef1f6;
  --axis:        #d7dee9;

  --radius:      14px;
  --radius-sm:   9px;
  --radius-xs:   6px;
  --shadow:      0 1px 2px rgba(15, 23, 42, .05), 0 4px 14px rgba(15, 23, 42, .045);
  --shadow-lift: 0 2px 6px rgba(15, 23, 42, .07), 0 14px 32px rgba(15, 23, 42, .09);
  --ring:        0 0 0 3px rgba(29, 78, 216, .16);

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

  --ease: cubic-bezier(.22, .61, .36, 1);
  --fast: 130ms;
  --slow: 420ms;

  /* Height of the bottom tab bar, so page padding can clear it. */
  --tabbar: 0px;
}

/* Dark is a separate set of steps chosen for the dark surface, not the
   light values inverted. Declared under both scopes so the OS setting
   and an explicit choice each win where they should. */
@media (prefers-color-scheme: dark) {
  :root:where(:not([data-theme="light"])) {
    color-scheme: dark;

    --bg:          #0e131f;
    --bg-accent:   #141c30;
    --surface:     #161c2b;
    --surface-alt: #1b2334;
    --surface-sunk:#111828;
    --border:      #26304a;
    --border-firm: #35415f;

    --text:        #e8edf7;
    --text-soft:   #a3b0c8;
    --text-faint:  #7b89a3;

    --brand:       #3987e5;
    --brand-dark:  #6aa6ee;
    --brand-soft:  #5c9aea;
    --brand-tint:  #17243c;

    --c-blue:      #3987e5;
    --c-green:     #199e70;
    --c-amber:     #c98500;
    --c-red:       #e0607a;
    --c-violet:    #9085e9;

    --green:       #34c395;
    --green-tint:  #10281f;
    --amber:       #e0a336;
    --amber-tint:  #2a2110;
    --red:         #e0607a;
    --red-tint:    #2e1620;
    --violet-tint: #1e1b33;
    --slate-tint:  #1f2839;

    --grid:        #202940;
    --axis:        #2f3a55;

    --shadow:      0 1px 2px rgba(0, 0, 0, .4), 0 4px 14px rgba(0, 0, 0, .3);
    --shadow-lift: 0 2px 6px rgba(0, 0, 0, .45), 0 14px 32px rgba(0, 0, 0, .4);
    --ring:        0 0 0 3px rgba(57, 135, 229, .28);
  }
}

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

  --bg:          #0e131f;
  --bg-accent:   #141c30;
  --surface:     #161c2b;
  --surface-alt: #1b2334;
  --surface-sunk:#111828;
  --border:      #26304a;
  --border-firm: #35415f;

  --text:        #e8edf7;
  --text-soft:   #a3b0c8;
  --text-faint:  #7b89a3;

  --brand:       #3987e5;
  --brand-dark:  #6aa6ee;
  --brand-soft:  #5c9aea;
  --brand-tint:  #17243c;

  --c-blue:      #3987e5;
  --c-green:     #199e70;
  --c-amber:     #c98500;
  --c-red:       #e0607a;
  --c-violet:    #9085e9;

  --green:       #34c395;
  --green-tint:  #10281f;
  --amber:       #e0a336;
  --amber-tint:  #2a2110;
  --red:         #e0607a;
  --red-tint:    #2e1620;
  --violet-tint: #1e1b33;
  --slate-tint:  #1f2839;

  --grid:        #202940;
  --axis:        #2f3a55;

  --shadow:      0 1px 2px rgba(0, 0, 0, .4), 0 4px 14px rgba(0, 0, 0, .3);
  --shadow-lift: 0 2px 6px rgba(0, 0, 0, .45), 0 14px 32px rgba(0, 0, 0, .4);
  --ring:        0 0 0 3px rgba(57, 135, 229, .28);
}

* { box-sizing: border-box; }

html {
  -webkit-text-size-adjust: 100%;
  scroll-behavior: smooth;
}

body {
  margin: 0;
  font-family: var(--font);
  font-size: 15px;
  line-height: 1.5;
  color: var(--text);
  background: var(--bg);
  /* A single soft wash behind the header rather than a flat field, so
     the page has somewhere to start. Fixed so it does not scroll away. */
  background-image: linear-gradient(180deg, var(--bg-accent) 0%, var(--bg) 340px);
  background-repeat: no-repeat;
  background-attachment: fixed;
  -webkit-font-smoothing: antialiased;
  overflow-wrap: break-word;
}

a { color: var(--brand); text-decoration: none; }
a:hover { text-decoration: underline; }

h1, h2, h3 { margin: 0 0 .6rem; line-height: 1.25; font-weight: 650; letter-spacing: -.011em; }
h1 { font-size: 1.5rem; }
h2 { font-size: 1.12rem; }
h3 { font-size: 1rem; }

:focus-visible {
  outline: 2px solid var(--brand);
  outline-offset: 2px;
  border-radius: var(--radius-xs);
}

/* ── Top bar ─────────────────────────────────────────────── */

.topbar {
  background: color-mix(in srgb, var(--surface) 82%, transparent);
  backdrop-filter: saturate(180%) blur(14px);
  -webkit-backdrop-filter: saturate(180%) blur(14px);
  border-bottom: 1px solid var(--border);
  position: sticky;
  top: 0;
  z-index: 50;
  padding-top: env(safe-area-inset-top);
}

@supports not (backdrop-filter: blur(1px)) {
  .topbar { background: var(--surface); }
}

.topbar-inner {
  max-width: 1320px;
  margin: 0 auto;
  padding: 0 1.25rem;
  display: flex;
  align-items: center;
  gap: 1.25rem;
  min-height: 58px;
  flex-wrap: wrap;
}

.brand {
  font-weight: 700;
  font-size: 1.05rem;
  color: var(--text);
  letter-spacing: -.015em;
  display: inline-flex;
  align-items: center;
  gap: .5rem;
}
.brand:hover { text-decoration: none; }

.brand-mark {
  width: 26px;
  height: 26px;
  border-radius: 8px;
  background: linear-gradient(145deg, var(--brand-soft), var(--brand-dark));
  display: grid;
  place-items: center;
  flex: none;
}
.brand-mark svg { width: 15px; height: 15px; display: block; }

.nav {
  display: flex;
  gap: .15rem;

  /* flex-basis 0, not auto. A wrapping flex container decides its line
     breaks from each item's hypothetical size *before* it shrinks
     anything, so a nav wide enough to sit alone on a line pushes the
     account controls onto a second row — a header twice as tall on
     every page, even with 300px to spare. A basis of 0 means the nav
     never forces that break; it grows into whatever is left and
     scrolls internally if ten destinations still do not fit. */
  flex: 1 1 0;
  min-width: 0;
  overflow-x: auto;
  scrollbar-width: none;
  -ms-overflow-style: none;
}
.nav::-webkit-scrollbar { display: none; }

/* When the nav does overflow, say so. A hidden scrollbar with no other
   cue means Plans, Import and Users simply cease to exist for anyone
   who does not think to drag a nav sideways. The class is set by
   app.js only when there is genuinely more to reach, so the last item
   is never dimmed for no reason. */
.nav.is-scrollable {
  -webkit-mask-image: linear-gradient(to right, #000 calc(100% - 34px), transparent);
          mask-image: linear-gradient(to right, #000 calc(100% - 34px), transparent);
}
.nav.is-scrollable.is-scrolled {
  -webkit-mask-image: linear-gradient(to right, transparent, #000 22px, #000 calc(100% - 34px), transparent);
          mask-image: linear-gradient(to right, transparent, #000 22px, #000 calc(100% - 34px), transparent);
}
.nav.is-scrollable.is-end {
  -webkit-mask-image: linear-gradient(to right, transparent, #000 22px);
          mask-image: linear-gradient(to right, transparent, #000 22px);
}

.nav a {
  padding: .45rem .7rem;
  border-radius: var(--radius-sm);
  color: var(--text-soft);
  font-weight: 550;
  white-space: nowrap;
  transition: background var(--fast) var(--ease), color var(--fast) var(--ease);
}
.nav a:hover { background: var(--surface-alt); color: var(--text); text-decoration: none; }
.nav a.is-active { background: var(--brand-tint); color: var(--brand-dark); font-weight: 650; }

.nav-toggle { display: none; }

.user-box {
  display: flex;
  align-items: center;
  gap: .5rem;
}

.user-name {
  font-size: .85rem;
  color: var(--text-soft);
  display: flex;
  align-items: center;
  gap: .4rem;
}

.role-tag {
  font-size: .68rem;
  text-transform: uppercase;
  letter-spacing: .04em;
  background: var(--slate-tint);
  color: var(--text-soft);
  padding: .1rem .4rem;
  border-radius: 4px;
  font-weight: 650;
}

/* Light / dark / system, cycled by one button. */
.theme-toggle {
  width: 34px;
  height: 34px;
  flex: none;
  display: grid;
  place-items: center;
  border: 1px solid var(--border);
  border-radius: 50%;
  background: var(--surface);
  color: var(--text-soft);
  cursor: pointer;
  transition: color var(--fast) var(--ease), border-color var(--fast) var(--ease), transform var(--fast) var(--ease);
}
.theme-toggle:hover { color: var(--brand); border-color: var(--brand); }
.theme-toggle:active { transform: scale(.92); }
/* The three glyphs are wrapped in spans; hide the span, not the svg
   inside it, or the wrapper shows and its icon stays hidden. */
.theme-toggle svg { width: 16px; height: 16px; }
.theme-toggle > span { display: none; line-height: 0; }
.theme-toggle > span.i-auto { display: block; }
:root[data-theme="light"] .theme-toggle > span.i-auto,
:root[data-theme="dark"]  .theme-toggle > span.i-auto { display: none; }
:root[data-theme="light"] .theme-toggle > span.i-sun  { display: block; }
:root[data-theme="dark"]  .theme-toggle > span.i-moon { display: block; }

/* ── Bottom tab bar (phones) ─────────────────────────────── */
/* The primary destinations, within thumb reach. Hidden on desktop,
   where the horizontal nav in the header does the same job better. */

.tabbar { display: none; }

/* ── Layout ──────────────────────────────────────────────── */

.wrap {
  max-width: 1320px;
  margin: 0 auto;
  padding: 1.5rem 1.25rem calc(3rem + var(--tabbar));
}

.page-head {
  display: flex;
  align-items: flex-end;
  justify-content: space-between;
  gap: 1rem;
  flex-wrap: wrap;
  margin-bottom: 1.25rem;
}

.page-head p { margin: .15rem 0 0; color: var(--text-soft); font-size: .9rem; }

.footer {
  text-align: center;
  color: var(--text-faint);
  font-size: .8rem;
  padding: 1.5rem 1rem calc(2.5rem + var(--tabbar));
}

.card {
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  box-shadow: var(--shadow);
  padding: 1.25rem;
  margin-bottom: 1.25rem;
  transition: box-shadow var(--slow) var(--ease), border-color var(--slow) var(--ease);
}

.card-head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 1rem;
  margin-bottom: .9rem;
  flex-wrap: wrap;
}
.card-head h2, .card-head h3 { margin: 0; }

.grid { display: grid; gap: 1.25rem; }
.grid-2 { grid-template-columns: repeat(2, minmax(0, 1fr)); }
.grid-3 { grid-template-columns: repeat(3, minmax(0, 1fr)); }

/* ── Stat tiles ──────────────────────────────────────────── */

.stats {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(186px, 1fr));
  gap: 1rem;
  margin-bottom: 1.25rem;
}

.stat {
  position: relative;
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  box-shadow: var(--shadow);
  padding: 1rem 1.1rem;
  overflow: hidden;
  transition: transform var(--slow) var(--ease), box-shadow var(--slow) var(--ease);
}

/* A 3px accent down the left edge, plus a very faint wash of the same
   hue. Enough to tell the tiles apart at a glance on a phone, not
   enough to compete with the number. */
.stat::before {
  content: "";
  position: absolute;
  inset: 0 auto 0 0;
  width: 3px;
  background: var(--tile, var(--brand));
}
.stat::after {
  content: "";
  position: absolute;
  inset: 0;
  background: radial-gradient(120% 90% at 0% 0%, var(--tile, var(--brand)) 0%, transparent 62%);
  opacity: .055;
  pointer-events: none;
}

.stat:hover { transform: translateY(-2px); box-shadow: var(--shadow-lift); }

.stat-top {
  display: flex;
  align-items: center;
  gap: .5rem;
  margin-bottom: .45rem;
}

.stat-icon {
  width: 28px;
  height: 28px;
  border-radius: 8px;
  flex: none;
  display: grid;
  place-items: center;
  color: var(--tile, var(--brand));
  background: color-mix(in srgb, var(--tile, var(--brand)) 13%, transparent);
}
.stat-icon svg { width: 15px; height: 15px; display: block; }

.stat-label {
  font-size: .74rem;
  text-transform: uppercase;
  letter-spacing: .05em;
  color: var(--text-faint);
  font-weight: 700;
}

.stat-value {
  font-size: 1.65rem;
  font-weight: 700;
  letter-spacing: -.025em;
  line-height: 1.15;
  color: var(--tile, var(--text));
}

.stat-note { font-size: .8rem; color: var(--text-soft); margin-top: .25rem; }

.stat.is-green  { --tile: var(--c-green); }
.stat.is-amber  { --tile: var(--c-amber); }
.stat.is-red    { --tile: var(--c-red); }
.stat.is-violet { --tile: var(--c-violet); }
.stat.is-plain  { --tile: var(--brand); }
.stat.is-plain .stat-value { color: var(--text); }

/* ── Meter ───────────────────────────────────────────────── */
/* One ratio against a limit: how much of what has been earned has
   actually landed in the bank. A meter, not a two-slice pie. */

.meter-head {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 1rem;
  flex-wrap: wrap;
  margin-bottom: .6rem;
}

.meter-figure {
  font-size: 1.9rem;
  font-weight: 700;
  letter-spacing: -.025em;
  line-height: 1.1;
}

.meter-track {
  height: 12px;
  border-radius: 999px;
  background: var(--surface-sunk);
  border: 1px solid var(--border);
  overflow: hidden;
}

.meter-fill {
  height: 100%;
  border-radius: 999px;
  background: linear-gradient(90deg, var(--c-green), color-mix(in srgb, var(--c-green) 62%, var(--c-blue)));
  transform-origin: left center;
  animation: barGrow 1s var(--ease) both;
}

.meter-legend {
  display: flex;
  gap: 1.1rem;
  flex-wrap: wrap;
  margin-top: .7rem;
  font-size: .82rem;
  color: var(--text-soft);
}
.meter-legend b { color: var(--text); font-weight: 650; }

.swatch {
  display: inline-block;
  width: 9px;
  height: 9px;
  border-radius: 3px;
  margin-right: .4rem;
  vertical-align: baseline;
}

/* ── Chart ───────────────────────────────────────────────── */

.chart {
  position: relative;
  width: 100%;
}

.chart svg { display: block; width: 100%; height: auto; overflow: visible; }

.chart .grid-line { stroke: var(--grid); stroke-width: 1; }
.chart .axis-line  { stroke: var(--axis); stroke-width: 1; }

/* Tick labels live in HTML rather than as <text> in the SVG, so they
   stay 11px whether the chart draws at 340px or 700px wide. Each is
   positioned at its point's percentage across the plot, which is the
   same coordinate the SVG used. */
.chart-axis {
  position: relative;
  height: 1.2rem;
  margin-top: .3rem;
}
.chart-axis span {
  position: absolute;
  transform: translateX(-50%);
  font-size: .69rem;
  font-weight: 600;
  color: var(--text-faint);
  white-space: nowrap;
}
/* The outermost labels butt against the card edge, so they anchor to
   their own side instead of straddling the point. */
.chart-axis span:first-child { transform: none; }
.chart-axis span:last-child  { transform: translateX(-100%); }

.chart .area { fill: url(#areaFade); }
.chart .line { fill: none; stroke: var(--c-blue); stroke-width: 2; stroke-linecap: round; stroke-linejoin: round; }

.chart .dot {
  fill: var(--surface);
  stroke: var(--c-blue);
  stroke-width: 2;
  r: 0;
  transition: r var(--fast) var(--ease);
}
.chart .col:hover .dot,
.chart .col.is-active .dot { r: 4.5; }

.chart .hit { fill: transparent; cursor: pointer; }

.chart .crosshair {
  stroke: var(--axis);
  stroke-width: 1;
  stroke-dasharray: 3 3;
  opacity: 0;
  transition: opacity var(--fast) var(--ease);
}
.chart .col:hover .crosshair,
.chart .col.is-active .crosshair { opacity: 1; }

.chart-tip {
  position: absolute;
  z-index: 5;
  pointer-events: none;
  opacity: 0;
  transform: translate(-50%, -108%);
  transition: opacity var(--fast) var(--ease);
  background: var(--surface);
  color: var(--text);
  border: 1px solid var(--border-firm);
  border-radius: var(--radius-sm);
  box-shadow: var(--shadow-lift);
  padding: .5rem .65rem;
  font-size: .8rem;
  white-space: nowrap;
  line-height: 1.45;
}
.chart-tip.is-on { opacity: 1; }
.chart-tip .k { color: var(--text-faint); font-weight: 650; text-transform: uppercase; letter-spacing: .04em; font-size: .68rem; display: block; }
.chart-tip .v { font-weight: 700; font-variant-numeric: tabular-nums; }

/* Growing in from the baseline reads as the data arriving, and it is
   the one place a little motion says something true. */
.chart .reveal {
  transform-origin: bottom;
  animation: chartRise .75s var(--ease) both;
}

@keyframes chartRise {
  from { transform: scaleY(.02); opacity: 0; }
  to   { transform: scaleY(1);   opacity: 1; }
}

/* ── Tables ──────────────────────────────────────────────── */

.table-scroll {
  overflow-x: auto;
  -webkit-overflow-scrolling: touch;
  margin: 0 -1.25rem;
  padding: 0 1.25rem;
}

table.data {
  width: 100%;
  border-collapse: collapse;
  font-size: .875rem;
  white-space: nowrap;
}

table.data th,
table.data td {
  padding: .6rem .7rem;
  text-align: left;
  border-bottom: 1px solid var(--border);
  vertical-align: middle;
}

table.data thead th {
  background: var(--surface-alt);
  font-size: .74rem;
  text-transform: uppercase;
  letter-spacing: .04em;
  color: var(--text-soft);
  font-weight: 700;
  position: sticky;
  top: 0;
  z-index: 1;
  border-bottom: 1px solid var(--border-firm);
}

table.data thead th a { color: inherit; display: inline-flex; gap: .25rem; align-items: center; }
table.data thead th a:hover { color: var(--brand); text-decoration: none; }

table.data tbody tr { transition: background var(--fast) var(--ease); }
table.data tbody tr:hover { background: var(--brand-tint); }
table.data tbody tr:last-child td { border-bottom: none; }

td.num, th.num { text-align: right; font-variant-numeric: tabular-nums; }
td.mono { font-family: var(--mono); font-size: .82rem; }
td.wrap-text { white-space: normal; min-width: 180px; }

.empty {
  text-align: center;
  padding: 2.5rem 1rem;
  color: var(--text-soft);
}

/* ── Badges ──────────────────────────────────────────────── */

.badge {
  display: inline-block;
  padding: .18rem .55rem;
  border-radius: 999px;
  font-size: .74rem;
  font-weight: 650;
  line-height: 1.45;
  white-space: nowrap;
  background: var(--slate-tint);
  color: var(--text-soft);
}

.badge-done      { background: var(--green-tint); color: var(--green); }
.badge-pending   { background: var(--amber-tint); color: var(--amber); }
.badge-progress  { background: var(--brand-tint); color: var(--brand-dark); }
.badge-hold      { background: var(--amber-tint); color: var(--amber); }
.badge-cancelled { background: var(--red-tint);   color: var(--red); }
.badge-credited  { background: var(--green-tint); color: var(--green); }
.badge-unpaid    { background: var(--slate-tint); color: var(--text-soft); }

/* ── Forms ───────────────────────────────────────────────── */

.form-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(230px, 1fr));
  gap: 1rem 1.1rem;
}

.field { display: flex; flex-direction: column; gap: .3rem; }
.field.full { grid-column: 1 / -1; }

.field label {
  font-size: .82rem;
  font-weight: 650;
  color: var(--text-soft);
}

.field .hint { font-size: .76rem; color: var(--text-faint); }

input[type=text], input[type=password], input[type=date], input[type=number],
input[type=search], input[type=email], input[type=file], select, textarea {
  width: 100%;
  padding: .55rem .65rem;
  border: 1px solid var(--border-firm);
  border-radius: var(--radius-sm);
  background: var(--surface);
  font: inherit;
  /* 16px is the threshold below which iOS Safari zooms the page on
     focus. Anything smaller makes every tap into a form field jerk
     the layout sideways. */
  font-size: 16px;
  color: var(--text);
  transition: border-color var(--fast) var(--ease), box-shadow var(--fast) var(--ease);
}

@media (min-width: 721px) {
  input[type=text], input[type=password], input[type=date], input[type=number],
  input[type=search], input[type=email], input[type=file], select, textarea {
    font-size: .9rem;
  }
}

input:focus, select:focus, textarea:focus {
  outline: none;
  border-color: var(--brand);
  box-shadow: var(--ring);
}

textarea { min-height: 80px; resize: vertical; }

.form-actions {
  display: flex;
  gap: .6rem;
  align-items: center;
  margin-top: 1.25rem;
  padding-top: 1.1rem;
  border-top: 1px solid var(--border);
  flex-wrap: wrap;
}

.filters {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(150px, 1fr));
  gap: .75rem;
  align-items: end;
}

/* The dashboard's period picker sizes to its content rather than
   stretching across the header. */
.filters-period { grid-template-columns: repeat(4, auto); }

/* ── Buttons ─────────────────────────────────────────────── */

.btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: .4rem;
  padding: .55rem .95rem;
  border: 1px solid transparent;
  border-radius: var(--radius-sm);
  background: linear-gradient(180deg, var(--brand-soft), var(--brand));
  color: #fff;
  font: inherit;
  font-size: .875rem;
  font-weight: 600;
  cursor: pointer;
  white-space: nowrap;
  min-height: 40px;
  transition: filter var(--fast) var(--ease), transform var(--fast) var(--ease),
              box-shadow var(--fast) var(--ease), background var(--fast) var(--ease);
}
.btn:hover { filter: brightness(1.06); text-decoration: none; color: #fff; box-shadow: 0 4px 12px color-mix(in srgb, var(--brand) 30%, transparent); }
.btn:active { transform: translateY(1px); }

.btn-secondary {
  background: var(--surface);
  color: var(--text);
  border-color: var(--border-firm);
}
.btn-secondary:hover { background: var(--surface-alt); color: var(--text); box-shadow: none; filter: none; }

.btn-ghost {
  background: transparent;
  color: var(--text-soft);
  border-color: transparent;
}
.btn-ghost:hover { background: var(--surface-alt); color: var(--text); box-shadow: none; filter: none; }

.btn-danger { background: linear-gradient(180deg, color-mix(in srgb, var(--c-red) 88%, #fff), var(--c-red)); }

.btn-sm { padding: .3rem .65rem; font-size: .8rem; min-height: 32px; }

.btn:disabled { opacity: .55; cursor: not-allowed; }

.actions { display: flex; gap: .35rem; }

/* A dropdown and its button read as one control, so they sit together
   rather than drifting apart when the toolbar wraps. */
.bulk-status {
  display: inline-flex;
  align-items: stretch;
  gap: .35rem;
}
.bulk-status select {
  width: auto;
  min-width: 8.5rem;
  padding-block: .3rem;
  font-size: .8rem;
}

/* ── Alerts ──────────────────────────────────────────────── */

.alert {
  padding: .75rem .9rem;
  border-radius: var(--radius-sm);
  margin-bottom: 1rem;
  font-size: .9rem;
  border: 1px solid transparent;
  animation: slideIn var(--slow) var(--ease) both;
}
.alert-success { background: var(--green-tint); color: var(--green); border-color: color-mix(in srgb, var(--c-green) 35%, transparent); }
.alert-error   { background: var(--red-tint);   color: var(--red);   border-color: color-mix(in srgb, var(--c-red) 35%, transparent); }
.alert-info    { background: var(--brand-tint); color: var(--brand-dark); border-color: color-mix(in srgb, var(--brand) 35%, transparent); }

@keyframes slideIn {
  from { opacity: 0; transform: translateY(-6px); }
  to   { opacity: 1; transform: none; }
}

/* ── Bar breakdowns ──────────────────────────────────────── */

.bars { display: flex; flex-direction: column; gap: .7rem; }

.bar-row { display: grid; grid-template-columns: 1fr auto; gap: .3rem .75rem; align-items: center; }
.bar-label { font-size: .85rem; font-weight: 550; }
.bar-value { font-size: .85rem; color: var(--text-soft); font-variant-numeric: tabular-nums; }

.bar-track {
  grid-column: 1 / -1;
  height: 8px;
  background: var(--surface-sunk);
  border-radius: 999px;
  overflow: hidden;
}

/* The width is inline on the element, so the proportions are right
   with scripting off, in an email-style renderer, or before app.js has
   parsed. The growth is a transform, which changes nothing about the
   final geometry — a bar cannot end up at the wrong length because an
   animation was interrupted, skipped or never started. */
.bar-fill {
  height: 100%;
  background: var(--c-blue);
  border-radius: 999px;
  transform-origin: left center;
  animation: barGrow .75s var(--ease) both;
}

@keyframes barGrow {
  from { transform: scaleX(0); }
  to   { transform: scaleX(1); }
}
.bar-fill.is-green  { background: var(--c-green); }
.bar-fill.is-amber  { background: var(--c-amber); }
.bar-fill.is-red    { background: var(--c-red); }
.bar-fill.is-violet { background: var(--c-violet); }

/* ── Bulk date entry ─────────────────────────────────────── */
/* Many small date boxes at once, for filling in a backlog of
   payments whose amounts are known but whose dates are not. */
.date-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(190px, 1fr));
  gap: .75rem 1rem;
}

.date-grid .field label {
  font-variant-numeric: tabular-nums;
}

/* ── Documents ───────────────────────────────────────────── */

.doc-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(230px, 1fr));
  gap: 1rem;
}

.doc {
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
  overflow: hidden;
  background: var(--surface-alt);
  transition: transform var(--fast) var(--ease), box-shadow var(--fast) var(--ease);
}
.doc:hover { transform: translateY(-2px); box-shadow: var(--shadow); }

.doc-preview {
  height: 150px;
  display: flex;
  align-items: center;
  justify-content: center;
  background: var(--slate-tint);
  overflow: hidden;
}

.doc-preview img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  display: block;
}

.doc-file {
  font-weight: 700;
  font-size: 1.1rem;
  color: var(--text-soft);
  letter-spacing: .05em;
}

.doc-meta {
  padding: .6rem .7rem;
  background: var(--surface);
}

/* A value filled in by text recognition. Deliberately loud — it is a
   suggestion awaiting confirmation, not saved data. */
input.is-suggested {
  border-color: var(--c-amber);
  background: var(--amber-tint);
}

input.is-suggested:focus {
  box-shadow: 0 0 0 3px color-mix(in srgb, var(--c-amber) 22%, transparent);
}

/* ── Pagination ──────────────────────────────────────────── */

.pager {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 1rem;
  margin-top: 1rem;
  flex-wrap: wrap;
  font-size: .85rem;
  color: var(--text-soft);
}
.pager-links { display: flex; gap: .3rem; flex-wrap: wrap; }
.pager-links a, .pager-links span {
  padding: .4rem .7rem;
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
  background: var(--surface);
  min-width: 40px;
  text-align: center;
}
.pager-links .is-current { background: var(--brand); color: #fff; border-color: var(--brand); }
.pager-links .is-disabled { color: var(--text-faint); }

/* ── Login page ──────────────────────────────────────────── */

.auth-shell {
  min-height: 100vh;
  min-height: 100dvh;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 1.5rem;
  background: var(--bg);
  background-image:
    radial-gradient(60% 42% at 50% 0%, var(--bg-accent) 0%, transparent 70%);
}

.auth-card {
  width: 100%;
  max-width: 380px;
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  box-shadow: var(--shadow-lift);
  padding: 1.75rem;
  animation: slideIn var(--slow) var(--ease) both;
}

.auth-card h1 { text-align: center; margin-bottom: .25rem; }
.auth-card .sub { text-align: center; color: var(--text-soft); font-size: .88rem; margin: 0 0 1.4rem; }
.auth-card .field { margin-bottom: .9rem; }
.auth-card .btn { width: 100%; padding: .65rem; min-height: 46px; }

.auth-logo {
  width: 52px;
  height: 52px;
  margin: 0 auto .9rem;
  border-radius: 15px;
  background: linear-gradient(145deg, var(--brand-soft), var(--brand-dark));
  display: grid;
  place-items: center;
  box-shadow: 0 8px 22px color-mix(in srgb, var(--brand) 32%, transparent);
}
.auth-logo svg { width: 27px; height: 27px; display: block; }

/* ── Utilities ───────────────────────────────────────────── */

.muted { color: var(--text-soft); }
.small { font-size: .82rem; }
.right { text-align: right; }
.nowrap { white-space: nowrap; }
.mt-0 { margin-top: 0; }
.mb-0 { margin-bottom: 0; }
.stack > * + * { margin-top: .75rem; }
.only-mobile { display: none; }

/* Tiles and cards fade up in sequence on first paint. Cheap, and it
   makes a server-rendered page feel like it arrived rather than
   flicked into place. */
.rise { animation: rise var(--slow) var(--ease) both; }
.rise:nth-child(2) { animation-delay: 40ms; }
.rise:nth-child(3) { animation-delay: 80ms; }
.rise:nth-child(4) { animation-delay: 120ms; }
.rise:nth-child(5) { animation-delay: 160ms; }
.rise:nth-child(6) { animation-delay: 200ms; }

@keyframes rise {
  from { opacity: 0; transform: translateY(10px); }
  to   { opacity: 1; transform: none; }
}

/* ── Phones ──────────────────────────────────────────────── */

@media (max-width: 720px) {
  :root { --tabbar: 62px; }

  body { font-size: 15px; }
  h1 { font-size: 1.3rem; }

  .wrap { padding: 1rem .85rem 2rem; }
  .card { padding: 1rem; border-radius: 12px; }
  .grid-2, .grid-3 { grid-template-columns: 1fr; }
  .grid { gap: 1rem; }
  .table-scroll { margin: 0 -1rem; padding: 0 1rem; }
  .only-mobile { display: block; }
  .no-mobile { display: none !important; }

  .topbar-inner { padding: .4rem .85rem; gap: .6rem; min-height: 52px; }
  .brand { font-size: 1rem; }

  /* The header nav becomes the bottom bar; what is left up top is the
     identity, the theme switch and the account menu. */
  .nav { display: none; }
  .user-box { margin-left: auto; }
  .user-name { display: none; }

  /* Two tiles per row reads better than one tall column — the whole
     KPI set stays on one screen. */
  .stats { grid-template-columns: repeat(2, minmax(0, 1fr)); gap: .7rem; }
  .stat { padding: .8rem .85rem; }
  .stat-value { font-size: 1.3rem; }
  .stat-label { font-size: .66rem; letter-spacing: .04em; }
  .stat-note { font-size: .74rem; }
  .stat-icon { width: 24px; height: 24px; border-radius: 7px; }
  .stat-icon svg { width: 13px; height: 13px; }
  /* A tile that needs the full width says so. */
  .stat.is-wide { grid-column: 1 / -1; }

  .meter-figure { font-size: 1.5rem; }

  .page-head { margin-bottom: 1rem; }
  .page-head > div:first-child { width: 100%; }

  .filters { grid-template-columns: 1fr 1fr; }
  .filters .field.full { grid-column: 1 / -1; }

  /* Period on its own line, the two dates side by side, Apply full
     width underneath — four across is unreadable at this size. */
  .filters-period { grid-template-columns: 1fr 1fr; width: 100%; }
  .filters-period .field:first-child { grid-column: 1 / -1; }
  .filters-period .field:last-child  { grid-column: 1 / -1; }
  .filters-period .field:last-child label { display: none; }
  .filters-period .btn { width: 100%; }

  .btn { min-height: 44px; padding: .6rem 1rem; }
  .btn-sm { min-height: 36px; }
  .form-actions .btn { flex: 1 1 auto; }

  .pager-links a, .pager-links span { min-width: 44px; min-height: 40px; display: inline-flex; align-items: center; justify-content: center; }

  /* ── Tables become cards ──────────────────────────────────
     A seven-column table in a 390px window is a horizontal scroll
     nobody performs — the columns past the third are simply never
     seen. Each row becomes its own block with the column name beside
     each value, which is the same information in a shape that fits.
     Only tables marked .as-cards convert; they are the ones whose
     cells carry data-label. */
  table.data.as-cards,
  table.data.as-cards tbody,
  table.data.as-cards tr,
  table.data.as-cards td { display: block; width: 100%; }

  table.data.as-cards { white-space: normal; }
  table.data.as-cards thead { display: none; }

  table.data.as-cards tr {
    border: 1px solid var(--border);
    border-radius: 12px;
    padding: .7rem .8rem;
    margin-bottom: .7rem;
    background: var(--surface);
  }
  table.data.as-cards tr:hover { background: var(--surface); }

  table.data.as-cards td {
    border: none;
    padding: .2rem 0;
    display: flex;
    align-items: baseline;
    justify-content: space-between;
    gap: 1rem;
    text-align: right;
  }

  table.data.as-cards td::before {
    content: attr(data-label);
    flex: none;
    font-size: .72rem;
    text-transform: uppercase;
    letter-spacing: .04em;
    font-weight: 700;
    color: var(--text-faint);
    text-align: left;
  }

  /* The first cell is the row's title: no label, bigger, on its own. */
  table.data.as-cards td.is-title {
    display: block;
    text-align: left;
    font-size: 1rem;
    font-weight: 650;
    padding-bottom: .45rem;
    margin-bottom: .35rem;
    border-bottom: 1px solid var(--border);
  }
  table.data.as-cards td.is-title::before { content: none; }

  table.data.as-cards td.num { text-align: right; }
  table.data.as-cards td:empty { display: none; }

  /* A fourteen-column table makes a fourteen-line card, which is worse
     than the scroll it replaced. Columns marked .is-secondary drop out
     of the card; the record page behind the customer's name still has
     every one of them. */
  table.data.as-cards td.is-secondary { display: none; }

  /* Totals keep their own strip rather than inheriting the card shape —
     the colspan they rely on has no meaning once the cells are blocks. */
  table.data.as-cards tfoot { display: block; }
  table.data.as-cards tfoot tr {
    display: flex;
    justify-content: space-between;
    gap: 1rem;
    background: var(--surface-alt);
    border-color: var(--border-firm);
    padding: .55rem .8rem;
    margin-bottom: .5rem;
  }
  table.data.as-cards tfoot th {
    display: block;
    border: none;
    padding: 0;
    background: none;
    position: static;
    text-transform: none;
    letter-spacing: 0;
    font-size: .85rem;
    color: var(--text);
  }
  table.data.as-cards tfoot th:empty { display: none; }
  table.data.as-cards tfoot th.right { text-align: left; color: var(--text-soft); }

  /* ── Bottom tab bar ─────────────────────────────────────── */
  .tabbar {
    display: flex;
    position: fixed;
    left: 0;
    right: 0;
    bottom: 0;
    z-index: 60;
    background: color-mix(in srgb, var(--surface) 88%, transparent);
    backdrop-filter: saturate(180%) blur(16px);
    -webkit-backdrop-filter: saturate(180%) blur(16px);
    border-top: 1px solid var(--border);
    padding-bottom: env(safe-area-inset-bottom);
  }

  @supports not (backdrop-filter: blur(1px)) {
    .tabbar { background: var(--surface); }
  }

  .tabbar a {
    flex: 1 1 0;
    min-width: 0;
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    gap: .15rem;
    padding: .45rem .2rem .5rem;
    min-height: 56px;
    color: var(--text-faint);
    font-size: .66rem;
    font-weight: 650;
    letter-spacing: .01em;
    text-decoration: none;
    position: relative;
    transition: color var(--fast) var(--ease);
    -webkit-tap-highlight-color: transparent;
  }
  .tabbar a svg { width: 21px; height: 21px; display: block; }
  .tabbar a span { overflow: hidden; text-overflow: ellipsis; max-width: 100%; white-space: nowrap; }
  .tabbar a:hover { text-decoration: none; }
  .tabbar a:active { background: var(--surface-alt); }

  .tabbar a.is-active { color: var(--brand); }
  /* A short bar above the active tab rather than a filled pill — it
     survives the blur behind it and does not shift the icons. */
  .tabbar a.is-active::before {
    content: "";
    position: absolute;
    top: 0;
    left: 50%;
    transform: translateX(-50%);
    width: 26px;
    height: 3px;
    border-radius: 0 0 3px 3px;
    background: var(--brand);
  }

  /* The overflow sheet, for everything that did not fit in five tabs. */
  .tab-sheet {
    position: fixed;
    inset: 0;
    z-index: 70;
    display: none;
    background: rgba(9, 14, 26, .45);
    backdrop-filter: blur(2px);
    -webkit-backdrop-filter: blur(2px);
  }
  .tab-sheet.is-open { display: block; animation: fadeIn var(--fast) var(--ease) both; }

  .tab-sheet-panel {
    position: absolute;
    left: 0;
    right: 0;
    bottom: 0;
    background: var(--surface);
    border-radius: 18px 18px 0 0;
    padding: .5rem .85rem calc(1rem + env(safe-area-inset-bottom));
    animation: sheetUp .26s var(--ease) both;
    max-height: 80vh;
    overflow-y: auto;
  }

  .tab-sheet-grip {
    width: 38px;
    height: 4px;
    border-radius: 999px;
    background: var(--border-firm);
    margin: .5rem auto .8rem;
  }

  .tab-sheet-panel a {
    display: flex;
    align-items: center;
    gap: .7rem;
    padding: .8rem .6rem;
    border-radius: var(--radius-sm);
    color: var(--text);
    font-weight: 550;
    min-height: 48px;
  }
  .tab-sheet-panel a:hover { text-decoration: none; background: var(--surface-alt); }
  .tab-sheet-panel a.is-active { background: var(--brand-tint); color: var(--brand-dark); font-weight: 650; }
  .tab-sheet-panel a svg { width: 19px; height: 19px; flex: none; color: var(--text-faint); }
  .tab-sheet-panel a.is-active svg { color: var(--brand); }
}

@keyframes fadeIn { from { opacity: 0; } to { opacity: 1; } }
@keyframes sheetUp {
  from { transform: translateY(100%); }
  to   { transform: none; }
}

/* Very narrow phones: one tile per row beats two cramped ones. */
@media (max-width: 360px) {
  .stats { grid-template-columns: 1fr; }
  .tabbar a { font-size: .6rem; }
}

/* Tablets keep the header nav but lose the two-up grids. */
@media (min-width: 721px) and (max-width: 1000px) {
  .grid-3 { grid-template-columns: 1fr; }
  .nav a { padding: .4rem .55rem; font-size: .9rem; }
}

/* ── Motion preferences ──────────────────────────────────── */
/* Everything above degrades to a plain, instant page. The bar and
   meter widths are set by script rather than by the animation, so
   they still arrive at the right value with the transition switched
   off — nothing here is load-bearing. */

@media (prefers-reduced-motion: reduce) {
  html { scroll-behavior: auto; }
  *, *::before, *::after {
    animation-duration: .001ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: .001ms !important;
  }
  .stat:hover, .doc:hover { transform: none; }
}

/* ── Print ───────────────────────────────────────────────── */

@media print {
  .topbar, .footer, .form-actions, .pager, .filters, .actions,
  .tabbar, .tab-sheet, .theme-toggle { display: none !important; }
  body { background: #fff; background-image: none; color: #000; }
  .card, .stat { box-shadow: none; border-color: #ccc; break-inside: avoid; }
  .stat::after { display: none; }
  table.data { white-space: normal; font-size: .75rem; }
  .chart .reveal { animation: none; }
}
