/* ============================================================================
   Invoicing — design system

   Reference points: a ledger book and a precision instrument. Hairline rules
   instead of drop shadows, tabular figures on every number, dense rows that
   respect an accountant's screen, one confident accent instead of a gradient.
   ========================================================================= */

/* ------------------------------------------------------------------ reset */

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

* { margin: 0; }

html {
  -webkit-text-size-adjust: 100%;
  tab-size: 4;
}

body {
  min-height: 100vh;
  line-height: 1.5;
  -webkit-font-smoothing: antialiased;
  text-rendering: optimizeLegibility;
}

img, svg, video, canvas { display: block; max-width: 100%; }

input, button, textarea, select { font: inherit; color: inherit; }

button { background: none; border: none; cursor: pointer; }

a { color: inherit; }

h1, h2, h3, h4 { font-weight: 600; line-height: 1.25; text-wrap: balance; }

/* ----------------------------------------------------------------- tokens */

:root {
  /* Warm paper neutrals — a plain white ground reads as unfinished next to
     the ink and green used elsewhere. */
  --paper:        #fbfaf7;
  --surface:      #ffffff;
  --surface-sunk: #f4f2ed;
  --surface-hover:#f7f5f1;

  --ink:          #1a1917;
  --ink-2:        #4a4741;
  --ink-3:        #7a756c;
  --ink-4:        #a8a299;

  --line:         #e5e1d8;
  --line-strong:  #d2cdc1;

  /* Bottle green: the ledger accent. Reserved for primary actions and the
     active navigation state so it keeps its meaning. */
  --accent:       #0e5240;
  --accent-hover: #0a4133;
  --accent-soft:  #e6efeb;
  --accent-line:  #b9d2c9;

  --positive:     #14663f;
  --positive-soft:#e4f0e9;
  --positive-line:#b5d5c4;

  --warning:      #8a5a09;
  --warning-soft: #fbf1de;
  --warning-line: #e8d3a6;

  --danger:       #93251f;
  --danger-soft:  #f9e9e7;
  --danger-line:  #e6bdb8;

  --neutral:      #5c5850;
  --neutral-soft: #efece6;
  --neutral-line: #dcd7cd;

  --focus:        #0e5240;

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

  --t-11: 0.6875rem;
  --t-12: 0.75rem;
  --t-13: 0.8125rem;
  --t-14: 0.875rem;
  --t-16: 1rem;
  --t-20: 1.25rem;
  --t-26: 1.625rem;
  --t-32: 2rem;

  /* 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;

  --r-sm: 4px;
  --r-md: 6px;
  --r-lg: 10px;

  --sidebar-w: 236px;
  --header-h: 56px;

  --speed: 120ms;
  --ease: cubic-bezier(0.2, 0, 0.1, 1);

  color-scheme: light;
}

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

    --paper:        #131311;
    --surface:      #1a1a17;
    --surface-sunk: #101010;
    --surface-hover:#22221e;

    --ink:          #eceae4;
    --ink-2:        #b8b4ab;
    --ink-3:        #8a857b;
    --ink-4:        #625e56;

    --line:         #2c2b27;
    --line-strong:  #3d3b35;

    --accent:       #4bbf95;
    --accent-hover: #63d0a8;
    --accent-soft:  #15271f;
    --accent-line:  #23473a;

    --positive:     #5ec18d;
    --positive-soft:#152418;
    --positive-line:#25422f;

    --warning:      #d9a441;
    --warning-soft: #29200e;
    --warning-line: #4a3a18;

    --danger:       #e07a70;
    --danger-soft:  #2b1614;
    --danger-line:  #4d2723;

    --neutral:      #9d988e;
    --neutral-soft: #212120;
    --neutral-line: #35342f;

    --focus:        #4bbf95;
  }
}

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

  --paper:        #131311;
  --surface:      #1a1a17;
  --surface-sunk: #101010;
  --surface-hover:#22221e;

  --ink:          #eceae4;
  --ink-2:        #b8b4ab;
  --ink-3:        #8a857b;
  --ink-4:        #625e56;

  --line:         #2c2b27;
  --line-strong:  #3d3b35;

  --accent:       #4bbf95;
  --accent-hover: #63d0a8;
  --accent-soft:  #15271f;
  --accent-line:  #23473a;

  --positive:     #5ec18d;
  --positive-soft:#152418;
  --positive-line:#25422f;

  --warning:      #d9a441;
  --warning-soft: #29200e;
  --warning-line: #4a3a18;

  --danger:       #e07a70;
  --danger-soft:  #2b1614;
  --danger-line:  #4d2723;

  --neutral:      #9d988e;
  --neutral-soft: #212120;
  --neutral-line: #35342f;

  --focus:        #4bbf95;
}

body {
  font-family: var(--font-ui);
  font-size: var(--t-14);
  background: var(--paper);
  color: var(--ink);
}

/* Every figure in this application is money, a count or a date: align them. */
.num, td.num, th.num, .money {
  font-variant-numeric: tabular-nums;
  font-feature-settings: "tnum" 1, "lnum" 1;
}

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

::selection { background: var(--accent-soft); color: var(--ink); }

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

.app {
  display: grid;
  grid-template-columns: var(--sidebar-w) 1fr;
  min-height: 100vh;
}

.sidebar {
  position: sticky;
  top: 0;
  height: 100vh;
  display: flex;
  flex-direction: column;
  background: var(--surface);
  border-right: 1px solid var(--line);
  overflow-y: auto;
}

.brand {
  display: flex;
  align-items: center;
  gap: var(--s-3);
  height: var(--header-h);
  padding: 0 var(--s-4);
  border-bottom: 1px solid var(--line);
  flex-shrink: 0;
  text-decoration: none;
}

.brand__mark {
  width: 26px; height: 26px;
  display: grid; place-items: center;
  border-radius: var(--r-sm);
  background: var(--accent);
  color: #fff;
  font-weight: 700;
  font-size: var(--t-12);
  letter-spacing: -0.02em;
  flex-shrink: 0;
}

.brand__name {
  font-weight: 600;
  font-size: var(--t-13);
  letter-spacing: -0.01em;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

/* Organization switcher — an accountant lives in this control. */
.org-switch {
  padding: var(--s-3) var(--s-3) var(--s-2);
  border-bottom: 1px solid var(--line);
}

.org-switch__label {
  font-size: var(--t-11);
  text-transform: uppercase;
  letter-spacing: 0.07em;
  color: var(--ink-4);
  font-weight: 600;
  padding: 0 var(--s-2) var(--s-1);
}

.nav {
  padding: var(--s-3) var(--s-2);
  display: flex;
  flex-direction: column;
  gap: 1px;
  flex: 1;
}

.nav__section {
  font-size: var(--t-11);
  text-transform: uppercase;
  letter-spacing: 0.07em;
  color: var(--ink-4);
  font-weight: 600;
  padding: var(--s-4) var(--s-2) var(--s-1);
}

.nav__item {
  display: flex;
  align-items: center;
  gap: var(--s-3);
  padding: 7px var(--s-2);
  border-radius: var(--r-sm);
  color: var(--ink-2);
  text-decoration: none;
  font-size: var(--t-13);
  transition: background var(--speed) var(--ease), color var(--speed) var(--ease);
}

.nav__item:hover { background: var(--surface-hover); color: var(--ink); }

.nav__item[aria-current="page"] {
  background: var(--accent-soft);
  color: var(--accent);
  font-weight: 600;
}

.nav__item svg { width: 16px; height: 16px; flex-shrink: 0; opacity: 0.85; }

.nav__badge {
  margin-left: auto;
  font-size: var(--t-11);
  font-variant-numeric: tabular-nums;
  color: var(--ink-3);
  background: var(--surface-sunk);
  padding: 1px 6px;
  border-radius: 20px;
}

.nav__item[aria-current="page"] .nav__badge {
  background: var(--surface);
  color: var(--accent);
}

.main { display: flex; flex-direction: column; min-width: 0; }

.topbar {
  position: sticky;
  top: 0;
  z-index: 20;
  display: flex;
  align-items: center;
  gap: var(--s-4);
  height: var(--header-h);
  padding: 0 var(--s-6);
  background: color-mix(in srgb, var(--paper) 88%, transparent);
  backdrop-filter: saturate(140%) blur(8px);
  border-bottom: 1px solid var(--line);
}

.topbar__title {
  font-size: var(--t-14);
  font-weight: 600;
  min-width: 0;
  flex: 1 1 auto;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.topbar__spacer { margin-left: auto; }

.content { padding: var(--s-6); flex: 1; }
/* A very wide monitor does not need forty-centimetre table rows: the page
   stops growing at a width the eye can still sweep, and sits centred. */
.content { width: 100%; max-width: 1680px; margin: 0 auto; }
.content--narrow { max-width: 860px; }

.page-head {
  display: flex;
  align-items: flex-start;
  gap: var(--s-4);
  margin-bottom: var(--s-6);
  flex-wrap: wrap;
}

.page-head__text { min-width: 0; }

.page-title {
  font-size: var(--t-26);
  letter-spacing: -0.02em;
  font-weight: 650;
}

.page-sub {
  color: var(--ink-3);
  font-size: var(--t-13);
  margin-top: 2px;
}

.page-head__actions {
  margin-left: auto;
  display: flex;
  flex-wrap: wrap;
  justify-content: flex-end;
  max-width: 100%;
  gap: var(--s-2);
  align-items: center;
}

/* ------------------------------------------------------------------ cards */

.card {
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: var(--r-lg);
}

.card__head {
  display: flex;
  align-items: center;
  gap: var(--s-3);
  padding: var(--s-4) var(--s-5);
  border-bottom: 1px solid var(--line);
}

.card__title { font-size: var(--t-13); font-weight: 600; }
.card__body { padding: var(--s-5); }
/* A thin strip above a table: a select-all box, a filter, a note. */
.card__body--strip { padding: var(--s-3) var(--s-4); border-bottom: 1px solid var(--line); }
.card__foot {
  padding: var(--s-3) var(--s-5);
  border-top: 1px solid var(--line);
  background: var(--surface-sunk);
  border-radius: 0 0 var(--r-lg) var(--r-lg);
}

/* --------------------------------------------------------------- KPI tiles */

.kpi-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
  gap: var(--s-3);
  margin-bottom: var(--s-6);
}

.kpi {
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: var(--r-lg);
  padding: var(--s-4) var(--s-5);
  display: flex;
  flex-direction: column;
  gap: var(--s-1);
  position: relative;
  overflow: hidden;
}

/* A 2px spine carries the semantic colour instead of tinting the whole tile. */
.kpi::before {
  content: "";
  position: absolute;
  inset: 0 auto 0 0;
  width: 2px;
  background: var(--kpi-accent, var(--line-strong));
}

.kpi--positive { --kpi-accent: var(--positive); }
.kpi--danger   { --kpi-accent: var(--danger); }
.kpi--warning  { --kpi-accent: var(--warning); }
.kpi--accent   { --kpi-accent: var(--accent); }
.kpi--link { color: inherit; text-decoration: none; }
.kpi--link:hover { border-color: var(--accent); }

.kpi__label {
  font-size: var(--t-11);
  text-transform: uppercase;
  letter-spacing: 0.06em;
  color: var(--ink-3);
  font-weight: 600;
}

.kpi__value {
  font-size: var(--t-26);
  font-weight: 600;
  letter-spacing: -0.02em;
  font-variant-numeric: tabular-nums;
  color: var(--kpi-value, var(--ink));
}

.kpi--positive .kpi__value { --kpi-value: var(--positive); }
.kpi--danger   .kpi__value { --kpi-value: var(--danger); }

.kpi__meta { font-size: var(--t-12); color: var(--ink-3); }

.kpi__count { display: flex; align-items: baseline; gap: var(--s-2); }

/* ----------------------------------------------------------------- tables */

.table-wrap {
  position: relative;
  max-width: 100%;
  overflow-x: auto;
  border: 1px solid var(--line);
  border-radius: var(--r-lg);
  background: var(--surface);
}

table.data {
  width: 100%;
  border-collapse: separate;
  border-spacing: 0;
  font-size: var(--t-13);
}

table.data thead th {
  position: sticky;
  top: 0;
  z-index: 1;
  background: var(--surface-sunk);
  text-align: left;
  font-size: var(--t-11);
  text-transform: uppercase;
  letter-spacing: 0.06em;
  color: var(--ink-3);
  font-weight: 600;
  padding: var(--s-3) var(--s-4);
  border-bottom: 1px solid var(--line);
  white-space: nowrap;
}

table.data tbody td {
  padding: var(--s-3) var(--s-4);
  border-bottom: 1px solid var(--line);
  vertical-align: middle;
}

table.data tbody tr:last-child td { border-bottom: 0; }
table.data tbody tr:hover { background: var(--surface-hover); }

table.data th.num, table.data td.num { text-align: right; }

/* Numbers, dates, amounts and badges never break; the one column that is
   allowed to wrap is the descriptive one, and its secondary line stays a
   line rather than a paragraph. Squeezed tables scroll sideways instead. */
table.data td.num, table.data .mono, table.data .badge, table.data .row-actions { white-space: nowrap; }
table.data td > .small.muted {
  display: block;
  max-width: 42ch;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

table.data tfoot td {
  padding: var(--s-3) var(--s-4);
  border-top: 1px solid var(--line-strong);
  font-weight: 600;
  background: var(--surface-sunk);
}

.cell-strong { font-weight: 600; color: var(--ink); }
.cell-muted { color: var(--ink-3); }
.cell-link { color: var(--accent); text-decoration: none; font-weight: 500; }
.cell-link:hover { text-decoration: underline; }

.row-actions { display: flex; gap: var(--s-1); justify-content: flex-end; }

/* ----------------------------------------------------------------- badges */

.badge {
  display: inline-flex;
  align-items: center;
  gap: 5px;
  padding: 2px 8px;
  border-radius: 20px;
  font-size: var(--t-11);
  font-weight: 600;
  letter-spacing: 0.02em;
  white-space: nowrap;
  border: 1px solid var(--badge-line, var(--neutral-line));
  background: var(--badge-bg, var(--neutral-soft));
  color: var(--badge-fg, var(--neutral));
}

.badge::before {
  content: "";
  width: 5px; height: 5px;
  border-radius: 50%;
  background: currentColor;
  flex-shrink: 0;
}

.badge--paid, .badge--approved, .badge--issued {
  --badge-bg: var(--positive-soft);
  --badge-fg: var(--positive);
  --badge-line: var(--positive-line);
}

.badge--unpaid, .badge--sent, .badge--pending {
  --badge-bg: var(--warning-soft);
  --badge-fg: var(--warning);
  --badge-line: var(--warning-line);
}

.badge--overdue, .badge--rejected, .badge--failed {
  --badge-bg: var(--danger-soft);
  --badge-fg: var(--danger);
  --badge-line: var(--danger-line);
}

.badge--draft, .badge--cancelled, .badge--viewed {
  --badge-bg: var(--neutral-soft);
  --badge-fg: var(--neutral);
  --badge-line: var(--neutral-line);
}

.badge--partially_paid {
  --badge-bg: var(--accent-soft);
  --badge-fg: var(--accent);
  --badge-line: var(--accent-line);
}

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

.btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 6px;
  height: 32px;
  padding: 0 var(--s-3);
  border-radius: var(--r-md);
  border: 1px solid var(--line-strong);
  background: var(--surface);
  color: var(--ink);
  font-size: var(--t-13);
  font-weight: 500;
  white-space: nowrap;
  text-decoration: none;
  transition: background var(--speed) var(--ease),
              border-color var(--speed) var(--ease),
              color var(--speed) var(--ease);
}

.btn:hover { background: var(--surface-hover); border-color: var(--ink-4); }

.btn[disabled], .btn[aria-disabled="true"] {
  opacity: 0.5;
  pointer-events: none;
}

.btn svg { width: 15px; height: 15px; }

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

@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) .btn--primary { color: #0b1a15; }
}
:root[data-theme="dark"] .btn--primary { color: #0b1a15; }

.btn--danger {
  color: var(--danger);
  border-color: var(--danger-line);
  background: var(--danger-soft);
}
.btn--danger:hover { background: var(--danger); color: #fff; border-color: var(--danger); }

.btn--ghost { border-color: transparent; background: transparent; }
.btn--ghost:hover { background: var(--surface-hover); border-color: var(--line); }

.btn--sm { height: 26px; padding: 0 var(--s-2); font-size: var(--t-12); }
.btn--icon { width: 32px; padding: 0; }
.btn--icon.btn--sm { width: 26px; }

/* Theme toggle: one icon per mode, the current one shown. No attribute on
   <html> means the palette follows the operating system. */
.theme-toggle__icon { display: none; line-height: 0; }
:root:not([data-theme]) .theme-toggle__icon--system,
:root[data-theme="light"] .theme-toggle__icon--light,
:root[data-theme="dark"] .theme-toggle__icon--dark { display: inline-flex; }

.btn-group { display: inline-flex; gap: var(--s-2); }

/* ------------------------------------------------------------------ forms */

.field { display: flex; flex-direction: column; gap: 5px; margin-bottom: var(--s-4); }

.field__label {
  font-size: var(--t-12);
  font-weight: 600;
  color: var(--ink-2);
}

.field__req { color: var(--danger); margin-left: 2px; }

/* The counterpart, for a field whose emptiness means something. */
.field__opt { font-weight: 400; color: var(--ink-4); }

/* A long list of things to tick: one per line, quiet rules between them. */
.check-list { list-style: none; margin: 0; padding: 0; max-height: 60vh; overflow-y: auto; }

/* The switcher becomes a way in to the chooser once a dropdown cannot cope. */
.org-switch__all { width: 100%; justify-content: flex-start; }
.check-row {
  display: flex;
  align-items: center;
  gap: var(--s-3);
  padding: 6px 2px;
  border-bottom: 1px solid var(--line);
}
.check-row:last-child { border-bottom: 0; }
.check-row > .small { margin-left: auto; }
.check { display: flex; align-items: center; gap: var(--s-2); cursor: pointer; min-width: 0; }
.check input[disabled] + span { color: var(--ink-3); }

/* A quantity and what it counts, side by side in one cell. */
.qty-cell { display: flex; gap: 4px; align-items: center; }
/* The number must stay readable next to its unit: never narrower than
   four digits and a separator. */
.qty-cell .input { min-width: 7ch; flex: 1 1 auto; }
.qty-cell .select { flex: 0 0 auto; width: auto; max-width: 11ch; }

.field__hint { font-size: var(--t-12); color: var(--ink-3); }
.field__link { font-size: var(--t-12); color: var(--accent); text-decoration: none; display: inline-flex; align-items: center; gap: 4px; margin-top: 4px; }
.field__link:hover { text-decoration: underline; }
.field__link svg { width: 12px; height: 12px; }

/* Searchable pickers: the list floats above everything, positioned from the
   box it belongs to, so a scrolling table cannot clip it. */
.combo__list {
  position: fixed;
  z-index: 80;
  margin: 0;
  padding: 4px;
  list-style: none;
  background: var(--surface);
  border: 1px solid var(--line-strong);
  border-radius: var(--r-md);
  box-shadow: 0 8px 24px rgb(0 0 0 / 0.14);
  max-height: 280px;
  overflow-y: auto;
  font-size: var(--t-13);
}
.combo__item { padding: 7px 10px; border-radius: var(--r-sm); cursor: pointer; display: flex; justify-content: space-between; gap: var(--s-3); }
.combo__item[aria-selected="true"], .combo__item:hover { background: var(--surface-hover); }
.combo__sub { color: var(--ink-3); font-size: var(--t-12); white-space: nowrap; }
.combo__item--create { color: var(--accent); border-top: 1px solid var(--line); margin-top: 4px; border-radius: 0; padding-top: 9px; }
.combo__item--empty { color: var(--ink-3); cursor: default; }
.combo__item--empty:hover { background: none; }

.field__error {
  font-size: var(--t-12);
  color: var(--danger);
  display: flex;
  align-items: center;
  gap: 5px;
}
.field__error svg { width: 14px; height: 14px; flex-shrink: 0; }
/* Any icon in running text that nothing else sizes stays text-sized. */
svg[aria-hidden="true"] { max-width: 1.25em; max-height: 1.25em; }
.empty__icon svg, .stamp-block svg, .brand__mark svg { max-width: none; max-height: none; }

.input, .select, .textarea {
  height: 34px;
  padding: 0 var(--s-3);
  border: 1px solid var(--line-strong);
  border-radius: var(--r-md);
  background: var(--surface);
  color: var(--ink);
  font-size: var(--t-13);
  width: 100%;
  transition: border-color var(--speed) var(--ease), box-shadow var(--speed) var(--ease);
}

.textarea { height: auto; padding: var(--s-2) var(--s-3); min-height: 76px; resize: vertical; line-height: 1.5; }

/* Every control is the same box, whatever it holds. Date, time and number
   inputs come with a native look that ignores height and width — WebKit on
   iPhone and iPad shrinks a date field to its text and centres it its own
   way — so the native look is switched off and the box sizes are pinned. */
.input, .select { box-sizing: border-box; min-height: 34px; line-height: 32px; display: block; }
/* hidden means hidden, whatever display an element's own class gives it:
   .field, .btn, .input and the rest all set display and would otherwise
   beat the browser's own [hidden] rule, leaving "hidden" things on screen. */
[hidden]:not([hidden="until-found"]) { display: none !important; }
.input[hidden], .select[hidden] { display: none; }
/* Buttons too: .btn sets display, which would otherwise beat the browser's
   own [hidden] rule and leave a "hidden" button on screen. */
.btn[hidden] { display: none; }
.input[type="date"], .input[type="time"], .input[type="month"], .input[type="datetime-local"] {
  -webkit-appearance: none;
  appearance: none;
  min-width: 0;
  width: 100%;
  line-height: normal;
}
.input[type="date"]::-webkit-date-and-time-value,
.input[type="time"]::-webkit-date-and-time-value,
.input[type="month"]::-webkit-date-and-time-value {
  height: 32px;
  display: flex;
  align-items: center;
  text-align: left;
}
.input::-webkit-datetime-edit { padding: 0; line-height: 32px; }
.input::-webkit-datetime-edit-fields-wrapper { padding: 0; }
.input::-webkit-calendar-picker-indicator { opacity: 0.6; cursor: pointer; margin-left: var(--s-2); }
.input::-webkit-calendar-picker-indicator:hover { opacity: 1; }
.input[type="number"] { -moz-appearance: textfield; appearance: textfield; }
.input[type="number"]::-webkit-outer-spin-button,
.input[type="number"]::-webkit-inner-spin-button { -webkit-appearance: none; margin: 0; }
.input[type="file"] { line-height: 32px; padding-top: 0; padding-bottom: 0; }

.input:hover, .select:hover, .textarea:hover { border-color: var(--ink-4); }

.input:focus, .select:focus, .textarea:focus {
  outline: none;
  border-color: var(--accent);
  box-shadow: 0 0 0 3px var(--accent-soft);
}

.input[aria-invalid="true"], .select[aria-invalid="true"] { border-color: var(--danger); }
.input[aria-invalid="true"]:focus { box-shadow: 0 0 0 3px var(--danger-soft); }

.input::placeholder { color: var(--ink-4); }

.select {
  appearance: none;
  /* WebKit lets a long selected option widen the page through the
     ancestors' scrollWidth even when the box itself is capped; a select
     never scrolls, so clipping costs nothing and keeps phones still. */
  overflow: hidden;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' fill='none' stroke='%237a756c' stroke-width='1.6' stroke-linecap='round'%3E%3Cpath d='M4 6.5 8 10.5 12 6.5'/%3E%3C/svg%3E");
  background-repeat: no-repeat;
  background-position: right 10px center;
  background-size: 14px;
  padding-right: 30px;
}

.input-group { display: flex; align-items: stretch; }
.input-group .input { border-radius: 0; }
.input-group > :first-child { border-radius: var(--r-md) 0 0 var(--r-md); }
.input-group > :last-child { border-radius: 0 var(--r-md) var(--r-md) 0; }

.input-affix {
  display: grid;
  place-items: center;
  min-height: 34px;
  white-space: nowrap;
  padding: 0 var(--s-3);
  border: 1px solid var(--line-strong);
  background: var(--surface-sunk);
  color: var(--ink-3);
  font-size: var(--t-13);
}
.input-group .input:not(:first-child) { border-left: 0; }
.input-group .input-affix:not(:first-child) { border-left: 0; }

.field-row {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
  gap: var(--s-4);
}

.checkbox, .radio {
  display: flex;
  align-items: center;
  gap: var(--s-2);
  font-size: var(--t-13);
  cursor: pointer;
}

.checkbox input, .radio input {
  width: 15px; height: 15px;
  accent-color: var(--accent);
  cursor: pointer;
}

fieldset { border: 0; padding: 0; }

legend {
  font-size: var(--t-11);
  text-transform: uppercase;
  letter-spacing: 0.06em;
  color: var(--ink-3);
  font-weight: 600;
  padding-bottom: var(--s-2);
}

/* --------------------------------------------------------------- messages */

.alert {
  display: flex;
  gap: var(--s-3);
  padding: var(--s-3) var(--s-4);
  border-radius: var(--r-md);
  border: 1px solid var(--alert-line, var(--line));
  background: var(--alert-bg, var(--surface-sunk));
  color: var(--alert-fg, var(--ink-2));
  font-size: var(--t-13);
  margin-bottom: var(--s-4);
}

.alert--error   { --alert-bg: var(--danger-soft);   --alert-fg: var(--danger);   --alert-line: var(--danger-line); }
.alert--success { --alert-bg: var(--positive-soft); --alert-fg: var(--positive); --alert-line: var(--positive-line); }
.alert--warning { --alert-bg: var(--warning-soft);  --alert-fg: var(--warning);  --alert-line: var(--warning-line); }
.alert--info    { --alert-bg: var(--accent-soft);   --alert-fg: var(--accent);   --alert-line: var(--accent-line); }

.alert svg { width: 16px; height: 16px; flex-shrink: 0; margin-top: 1px; }
.alert strong { font-weight: 600; }

.empty { padding: var(--s-12) var(--s-6); text-align: center; color: var(--ink-3); }
.empty__icon { display: block; width: 40px; height: 40px; margin: 0 auto var(--s-3); color: var(--ink-3); }
.empty__icon svg { width: 100%; height: 100%; }
.empty__title { font-size: var(--t-14); font-weight: 600; color: var(--ink-2); margin-bottom: var(--s-1); }
.empty__text { font-size: var(--t-13); max-width: 42ch; margin: 0 auto var(--s-4); }

/* --------------------------------------------------------------- toolbar */

.toolbar {
  display: flex;
  align-items: center;
  gap: var(--s-3);
  flex-wrap: wrap;
  padding: var(--s-3) var(--s-4);
  border: 1px solid var(--line);
  border-bottom: 0;
  border-radius: var(--r-lg) var(--r-lg) 0 0;
  background: var(--surface-sunk);
}

.toolbar + .table-wrap { border-radius: 0 0 var(--r-lg) var(--r-lg); }

.search { position: relative; flex: 1; min-width: 180px; max-width: 320px; }

.search .input { padding-left: 30px; height: 30px; }

.search svg {
  position: absolute;
  left: 9px; top: 50%;
  transform: translateY(-50%);
  width: 14px; height: 14px;
  color: var(--ink-4);
  pointer-events: none;
}

.seg {
  display: inline-flex;
  flex-wrap: wrap;
  max-width: 100%;
  padding: 2px;
  gap: 2px;
  background: var(--surface);
  border: 1px solid var(--line-strong);
  border-radius: var(--r-md);
}

.seg__btn {
  padding: 3px var(--s-3);
  border-radius: var(--r-sm);
  font-size: var(--t-12);
  font-weight: 500;
  color: var(--ink-3);
  text-decoration: none;
  transition: background var(--speed) var(--ease), color var(--speed) var(--ease);
}

.seg__btn:hover { color: var(--ink); }

.seg__btn[aria-selected="true"] {
  background: var(--accent-soft);
  color: var(--accent);
  font-weight: 600;
}

/* ------------------------------------------------------------- pagination */

.pager {
  display: flex;
  align-items: center;
  gap: var(--s-3);
  padding: var(--s-3) var(--s-4);
  font-size: var(--t-12);
  color: var(--ink-3);
  border-top: 1px solid var(--line);
}

.pager__spacer { margin-left: auto; }

/* The head of an editor, title and save button together, keeps its place
   under the top bar while the form scrolls beneath it. It reaches the edges
   of the content by undoing the content's own padding. */
.page-head--sticky {
  position: sticky;
  top: var(--header-h);
  z-index: 15;
  margin: calc(-1 * var(--s-6)) calc(-1 * var(--s-6)) var(--s-6);
  padding: var(--s-4) var(--s-6);
  background: color-mix(in srgb, var(--paper) 92%, transparent);
  backdrop-filter: blur(8px);
  -webkit-backdrop-filter: blur(8px);
  border-bottom: 1px solid var(--line);
}
@media (max-width: 900px) {
  .page-head--sticky {
    margin: calc(-1 * var(--s-4)) calc(-1 * var(--s-4)) var(--s-4);
    padding: var(--s-3) var(--s-4);
  }
}
/* On a phone the head wraps to several rows and would cover half the
   screen; there it scrolls with the page as before. */
@media (max-width: 600px) {
  .page-head--sticky { position: static; }
}

.totals__note { margin: var(--s-1) 0 0; text-align: right; }

/* A notice on its way out. */
.alert--fading { opacity: 0; transition: opacity 0.7s ease; }

/* A column heading that orders the list: reads as a heading, acts as a link. */
.th-sort { color: inherit; text-decoration: none; white-space: nowrap; }
.th-sort:hover { text-decoration: underline; }

/* What the editor last did on its own, beside the save button. */
.autosave-status { align-self: center; white-space: nowrap; }

/* The little corner button that opens a field in a bigger box, and the box. */
.expand-btn {
  position: fixed;
  z-index: 30;
  width: 22px;
  height: 22px;
  padding: 3px;
  border: 0;
  border-radius: 6px;
  background: color-mix(in srgb, var(--surface) 85%, transparent);
  color: var(--ink-3);
  cursor: pointer;
}
.expand-btn:hover { color: var(--ink); background: var(--surface); }
.expand-btn svg { width: 100%; height: 100%; display: block; }

dialog.expand {
  width: min(92vw, 760px);
  padding: var(--s-5);
  border: 1px solid var(--line);
  border-radius: 12px;
  background: var(--surface);
  color: var(--ink);
  box-shadow: 0 20px 60px rgba(0, 0, 0, 0.25);
}
dialog.expand::backdrop { background: rgba(0, 0, 0, 0.35); }
.expand__title { font-size: var(--t-14); font-weight: 600; margin: 0 0 var(--s-3); }
.expand__area { width: 100%; min-height: 40vh; resize: vertical; font: inherit; line-height: 1.45; }
.expand__row { display: flex; gap: var(--s-2); justify-content: flex-end; margin-top: var(--s-3); }

/* ---------------------------------------------------- document line editor */

.lines { width: 100%; border-collapse: separate; border-spacing: 0; font-size: var(--t-13); }

.lines th {
  text-align: left;
  font-size: var(--t-11);
  text-transform: uppercase;
  letter-spacing: 0.06em;
  color: var(--ink-3);
  font-weight: 600;
  padding: 0 var(--s-2) var(--s-2);
}

/* Every heading sits flush left over its column. A right-aligned heading
   over a cell that holds a number and a unit picker lands on the picker,
   and the number below it looks untitled. */
.lines th.num { text-align: left; }

.lines td { padding: 3px var(--s-1); vertical-align: top; }
.lines td:first-child { padding-left: 0; }
.lines td .input, .lines td .select { height: 30px; font-size: var(--t-13); }
.lines td .input.num { text-align: right; }

.lines__drag {
  width: 22px;
  color: var(--ink-4);
  cursor: grab;
  text-align: center;
  padding-top: 9px;
}

.totals {
  margin-left: auto;
  width: min(340px, 100%);
  display: flex;
  flex-direction: column;
  gap: var(--s-1);
  font-size: var(--t-13);
}

.totals__row {
  display: flex;
  justify-content: space-between;
  align-items: center;
  gap: var(--s-4);
  padding: var(--s-1) 0;
}

.totals__label { color: var(--ink-2); }
.totals__value { font-variant-numeric: tabular-nums; font-weight: 500; }

.totals__row--grand {
  border-top: 1px solid var(--line-strong);
  margin-top: var(--s-2);
  padding-top: var(--s-3);
  font-size: var(--t-16);
  font-weight: 650;
}

.totals__row--grand .totals__value { color: var(--accent); }

/* ------------------------------------------------------------------ misc */

.stack { display: flex; flex-direction: column; gap: var(--s-4); }
.row { display: flex; align-items: center; gap: var(--s-3); }
.row--wrap { flex-wrap: wrap; }
.spacer { margin-left: auto; }
.muted { color: var(--ink-3); }
.small { font-size: var(--t-12); }
.mono { font-family: var(--font-num); font-size: 0.95em; }
.nowrap { white-space: nowrap; }
.sr-only {
  position: absolute; width: 1px; height: 1px;
  padding: 0; margin: -1px; overflow: hidden;
  clip: rect(0,0,0,0); white-space: nowrap; border: 0;
}

hr.sep { border: 0; border-top: 1px solid var(--line); margin: var(--s-5) 0; }

/* Utilities.
   These exist because the Content-Security-Policy carries no 'unsafe-inline'
   for styles, so a style="" attribute in a template is blocked outright. That
   is the right trade — the same directive is what makes an injected <style> or
   handler inert — but it means spacing tweaks live here rather than in markup. */

.m-0 { margin: 0 !important; }
.mb-0 { margin-bottom: 0 !important; }
.mb-2 { margin-bottom: var(--s-2); }
.mb-3 { margin-bottom: var(--s-3); }
.mb-4 { margin-bottom: var(--s-4); }
.mb-6 { margin-bottom: var(--s-6); }
.mt-2 { margin-top: var(--s-2); }
.mt-3 { margin-top: var(--s-3); }
.mt-6 { margin-top: var(--s-6); }

.gap-2 { gap: var(--s-2); }
.items-start { align-items: flex-start; }
.text-right { text-align: right; }

/* A table or card nested inside another surface, which should not draw its
   own frame. */
.flush { border: 0 !important; }

.totals-col { min-width: min(300px, 100%); flex: 1; }
.field-narrow { max-width: 200px; }
.select--fit { width: auto; max-width: 100%; }
.panel-narrow { max-width: 420px; min-width: 0; width: 100%; }
/* Long keys and URLs in a narrow panel scroll or wrap rather than widen it. */
.code-block { overflow-x: auto; max-width: 100%; }
.panel-narrow .mono, .card .mono li { overflow-wrap: anywhere; }

/* A block of machine-readable text shown for inspection. It scrolls inside
   its own frame so a long document cannot widen the page. */
.code-block {
  margin: 0;
  padding: var(--s-3);
  max-height: 60vh;
  overflow: auto;
  background: var(--surface-sunk);
  border: 1px solid var(--line);
  border-radius: var(--r-md);
  font-family: var(--font-num, ui-monospace, monospace);
  font-size: var(--t-12);
  line-height: 1.5;
  white-space: pre;
  tab-size: 2;
}

/* A settings screen puts the list and its editor side by side on a wide
   viewport, and stacks them once there is no room for both. */
.col-grow { flex: 1 1 460px; min-width: 0; }

/* The language chooser sits in the top bar and should read as a control, not
   as a form field: no frame until it is reached for. */
.lang-switch { display: inline-flex; }

/* The clock in the top bar. Closed, a quiet line of date and time; open,
   a small panel under it with the full date and the zone chooser. The
   panel is anchored to the right so it never runs off a narrow screen. */
.clock { position: relative; flex: 0 0 auto; }
.clock__summary {
  list-style: none;
  cursor: pointer;
  display: inline-flex;
  align-items: center;
  gap: var(--s-2);
  height: 32px;
  padding: 0 var(--s-2);
  border: 1px solid transparent;
  border-radius: var(--r-md);
  font-size: var(--t-13);
  line-height: 1;
  color: var(--ink-2);
  white-space: nowrap;
  user-select: none;
}
.clock__summary::-webkit-details-marker { display: none; }
.clock__summary:hover, .clock[open] > .clock__summary { border-color: var(--line); background: var(--surface-hover); }
.clock__summary:focus-visible { outline: none; border-color: var(--accent); }
.clock__time { font-weight: 600; font-variant-numeric: tabular-nums; color: var(--ink); }
.clock__date { font-variant-numeric: tabular-nums; }
.clock__zone { color: var(--ink-3); font-size: var(--t-12); }
/* The panel is shown only while the box is open. Said explicitly: WebKit
   hides a closed <details>' content with display: none in its own
   stylesheet, which an unconditional display: grid would override — the
   panel then hangs open on every iPhone and pushes the page wider than
   the screen. */
.clock__panel {
  display: none;
  position: absolute;
  top: calc(100% + 6px);
  right: 0;
  z-index: 30;
  min-width: 320px;
  max-width: min(400px, calc(100vw - 2 * var(--s-3)));
  padding: var(--s-3);
  gap: var(--s-2);
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: var(--r-lg);
  box-shadow: 0 8px 24px rgba(0, 0, 0, .12);
}
.clock[open] > .clock__panel { display: grid; }
.clock__full { font-size: var(--t-13); color: var(--ink); }
.clock__panel .select { width: 100%; max-width: none; }
@media (max-width: 1024px) {
  /* Room is short on a tablet: the date and the zone wait inside the panel. */
  .clock__date, .clock__zone { display: none; }
}
@media (max-width: 600px) {
  .clock__summary { padding: 0 var(--s-1); font-size: var(--t-12); }
  .clock__panel { position: fixed; top: calc(var(--header-h) + 4px); right: var(--s-3); left: var(--s-3); max-width: none; }
}

.select--bare {
  border-color: transparent;
  background-color: transparent;
  padding-inline: var(--s-2);
  padding-right: 28px;
  min-width: 0;
  width: auto;
}

.select--bare:hover { border-color: var(--line); }
.select--bare:focus-visible { border-color: var(--accent); }

/* The acceptance mark. Sized in one place so the page, the print sheet and
   the preview all show it at the same scale. */
.stamp-block {
  flex: 0 0 auto;
  width: 150px;
  max-width: 40vw;
}

.stamp-block svg { width: 100%; height: auto; display: block; }

/* A proportion bar inside a table row. Kept deliberately plain: it is there
   to make the ranking scannable, not to be a chart. */
.bar {
  height: 6px;
  min-width: 60px;
  border-radius: 3px;
  background: var(--surface-sunk);
  overflow: hidden;
}

.bar__fill {
  display: block;
  height: 100%;
  background: var(--accent);
  border-radius: 3px;
}

/* The uploaded letterhead, shown against a chequerboard so a transparent
   PNG is visibly transparent rather than mistaken for a white background. */
.logo-preview {
  display: inline-flex;
  padding: var(--s-3);
  border: 1px solid var(--line);
  border-radius: var(--r-md);
  background-color: var(--surface);
  background-image:
    linear-gradient(45deg, var(--surface-sunk) 25%, transparent 25%),
    linear-gradient(-45deg, var(--surface-sunk) 25%, transparent 25%),
    linear-gradient(45deg, transparent 75%, var(--surface-sunk) 75%),
    linear-gradient(-45deg, transparent 75%, var(--surface-sunk) 75%);
  background-size: 14px 14px;
  background-position: 0 0, 0 7px, 7px -7px, -7px 0;
}

.logo-preview img {
  max-width: 320px;
  max-height: 110px;
  width: auto;
  height: auto;
}



/* Column proportions for the document line editor. Percentages rather than
   pixels so the table reflows on a narrow screen instead of clipping. */
.lines th:nth-child(1) { width: 30%; min-width: 200px; }
.lines th:nth-child(2) { width: 14%; min-width: 130px; }
.lines th:nth-child(3) { width: 10%; min-width: 80px; }
.lines th:nth-child(4) { width: 13%; min-width: 110px; }
.lines th:nth-child(5) { width: 9%; min-width: 80px; }
.lines th:nth-child(6) { width: 13%; min-width: 100px; }
.lines th:nth-child(7) { width: 9%; min-width: 80px; }
.lines th:nth-child(8) { width: 96px; }

/* The line's description grows with its text instead of scrolling inside
   a fixed height; the script sets the height as the text changes. */
.lines td textarea.input {
  height: auto;
  min-height: 30px;
  padding-top: 6px;
  padding-bottom: 6px;
  line-height: 1.3;
  resize: none;
  overflow: hidden;
  white-space: pre-wrap;
}
.pre-line { white-space: pre-line; }

/* An option is priced but not counted, and looks the part. */
.line-row--optional textarea[name="line_name"] { font-style: italic; }
.line-row--optional .line-net { color: var(--ink-3); }

/* The quantity heading names both halves of its cell: the figure and the
   unit beside it, laid out like the cell below. */
.th-split { display: flex; gap: 4px; }
.th-split > span:first-child { flex: 1 1 auto; min-width: 7ch; }
.th-split > span:last-child { flex: 0 0 auto; }
@media (max-width: 1024px) {
  .th-split { flex-direction: column; gap: 0; }
}

/* Up, down, remove: three small buttons at the end of a row. */
.line-tools { display: flex; gap: 0; white-space: nowrap; }

/* A line of text keeps its controls in the form, for the arrays to stay
   aligned, but shows none of them. */
.line-row--text .qty-cell,
.line-row--text select[name="line_tax_rate_id"],
.line-row--text input[name="line_price"],
.line-row--text .line-net { visibility: hidden; }
.doc-line--text td { font-weight: 600; }

/* The computed net sits on the same baseline as the inputs beside it. */
.lines .line-net { padding-top: 9px; }

.avatar-sm { width: 22px; height: 22px; font-size: 10px; }

/* One organization in the chooser. */
.org-choice {
  width: 100%;
  height: auto;
  padding: var(--s-3) var(--s-4);
  justify-content: flex-start;
  gap: var(--s-3);
}

.org-choice { white-space: normal; }
.org-choice__text { text-align: left; min-width: 0; overflow-wrap: anywhere; }
.org-choice__name { display: block; font-weight: 600; }

/* --------------------------------------------------------------- auth page */

.auth {
  min-height: 100vh;
  display: grid;
  grid-template-columns: minmax(0, 1fr);
  place-items: center;
  padding: var(--s-6);
  background:
    radial-gradient(1000px 500px at 50% -10%, var(--accent-soft), transparent 70%),
    var(--paper);
}

.auth__card {
  width: 100%;
  min-width: 0;
  max-width: 380px;
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: var(--r-lg);
  padding: var(--s-8);
}

.auth__brand { display: flex; align-items: center; gap: var(--s-3); margin-bottom: var(--s-6); }
/* The company's own mark, when it has uploaded one. */
.brand__logo { max-height: 56px; max-width: 220px; width: auto; height: auto; display: block; }
.auth__title { font-size: var(--t-20); letter-spacing: -0.02em; margin-bottom: var(--s-1); }
.auth__sub { color: var(--ink-3); font-size: var(--t-13); margin-bottom: var(--s-6); }
.auth__foot { margin-top: var(--s-5); text-align: center; font-size: var(--t-12); color: var(--ink-3); }
.auth__foot a { color: var(--accent); text-decoration: none; }
.auth__foot a:hover { text-decoration: underline; }
.auth__legal { flex-basis: 100%; margin-top: var(--s-2); }
.sidebar__legal { display: block; padding: var(--s-2) var(--s-3); text-decoration: none; }
.sidebar__legal:hover { text-decoration: underline; }
/* The controls row under the sign-in card: language and theme side by
   side, wrapping onto two lines when the card is narrow. */
.auth__foot--bar { display: flex; flex-wrap: wrap; align-items: center; justify-content: center; gap: var(--s-2); }

.auth .btn--primary { width: 100%; height: 36px; }

/* An offer read on a desk gets the room a document needs; the buttons keep
   a hand's width rather than stretching across it. */
.auth__card--wide { max-width: 880px; }

/* The privacy and cookie policy: a long text that must read well on a
   phone and print cleanly, in a card that otherwise holds forms. */
.legal { text-align: left; line-height: 1.55; }
.legal section { margin-top: var(--s-6); }
.legal h2 { font-size: var(--t-16); margin: 0 0 var(--s-2); letter-spacing: -0.01em; }
.legal p { margin: 0 0 var(--s-3); }
.legal__address { font-style: normal; margin: 0 0 var(--s-3); padding-left: var(--s-4); border-left: 2px solid var(--line); }
.legal__list { margin: 0 0 var(--s-3); }
.legal__list dt { font-weight: 600; margin-top: var(--s-3); }
.legal__list dd { margin: var(--s-1) 0 0; color: var(--ink-2); }
.legal__bullets { margin: 0 0 var(--s-3); padding-left: 1.25em; }
.legal__bullets li { margin-bottom: var(--s-2); }
.legal__cookies td { vertical-align: top; }
.legal__cookies td.mono { white-space: nowrap; }
.auth__card--wide .btn--primary, .auth__card--wide .btn--block { max-width: 360px; }

/* A secondary full-width action under the primary one — the passkey
   sign-in buttons, so far — matched to it in size rather than falling back
   to the compact default .btn, which reads as a stray, misaligned control
   next to a full-width submit button. */
.auth .btn--block { width: 100%; height: 36px; margin-top: var(--s-2); }

/* "or" between the password form and the passkey path: one word with a
   hairline on either side, so the two ways in read as alternatives. */
.auth__or {
  display: flex;
  align-items: center;
  gap: var(--s-3);
  margin: var(--s-5) 0;
  color: var(--ink-3);
  font-size: var(--t-12);
}
.auth__or::before,
.auth__or::after { content: ""; flex: 1; height: 1px; background: var(--line); }

/* The passkey section: the "or" divider, an opener button, and the
   dedicated email prompt it swaps for once pressed. The divider lives
   inside the section so that nothing passkey-related — not even a lone
   "or" — shows when WebAuthn is unavailable. */
.auth__alt .auth__or { margin-top: var(--s-5); }
.auth__alt .auth__or + .btn--block { margin-top: 0; }
.auth__passkey { margin-top: var(--s-3); }
.auth__passkey .auth__sub { margin-bottom: var(--s-4); }
/* No status yet: take no room, so the footer link sits right under Cancel. */
.auth__passkey .passkey-status:empty { display: none; }

/* The status line a passkey attempt reports into (an error, most of the
   time) — given breathing room from whatever sits above it instead of
   sitting flush against it, which the global margin reset otherwise leaves
   it. Centred only inside the auth card, where it sits under full-width
   buttons; left-aligned like the rest of the form on the account page. */
.passkey-status { margin-top: var(--s-3); color: var(--danger); min-height: 1em; }
.auth .passkey-status { text-align: center; }

/* Six-digit TOTP entry */
.code-input {
  font-family: var(--font-num);
  font-size: var(--t-20);
  letter-spacing: 0.4em;
  text-align: center;
  height: 44px;
  padding-left: 0.4em;
}

/* Text kept as typed — the clauses of a contract keep their paragraphs. */
.prewrap { white-space: pre-wrap; }

/* ------------------------------------------------------------- responsive */

@media (max-width: 900px) {
  .app { grid-template-columns: 1fr; }

  /* The drawer is pinned to top and bottom rather than given a height:
     on iOS 100vh is the viewport with the browser bars hidden, so a
     100vh drawer runs past the bottom bar and its last items can never
     be scrolled into view. top/bottom follow the visible viewport, and
     the safe-area inset keeps the last item above the home indicator. */
  .sidebar {
    position: fixed;
    inset: 0 auto 0 0;
    height: auto;
    max-height: none;
    width: var(--sidebar-w);
    z-index: 60;
    transform: translateX(-100%);
    transition: transform 180ms var(--ease);
    overscroll-behavior: contain;
    -webkit-overflow-scrolling: touch;
    padding-bottom: env(safe-area-inset-bottom);
  }

  .sidebar[data-open="true"] { transform: none; }

  /* While the drawer is open the page behind it must not scroll: on iOS
     a scrolling body also moves the fixed drawer and the bottom bar. */
  body[data-nav-open] { overflow: hidden; position: fixed; inset: 0; width: 100%; }

  .scrim { position: fixed; inset: 0; background: rgb(0 0 0 / 0.4); z-index: 50; }

  .content { padding: var(--s-4); }
  .kpi-grid { grid-template-columns: repeat(auto-fit, minmax(160px, 1fr)); }

  table.data { min-width: 560px; }
  /* A three-column schedule fits a phone without scrolling. */
  table.data--fit { min-width: 0; }
  /* The name column keeps enough room to read a name in two lines rather
     than eight; the table scrolls sideways for the rest. */
  table.data td > .cell-link, table.data td > .cell-strong { display: inline-block; min-width: 16ch; }
  .topbar { gap: var(--s-2); padding: 0 var(--s-3); }
  .topbar .select--bare { max-width: 110px; }

  /* The line editor still scrolls sideways on a phone, but with less to
     scroll: the columns give up the room a desktop can afford them. */
  .lines th:nth-child(1) { min-width: 150px; }
  .lines th:nth-child(2) { min-width: 100px; }
  .lines th:nth-child(3) { min-width: 70px; }
  .lines th:nth-child(4) { min-width: 96px; }
  .lines th:nth-child(5) { min-width: 90px; }
  .lines th:nth-child(6) { min-width: 80px; }
}

/* On a phone, upright or on its side, the unit goes under the number
   rather than beside it, so the number has the whole column and can be
   read and tapped. */
@media (max-width: 1024px) {
  .qty-cell { flex-direction: column; align-items: stretch; gap: 2px; }
  .qty-cell .input { min-width: 0; }
  .qty-cell .select { max-width: none; width: 100%; }
}

@media (max-width: 600px) {
  /* Initials are enough for the account button; the name is on the page it opens. */
  .topbar .nowrap { display: none; }
  /* The bar holds seven things on a phone: tighter gaps and a narrower
     language menu leave the page title room to be read. */
  .topbar { gap: var(--s-1); }
  .topbar .select--bare { max-width: 82px; padding-right: 22px; background-position: right 6px center; }
  /* Buttons keep their size; only the title gives way. */
  .topbar > .btn, .topbar > form, .topbar > .clock { flex: 0 0 auto; }
  /* The head's actions on a phone: a grid of two equal columns, so ten
     buttons make five even rows rather than a ragged heap. The main
     button takes a whole row at the top; a status badge and the autosave
     note each take a row of their own; a form around a button is
     transparent to the grid; a lone action spans both columns. */
  .page-head__actions {
    margin-left: 0;
    width: 100%;
    display: grid;
    grid-template-columns: repeat(2, minmax(0, 1fr));
    gap: var(--s-2);
  }
  .page-head__actions > form { display: contents; }
  .page-head__actions .btn {
    width: 100%;
    min-height: 40px;
    height: auto;
    padding: var(--s-1) var(--s-2);
    white-space: normal;
    text-align: center;
    line-height: 1.25;
  }
  .page-head__actions .btn--primary { grid-column: 1 / -1; order: -1; }
  .page-head__actions .btn--ghost { border-color: var(--line); }
  .page-head__actions > .badge { justify-self: center; align-self: center; }
  .page-head__actions > .autosave-status { grid-column: 1 / -1; justify-self: center; }
  .page-head__actions > .autosave-status:empty { display: none; }
  .page-head__actions > :only-child { grid-column: 1 / -1; }
}
@media (max-width: 360px) {
  /* On the narrowest phones the bar's title goes: the page heading sits
     right under it and says the same. */
  .topbar { padding: 0 var(--s-2); }
  .topbar__title { display: none; }
  .topbar .select--bare { max-width: 70px; }
}

/* Fingers, not pointers: the small buttons in tables and toolbars grow to
   something that can actually be hit. */
@media (pointer: coarse) {
  .btn--sm { height: 34px; font-size: var(--t-13); }
  .btn--icon.btn--sm { width: 34px; }
  .seg__btn { padding: 7px var(--s-3); }
  .lines td .input, .lines td .select { height: 36px; }
}

@media (min-width: 901px) {
  .nav-toggle, .scrim { display: none; }
}

@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after {
    animation-duration: 0.01ms !important;
    transition-duration: 0.01ms !important;
  }
}

/* ------------------------------------------------------------------ print */

@media print {
  .sidebar, .topbar, .page-head__actions, .toolbar, .pager, .row-actions {
    display: none !important;
  }
  .app { grid-template-columns: 1fr; }
  .content { padding: 0; }
  .card, .table-wrap { border: 0; }
  table.data thead th { background: transparent; }
  body { background: #fff; }
}

/* ------------------------------------------------------------------- htmx */

.htmx-indicator { opacity: 0; transition: opacity var(--speed) var(--ease); }
.htmx-request .htmx-indicator,
.htmx-request.htmx-indicator { opacity: 1; }

.htmx-request.btn { pointer-events: none; opacity: 0.7; }

/* A thin progress line while a request is in flight. */
#progress {
  position: fixed;
  top: 0; left: 0;
  height: 2px;
  width: 0;
  background: var(--accent);
  z-index: 100;
  transition: width 200ms var(--ease), opacity 200ms var(--ease);
  opacity: 0;
}

#progress[data-active="true"] { opacity: 1; }

/* The papers attached to an expense: one row each, the actions at the end. */
.doc-list { list-style: none; margin: 0; padding: 0; display: grid; gap: var(--s-2); }
.doc-list__item { display: flex; align-items: center; gap: var(--s-3); padding: var(--s-2) var(--s-3); border: 1px solid var(--line); border-radius: var(--r-md); background: var(--surface); }
.doc-list__item > svg { flex: 0 0 auto; color: var(--ink-3); }
.doc-list__text { flex: 1 1 auto; min-width: 0; overflow-wrap: anywhere; }
