/*
 * Hand-authored styles for UI added after the Tailwind build was frozen.
 * The compiled app.css only contains utilities that existed when it was
 * generated and the build toolchain is not on the server, so new components
 * are written as plain CSS here against the same design tokens (--c-*), which
 * already flip under [data-theme=dark].
 */

/* ============================================================ site texts */

.tx-bar {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: space-between;
  gap: 16px;
  padding: 14px 16px;
  margin-bottom: 18px;
  border: 1px solid rgb(var(--c-line));
  background: rgb(var(--c-surface));
  box-shadow: var(--shadow-card);
}

.tx-hint {
  flex: 1 1 320px;
  margin: 0;
  font-size: 12.5px;
  line-height: 1.45;
  color: rgb(var(--c-muted));
}

.tx-filters { display: flex; gap: 0; border: 1px solid rgb(var(--c-line)); }

.tx-filter {
  padding: 7px 13px;
  font-family: var(--font-mono);
  font-size: 10.5px;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: rgb(var(--c-muted));
  background: transparent;
  border: 0;
  border-right: 1px solid rgb(var(--c-line));
  cursor: pointer;
  transition: color .15s, background-color .15s;
}
.tx-filter:last-child { border-right: 0; }
.tx-filter:hover { color: rgb(var(--c-ember)); }
.tx-filter.is-on { background: rgb(var(--c-ember)); color: #fff; }

.tx-list { display: flex; flex-direction: column; gap: 14px; }

.tx-key {
  border: 1px solid rgb(var(--c-line));
  background: rgb(var(--c-surface));
  box-shadow: var(--shadow-card);
}

.tx-key-head {
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 9px 14px;
  border-bottom: 1px solid rgb(var(--c-line));
  background: rgb(var(--c-raised));
}

.tx-key-name {
  font-family: var(--font-mono);
  font-size: 11.5px;
  letter-spacing: 0.06em;
  color: rgb(var(--c-ink));
}

.tx-tag {
  font-family: var(--font-mono);
  font-size: 9px;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  padding: 2px 6px;
  border: 1px solid currentColor;
}
.tx-tag-changed { color: rgb(var(--c-ember)); }
.tx-tag-warn { color: #d97706; }

.tx-grid { display: grid; grid-template-columns: 1fr; gap: 0; }
@media (min-width: 900px) {
  .tx-grid { grid-template-columns: repeat(3, 1fr); }
  .tx-cell + .tx-cell { border-top: 0; border-left: 1px solid rgb(var(--c-line)); }
}

.tx-cell { padding: 12px 14px 14px; }
.tx-cell + .tx-cell { border-top: 1px solid rgb(var(--c-line)); }

.tx-lang { display: flex; align-items: baseline; gap: 7px; margin-bottom: 6px; cursor: pointer; }

.tx-lang-code {
  font-family: var(--font-mono);
  font-size: 10px;
  letter-spacing: 0.16em;
  padding: 1px 5px;
  color: rgb(var(--c-muted));
  border: 1px solid rgb(var(--c-line));
}
.tx-cell.is-edited .tx-lang-code { color: rgb(var(--c-ember)); border-color: rgb(var(--c-ember)); }

.tx-lang-name { font-size: 11.5px; color: rgb(var(--c-muted)); }

.tx-input { font-size: 14px; }
.tx-cell.is-edited .tx-input { border-color: rgb(var(--c-ember)); }
textarea.tx-input { resize: vertical; line-height: 1.5; }

.tx-default {
  margin: 6px 0 0;
  font-family: var(--font-mono);
  font-size: 10.5px;
  line-height: 1.4;
  color: rgb(var(--c-muted));
}

.tx-empty {
  padding: 32px 0;
  text-align: center;
  color: rgb(var(--c-muted));
  font-size: 13.5px;
}

.tx-actions { display: flex; justify-content: flex-end; margin-top: 18px; }

/* ======================================================= customer rating */

/* Tier pill. Colour climbs with the tier so a list scans at a glance. */
.rt-tier {
  display: inline-flex;
  align-items: baseline;
  gap: 5px;
  padding: 2px 8px;
  font-family: var(--font-mono);
  font-size: 10.5px;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  border: 1px solid;
  white-space: nowrap;
}
.rt-tier-0 { color: rgb(var(--c-muted)); border-color: rgb(var(--c-line)); }
.rt-tier-1 { color: #0d9488; border-color: #0d9488; }
.rt-tier-2 { color: rgb(var(--c-ember)); border-color: rgb(var(--c-ember)); }
.rt-tier-3 { color: #fff; background: rgb(var(--c-ember)); border-color: rgb(var(--c-ember)); }
.rt-tier-pct { font-weight: 600; }

.rt-late {
  display: inline-block;
  margin-top: 3px;
  font-family: var(--font-mono);
  font-size: 9.5px;
  letter-spacing: 0.08em;
  color: #dc2626;
}

/* Progress from the current tier to the next. */
.rt-track {
  position: relative;
  height: 5px;
  margin-top: 7px;
  background: rgb(var(--c-raised));
  border: 1px solid rgb(var(--c-line));
  overflow: hidden;
}
.rt-track-fill { position: absolute; inset: 0 auto 0 0; background: rgb(var(--c-ember)); }

.rt-panel { border: 1px solid rgb(var(--c-line)); background: rgb(var(--c-surface)); box-shadow: var(--shadow-card); }
.rt-panel-head {
  display: flex; align-items: center; justify-content: space-between; gap: 10px;
  padding: 11px 15px; border-bottom: 1px solid rgb(var(--c-line)); background: rgb(var(--c-raised));
}
.rt-panel-title {
  font-family: var(--font-mono); font-size: 10.5px; letter-spacing: 0.2em;
  text-transform: uppercase; color: rgb(var(--c-muted));
}
.rt-panel-body { padding: 15px; }

.rt-figure { font-family: var(--font-mono); font-size: 21px; letter-spacing: -0.01em; color: rgb(var(--c-ink)); }
.rt-figure-label {
  font-family: var(--font-mono); font-size: 10px; letter-spacing: 0.18em;
  text-transform: uppercase; color: rgb(var(--c-muted)); margin-bottom: 3px;
}
.rt-note { margin-top: 9px; font-size: 12.5px; line-height: 1.45; color: rgb(var(--c-muted)); }

/* Band ladder: one row per tier, the active one marked. */
.rt-bands { display: flex; flex-direction: column; border: 1px solid rgb(var(--c-line)); }
.rt-band {
  display: flex; align-items: center; justify-content: space-between; gap: 10px;
  padding: 8px 12px; font-size: 13px;
  border-bottom: 1px solid rgb(var(--c-line));
}
.rt-band:last-child { border-bottom: 0; }
.rt-band.is-current { background: rgb(var(--c-ember) / 0.08); box-shadow: inset 3px 0 0 rgb(var(--c-ember)); }
.rt-band-range { font-family: var(--font-mono); font-size: 12px; color: rgb(var(--c-muted)); }
.rt-band-pct { font-family: var(--font-mono); font-weight: 600; color: rgb(var(--c-ink)); }

/* The bracket walk for one sale, shown as it is computed. */
.rt-split { border: 1px dashed rgb(var(--c-line)); }
.rt-split-row {
  display: flex; align-items: center; justify-content: space-between; gap: 10px;
  padding: 7px 12px; font-family: var(--font-mono); font-size: 12px;
  border-bottom: 1px dashed rgb(var(--c-line));
}
.rt-split-row:last-child { border-bottom: 0; }
.rt-split-row.is-total { background: rgb(var(--c-raised)); font-weight: 600; }
.rt-split-zero { color: rgb(var(--c-muted)); }
.rt-split-earn { color: rgb(var(--c-ember)); }

/* Invoice-form panel. */
.rt-invoice { margin-top: 14px; }
.rt-apply { display: flex; align-items: center; gap: 9px; margin-top: 12px; flex-wrap: wrap; }

.rt-hist { width: 100%; border-collapse: collapse; }
.rt-hist th, .rt-hist td { padding: 7px 10px; text-align: left; border-bottom: 1px solid rgb(var(--c-line)); font-size: 13px; }
.rt-hist th {
  font-family: var(--font-mono); font-size: 9.5px; letter-spacing: 0.16em;
  text-transform: uppercase; color: rgb(var(--c-muted));
}
.rt-hist td.rt-num, .rt-hist th.rt-num { text-align: right; font-family: var(--font-mono); }
.rt-hist tr:last-child td { border-bottom: 0; }

/* ================================================ WhatsApp order handoff */

/* The one action the confirmation page exists for, so it gets the weight. */
.wa-cta {
  margin-top: 26px;
  padding: 20px;
  border: 1px solid rgb(37 211 102 / 0.42);
  background: rgb(37 211 102 / 0.07);
}

.wa-cta-note { margin: 0 0 14px; font-size: 14.5px; line-height: 1.55; color: rgb(var(--c-ink)); }

.wa-btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 10px;
  padding: 13px 26px;
  font-family: var(--font-display);
  font-size: 14px;
  font-weight: 600;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: #fff;
  background: #25d366;
  border: 1px solid #25d366;
  transition: background-color .18s ease, transform .18s ease, box-shadow .18s ease;
}
.wa-btn:hover {
  background: #1da851;
  border-color: #1da851;
  transform: translateY(-1px);
  box-shadow: 0 8px 22px -10px rgb(37 211 102 / 0.9);
}
.wa-btn:active { transform: translateY(0); }
.wa-btn:focus-visible { outline: 2px solid rgb(var(--c-ink)); outline-offset: 3px; }

.wa-cta-sub { margin: 12px 0 0; font-size: 12.5px; line-height: 1.5; color: rgb(var(--c-muted)); }

@media (prefers-reduced-motion: reduce) {
  .wa-btn { transition: background-color .18s ease; }
  .wa-btn:hover { transform: none; }
}

/* ========================================================= panel calculator */

/*
 * Slides in from the right on top of the workbench. It is deliberately not a
 * modal: an operator opens it to check a figure against the page behind it, so
 * the page stays visible, scrollable and usable while it is open.
 */
.calc-panel {
  position: fixed;
  top: 0;
  right: 0;
  z-index: 60;
  display: flex;
  flex-direction: column;
  width: 320px;
  max-width: 100vw;
  height: 100vh;
  overflow-y: auto;
  padding: 0 0 16px;
  background: rgb(var(--c-surface));
  border-left: 1px solid rgb(var(--c-line));
  box-shadow: var(--shadow-lift);
  transform: translateX(0);
}

/* Entrance only; closing just unmounts, so nothing lingers on screen. */
@media (prefers-reduced-motion: no-preference) {
  .calc-panel { animation: calc-in .18s cubic-bezier(.16, 1, .3, 1); }
  @keyframes calc-in { from { transform: translateX(14px); opacity: 0; } to { transform: none; opacity: 1; } }
}

.calc-head {
  position: sticky;
  top: 0;
  z-index: 1;
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: 14px 16px;
  background: rgb(var(--c-raised));
  border-bottom: 1px solid rgb(var(--c-line));
}

.calc-title {
  margin: 0;
  font-family: var(--font-mono);
  font-size: 10.5px;
  letter-spacing: 0.22em;
  text-transform: uppercase;
  color: rgb(var(--c-muted));
}

.calc-x {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 28px;
  height: 28px;
  color: rgb(var(--c-muted));
  background: transparent;
  border: 1px solid rgb(var(--c-line));
  cursor: pointer;
  transition: color .15s, border-color .15s;
}
.calc-x:hover { color: rgb(var(--c-ember)); border-color: rgb(var(--c-ember)); }

/* ---------------------------------------------------------------- screen */

.calc-screen { padding: 16px 16px 12px; border-bottom: 1px solid rgb(var(--c-line)); }

.calc-sub {
  min-height: 15px;
  margin: 0 0 4px;
  font-family: var(--font-mono);
  font-size: 12px;
  text-align: right;
  color: rgb(var(--c-muted));
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.calc-out {
  display: block;
  font-family: var(--font-mono);
  font-size: 28px;
  line-height: 1.15;
  text-align: right;
  color: rgb(var(--c-ink));
  word-break: break-all;
}
.calc-out.is-error { color: #dc2626; font-size: 20px; }

.calc-screen-tools {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 8px;
  margin-top: 8px;
  min-height: 20px;
}

.calc-mem-flag {
  font-family: var(--font-mono);
  font-size: 10px;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: rgb(var(--c-ember));
}

.calc-copy, .calc-clear {
  margin-left: auto;
  padding: 3px 8px;
  font-family: var(--font-mono);
  font-size: 9.5px;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: rgb(var(--c-muted));
  background: transparent;
  border: 1px solid rgb(var(--c-line));
  cursor: pointer;
  transition: color .15s, border-color .15s;
}
.calc-copy:hover, .calc-clear:hover { color: rgb(var(--c-ember)); border-color: rgb(var(--c-ember)); }

/* ------------------------------------------------------------------- pad */

.calc-pad {
  display: grid;
  grid-template-columns: repeat(4, 1fr);
  gap: 1px;
  padding: 1px;
  background: rgb(var(--c-line));
}

.calc-key {
  padding: 14px 0;
  font-family: var(--font-mono);
  font-size: 16px;
  color: rgb(var(--c-ink));
  background: rgb(var(--c-surface));
  border: 0;
  cursor: pointer;
  transition: background-color .12s, color .12s;
}
.calc-key:hover { background: rgb(var(--c-raised)); }
.calc-key:active, .calc-key.is-lit { background: rgb(var(--c-ember)); color: #fff; }
.calc-key:focus-visible { outline: 2px solid rgb(var(--c-ember)); outline-offset: -2px; }

.calc-key-mem { font-size: 11.5px; letter-spacing: 0.06em; color: rgb(var(--c-muted)); }
.calc-key-fn { color: rgb(var(--c-muted)); }
.calc-key-op { color: rgb(var(--c-ember)); font-size: 17px; }
/* = is a tall key across the last two rows, the way a desk calculator has
   it, which is what leaves 0 / 00 / 000 a row of their own. */
.calc-key-eq { background: rgb(var(--c-ember)); color: #fff; grid-row: span 2; }
.calc-key-eq:hover { background: rgb(var(--c-ember-hi)); }

/* The radical is drawn, not typed: U+221A carries no overbar and reads as
   a tick in several of the fonts this panel may fall back to. */
.calc-glyph { width: 22px; height: 18px; display: block; margin: 0 auto; }

.calc-sup-base { font-size: 15px; }
.calc-sup-base sup { font-size: 0.62em; vertical-align: super; line-height: 0; }


.calc-hint {
  margin: 16px 16px 0;
  font-size: 11px;
  line-height: 1.5;
  color: rgb(var(--c-muted));
}

/* The workbench is not covered on a narrow screen, so take the full width. */
@media (max-width: 480px) {
  .calc-panel { width: 100vw; border-left: 0; }
}


/* ===================================================== buttons: no zoom */

/*
 * app.css scales every button on hover (1.1) and on press (1.05). It is the
 * compiled Tailwind output and the build is not available here, so the effect
 * is switched off from this file, which every layout loads after it.
 *
 * The press rule goes too, not just the hover one: with hover back at 1.0 a
 * remaining :active scale would make the button grow when clicked, which is
 * the opposite of the press-down feel it was there to give.
 *
 * Only the transform is dropped. The skewed sweep on ::before, the colour
 * change and the shadow are what make these buttons feel alive, and they stay.
 */
.btn:hover, .btn:active,
.btn-ember:hover, .btn-ember:active,
.btn-dark:hover, .btn-dark:active,
.btn-ghost:hover, .btn-ghost:active,
.btn-invert:hover, .btn-invert:active,
.btn-sm:hover, .btn-sm:active {
  transform: none;
}

/* Tailwind's important variant sets the scale with !important, so matching it
   is the only way to win. Not currently used in any markup, covered anyway. */
.\!btn:hover, .\!btn:active { transform: none !important; }

/* ========================================================= ERP overview */

.erpx-bar {
  padding: 15px 16px 13px;
  margin-bottom: 18px;
  border: 1px solid rgb(var(--c-line));
  background: rgb(var(--c-surface));
  box-shadow: var(--shadow-card);
}

.erpx-presets { display: flex; flex-wrap: wrap; gap: 0; border: 1px solid rgb(var(--c-line)); width: max-content; max-width: 100%; }

.erpx-preset {
  padding: 7px 14px;
  font-family: var(--font-mono);
  font-size: 10.5px;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: rgb(var(--c-muted));
  background: transparent;
  border: 0;
  border-right: 1px solid rgb(var(--c-line));
  cursor: pointer;
  transition: color .15s, background-color .15s;
}
.erpx-preset:last-child { border-right: 0; }
.erpx-preset:hover { color: rgb(var(--c-ember)); }
.erpx-preset.is-on { background: rgb(var(--c-ember)); color: #fff; }

.erpx-fields {
  display: flex;
  flex-wrap: wrap;
  align-items: flex-end;
  gap: 10px;
  margin-top: 13px;
}
.erpx-field { display: flex; flex-direction: column; gap: 4px; min-width: 130px; }
.erpx-field .field { padding: 7px 10px; font-size: 13px; }
.erpx-field .select-wrap { min-width: 150px; }

.erpx-label {
  font-family: var(--font-mono);
  font-size: 9.5px;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: rgb(var(--c-muted));
}

.erpx-range {
  margin: 11px 0 0;
  font-family: var(--font-mono);
  font-size: 11px;
  color: rgb(var(--c-muted));
}

.erpx-note {
  margin: 14px 0 0;
  padding: 9px 13px;
  font-size: 12.5px;
  color: rgb(var(--c-muted));
  border-left: 3px solid rgb(var(--c-ember));
  background: rgb(var(--c-raised));
}

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

.erpx-tiles { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 14px; }
@media (min-width: 900px) { .erpx-tiles { grid-template-columns: repeat(5, minmax(0, 1fr)); } }

.erpx-delta {
  margin: 6px 0 0;
  font-family: var(--font-mono);
  font-size: 10.5px;
  letter-spacing: 0.04em;
}
.erpx-delta.is-up { color: #10B981; }
.erpx-delta.is-down { color: #dc2626; }
.erpx-delta.is-flat { color: rgb(var(--c-muted)); }

/* Physical / online share, as one two-tone bar. */
.erpx-split { display: flex; height: 6px; margin-top: 8px; border: 1px solid rgb(var(--c-line)); overflow: hidden; }
.erpx-split-a { background: rgb(var(--c-ember)); }
.erpx-split-b { background: #6366f1; }

/* ------------------------------------------------------------ charts */

.erpx-legend {
  display: flex;
  flex-wrap: wrap;
  gap: 14px;
  font-family: var(--font-mono);
  font-size: 10.5px;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: rgb(var(--c-muted));
}
.erpx-legend span { display: inline-flex; align-items: center; gap: 6px; }
.erpx-key { width: 10px; height: 10px; display: inline-block; }

.erpx-hint { margin: 0; font-size: 12px; line-height: 1.45; color: rgb(var(--c-muted)); }

/* Bars respond to the cursor so it is obvious they carry a figure. */
.erpx-bar-chart rect, svg rect[data-tip] { transition: opacity .12s; }
svg rect[data-tip]:hover { opacity: 1; filter: brightness(1.12); }

/* --------------------------------------------------- horizontal rows */

.erpx-rows { display: flex; flex-direction: column; gap: 13px; }

.erpx-row { cursor: default; }

.erpx-row-head { display: flex; align-items: baseline; justify-content: space-between; gap: 10px; margin-bottom: 5px; }

.erpx-row-label {
  font-size: 13px;
  color: rgb(var(--c-ink));
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.erpx-row-value { font-size: 12.5px; color: rgb(var(--c-ink)); white-space: nowrap; }

.erpx-track {
  position: relative;
  height: 7px;
  background: rgb(var(--c-raised));
  border: 1px solid rgb(var(--c-line));
  overflow: hidden;
}

.erpx-fill { position: absolute; inset: 0 auto 0 0; transition: width .25s ease; }

/* What of that bar is still unpaid, hatched over the top of it. */
.erpx-fill-over {
  position: absolute;
  inset: 0 auto 0 0;
  background: repeating-linear-gradient(
    45deg,
    rgb(var(--c-ember)) 0 3px,
    transparent 3px 6px
  );
}

.erpx-row-sub {
  display: block;
  margin-top: 4px;
  font-family: var(--font-mono);
  font-size: 10px;
  color: rgb(var(--c-muted));
}

.erpx-empty { margin: 0; padding: 26px 0; text-align: center; font-size: 13px; color: rgb(var(--c-muted)); }

@media (prefers-reduced-motion: reduce) {
  .erpx-fill { transition: none; }
}

/* The cron line is meant to be copied into hPanel verbatim. */
.tg-cron {
  display: block;
  margin-top: 9px;
  padding: 9px 12px;
  font-family: var(--font-mono);
  font-size: 11.5px;
  line-height: 1.5;
  color: rgb(var(--c-ink));
  background: rgb(var(--c-raised));
  border: 1px solid rgb(var(--c-line));
  overflow-x: auto;
  white-space: nowrap;
  user-select: all;
}

/* ============================================== shared ERP list toolbar */

/* The search box earns more room than a dropdown; the rest share what is left. */
.lf-bar .erpx-fields { margin-top: 0; }
.lf-search { flex: 1 1 260px; min-width: 200px; }
.lf-search .field { width: 100%; }

/* Delete sits apart from the safe actions, and only colours on approach. */
.lf-danger { color: rgb(var(--c-muted)); }
.lf-danger:hover { color: #dc2626; border-color: #dc2626; }

.lf-actions { display: flex; align-items: center; justify-content: flex-end; gap: 6px; }
.lf-actions form { display: inline-flex; }


/* ================================================ site buttons: proportion */

/*
 * The compiled button in app.css is drawn for a hero call to action: 15px
 * display type at 5px of tracking inside 1em/2em of padding. Beside a form
 * field that reads as a slab — on the calculator "ƏLAVƏ ET" ends up wider
 * than the input it belongs to, and "WHATSAPP İLƏ GÖNDƏR" breaks onto two
 * lines inside its own panel.
 *
 * What makes these buttons the site's own stays untouched: the ember and
 * neutral colours, the outlined shape, the skewed sweep that fills them on
 * hover. Only the metrics change — tracking down to a still-industrial
 * 0.15em, padding and radius pulled in, the outline taken to a hairline, and
 * the one-second transition brought to a speed that reads as a response.
 *
 * These are the metrics everywhere. The admin panel used to be left on the
 * compiled defaults, which made its buttons a different size from the site's;
 * one product, one button. Only the calculator rules below stay scoped to
 * .site-ui, because the element they align to exists only there.
 */

.btn,
.btn-ember,
.btn-ghost,
.btn-dark,
.btn-invert {
  padding: 0.92em 1.7em;
  font-size: 12.5px;
  line-height: 1.15;
  letter-spacing: 0.15em;
  border-radius: 2px;
  outline-width: 1px;
  outline-offset: 0;
  transition: color 0.2s ease, outline-color 0.2s ease, box-shadow 0.2s ease;
}

/* The sweep is the signature, so it keeps its shape and only loses the crawl. */
.btn::before,
.btn-ember::before,
.btn-ghost::before,
.btn-dark::before,
.btn-invert::before {
  transition: width 0.4s cubic-bezier(0.22, 0.61, 0.36, 1);
}

/* Secondary actions sit under a primary one and should read as quieter. */
.btn-sm {
  padding: 0.7em 1.25em;
  font-size: 11px;
  letter-spacing: 0.13em;
}

/*
 * The 17px bloom was sized for the old 1.1 hover scale that has since been
 * removed; on a static button it floats. A tighter, lower shadow reads as
 * the button lifting off the surface rather than glowing behind it.
 */
.btn-ember:hover { box-shadow: 0 3px 14px -7px rgb(var(--c-ember)); }
.btn-ghost:hover,
.btn-dark:hover { box-shadow: 0 3px 14px -7px rgb(var(--c-btn-neutral)); }
.btn-invert:hover { box-shadow: 0 3px 14px -7px hsla(0, 0%, 100%, 0.75); }

/* Keyboard users get the same ring the fields use, not the button's outline. */
.btn:focus-visible,
.btn-ember:focus-visible,
.btn-ghost:focus-visible,
.btn-dark:focus-visible,
.btn-invert:focus-visible {
  outline: 2px solid rgb(var(--c-ember));
  outline-offset: 2px;
}

/*
 * On the calculator the add button is a fifth column beside four label-plus-
 * field pairs. It has no label of its own, so it sits on the field's baseline
 * and matches the field's height rather than stretching over the label too.
 */
.site-ui .calc-picker .calc-add {
  align-self: end;
  height: 46px;
  padding-top: 0;
  padding-bottom: 0;
}

/* The basket's three actions: one primary, then a quieter pair beneath it. */
.site-ui .calc-basket .btn-ember { width: 100%; }
.site-ui .calc-basket .btn-sm { letter-spacing: 0.12em; }


/* ==================================================== bulk select + delete */

/*
 * The bar above a selectable list. It is revealed by script, so a browser
 * without JavaScript never shows a selection UI it cannot drive.
 */
.bulk-bar {
  display: flex;
  align-items: center;
  justify-content: space-between;
  flex-wrap: wrap;
  gap: 10px;
  padding: 9px 14px;
  margin-bottom: 12px;
  border: 1px solid rgb(var(--c-line));
  background: rgb(var(--c-surface));
}

.bulk-count {
  font-family: var(--font-mono);
  font-size: 11px;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: rgb(var(--c-muted));
}
.bulk-count [data-bulk-count] { color: rgb(var(--c-ember)); font-weight: 700; }

.bulk-acts { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; }

/* Destructive, but not shouting until the pointer is on it. */
.bulk-danger:not(:disabled) { color: #dc2626; outline-color: #dc2626; }
.bulk-danger:not(:disabled)::before { background-color: #dc2626; }
.bulk-danger:not(:disabled):hover { color: #fff; box-shadow: 0 3px 14px -7px #dc2626; }
.bulk-danger:disabled {
  opacity: 0.4;
  cursor: not-allowed;
  color: rgb(var(--c-muted));
  outline-color: rgb(var(--c-line));
}
.bulk-danger:disabled::before { background-color: transparent; }

/* The checkbox column: narrow, and never the thing the eye lands on first. */
.pick-cell { width: 34px; padding-right: 0 !important; }
.pick-box {
  width: 15px;
  height: 15px;
  accent-color: rgb(var(--c-ember));
  cursor: pointer;
  vertical-align: middle;
}

/* A ticked row is tinted rather than outlined, so the table keeps its rhythm. */
tr.is-picked > td { background: rgb(var(--c-ember) / 0.07); }


/* ======================================================== notification feed */

/*
 * The notice page is read once and then emptied, so it is built as a feed
 * rather than a table: tiles for how much is waiting and of what kind, then
 * items that carry their own colour and their own one link out.
 */

.nt-tiles {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(132px, 1fr));
  gap: 10px;
  margin-bottom: 18px;
}

.nt-tile {
  padding: 14px 16px 12px;
  border: 1px solid rgb(var(--c-line));
  background: rgb(var(--c-surface));
  border-top: 2px solid rgb(var(--c-line));
  transition: border-color 0.2s ease;
}
.nt-tile-value {
  font-family: var(--font-display);
  font-size: 26px;
  font-weight: 700;
  line-height: 1;
  letter-spacing: -0.01em;
}
.nt-tile-label {
  margin-top: 6px;
  font-family: var(--font-mono);
  font-size: 10px;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: rgb(var(--c-muted));
}

/* A tile only takes colour when it has something to report. */
.nt-tile.is-live  { border-top-color: rgb(var(--c-ember)); }
.nt-tile.is-live  .nt-tile-value { color: rgb(var(--c-ember)); }
.nt-tile.is-red   { border-top-color: #dc2626; }
.nt-tile.is-red   .nt-tile-value { color: #dc2626; }
.nt-tile.is-ember { border-top-color: rgb(var(--c-ember)); }
.nt-tile.is-ember .nt-tile-value { color: rgb(var(--c-ember)); }
.nt-tile.is-blue  { border-top-color: #3b82f6; }
.nt-tile.is-blue  .nt-tile-value { color: #3b82f6; }

.nt-toolbar {
  display: flex;
  align-items: center;
  justify-content: space-between;
  flex-wrap: wrap;
  gap: 10px;
  margin-bottom: 12px;
}
.nt-chips { display: flex; flex-wrap: wrap; gap: 6px; }

.nt-chip {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  padding: 6px 12px;
  border: 1px solid rgb(var(--c-line));
  font-family: var(--font-mono);
  font-size: 10.5px;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: rgb(var(--c-muted));
  transition: color 0.18s ease, border-color 0.18s ease;
}
.nt-chip:hover { color: rgb(var(--c-ember)); border-color: rgb(var(--c-ember)); }
.nt-chip.is-on {
  color: #fff;
  background: rgb(var(--c-ember));
  border-color: rgb(var(--c-ember));
}
.nt-chip-n {
  padding: 1px 5px;
  border-radius: 2px;
  background: rgb(var(--c-ember) / 0.16);
  color: rgb(var(--c-ember));
}
.nt-chip.is-on .nt-chip-n { background: rgb(255 255 255 / 0.24); color: #fff; }

.nt-feed { display: flex; flex-direction: column; gap: 8px; }

/*
 * The spine on the left is the whole status language of the feed: lit means
 * unread, and its colour says what kind of notice it is.
 */
.nt-item {
  display: grid;
  grid-template-columns: auto auto minmax(0, 1fr);
  align-items: start;
  gap: 12px;
  padding: 13px 16px;
  border: 1px solid rgb(var(--c-line));
  border-left: 3px solid rgb(var(--c-line));
  background: rgb(var(--c-surface));
  transition: border-color 0.18s ease, background 0.18s ease;
}
.nt-item:hover { border-color: rgb(var(--c-muted) / 0.5); }
.nt-item.is-picked { background: rgb(var(--c-ember) / 0.07); }

.nt-item.nt-red   { border-left-color: #dc2626; }
.nt-item.nt-ember { border-left-color: rgb(var(--c-ember)); }
.nt-item.nt-blue  { border-left-color: #3b82f6; }
.nt-item.nt-grey  { border-left-color: rgb(var(--c-muted)); }

/* Read notices stay legible but stop competing for attention. */
.nt-item.is-read { background: transparent; border-left-color: rgb(var(--c-line)); }
.nt-item.is-read .nt-title { font-weight: 400; color: rgb(var(--c-muted)); }
.nt-item.is-read .nt-glyph { opacity: 0.5; }

.nt-pick { margin-top: 4px; }

.nt-glyph {
  display: grid;
  place-items: center;
  width: 28px;
  height: 28px;
  border-radius: 50%;
}
.nt-red   .nt-glyph { color: #dc2626; background: rgb(220 38 38 / 0.12); }
.nt-ember .nt-glyph { color: rgb(var(--c-ember)); background: rgb(var(--c-ember) / 0.12); }
.nt-blue  .nt-glyph { color: #3b82f6; background: rgb(59 130 246 / 0.12); }
.nt-grey  .nt-glyph { color: rgb(var(--c-muted)); background: rgb(var(--c-muted) / 0.12); }

.nt-body { min-width: 0; }
.nt-head { display: flex; align-items: baseline; justify-content: space-between; gap: 12px; }
.nt-title { font-size: 13.5px; font-weight: 600; }
.nt-time {
  flex-shrink: 0;
  font-family: var(--font-mono);
  font-size: 10.5px;
  color: rgb(var(--c-muted));
}
.nt-text { margin-top: 3px; font-size: 12.5px; line-height: 1.55; color: rgb(var(--c-muted)); }

.nt-acts { display: flex; flex-wrap: wrap; align-items: center; gap: 14px; margin-top: 8px; }
.nt-acts form { display: inline-flex; }
.nt-act {
  font-family: var(--font-mono);
  font-size: 10.5px;
  letter-spacing: 0.13em;
  text-transform: uppercase;
  color: rgb(var(--c-muted));
  transition: color 0.16s ease;
}
.nt-act:hover { color: rgb(var(--c-ember)); }
.nt-act.is-link { color: rgb(var(--c-ember)); }
.nt-act.is-danger:hover { color: #dc2626; }

/* An empty feed is the goal, not a failure, so it reads as one. */
.nt-empty {
  display: grid;
  justify-items: center;
  gap: 6px;
  padding: 54px 20px;
  border: 1px dashed rgb(var(--c-line));
  color: rgb(var(--c-muted));
  text-align: center;
}
.nt-empty svg { color: rgb(var(--c-ember)); opacity: 0.65; }
.nt-empty-title { margin-top: 4px; font-size: 13.5px; color: rgb(var(--c-ink)); }
.nt-empty-text { font-size: 12px; }

@media (max-width: 640px) {
  .nt-head { flex-direction: column; align-items: flex-start; gap: 2px; }
}


/* ============================================== export bar + print layout */

.ex-bar { display: flex; flex-wrap: wrap; align-items: center; gap: 8px; margin-bottom: 18px; }
.ex-hint { font-family: var(--font-mono); font-size: 10.5px; color: rgb(var(--c-muted)); }

/*
 * What a page becomes on paper — and, through the browser's own "Save as PDF"
 * destination, what it becomes as a PDF.
 *
 * The panel chrome is navigation, not content: on paper it is dead weight and
 * it pushes the actual figures onto a second page. Backgrounds go too, since
 * most printers drop them anyway and a half-printed tint reads as a defect.
 */
@media print {
  .sidebar, .admin-sidebar, .topbar, .app-topbar,
  .noprint, .ex-bar, .bulk-bar, .lf-bar, .erpx-bar,
  .pick-cell, .pager, nav[aria-label="Pagination"],
  #confirm-modal, #flash-toast, #flash-toast-err { display: none !important; }

  .app-shell, .admin-shell {
    display: block !important;
    grid-template-columns: none !important;
  }
  main, main.workbench, .workbench {
    padding: 0 !important;
    max-width: none !important;
    overflow: visible !important;
  }

  body {
    background: #fff !important;
    color: #000 !important;
    font-size: 11pt;
  }

  .card, .rp-panel, .rp-stat, .rp-net {
    border: 1px solid #ccc !important;
    background: transparent !important;
    box-shadow: none !important;
    break-inside: avoid;
  }

  .table-wrap { overflow: visible !important; }
  table { width: 100% !important; }
  thead { display: table-header-group; }
  tr { break-inside: avoid; }

  a[href]::after { content: none !important; }

  @page { margin: 14mm; }
}


/* ===================================================== report: the document */

.rp-masthead {
  display: flex;
  align-items: flex-end;
  justify-content: space-between;
  flex-wrap: wrap;
  gap: 16px;
  padding-bottom: 16px;
  margin-bottom: 18px;
  border-bottom: 2px solid rgb(var(--c-ink));
}
.rp-eyebrow {
  font-family: var(--font-mono);
  font-size: 10px;
  letter-spacing: 0.22em;
  text-transform: uppercase;
  color: rgb(var(--c-muted));
}
.rp-range {
  margin-top: 4px;
  font-family: var(--font-display);
  font-size: 30px;
  font-weight: 700;
  line-height: 1;
  letter-spacing: -0.015em;
}
.rp-meta { margin-top: 6px; font-family: var(--font-mono); font-size: 10.5px; color: rgb(var(--c-muted)); }
.rp-meta-sep { margin: 0 6px; }

.rp-nav { display: flex; align-items: center; gap: 12px; flex-wrap: wrap; }
.rp-tabs { display: flex; border: 1px solid rgb(var(--c-line)); }
.rp-tab {
  padding: 7px 14px;
  font-family: var(--font-mono);
  font-size: 10.5px;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: rgb(var(--c-muted));
  border-right: 1px solid rgb(var(--c-line));
  transition: color 0.16s ease, background 0.16s ease;
}
.rp-tab:last-child { border-right: 0; }
.rp-tab:hover { color: rgb(var(--c-ember)); }
.rp-tab.is-on { background: rgb(var(--c-ember)); color: #fff; }
.rp-step { display: flex; gap: 6px; }

/* The result gets the space, because it is the one figure people came for. */
.rp-headline {
  display: grid;
  grid-template-columns: minmax(0, 1fr);
  gap: 12px;
  margin-bottom: 18px;
}
@media (min-width: 900px) {
  .rp-headline { grid-template-columns: minmax(260px, 0.9fr) minmax(0, 2fr); align-items: stretch; }
}

.rp-net {
  padding: 20px 22px;
  border: 1px solid rgb(var(--c-line));
  border-left: 4px solid rgb(var(--c-line));
  background: rgb(var(--c-surface));
}
.rp-net.is-good { border-left-color: #059669; }
.rp-net.is-bad  { border-left-color: #dc2626; }
.rp-net-label {
  font-family: var(--font-mono);
  font-size: 10px;
  letter-spacing: 0.2em;
  text-transform: uppercase;
  color: rgb(var(--c-muted));
}
.rp-net-value {
  margin-top: 8px;
  font-family: var(--font-display);
  font-size: 40px;
  font-weight: 700;
  line-height: 1;
  letter-spacing: -0.02em;
}
.rp-net.is-good .rp-net-value { color: #059669; }
.rp-net.is-bad  .rp-net-value { color: #dc2626; }
.rp-net-foot { display: flex; align-items: baseline; gap: 8px; margin-top: 10px; }
.rp-vs, .rp-was { font-family: var(--font-mono); font-size: 10.5px; color: rgb(var(--c-muted)); }
.rp-margin {
  margin-top: 12px;
  padding-top: 10px;
  border-top: 1px solid rgb(var(--c-line));
  font-size: 12px;
  color: rgb(var(--c-muted));
}
.rp-margin strong { color: rgb(var(--c-ink)); font-family: var(--font-mono); }

.rp-pair { display: grid; grid-template-columns: repeat(auto-fit, minmax(190px, 1fr)); gap: 12px; }

/* Four stats stretched into one row next to the hero leave a lot of air in
   each card. Two rows of two sit at the hero's height instead. */
@media (min-width: 900px) {
  .rp-pair.is-quad { grid-template-columns: repeat(2, minmax(0, 1fr)); }
}
.rp-stat {
  padding: 16px 18px;
  border: 1px solid rgb(var(--c-line));
  border-top: 2px solid rgb(var(--c-line));
  background: rgb(var(--c-surface));
}
.rp-stat.is-in  { border-top-color: #059669; }
.rp-stat.is-out { border-top-color: rgb(var(--c-ember)); }
.rp-stat-label {
  font-family: var(--font-mono);
  font-size: 10px;
  letter-spacing: 0.18em;
  text-transform: uppercase;
  color: rgb(var(--c-muted));
}
.rp-stat-value {
  margin-top: 6px;
  font-family: var(--font-display);
  font-size: 23px;
  font-weight: 600;
  line-height: 1.05;
}
.rp-stat.is-in  .rp-stat-value { color: #059669; }
.rp-stat.is-out .rp-stat-value { color: rgb(var(--c-ember)); }
.rp-stat-foot { display: flex; align-items: baseline; gap: 8px; margin-top: 6px; }
.rp-stat-note { margin-top: 8px; font-family: var(--font-mono); font-size: 10px; color: rgb(var(--c-muted)); line-height: 1.5; }

/* Green is not automatically good: spending up is spending up. */
.rp-trend {
  font-family: var(--font-mono);
  font-size: 11px;
  font-weight: 700;
  letter-spacing: 0.04em;
}
.rp-trend.is-up   { color: #059669; }
.rp-trend.is-down { color: #dc2626; }
.rp-trend.is-flat { color: rgb(var(--c-muted)); font-weight: 400; }

.rp-panel {
  padding: 18px 20px;
  margin-bottom: 14px;
  border: 1px solid rgb(var(--c-line));
  background: rgb(var(--c-surface));
}
.rp-panel-head { display: flex; align-items: center; justify-content: space-between; gap: 14px; flex-wrap: wrap; }
.rp-panel-title {
  font-family: var(--font-mono);
  font-size: 10.5px;
  letter-spacing: 0.2em;
  text-transform: uppercase;
  color: rgb(var(--c-muted));
}
.rp-empty { margin-top: 14px; font-size: 12.5px; color: rgb(var(--c-muted)); }
.rp-split { display: grid; grid-template-columns: minmax(0, 1fr); gap: 14px; }
@media (min-width: 900px) { .rp-split { grid-template-columns: repeat(2, minmax(0, 1fr)); } }

.rp-legend { display: flex; gap: 14px; font-family: var(--font-mono); font-size: 10px; letter-spacing: 0.14em; text-transform: uppercase; color: rgb(var(--c-muted)); }
.rp-legend span { display: inline-flex; align-items: center; gap: 6px; }
.rp-key { width: 9px; height: 9px; display: inline-block; }
.rp-key.is-in  { background: #059669; }
.rp-key.is-out { background: rgb(var(--c-ember)); }

/* Gridlines give the bars something to be measured against. */
.rp-chart { position: relative; margin-top: 18px; }
.rp-grid { position: absolute; inset: 0 0 22px 0; display: flex; flex-direction: column; justify-content: space-between; }
.rp-grid span { height: 1px; background: rgb(var(--c-line)); }
.rp-bars {
  position: relative;
  display: flex;
  align-items: flex-end;
  gap: 10px;
  height: 190px;
  overflow-x: auto;
  padding-bottom: 2px;
}
.rp-col { display: flex; flex-direction: column; align-items: center; gap: 6px; flex-shrink: 0; height: 100%; justify-content: flex-end; }
.rp-col-bars { display: flex; align-items: flex-end; gap: 3px; height: 100%; }
.rp-bar { width: 13px; display: block; transition: opacity 0.16s ease; }
.rp-bar.is-in  { background: #059669; }
.rp-bar.is-out { background: rgb(var(--c-ember)); }
.rp-col:hover .rp-bar { opacity: 0.72; }
.rp-col-label { font-family: var(--font-mono); font-size: 9.5px; color: rgb(var(--c-muted)); white-space: nowrap; }

.rp-list { margin-top: 16px; display: flex; flex-direction: column; gap: 11px; }
.rp-row { display: grid; grid-template-columns: 7.5rem minmax(0, 1fr) 5.5rem 2.5rem; align-items: center; gap: 10px; }
.rp-row-name { font-family: var(--font-mono); font-size: 10px; letter-spacing: 0.12em; text-transform: uppercase; color: rgb(var(--c-muted)); }
.rp-row-track { height: 7px; background: rgb(var(--c-muted) / 0.14); position: relative; }
.rp-row-fill { position: absolute; inset-block: 0; left: 0; }
.rp-row-fill.is-in  { background: #059669; }
.rp-row-fill.is-out { background: rgb(var(--c-ember)); }
.rp-row-value { font-family: var(--font-mono); font-size: 12px; text-align: right; }
.rp-row-share { font-family: var(--font-mono); font-size: 10.5px; text-align: right; color: rgb(var(--c-muted)); }

.rp-rank { margin-top: 14px; display: flex; flex-direction: column; }
.rp-rank-row {
  display: grid;
  grid-template-columns: 1.4rem minmax(0, 1fr) auto;
  align-items: baseline;
  gap: 10px;
  padding: 9px 0;
  border-bottom: 1px solid rgb(var(--c-line));
}
.rp-rank-row:last-child { border-bottom: 0; }
.rp-rank-n { font-family: var(--font-mono); font-size: 10px; color: rgb(var(--c-muted)); }
.rp-rank-name { font-size: 12.5px; min-width: 0; }
.rp-rank-sub { display: block; font-family: var(--font-mono); font-size: 10px; color: rgb(var(--c-muted)); margin-top: 2px; }
.rp-rank-value { font-family: var(--font-mono); font-size: 12.5px; font-weight: 600; color: #059669; }
.rp-rank-value.is-out { color: rgb(var(--c-ember)); }

.rp-total > td { border-top: 2px solid rgb(var(--c-ink)); font-weight: 700; }

@media (max-width: 560px) {
  .rp-range { font-size: 24px; }
  .rp-net-value { font-size: 32px; }
  .rp-row { grid-template-columns: 6rem minmax(0, 1fr) 4.5rem; }
  .rp-row-share { display: none; }
}


/* ============================================================= line chart */

/*
 * Shared by the visitor, report and ERP overview charts.
 *
 * The lines are deliberately thin. A heavy stroke reads as a bar and fights
 * the gridlines; at 1.6px the shape of the data is what carries, which is the
 * whole point of drawing it as a line.
 */

.lc { width: 100%; display: block; overflow: visible; }

.lc-grid { stroke: rgb(var(--c-line)); stroke-width: 0.8; stroke-dasharray: 3 7; }
.lc-grid.is-base { stroke-width: 1.2; stroke-dasharray: none; }

.lc-axis {
  font-family: var(--font-mono);
  font-size: 9px;
  fill: rgb(var(--c-muted));
}

.lc-line {
  fill: none;
  stroke-width: 1.6;
  stroke-linejoin: round;
  stroke-linecap: round;
}

/* Last period is context, so it is drawn quieter and dotted. */
.lc-line.is-compare {
  stroke: rgb(var(--c-muted));
  stroke-width: 1.4;
  stroke-dasharray: 4 7;
  opacity: 0.75;
}

/* Each line names itself at its own end, which saves matching a colour to a
   word in a legend somewhere else on the page. */
.lc-tag {
  font-family: var(--font-mono);
  font-size: 10px;
  font-weight: 700;
  letter-spacing: 0.04em;
}
.lc-tag.is-compare { fill: rgb(var(--c-muted)); font-weight: 400; }

/*
 * Markers stay hidden until the pointer is over their bucket. A two-year
 * series with a dot on every point is a field of dots, not a line.
 */
.lc-dot { opacity: 0; transition: opacity 0.12s ease; }
.lc-dot.is-compare { fill: rgb(var(--c-muted)); }

.lc-guide {
  stroke: rgb(var(--c-ember));
  stroke-width: 1.2;
  stroke-dasharray: 4 4;
  opacity: 0;
  transition: opacity 0.12s ease;
}

.lc-slot:hover .lc-dot,
.lc-slot:hover .lc-guide { opacity: 1; }

/* ==================================================== instalments & debts */

/* A second figure under a stat card's headline — kilograms under tonnes,
   the metre equivalent under the weight. */
.stat-sub {
  margin: 4px 0 0;
  font-family: var(--font-mono);
  font-size: 11px;
  letter-spacing: 0.04em;
  color: rgb(var(--c-muted));
}

/* The expanded row under an invoice: what has been paid so far, and the form
   for the next instalment. */
.pay-row > td {
  background: rgb(var(--c-surface-2));
  border-top: 0;
  padding: 14px 16px 16px;
}

.pay-count {
  display: inline-block;
  margin-left: 6px;
  font-family: var(--font-mono);
  font-size: 10px;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: rgb(var(--c-muted));
}

.pay-list {
  list-style: none;
  margin: 0 0 12px;
  padding: 0;
  display: flex;
  flex-direction: column;
  gap: 6px;
}

.pay-item {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  gap: 10px;
  padding: 7px 10px;
  border: 1px solid rgb(var(--c-line));
  background: rgb(var(--c-surface));
  font-size: 12.5px;
}

.pay-seq {
  flex: 0 0 auto;
  width: 20px;
  height: 20px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  font-family: var(--font-mono);
  font-size: 10.5px;
  color: rgb(var(--c-muted));
  border: 1px solid rgb(var(--c-line));
}

.pay-amount {
  font-family: var(--font-mono);
  font-weight: 600;
  color: #10b981;
}

.pay-meta { color: rgb(var(--c-muted)); }

.pay-left {
  margin-left: auto;
  font-family: var(--font-mono);
  font-size: 11.5px;
  color: rgb(var(--c-muted));
}

.pay-note {
  flex: 1 1 100%;
  font-size: 12px;
  color: rgb(var(--c-muted));
}

.pay-form {
  display: flex;
  flex-wrap: wrap;
  align-items: flex-end;
  gap: 10px;
  padding-top: 12px;
  border-top: 1px solid rgb(var(--c-line));
}

.pay-form .erpx-field { margin: 0; }

/* ========================================================= blacklist note */

.bl-auto,
.bl-reason {
  margin: 8px 0 0;
  padding: 8px 10px;
  border-left: 2px solid #dc2626;
  background: rgba(220, 38, 38, 0.06);
  font-size: 12.5px;
  line-height: 1.5;
}

.bl-reason { border-left-color: rgb(var(--c-muted)); background: rgba(120, 120, 120, 0.06); }

/* ======================================================== production form */

.prod-hint {
  margin: 0;
  font-size: 11.5px;
  line-height: 1.5;
  color: rgb(var(--c-muted));
}

/* ==================================================== responsive admin shell
 *
 * The compiled app.css lays the panel out as a two-column grid and, below
 * 1025px, hides the sidebar outright with `display: none` / `.open { display:
 * flex }`. That is a switch, not a drawer: it cannot animate, it has nothing
 * to tap outside it, and the page behind it keeps scrolling. Everything below
 * turns it into a proper off-canvas drawer, overriding app.css by holding one
 * extra class in the selector rather than by reaching for !important.
 */

@media (max-width: 1024px) {
  .admin-shell .admin-sidebar {
    display: flex;
    width: min(84vw, 290px);
    transform: translateX(-100%);
    visibility: hidden;
    transition: transform 0.26s cubic-bezier(0.16, 1, 0.3, 1), visibility 0.26s;
    box-shadow: 0 0 40px rgba(0, 0, 0, 0.45);
    /* Notch and home-indicator safe areas — the drawer runs edge to edge. */
    padding-left: env(safe-area-inset-left);
    padding-bottom: env(safe-area-inset-bottom);
    overscroll-behavior: contain;
    -webkit-overflow-scrolling: touch;
  }

  .admin-shell .admin-sidebar.open {
    transform: translateX(0);
    visibility: visible;
  }

  .admin-scrim {
    position: fixed;
    inset: 0;
    z-index: 55;
    background: rgba(15, 17, 21, 0.55);
    -webkit-backdrop-filter: blur(2px);
    backdrop-filter: blur(2px);
    opacity: 0;
    transition: opacity 0.26s ease;
  }

  .admin-scrim.is-on { opacity: 1; }

  /* The page behind a drawer must not scroll with it. */
  body.sidebar-locked { overflow: hidden; }

  .sidebar-close {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 40px;
    height: 40px;
    margin-right: 10px;
    flex: 0 0 auto;
    color: rgba(255, 255, 255, 0.65);
    background: transparent;
    border: 1px solid rgba(255, 255, 255, 0.15);
    border-radius: 6px;
    cursor: pointer;
  }

  .sidebar-close:hover,
  .sidebar-close:focus-visible { color: #fff; border-color: rgba(255, 255, 255, 0.4); }
}

/* On a desktop the drawer does not exist, so neither does its furniture. */
@media (min-width: 1025px) {
  .admin-scrim,
  .sidebar-close { display: none; }
}

@media (prefers-reduced-motion: reduce) {
  .admin-shell .admin-sidebar,
  .admin-scrim { transition: none; }
}

/* ------------------------------------------------------ figures and filters
 *
 * The stat rows were written as fixed Tailwind column counts (grid-cols-3,
 * grid-cols-4), which on a 375px phone gives three unreadable slivers. One
 * auto-fitting track definition replaces every one of them and needs no
 * breakpoint: the cards claim a full row on a phone and sit side by side the
 * moment there is room.
 */
.stat-row {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(min(100%, 150px), 1fr));
  gap: 1rem;
}

@media (max-width: 640px) {
  /* Toolbars: the search box takes the row, the rest shares what is left. */
  .erpx-fields > .erpx-field,
  .erpx-fields > .erpx-field .select-wrap { min-width: 0; }

  .erpx-fields > .lf-search { flex: 1 1 100%; }

  .erpx-fields > .erpx-field { flex: 1 1 140px; }

  .erpx-fields .field { width: 100%; }

  .tx-bar { flex-direction: column; align-items: stretch; }

  .tx-filters { justify-content: flex-start; flex-wrap: wrap; }

  /* Instalment rows stack rather than squeeze onto one line. */
  .pay-left { margin-left: 0; flex-basis: 100%; }

  .pay-form > .erpx-field { flex: 1 1 100%; }

  .pay-form > button { width: 100%; }

  /* A two-up field pair inside a narrow card is two unusable inputs. */
  .card .grid.grid-cols-2 { grid-template-columns: minmax(0, 1fr); }
}

@media (max-width: 420px) {
  /* The topbar carries five controls; at this width they need the room the
     brand chip and separator were using. */
  .admin-topbar .h-16 { padding-left: 12px; padding-right: 12px; gap: 8px; }

  .admin-main { padding: 14px; }

  .admin-topbar h1 { font-size: 15px; }
}

/* --------------------------------------------------------- date fields
 *
 * iOS Safari gives input[type=date] an intrinsic width from its formatted
 * value and centres the text inside it. `width: 100%` does not hold against
 * that, so on a phone the field grows past the card that contains it. Turning
 * the native appearance off and pinning min/max width brings it back in line
 * with every other input, and the alignment stops it reading as a button.
 */
.field[type="date"],
.field[type="time"],
.field[type="datetime-local"],
.field[type="month"] {
  -webkit-appearance: none;
  appearance: none;
  min-width: 0;
  max-width: 100%;
  text-align: left;
  /* Native pickers are shorter than a text input; match the others. */
  min-height: 42px;
}

/* Safari draws the calendar glyph as a pseudo-element that is easy to lose. */
.field[type="date"]::-webkit-date-and-time-value { text-align: left; }
.field[type="date"]::-webkit-calendar-picker-indicator { opacity: 0.55; }

/* ------------------------------------------------------------- wide tables
 *
 * .table carries min-width: 640px, so on a phone the wrapper scrolls. That is
 * the right behaviour — a six-column ledger does not become readable by being
 * squeezed — but it needs to look deliberate: a hint that there is more to the
 * right, momentum scrolling, and an empty-state message that stays where the
 * eye is instead of sitting 300px off-screen.
 */
.table-wrap {
  -webkit-overflow-scrolling: touch;
  overscroll-behavior-x: contain;
}

/* A cell spanning the whole table (the "no data" row) is pinned to the left
   edge of the viewport rather than centred across a width nobody can see. */
.table td[colspan] {
  position: sticky;
  left: 0;
}

@media (max-width: 900px) {
  .table td[colspan] {
    text-align: left;
    /* Sticky needs a background of its own or the scrolled rows show through. */
    background: rgb(var(--c-surface));
    /* Hold the message inside the viewport, not the scroll width. */
    max-width: 100vw;
  }

  /* A fading right edge is the only honest signal that a table scrolls. */
  .table-wrap {
    background:
      linear-gradient(to right, rgb(var(--c-surface)) 30%, rgba(var(--c-surface), 0)) left / 24px 100% no-repeat,
      linear-gradient(to left, rgb(var(--c-surface)) 30%, rgba(var(--c-surface), 0)) right / 24px 100% no-repeat,
      radial-gradient(farthest-side at 0 50%, rgba(0, 0, 0, 0.13), transparent) left / 12px 100% no-repeat,
      radial-gradient(farthest-side at 100% 50%, rgba(0, 0, 0, 0.13), transparent) right / 12px 100% no-repeat,
      rgb(var(--c-surface));
    background-attachment: local, local, scroll, scroll, scroll;
  }
}

/* ----------------------------------------------------------- filter rows
 *
 * The stock screens carry a hand-rolled filter row with fixed Tailwind widths
 * (!w-52, !w-40). Those do not fit a 390px phone, so the row is marked and the
 * widths are handed back below.
 */
.filter-row {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 10px;
}

/* Widths live here rather than as fixed utilities in the markup, so one
   media query below can hand them all back on a narrow screen. */
.filter-row > .fr-search { flex: 1 1 210px; max-width: 320px; }

.filter-row > .select-wrap { flex: 0 1 180px; }

.filter-row > .select-wrap > .field { width: 100%; }

@media (max-width: 640px) {
  .filter-row > .field,
  .filter-row > .select-wrap,
  .filter-row > .select-wrap > .field {
    width: 100%;
    min-width: 0;
    max-width: none;
  }

  .filter-row > .fr-search,
  .filter-row > .select-wrap { flex: 1 1 100%; }

  .filter-row > label { flex: 1 1 100%; }

  .filter-row > .btn-ember,
  .filter-row > .btn-ghost,
  .filter-row > .btn-dark { flex: 1 1 auto; justify-content: center; }
}

/* -------------------------------------------------- notification tools */

.nt-tools { display: flex; flex-wrap: wrap; align-items: center; gap: 8px; }

/* Why the feed is emptier than the ledger suggests it should be. */
.nt-snooze {
  margin: 0 0 16px;
  padding: 9px 12px;
  border-left: 2px solid rgb(var(--c-ember));
  background: rgba(var(--c-ember), 0.06);
  font-size: 12.5px;
  line-height: 1.5;
  color: rgb(var(--c-muted));
}

/* ------------------------------------------------- invoice line: free text
 *
 * The first cell of an invoice line now holds two controls: the product, and
 * a description that stands in for one. A line with no product is how a debt
 * carried over from before the system is written down, so the description is
 * not a nicety — it is the only thing naming that line.
 */
.inv-item { display: grid; gap: 6px; min-width: 0; }

.inv-item .inv-desc { font-size: 13px; padding-top: 7px; padding-bottom: 7px; }

.inv-free-note {
  margin: 0 0 14px;
  padding: 9px 12px;
  border-left: 2px solid rgb(var(--c-line));
  background: rgb(var(--c-raised));
  font-size: 12.5px;
  line-height: 1.55;
  color: rgb(var(--c-muted));
}

/* ------------------------------------------------- how a sale is settled */

.settle-row { display: flex; flex-wrap: wrap; gap: 8px; }

.settle-opt {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  padding: 9px 14px;
  border: 1px solid rgb(var(--c-line));
  background: rgb(var(--c-raised));
  cursor: pointer;
  font-size: 13.5px;
  border-radius: 6px;
  transition: border-color .15s, background .15s, color .15s;
}

.settle-opt:hover { border-color: rgb(var(--c-ember)); }

/* :has lets the whole chip light up from the radio inside it; browsers
   without it still show a perfectly usable radio and label. */
.settle-opt:has(input:checked) {
  border-color: rgb(var(--c-ember));
  background: rgba(var(--c-ember), 0.08);
  color: rgb(var(--c-ember));
}

/* Credit is the one that leaves money outstanding, so it reads differently. */
.settle-opt.is-credit:has(input:checked) {
  border-color: rgb(var(--c-ink));
  background: rgba(var(--c-ink), 0.06);
  color: rgb(var(--c-ink));
}

.settle-opt input { accent-color: rgb(var(--c-ember)); width: 15px; height: 15px; }

.settle-amount { margin-top: 14px; }

.settle-note {
  margin: 8px 0 0;
  font-family: var(--font-mono);
  font-size: 11.5px;
  color: rgb(var(--c-muted));
}

/* --------------------------------------------------- debt status actions */

.debt-status {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 10px;
  padding-bottom: 12px;
  margin-bottom: 4px;
  border-bottom: 1px solid rgb(var(--c-line));
}

.debt-status form { display: flex; align-items: center; gap: 8px; }

.debt-status .select-wrap { min-width: 120px; }

.debt-status-note {
  margin-left: auto;
  font-family: var(--font-mono);
  font-size: 10.5px;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: rgb(var(--c-muted));
}

/* ---------------------------------------------------------- debt calls */

.call-form { display: flex; flex-wrap: wrap; align-items: center; gap: 7px; }

.call-form .field { padding: 6px 9px; font-size: 12.5px; min-width: 0; }

.call-form .select-wrap { flex: 0 0 auto; }

.call-form input[type="text"] { flex: 1 1 130px; }

.call-form input[type="date"] { flex: 0 0 auto; width: 145px; }

.call-line {
  margin: 8px 0 0;
  font-size: 12.5px;
  line-height: 1.5;
  color: rgb(var(--c-muted));
}

.call-log {
  list-style: none;
  margin: 14px 0 0;
  padding: 0;
  display: flex;
  flex-direction: column;
  gap: 5px;
  max-height: 240px;
  overflow-y: auto;
}

.call-entry {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  gap: 8px;
  padding: 6px 9px;
  border-left: 2px solid rgb(var(--c-line));
  background: rgb(var(--c-raised));
  font-size: 12px;
}

.call-date { font-family: var(--font-mono); color: rgb(var(--c-muted)); }

.call-outcome { font-weight: 600; }

.call-outcome.is-paid { color: #10b981; }

.call-outcome.is-promised { color: rgb(var(--c-ember)); }

.call-outcome.is-refused,
.call-outcome.is-no_answer { color: #dc2626; }

.call-amount { margin-left: auto; font-family: var(--font-mono); color: rgb(var(--c-muted)); }

.call-promise,
.call-note { flex: 1 1 100%; color: rgb(var(--c-muted)); }

@media (max-width: 640px) {
  .call-form > * { flex: 1 1 100%; }
  .call-form input[type="date"] { width: 100%; }
  .debt-status { flex-direction: column; align-items: stretch; }
  .debt-status form { flex-wrap: wrap; }
  .debt-status-note { margin-left: 0; }
  .settle-opt { flex: 1 1 45%; justify-content: center; }
}

/* What has already been received, on the invoice editor where the settlement
   radios do not belong. */
.settle-recorded {
  margin: 0 0 6px;
  font-size: 14px;
  color: rgb(var(--c-ink));
}

/* Payments already received, on the invoice editor: each one can be told
   apart — cash from transfer — long after it landed. */
.settle-sub {
  margin: 14px 0 8px;
  font-family: var(--font-mono);
  font-size: 10px;
  letter-spacing: 0.18em;
  text-transform: uppercase;
  color: rgb(var(--c-muted));
}

.settle-list {
  list-style: none;
  margin: 0 0 4px;
  padding: 0;
  display: flex;
  flex-direction: column;
  gap: 6px;
}

.settle-item {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 10px;
  padding: 7px 10px;
  border: 1px solid rgb(var(--c-line));
  background: rgb(var(--c-raised));
  font-size: 13px;
}

.settle-date { font-family: var(--font-mono); color: rgb(var(--c-muted)); min-width: 82px; }

.settle-sum { font-family: var(--font-mono); font-weight: 600; min-width: 96px; }

.settle-method { min-width: 150px; }

.settle-method .field { padding: 6px 9px; font-size: 12.5px; }

.settle-item-note { flex: 1 1 100%; font-size: 12px; color: rgb(var(--c-muted)); }

@media (max-width: 640px) {
  .settle-item > * { flex: 1 1 100%; }
  .settle-method { min-width: 0; }
}

/* ------------------------------------------------------ sortable headings */

.sort-head {
  display: inline-flex;
  align-items: center;
  gap: 5px;
  color: inherit;
  text-decoration: none;
  white-space: nowrap;
  transition: color .15s;
}

.sort-head:hover { color: rgb(var(--c-ember)); }

.sort-head.is-on { color: rgb(var(--c-ember)); }

.sort-arrow { flex: 0 0 auto; opacity: 0.85; }

/* Both arrows are always drawn so the heading keeps its width; the one that
   is in force is the only one at full strength. */
.sort-arrow path { opacity: 0.22; }

.sort-arrow path.is-lit { opacity: 1; }

th:has(> .sort-head) { padding-top: 0; padding-bottom: 0; }

th > .sort-head { padding: 0.75rem 0; }

/* ------------------------------------------- how an invoice was settled */

.pm-set { display: inline-flex; flex-wrap: wrap; gap: 4px; }

.pm-badge {
  display: inline-block;
  padding: 2px 7px;
  border: 1px solid currentColor;
  border-radius: 4px;
  font-family: var(--font-mono);
  font-size: 10px;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  white-space: nowrap;
  line-height: 1.5;
}

/* Money in, by how it arrived. Credit is the one that is still outstanding,
   so it is the only one that borrows the alert colour. */
.pm-cash   { color: #10b981; background: rgba(16, 185, 129, 0.08); }
.pm-bank   { color: #3b82f6; background: rgba(59, 130, 246, 0.08); }
.pm-card   { color: #8b5cf6; background: rgba(139, 92, 246, 0.08); }
.pm-other  { color: rgb(var(--c-muted)); background: rgba(120, 120, 120, 0.08); }
.pm-credit { color: rgb(var(--c-ember)); background: rgba(var(--c-ember), 0.1); }
.pm-void   { color: #dc2626; background: rgba(220, 38, 38, 0.08); }

/* ------------------------------------------------------- row action menu */

.row-actions { display: inline-flex; }

.row-actions-trigger { padding: 0; }

.row-actions.is-open .row-actions-trigger {
  border-color: rgb(var(--c-ember));
  color: rgb(var(--c-ember));
}

/* Fixed, and placed by script: the table around it scrolls sideways, so an
   absolutely positioned menu would be clipped at that edge. */
.row-actions-menu {
  position: fixed;
  z-index: 70;
  min-width: 176px;
  padding: 5px;
  border: 1px solid rgb(var(--c-line));
  background: rgb(var(--c-surface));
  border-radius: 8px;
  box-shadow: var(--shadow-lift);
}

.row-actions-menu form { display: block; margin: 0; }

.ra-item {
  display: flex;
  align-items: center;
  gap: 9px;
  width: 100%;
  padding: 8px 10px;
  border: 0;
  background: transparent;
  color: rgb(var(--c-ink));
  font-family: var(--font-body);
  font-size: 13.5px;
  text-align: left;
  text-decoration: none;
  border-radius: 5px;
  cursor: pointer;
  transition: background .12s, color .12s;
}

.ra-item:hover,
.ra-item:focus-visible {
  background: rgba(var(--c-ember), 0.1);
  color: rgb(var(--c-ember));
}

.ra-item.is-danger:hover,
.ra-item.is-danger:focus-visible { background: rgba(220, 38, 38, 0.1); color: #dc2626; }

.ra-icon {
  flex: 0 0 auto;
  width: 14px;
  height: 14px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  color: rgb(var(--c-muted));
}

.ra-item:hover .ra-icon,
.ra-item:focus-visible .ra-icon { color: inherit; }

/* The actions column is now one button wide. */
.table th:last-child,
.table td:last-child { white-space: nowrap; }

/* ------------------------------------------------- table cells that must
 *                                                    stay on one line
 *
 * A phone number broken across two lines is not a phone number, and
 * "60 177.00 ₼" split before the manat sign reads as two figures. Both are
 * single tokens to a reader even though they contain spaces — the thousands
 * separator and the space before the currency are exactly where a browser
 * chooses to break.
 *
 * Only the structured columns are pinned. Free text — a company name, a note,
 * an address — still wraps, because forcing those onto one line would push
 * the table sideways for no gain.
 */
/*
 * Cells hold one line by default. A name split as "Zamin / Sədərək", a phone
 * as "070 966 85 / 00" or a figure as "60 177.00 / ₼" all read as two things
 * when they are one, and each wrapped cell makes the whole row taller.
 *
 * The table simply grows instead: a cell that cannot wrap contributes its full
 * width to the table's minimum, a table never shrinks below that, and the
 * wrapper around it already scrolls — which is how a table wider than the
 * screen has always been handled here.
 */
.table td,
.table th { white-space: nowrap; }

/*
 * Except where the content really is prose — a delivery address, a knowledge
 * base answer, a payable's description, an invoice line's wording. Forcing
 * those onto one line would drag the table out to a width nobody can use, so
 * they keep wrapping and are given room to do it in.
 */
.table td.cell-wrap,
.table th.cell-wrap {
  white-space: normal;
  min-width: 200px;
}

/* Status and settlement chips are labels, not sentences. */
.table .badge-grey,
.table .badge-red,
.table .badge-green,
.table .badge-blue,
.table .badge-ember,
.table .pm-badge,
.table .rt-badge { white-space: nowrap; }

/* No width rule is needed beyond this. A cell that cannot wrap contributes its
   full width to the table's minimum, and a table never shrinks below that — so
   it grows past the container on its own and the wrapper scrolls, which is
   already how a table wider than the screen is handled here. Forcing
   `min-width: max-content` instead would drag prose columns onto one line too
   and make every table needlessly wide. */

/* ------------------------------------------------------------- dialogs
 *
 * The shared .modal-box is 28rem wide, which suits a yes/no question. A form
 * needs more room and a way to scroll when the screen is short, so the wider
 * variant below carries its own head, body and foot.
 */
.modal-box-lg {
  max-width: 34rem;
  padding: 0;
  display: flex;
  flex-direction: column;
  max-height: min(88vh, 780px);
}

.modal-head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
  padding: 16px 20px;
  border-bottom: 1px solid rgb(var(--c-line));
}

.modal-title {
  margin: 0;
  font-family: var(--font-display);
  font-size: 15px;
  font-weight: 600;
  letter-spacing: 0.04em;
  text-transform: uppercase;
}

.modal-close {
  flex: 0 0 auto;
  width: 32px;
  height: 32px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  border: 1px solid rgb(var(--c-line));
  background: transparent;
  color: rgb(var(--c-muted));
  border-radius: 6px;
  cursor: pointer;
  transition: color .15s, border-color .15s;
}

.modal-close:hover { color: rgb(var(--c-ink)); border-color: rgb(var(--c-ink)); }

/*
 * The form scrolls, the heading and the buttons stay put.
 *
 * `min-height: 0` is what makes that true. A flex child defaults to
 * min-height: auto, which refuses to shrink below its content — so the box
 * would keep its capped height while the form pushed the footer out through
 * the bottom, and the save button would sit off the screen on a phone.
 */
.modal-head,
.modal-foot { flex: 0 0 auto; }

/*
 * The dialog's body and footer sit inside the <form>, not directly inside the
 * box — so the form is the column that has to stretch and be allowed to
 * shrink. Without this the flex rules below apply to nothing and the footer
 * hangs out through the bottom of a capped box.
 */
.modal-box-lg > form {
  display: flex;
  flex-direction: column;
  flex: 1 1 auto;
  min-height: 0;
}

.modal-body {
  flex: 1 1 auto;
  min-height: 0;
  padding: 18px 20px;
  overflow-y: auto;
  overscroll-behavior: contain;
  -webkit-overflow-scrolling: touch;
}

.modal-foot {
  display: flex;
  align-items: center;
  justify-content: flex-end;
  gap: 10px;
  padding: 14px 20px;
  border-top: 1px solid rgb(var(--c-line));
}

/* The page behind a dialog must not scroll with it. */
body.modal-locked { overflow: hidden; }

@media (max-width: 640px) {
  .modal-box-lg { max-height: 92vh; }
  .modal-foot > * { flex: 1 1 auto; justify-content: center; }
}

/* ------------------------------------------------------------ bundles
 *
 * Two quiet lines that answer the same question in two places: what does a
 * bundle of this actually come to, and what will this entry put in the ledger.
 * Both are derived as the operator types, so they read as a note rather than
 * as a field — no border, no label, just the arithmetic spelled out.
 */
.pack-summary,
.pack-preview {
  margin-top: 6px;
  font-family: var(--font-mono);
  font-size: 11.5px;
  line-height: 1.5;
  color: rgb(var(--c-muted));
}

/* The conversion preview is the figure that will be saved, so it is stated in
   the reading colour rather than the muted one. */
.pack-preview {
  padding: 7px 10px;
  border-radius: 6px;
  background: rgb(var(--c-ink) / 0.04);
  color: rgb(var(--c-ink));
}

/* A conversion that cannot be done is a refusal, not a figure. */
.pack-preview.is-bad {
  background: rgba(220, 38, 38, 0.07);
  color: rgb(185, 28, 28);
}

/* The stock figure read back in the units the yard counts in, under the one
   the ledger keeps. Muted and smaller: it is the same holding said again, not
   a second number to add up. */
.stock-readback {
  display: block;
  margin-top: 2px;
  font-size: 10.5px;
  font-weight: 400;
  color: rgb(var(--c-muted));
}

/* ------------------------------------------------------------ suppliers
 *
 * Two debts running in opposite directions. A bare figure cannot say which
 * way the money goes — 500 owed to a firm and 500 owed by them are the same
 * number and opposite facts — so the amount and the direction travel together.
 */
.net-cell {
  display: inline-flex;
  flex-direction: column;
  align-items: flex-end;
  gap: 3px;
}

.net-offset {
  font-family: var(--font-mono);
  font-size: 10px;
  color: rgb(var(--c-muted));
}

/* What the two sides cancel out, spelled out on the supplier's own page. It
   is a statement about money that does not have to move, so it reads as a
   note rather than as another figure to add up. */
.offset-note {
  padding: 14px 16px;
  border-left: 3px solid rgb(var(--c-ember));
  background: rgb(var(--c-ember) / 0.06);
  border-radius: 0 6px 6px 0;
}

.offset-title {
  margin: 0 0 4px;
  font-family: var(--font-mono);
  font-size: 10px;
  text-transform: uppercase;
  letter-spacing: 0.16em;
  color: rgb(var(--c-ember));
}

.offset-body {
  margin: 0;
  font-size: 13px;
  line-height: 1.6;
  color: rgb(var(--c-ink));
}

/* ------------------------------------------------------ warehouse board
 *
 * A day is a discrete count of what was booked, not a reading along a
 * continuum, so the chart is bars rather than a line. Each day carries both
 * directions side by side: in and out are the pair anybody reads together.
 */
.wh-ranges {
  display: flex;
  flex-wrap: wrap;
  gap: 6px;
}

.wh-range {
  font-family: var(--font-mono);
  font-size: 11px;
  text-transform: uppercase;
  letter-spacing: 0.14em;
  padding: 6px 12px;
  border: 1px solid rgb(var(--c-line));
  color: rgb(var(--c-muted));
  white-space: nowrap;
}

.wh-range:hover { border-color: rgb(var(--c-ember)); color: rgb(var(--c-ember)); }

.wh-range.is-on {
  background: rgb(var(--c-ember));
  border-color: rgb(var(--c-ember));
  color: #fff;
}

.wh-chart {
  display: flex;
  align-items: flex-end;
  gap: 6px;
  height: 160px;
  padding: 12px 4px 0;
  border: 1px solid rgb(var(--c-line));
  overflow-x: auto;
}

.wh-day {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 6px;
  flex: 1 0 34px;
  height: 100%;
}

/* The bars share the column and are anchored to the floor, so their heights
   are comparable at a glance without any axis to read. */
.wh-bars {
  display: flex;
  align-items: flex-end;
  justify-content: center;
  gap: 2px;
  width: 100%;
  flex: 1;
  min-height: 0;
}

.wh-bar {
  display: block;
  width: 9px;
  border-radius: 2px 2px 0 0;
  min-height: 2px;
}

.wh-bar.is-in { background: rgb(16 185 129); }
.wh-bar.is-out { background: rgb(var(--c-ember)); }
.wh-bar.is-quiet { width: 20px; background: rgb(var(--c-line)); }

.wh-day-label {
  font-family: var(--font-mono);
  font-size: 9.5px;
  color: rgb(var(--c-muted));
  white-space: nowrap;
}

.wh-legend {
  margin-top: 8px;
  font-family: var(--font-mono);
  font-size: 11px;
  color: rgb(var(--c-muted));
}

.wh-key {
  display: inline-block;
  width: 10px;
  height: 10px;
  border-radius: 2px;
  vertical-align: -1px;
  margin-right: 4px;
}

.wh-key.is-in { background: rgb(16 185 129); }
.wh-key.is-out { background: rgb(var(--c-ember)); }

/* Two date fields on the filter row stay legible on a phone rather than
   collapsing to a width that hides half the date. */
.fr-date { min-width: 140px; flex: 0 1 160px; }

/* A field the form stopped on. The dialog says what is wrong; this says
   where — the two together are what makes the message actionable. */
.field.is-bad,
.inv-desc.is-bad {
  border-color: rgb(220, 38, 38);
  background: rgba(220, 38, 38, 0.04);
}

.field.is-bad:focus,
.inv-desc.is-bad:focus {
  outline: 2px solid rgba(220, 38, 38, 0.4);
  outline-offset: 1px;
}

/* Today, or a date of one's own. Two radios rather than a permanently open
   calendar: today is the answer nearly every time, and a date field that is
   always there invites second-guessing a figure that was never in doubt. */
.date-choice {
  display: flex;
  flex-wrap: wrap;
  gap: 14px;
  margin-top: 2px;
}

.date-opt {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  font-size: 13px;
  cursor: pointer;
}

.date-opt input {
  width: 15px;
  height: 15px;
  accent-color: rgb(var(--c-ember));
}

/* ------------------------------------------------- stock standing dot
 *
 * A badge said the same words on every low row and pushed the name sideways
 * on all of them. A dot says it in space the row already has, and a page of
 * them can be read straight down the left edge.
 *
 * Red pulses faster than green on purpose: the one that needs somebody to do
 * something is the one that should catch the eye first.
 */
.stock-dot {
  display: inline-block;
  width: 8px;
  height: 8px;
  margin-right: 8px;
  border-radius: 50%;
  vertical-align: 1px;
  flex: none;
}

.stock-dot.is-low {
  background: rgb(220, 38, 38);
  animation: stock-pulse-low 1.1s ease-in-out infinite;
}

.stock-dot.is-ok {
  background: rgb(16, 185, 129);
  animation: stock-pulse-ok 2.4s ease-in-out infinite;
}

/* Nothing is being watched, so neither colour would be honest — and a still
   dot is the one thing that reads as "no answer here". */
.stock-dot.is-off {
  background: rgb(var(--c-line));
  box-shadow: inset 0 0 0 1px rgb(var(--c-muted) / 0.4);
}

@keyframes stock-pulse-low {
  0%, 100% { opacity: 1; box-shadow: 0 0 0 0 rgba(220, 38, 38, 0.45); }
  50%      { opacity: 0.4; box-shadow: 0 0 0 4px rgba(220, 38, 38, 0); }
}

@keyframes stock-pulse-ok {
  0%, 100% { opacity: 1; box-shadow: 0 0 0 0 rgba(16, 185, 129, 0.4); }
  50%      { opacity: 0.45; box-shadow: 0 0 0 4px rgba(16, 185, 129, 0); }
}

/* A blinking dot is a distraction to anyone who has asked for less motion —
   the colour still carries the whole message without it. */
@media (prefers-reduced-motion: reduce) {
  .stock-dot { animation: none; }
}

/* The name itself carries the warning now, so it does not need a label
   beside it saying what its colour already says. */
.prod-low {
  color: rgb(185, 28, 28);
  font-weight: 600;
}

.prod-low:hover { color: rgb(var(--c-ember)); }

/* Nothing on the shelf. Darker than "low" and still: a rack with none left is
   not a warning that stock is falling, it is the fall already finished. */
/*
 * Nothing on the shelf: orange, and still.
 *
 * Deliberately not the ember accent — that colour means "this is a link or a
 * button" everywhere else on the page, and a product name wearing it would
 * read as something to click rather than something to reorder.
 *
 * It does not pulse. Red pulsing means stock is falling and there is time to
 * act; an empty rack is that fall already finished, and a second blinking
 * colour would only compete with the one that still needs attention.
 */
.stock-dot.is-empty {
  background: rgb(234, 88, 12);
  box-shadow: inset 0 0 0 1px rgba(154, 52, 18, 0.6);
}

/* The standing column holds one dot, so it takes only the width of its own
   heading and gives the rest back to the figures. */
.table th.col-dot,
.table td.col-dot {
  width: 1%;
  text-align: center;
  padding-left: 10px;
  padding-right: 10px;
}

.table td.col-dot .stock-dot { margin-right: 0; }

/* A product with none left is named in orange — the same colour as its dot,
   so the row reads as one statement rather than two signals. */
.prod-empty {
  color: rgb(194, 65, 12);
  font-weight: 600;
}

.prod-empty:hover { color: rgb(var(--c-ember)); }

/* And its row is tinted to match, rather than sharing the red of the ones
   that are merely running low. */
.row-empty > td { background: rgba(234, 88, 12, 0.05); }

/* The figure itself too: nought is the whole point of the row. */
.qty-empty { color: rgb(194, 65, 12); font-weight: 500; }

/* ------------------------------------------------- price, edited in place
 *
 * The cell has to read as a figure first and a control second: a list of
 * prices that looks like a row of form fields is harder to scan than one that
 * looks like prices, and scanning is what the page is mostly for.
 */
.price-cell {
  display: inline-flex;
  align-items: center;
  gap: 5px;
  padding: 2px 6px;
  margin: -2px -6px;
  border: 1px solid transparent;
  border-radius: 5px;
  background: none;
  font: inherit;
  color: inherit;
  cursor: text;
}

.price-cell:hover {
  border-color: rgb(var(--c-line));
  background: rgb(var(--c-raised));
}

.price-cell:focus-visible {
  outline: 2px solid rgb(var(--c-ember));
  outline-offset: 1px;
}

/* The pencil only appears on approach — otherwise every row carries an icon
   that says the same thing about every row. */
.price-pen {
  opacity: 0;
  color: rgb(var(--c-muted));
  flex: none;
}
.price-cell:hover .price-pen,
.price-cell:focus-visible .price-pen { opacity: 1; }

.inline-input {
  width: 88px;
  padding: 2px 6px;
  border: 1px solid rgb(var(--c-ember));
  border-radius: 5px;
  font-family: var(--font-mono);
  font-size: 13px;
  font-weight: 700;
  text-align: right;
  color: rgb(var(--c-ink));
  background: #fff;
}

.inline-input:focus { outline: 2px solid rgb(var(--c-ember) / 0.35); outline-offset: 1px; }
.inline-input.is-bad { border-color: rgb(220, 38, 38); background: rgba(220, 38, 38, 0.05); }
.inline-input:disabled { opacity: .6; }

/* Saved. Long enough to notice, short enough not to linger over the next edit. */
.is-saved .price-cell {
  border-color: rgb(16 185 129 / 0.55);
  background: rgb(16 185 129 / 0.10);
  transition: background .5s, border-color .5s;
}



/* ---------------------------------------------------------------- image tile X
 *
 * A thumbnail is the thing you click to look at, so the whole picture must not
 * be a delete target. The X sits in the corner, asks before it marks, and then
 * turns into an undo arrow -- the same button, because the mark is one state to
 * toggle rather than two separate controls.
 */
/* the image, not the tile, is what must not carry an inline baseline gap --
   zeroing it on the tile itself crushed the badge and banner text inside it */
.img-tile > img { display: block; }

.img-x {
  position: absolute;
  top: 4px;
  right: 4px;
  width: 22px;
  height: 22px;
  display: grid;
  place-items: center;
  padding: 0;
  border: 1px solid rgba(255, 255, 255, 0.55);
  background: rgba(23, 24, 27, 0.72);
  color: #fff;
  cursor: pointer;
  transition: background-color 0.15s, border-color 0.15s, transform 0.15s;
}

.img-x svg {
  width: 13px;
  height: 13px;
  fill: none;
  stroke: currentColor;
  stroke-width: 2.4;
  stroke-linecap: round;
  stroke-linejoin: round;
}

.img-x:hover { background: #ef4444; border-color: #ef4444; transform: scale(1.08); }
.img-x:focus-visible { outline: 2px solid rgb(var(--c-ember)); outline-offset: 2px; }

.img-x .img-x-undo { display: none; }

/* once marked, the same button offers the way back */
.img-tile input:checked ~ .img-x { background: rgba(23, 24, 27, 0.85); border-color: rgba(255, 255, 255, 0.8); }
.img-tile input:checked ~ .img-x:hover { background: rgb(var(--c-ember)); border-color: rgb(var(--c-ember)); }
.img-tile input:checked ~ .img-x .img-x-cross { display: none; }
.img-tile input:checked ~ .img-x .img-x-undo { display: inline; }

@media (prefers-reduced-motion: reduce) {
  .img-x { transition: none; }
  .img-x:hover { transform: none; }
}

/* ------------------------------------------------------- peer-checked fallback
 *
 * app.css is a frozen Tailwind build and none of the `peer-checked:` variants
 * the admin views ask for ever made it into it, so ticking a checkbox changed
 * nothing on screen: an image marked for removal looked exactly like one that
 * was staying, and the chosen illustration was never highlighted. These are the
 * rules that build would have produced, written by hand.
 */
.peer:checked ~ .peer-checked\:opacity-0 { opacity: 0; }
.peer:checked ~ .peer-checked\:opacity-30 { opacity: 0.3; }
.peer:checked ~ .peer-checked\:opacity-100 { opacity: 1; }

.peer:checked ~ .peer-checked\:border-red-500 { border-color: #ef4444; }
.peer:checked ~ .peer-checked\:border-ember { border-color: rgb(var(--c-ember)); }
.peer:checked ~ .peer-checked\:text-ember { color: rgb(var(--c-ember)); }

/* one ring width, whichever colour the same element also asks for */
.peer:checked ~ .peer-checked\:ring-1 { box-shadow: 0 0 0 1px var(--ring-c, currentColor); }
.peer:checked ~ .peer-checked\:ring-red-500 { --ring-c: #ef4444; }
.peer:checked ~ .peer-checked\:ring-ember { --ring-c: rgb(var(--c-ember)); }

/* Three more the frozen build left out, all three wanted by the thumbnail tile
 * above and by nothing else in the app: the ERP's red row tint is the separate
 * `!bg-red-500/5`, which app.css does carry. Without these the lead badge lost
 * its corner and sat under the picture, and the removal banner drew white text
 * on no background at all. */
.top-1 { top: 0.25rem; }
.left-1 { left: 0.25rem; }
.bg-red-500 { background-color: #ef4444; }

/* --------------------------------------------------- product image fills its panel
 *
 * The picture used to stop at 4:3 and leave a slab of empty ground beneath it,
 * because the variants table beside it is always taller. Now the picture takes
 * whatever height is left once the drawing button has had its strip; on narrow
 * screens there is nothing to fill, so the 4:3 shape stays.
 */
.min-h-0 { min-height: 0px; }

@media (min-width: 1024px) {
  .lg\:aspect-auto { aspect-ratio: auto; }
}

/* ------------------------------------------------------------ crew share bar
 * A thin bar under each name on the output table, so the pecking order for the
 * period reads at a glance instead of by comparing five-digit numbers.
 */
.crew-share {
  display: block;
  height: 3px;
  margin-top: 5px;
  max-width: 160px;
  background: rgb(var(--c-ember) / 0.14);
}

.crew-share > span {
  display: block;
  height: 100%;
  background: rgb(var(--c-ember));
}

/* ------------------------------------------------------------ hidden, meant it
 *
 * Preflight hides the attribute with `[hidden]:where(:not([hidden=until-found]))`,
 * and :where() contributes nothing to specificity -- so the rule weighs the same
 * as a single class and loses to any display utility sitting on the same tag.
 * A div that is both `.grid` and `hidden` therefore stays on screen. Repeating
 * the attribute costs nothing, reads as what it is, and outweighs one class
 * without reaching for !important.
 *
 * This replaced five per-class patches -- .tx-key, .rt-invoice, .calc-panel,
 * .calc-mem-flag and .bulk-bar each carried their own copy. If something with
 * `hidden` shows anyway, a sixth patch is not the answer: look for a rule
 * heavier than two classes setting display on it.
 */
[hidden][hidden] { display: none; }

/* ------------------------------------------------------- picking one crew line
 *
 * The dropdown sits inside a table row, so the full-height form control the
 * rest of the panel uses would push every row apart. When a line is picked the
 * three columns that still describe the whole period fade back rather than
 * disappear: they are still true, just no longer the answer to what was asked.
 */
.crew-pick-cell { min-width: 168px; }

.crew-pick {
  padding: 5px 26px 5px 9px;
  font-size: 12.5px;
  line-height: 1.35;
  max-width: 220px;
}

tr.is-narrowed [data-crew-whole] { opacity: 0.32; }
tr.is-narrowed [data-crew-cell] { color: rgb(var(--c-ember)); }

/* ------------------------------------------------------- searching a long list
 * The box sits above the select it filters, close enough to read as one control.
 */
.select-search { margin-bottom: 6px; }

.select-search-empty {
  margin: 0 0 6px;
  font-family: var(--font-mono);
  font-size: 11.5px;
  color: rgb(var(--c-muted));
}


/* ------------------------------------------------------------- audit pickers
 * Each button says what it checks and, underneath, what that means -- the names
 * alone ("Returns", "Purchases") do not tell a new operator which one answers
 * the question they actually have.
 */
.audit-picks {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(min(100%, 190px), 1fr));
  gap: 10px;
}

.audit-pick {
  display: block;
  padding: 13px 15px;
  border: 1px solid rgb(var(--c-line));
  background: rgb(var(--c-surface));
  border-radius: 10px;
  text-decoration: none;
  transition: border-color 0.15s, transform 0.15s, box-shadow 0.15s;
}

.audit-pick:hover {
  border-color: rgb(var(--c-ember));
  transform: translateY(-1px);
  box-shadow: var(--shadow-card);
}

.audit-pick.is-on {
  border-color: rgb(var(--c-ember));
  box-shadow: inset 3px 0 0 rgb(var(--c-ember));
}

.audit-pick-name {
  display: block;
  font-family: var(--font-display);
  font-weight: 600;
  font-size: 14.5px;
  color: rgb(var(--c-ink));
}

.audit-pick-note {
  display: block;
  margin-top: 3px;
  font-family: var(--font-mono);
  font-size: 11px;
  line-height: 1.4;
  color: rgb(var(--c-muted));
}

/* ------------------------------------------------------------- a fifth badge
 * Stock movements come in five kinds and the palette had four colours that
 * already mean something elsewhere: green for good, red for late, ember for
 * ours, blue for incoming. A return is none of those -- it is not a fault and
 * not a sale -- so it gets its own.
 */
.badge-plum {
  display: inline-flex;
  align-items: center;
  gap: 0.375rem;
  padding: 0.25rem 0.625rem;
  border: 1px solid rgba(147, 51, 234, 0.4);
  border-radius: 0.25rem;
  background: rgba(147, 51, 234, 0.1);
  color: rgb(147 51 234);
  font-family: var(--font-mono);
  font-size: 11px;
  text-transform: uppercase;
  letter-spacing: 0.05em;
}

/* The root always carries an explicit theme here, so one selector is enough. */
[data-theme="dark"] .badge-plum { color: rgb(192 132 252); }

/* ------------------------------------------------ what the customer said
 * A dropdown inside a table row, so it has to sit on one line and not push the
 * rows apart. The sentence under it is the saved answer, coloured by whether it
 * was a promise, a refusal, or something in between.
 */
.call-cell { min-width: 190px; }

.call-pick,
.call-note {
  /* Stacked, not side by side: the note appears under the dropdown it belongs
     to, and a wide answer widens nothing. */
  display: block;
  width: 100%;
  max-width: 230px;
  padding: 5px 26px 5px 9px;
  font-size: 12.5px;
  line-height: 1.35;
}

.call-note { padding-right: 9px; margin-top: 5px; }

.call-said {
  display: block;
  margin-top: 5px;
  font-family: var(--font-mono);
  font-size: 10.5px;
  line-height: 1.4;
  max-width: 230px;
}

.call-said.is-unset { color: rgb(var(--c-muted)); }
.call-said.is-good  { color: rgb(5 150 105); }
.call-said.is-bad   { color: rgb(239 68 68); }
.call-said.is-noted { color: rgb(var(--c-ink) / 0.75); }

[data-theme="dark"] .call-said.is-good { color: rgb(52 211 153); }

/* Saving, saved, and failed — the collector is on the phone and needs to know
   the answer landed without looking away from the row. */
.call-cell.is-saving { opacity: 0.55; }
.call-cell.is-saved .call-said { animation: call-flash 1.1s ease-out; }
.call-cell.is-bad-save .call-said { color: rgb(239 68 68); }

@keyframes call-flash {
  0%   { background: rgb(var(--c-ember) / 0.28); }
  100% { background: transparent; }
}

@media (prefers-reduced-motion: reduce) {
  .call-cell.is-saved .call-said { animation: none; }
}

/* --------------------------------------------------------- returns on a bill
 * The plum of the return badge, so a deduction on the invoice and the movement
 * it created in the warehouse are recognisably the same event.
 */
.text-plum { color: rgb(147 51 234); }
[data-theme="dark"] .text-plum { color: rgb(192 132 252); }

.iret-qty {
  width: 92px;
  padding: 5px 9px;
  font-size: 12.5px;
  text-align: right;
  display: inline-block;
}

/* ------------------------------------------------------------------- tabstrip
 * Icon over label, the way a phone's bottom bar reads: the glyph is what the
 * eye lands on and the word only confirms it, which is faster than reading a
 * row of similar-length words. The strip scrolls sideways rather than wrapping
 * -- a wrapped strip shifts the page every time a tab is added.
 */
.tabstrip {
  display: flex;
  gap: 4px;
  padding: 6px;
  margin-bottom: 1.25rem;
  overflow-x: auto;
  border: 1px solid rgb(var(--c-line));
  border-radius: 12px;
  background: rgb(var(--c-surface));
  box-shadow: var(--shadow-card);
  scrollbar-width: none;
}

.tabstrip::-webkit-scrollbar { display: none; }

.tabstrip-tab {
  flex: 1 0 auto;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 5px;
  min-width: 92px;
  padding: 11px 14px 9px;
  border-radius: 9px;
  font-family: var(--font-mono);
  font-size: 10.5px;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  white-space: nowrap;
  color: rgb(var(--c-muted));
  text-decoration: none;
  transition: color 0.16s, background-color 0.16s, transform 0.16s;
}

.tabstrip-icon {
  display: block;
  line-height: 0;
  opacity: 0.75;
  transition: opacity 0.16s, transform 0.16s;
}

.tabstrip-icon .glyph svg { width: 19px; height: 19px; }

.tabstrip-tab:hover {
  color: rgb(var(--c-ink));
  background: rgb(var(--c-raised));
}

.tabstrip-tab:hover .tabstrip-icon { opacity: 1; }

/* The one you are on: filled, not merely underlined -- an underline is easy to
   lose in a row of nine. */
.tabstrip-tab.is-on {
  color: rgb(var(--c-ember));
  background: rgb(var(--c-ember) / 0.11);
  box-shadow: inset 0 0 0 1px rgb(var(--c-ember) / 0.28);
}

.tabstrip-tab.is-on .tabstrip-icon {
  opacity: 1;
  transform: translateY(-1px);
}

.tabstrip-count {
  padding: 0 6px;
  border-radius: 999px;
  background: rgb(var(--c-raised));
  font-size: 9.5px;
  line-height: 15px;
  letter-spacing: 0;
  color: rgb(var(--c-muted));
}

.tabstrip-tab.is-on .tabstrip-count {
  background: rgb(var(--c-ember) / 0.18);
  color: rgb(var(--c-ember));
}

@media (max-width: 640px) {
  .tabstrip-tab { min-width: 74px; padding: 9px 8px 7px; font-size: 9.5px; }
  .tabstrip-icon .glyph svg { width: 17px; height: 17px; }
}

@media (prefers-reduced-motion: reduce) {
  .tabstrip-tab,
  .tabstrip-icon { transition: none; }
  .tabstrip-tab.is-on .tabstrip-icon { transform: none; }
}

/* ------------------------------------------------------------------ kpi cards
 * The eight figures the panel opens on. The glyph sits in a tinted disc on the
 * left rather than floating above the number: a row of eight cards is scanned
 * along, not down, and the eye follows the colour.
 */
.kpi-row {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(min(100%, 232px), 1fr));
  gap: 14px;
}

.kpi {
  display: flex;
  align-items: center;
  gap: 13px;
  padding: 16px 18px;
  border: 1px solid rgb(var(--c-line));
  border-radius: 12px;
  background: rgb(var(--c-surface));
  box-shadow: var(--shadow-card);
  text-decoration: none;
  transition: transform 0.16s, box-shadow 0.16s, border-color 0.16s;
}

.kpi.is-link:hover {
  transform: translateY(-2px);
  border-color: rgb(var(--c-ember) / 0.45);
  box-shadow: 0 10px 26px -14px rgb(var(--c-ink) / 0.35);
}

.kpi-icon {
  flex: 0 0 auto;
  display: grid;
  place-items: center;
  width: 40px;
  height: 40px;
  border-radius: 11px;
  line-height: 0;
}

.kpi-icon .glyph svg { width: 19px; height: 19px; }

.kpi-body { min-width: 0; }

.kpi-label {
  display: block;
  font-family: var(--font-mono);
  font-size: 10px;
  letter-spacing: 0.17em;
  text-transform: uppercase;
  color: rgb(var(--c-muted));
}

.kpi-value {
  display: block;
  margin-top: 3px;
  font-family: var(--font-display);
  font-size: 22px;
  font-weight: 600;
  line-height: 1.15;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  color: rgb(var(--c-ink));
}

/* Tone says what the figure means, not what it is about. */
.kpi.is-warn .kpi-icon { background: rgb(var(--c-ember) / 0.13); color: rgb(var(--c-ember)); }
.kpi.is-warn .kpi-value { color: rgb(var(--c-ember)); }

.kpi.is-bad .kpi-icon  { background: rgba(239, 68, 68, 0.13); color: rgb(239 68 68); }
.kpi.is-bad .kpi-value { color: rgb(239 68 68); }

.kpi.is-good .kpi-icon { background: rgba(16, 185, 129, 0.13); color: rgb(5 150 105); }

.kpi.is-calm .kpi-icon { background: rgb(var(--c-raised)); color: rgb(var(--c-muted)); }

[data-theme="dark"] .kpi.is-good .kpi-icon { color: rgb(52 211 153); }

@media (prefers-reduced-motion: reduce) {
  .kpi { transition: none; }
  .kpi.is-link:hover { transform: none; }
}

/* ------------------------------------------------------- a line running back
 * A return line on an invoice, tinted so a mixed document can be read at a
 * glance rather than by checking every dropdown.
 */
.inv-row.is-return { background: rgb(147 51 234 / 0.05); border-radius: 8px; }
.inv-row.is-return .inv-line-total { color: rgb(147 51 234); }
[data-theme="dark"] .inv-row.is-return .inv-line-total { color: rgb(192 132 252); }

.inv-kind-wrap .inv-kind { padding-right: 24px; font-size: 12.5px; }

/* An invoice line, laid out: what was sold, which way it ran, and the numbers.
   app.css is a frozen build with no seven-column template in it, so the widths
   live here and the header and the rows both name the same class -- otherwise
   the two drift apart the next time a column is added. */
@media (min-width: 640px) {
  .inv-grid {
    grid-template-columns: 1fr 104px 100px 120px 100px 110px 40px;
  }
}

/* ------------------------------------------------------- customer statement
 * The account on one page. Two totals sit here that must never be mistaken for
 * one another, so the collectible balance is the one given weight and the
 * combined figure carries its arithmetic underneath it in words.
 */
.stmt-cards {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(min(100%, 170px), 1fr));
  gap: 12px;
}

.stmt-card {
  padding: 14px 16px;
  border: 1px solid rgb(var(--c-line));
  border-radius: 12px;
  background: rgb(var(--c-surface));
}

.stmt-card.is-key {
  border-color: rgb(var(--c-ember) / 0.45);
  background: rgb(var(--c-ember) / 0.05);
}

.stmt-card-label {
  margin: 0 0 6px;
  font-family: var(--font-mono);
  font-size: 10.5px;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: rgb(var(--c-muted));
}

.stmt-card-value {
  margin: 0;
  font-family: var(--font-mono);
  font-size: 19px;
  font-weight: 600;
}

.stmt-card-note {
  margin: 5px 0 0;
  font-size: 11px;
  line-height: 1.35;
  color: rgb(var(--c-muted));
}

.stmt-opening td,
.stmt-closing td {
  background: rgb(var(--c-muted) / 0.06);
  font-family: var(--font-mono);
  font-size: 12px;
}

.stmt-head { margin-bottom: 18px; }
.stmt-head h1 { margin: 0 0 4px; font-size: 22px; }
.stmt-head p { margin: 0; font-family: var(--font-mono); font-size: 12px; color: rgb(var(--c-muted)); }

/* A row's own filter box sits directly above the list it narrows. */
.inv-search { margin-bottom: 6px; }

@media print {
  .stmt-card { break-inside: avoid; }
  .stmt-table { font-size: 11px; }
}

/* ------------------------------------------------------------- usage guide
 * <details> carries the open/closed state, so the only work here is making it
 * look like a panel component instead of a browser default. The marker is
 * removed on both engines and replaced with a chevron that turns.
 */
.guide-list { display: grid; gap: 10px; max-width: 900px; }

.guide-sec {
  border: 1px solid rgb(var(--c-line));
  border-radius: 12px;
  background: rgb(var(--c-surface));
  overflow: hidden;
}

.guide-sec[open] { border-color: rgb(var(--c-ember) / 0.4); }

.guide-sum {
  display: flex;
  align-items: center;
  gap: 12px;
  padding: 14px 16px;
  cursor: pointer;
  list-style: none;
  user-select: none;
}

.guide-sum::-webkit-details-marker { display: none; }
.guide-sum:hover { background: rgb(var(--c-muted) / 0.05); }

.guide-ico {
  display: grid;
  place-items: center;
  width: 34px;
  height: 34px;
  flex: none;
  border-radius: 9px;
  background: rgb(var(--c-ember) / 0.1);
  color: rgb(var(--c-ember));
}

.guide-ico svg { width: 17px; height: 17px; }

.guide-name { font-size: 15px; font-weight: 600; }

.guide-only { flex: none; }

.guide-count {
  margin-left: auto;
  font-family: var(--font-mono);
  font-size: 11px;
  color: rgb(var(--c-muted));
}

.guide-chev {
  flex: none;
  font-size: 20px;
  line-height: 1;
  color: rgb(var(--c-muted));
  transition: transform 0.15s ease;
}

.guide-sec[open] .guide-chev { transform: rotate(90deg); }

.guide-body { padding: 0 16px 16px 62px; }

.guide-intro {
  margin: 0 0 16px;
  font-size: 13.5px;
  line-height: 1.6;
  color: rgb(var(--c-muted));
}

.guide-page + .guide-page {
  margin-top: 18px;
  padding-top: 18px;
  border-top: 1px solid rgb(var(--c-line));
}

.guide-page-name {
  margin: 0 0 8px;
  font-family: var(--font-mono);
  font-size: 11px;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: rgb(var(--c-ember));
}

.guide-para {
  margin: 0 0 9px;
  font-size: 13.5px;
  line-height: 1.65;
}

.guide-para:last-child { margin-bottom: 0; }
.guide-para strong { font-weight: 600; }

@media (max-width: 640px) {
  .guide-body { padding-left: 16px; }
  .guide-count { display: none; }
}

/* A screenshot inside the guide. Bordered and inset so it reads as a picture of
   the panel rather than part of the page it is sitting in, and clickable
   because the small print in a form cannot be read at this width. */
.guide-shot {
  margin: 12px 0 16px;
}

.guide-shot img {
  display: block;
  width: 100%;
  height: auto;
  border: 1px solid rgb(var(--c-line));
  border-radius: 8px;
  background: rgb(var(--c-bg));
}

.guide-shot a:hover img {
  border-color: rgb(var(--c-ember) / 0.5);
}

.guide-shot figcaption {
  margin-top: 6px;
  font-family: var(--font-mono);
  font-size: 11px;
  line-height: 1.45;
  color: rgb(var(--c-muted));
}

@media print {
  .guide-shot { break-inside: avoid; }
}

/* -------------------------------------------------- retail / wholesale rows
 * One more column on the invoice line: what is being counted. app.css carries
 * no eight-column template either, so the widths stay here beside the seven-
 * column one they replace.
 */
@media (min-width: 640px) {
  .inv-grid {
    grid-template-columns: 1fr 96px 104px 88px 108px 88px 104px 36px;
  }
}

.inv-mode-row {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 14px;
}

.inv-mode-row .select-wrap { flex: none; min-width: 190px; }

.inv-mode-note {
  margin: 0;
  font-size: 12.5px;
  line-height: 1.5;
  color: rgb(var(--c-muted));
}

/* A locked picker still has to be readable — it is saying what will happen,
   not merely refusing input. */
.inv-unit-wrap.is-locked { opacity: 0.72; }
.inv-unit-wrap.is-locked select { cursor: not-allowed; }

/* What the customer already owes, under the payment box. Quiet, because it is
   context for the figure being typed rather than a figure of its own. */
.settle-owed {
  margin: 6px 0 0;
  font-family: var(--font-mono);
  font-size: 11.5px;
  color: rgb(var(--c-muted));
}

/* A stat card that opens something. It has to look pressable without becoming
   a button -- the figure is still the point of it. */
.stat-card.is-link {
  display: block;
  text-decoration: none;
  color: inherit;
  transition: border-color 0.15s ease, transform 0.15s ease;
}

.stat-card.is-link:hover {
  border-color: rgb(var(--c-ember) / 0.45);
  transform: translateY(-1px);
}

.stat-card.is-link:focus-visible {
  outline: 2px solid rgb(var(--c-ember));
  outline-offset: 2px;
}

/* --------------------------------------------------- customer colour tags
 * A note the office makes about a person, read down a list at a glance. The
 * six are picked to stay apart from each other for the common kinds of colour
 * blindness, and each carries a title so the meaning is never colour alone.
 */
.cust-dot {
  display: inline-block;
  width: 9px;
  height: 9px;
  border-radius: 50%;
  margin-right: 7px;
  vertical-align: middle;
  flex: none;
}

.cust-dot.is-green  { background: #1F9D63; }
.cust-dot.is-amber  { background: #E0A22B; }
.cust-dot.is-red    { background: #D64545; }
.cust-dot.is-blue   { background: #3B7DD8; }
.cust-dot.is-purple { background: #8B5CF6; }
.cust-dot.is-grey   { background: #9CA3AF; }

.cust-swatches {
  display: flex;
  flex-wrap: wrap;
  gap: 8px;
  margin-top: 6px;
}

.cust-swatch {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  padding: 5px 10px 5px 8px;
  border: 1px solid rgb(var(--c-line));
  border-radius: 999px;
  cursor: pointer;
  font-size: 12.5px;
  user-select: none;
}

.cust-swatch input { position: absolute; opacity: 0; width: 0; height: 0; }

.cust-swatch-dot {
  width: 11px;
  height: 11px;
  border-radius: 50%;
  flex: none;
  border: 1px solid rgb(var(--c-line));
}

.cust-swatch.is-green  .cust-swatch-dot { background: #1F9D63; border-color: #1F9D63; }
.cust-swatch.is-amber  .cust-swatch-dot { background: #E0A22B; border-color: #E0A22B; }
.cust-swatch.is-red    .cust-swatch-dot { background: #D64545; border-color: #D64545; }
.cust-swatch.is-blue   .cust-swatch-dot { background: #3B7DD8; border-color: #3B7DD8; }
.cust-swatch.is-purple .cust-swatch-dot { background: #8B5CF6; border-color: #8B5CF6; }
.cust-swatch.is-grey   .cust-swatch-dot { background: #9CA3AF; border-color: #9CA3AF; }
.cust-swatch.is-none   .cust-swatch-dot { background: transparent; }

/* The chosen one is outlined rather than filled: the swatch already carries
   the colour, and filling it too would hide which one that is. */
.cust-swatch:has(input:checked) {
  border-color: rgb(var(--c-ember));
  box-shadow: inset 0 0 0 1px rgb(var(--c-ember));
}

.cust-swatch:focus-within {
  outline: 2px solid rgb(var(--c-ember));
  outline-offset: 2px;
}

/* The two price boxes on an invoice line: the rate agreed with the customer,
   and what that comes to for one of whatever is being counted. Stacked, so the
   row keeps its eight columns. */
.inv-price-cell { display: grid; gap: 4px; }
.inv-price-cell .inv-rate { font-size: 12px; opacity: 0.85; }
.inv-price-cell .inv-rate:focus { opacity: 1; }

/* ------------------------------------------------------------- limit screen
 * One card per customer. The four weekly targets are equal by design, which is
 * the whole idea and also what made the first version unreadable: four
 * identical figures in a row say nothing about where the customer is. So the
 * card leads with a verdict, then a bar for the month, and each week shows its
 * state and what arrived -- with the target underneath as the reference it is.
 */
.limit-list { display: grid; gap: 14px; }

.limit-card {
  border: 1px solid rgb(var(--c-line));
  border-radius: 14px;
  background: rgb(var(--c-surface));
  padding: 18px 20px;
}

/* Over the limit is the one thing this screen exists to shout about. */
.limit-card.is-over { border-color: rgb(var(--c-ember) / 0.55); }

.limit-head {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
  margin-bottom: 12px;
}

.limit-who { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; }

.limit-name {
  font-size: 16px;
  font-weight: 600;
  color: inherit;
  text-decoration: none;
}

.limit-name:hover { color: rgb(var(--c-ember)); }

.limit-day {
  font-family: var(--font-mono);
  font-size: 10.5px;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: rgb(var(--c-muted));
  border: 1px solid rgb(var(--c-line));
  border-radius: 999px;
  padding: 2px 8px;
}

/* The sentence the screen exists to say. */
.limit-verdict { margin: 0; font-size: 13.5px; font-weight: 600; }
.limit-verdict.is-behind { color: #D64545; }
.limit-verdict.is-ok { color: #1F9D63; }

.limit-bar {
  display: block;
  height: 7px;
  border-radius: 999px;
  background: rgb(var(--c-muted) / 0.16);
  overflow: hidden;
}

.limit-bar-fill {
  display: block;
  height: 100%;
  border-radius: 999px;
  background: rgb(var(--c-ember));
}

.limit-bar-note {
  margin: 6px 0 16px;
  font-family: var(--font-mono);
  font-size: 11.5px;
  color: rgb(var(--c-muted));
}

.limit-bar-note strong { color: rgb(var(--c-fg)); }

.limit-weeks {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(min(100%, 168px), 1fr));
  gap: 10px;
  margin: 0 0 16px;
  padding: 0;
  list-style: none;
}

.limit-week {
  border: 1px solid rgb(var(--c-line));
  border-top-width: 3px;
  border-radius: 10px;
  padding: 11px 13px 13px;
}

/* The state is what the eye looks for, so it is the top edge. */
.limit-week.is-future   { border-top-color: rgb(var(--c-line)); opacity: 0.6; }
.limit-week.is-done     { border-top-color: #1F9D63; }
.limit-week.is-now_done { border-top-color: #1F9D63; background: rgb(31 157 99 / 0.05); }
.limit-week.is-now      { border-top-color: rgb(var(--c-ember)); background: rgb(var(--c-ember) / 0.06); }
.limit-week.is-missed   { border-top-color: #D64545; background: rgb(214 69 69 / 0.05); }

.limit-week-top {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 6px;
}

.limit-week-no,
.limit-week-state {
  font-family: var(--font-mono);
  font-size: 9.5px;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: rgb(var(--c-muted));
}

.limit-week.is-now .limit-week-state      { color: rgb(var(--c-ember)); font-weight: 700; }
.limit-week.is-missed .limit-week-state   { color: #D64545; font-weight: 700; }
.limit-week.is-done .limit-week-state,
.limit-week.is-now_done .limit-week-state { color: #1F9D63; }

.limit-week-when {
  margin: 2px 0 8px;
  font-family: var(--font-mono);
  font-size: 10.5px;
  color: rgb(var(--c-muted));
}

.limit-week-figure {
  margin: 0 0 7px;
  display: flex;
  align-items: baseline;
  gap: 5px;
  flex-wrap: wrap;
}

.limit-week-short,
.limit-week-paid-in {
  font-family: var(--font-mono);
  font-size: 16px;
  font-weight: 700;
}

.limit-week-short { color: #D64545; }
.limit-week-paid-in { color: #1F9D63; }

.limit-week-word { font-size: 11px; color: rgb(var(--c-muted)); }

.limit-week-bar {
  display: block;
  height: 4px;
  border-radius: 999px;
  background: rgb(var(--c-muted) / 0.18);
  overflow: hidden;
}

.limit-week-bar > span {
  display: block;
  height: 100%;
  border-radius: 999px;
  background: rgb(var(--c-muted) / 0.4);
}

.limit-week.is-missed .limit-week-bar > span   { background: #D64545; }
.limit-week.is-now .limit-week-bar > span      { background: rgb(var(--c-ember)); }
.limit-week.is-done .limit-week-bar > span,
.limit-week.is-now_done .limit-week-bar > span { background: #1F9D63; }

.limit-week-target {
  margin: 6px 0 0;
  font-family: var(--font-mono);
  font-size: 10.5px;
  color: rgb(var(--c-muted));
}

.limit-form {
  display: flex;
  flex-wrap: wrap;
  align-items: flex-end;
  gap: 12px;
  padding-top: 14px;
  border-top: 1px solid rgb(var(--c-line));
}

.limit-link {
  margin-left: auto;
  font-family: var(--font-mono);
  font-size: 11.5px;
  color: rgb(var(--c-ember));
  text-decoration: none;
}

.limit-link:hover { text-decoration: underline; }

/* The customer's name on the debt list doubles as the handle that folds the
   instalments under it away. It has to still read as a name. */
.debt-fold {
  display: inline-flex;
  align-items: center;
  gap: 5px;
  background: none;
  border: 0;
  padding: 0;
  font: inherit;
  color: inherit;
  cursor: pointer;
  text-align: left;
}

.debt-fold:hover { color: rgb(var(--c-ember)); }

.debt-fold-caret {
  font-size: 10px;
  line-height: 1;
  color: rgb(var(--c-muted));
  transition: transform 0.15s ease;
}

.debt-fold.is-folded .debt-fold-caret { transform: rotate(-90deg); }

.debt-fold:focus-visible {
  outline: 2px solid rgb(var(--c-ember));
  outline-offset: 2px;
  border-radius: 3px;
}

/* A week still ahead shows what it will ask for, in the same neutral ink as
   its dates -- not the red of something already missed. */
.limit-week-ahead {
  font-family: var(--font-mono);
  font-size: 16px;
  font-weight: 700;
  color: rgb(var(--c-muted));
}

/* ------------------------------------------ invoice row: what is on the shelf
 * The stock figure, and under it what one bundle of this product is. Two
 * profiles whose names differ by one letter can hold quite different bundles --
 * Tavan C 3 m goes twelve to the bundle and Tavan U 3 m goes sixteen -- and a
 * line priced against the wrong one looks perfectly correct.
 */
.inv-stock-cell {
  display: flex;
  flex-direction: column;
  align-items: flex-end;
  gap: 1px;
  line-height: 1.25;
}

.inv-pack-note {
  font-family: var(--font-mono);
  font-size: 10px;
  color: rgb(var(--c-muted));
  white-space: nowrap;
}

/* The save already refuses an oversold line; this says so before the save. */
.inv-stock-cell.is-short .inv-stock-line { color: #D64545; font-weight: 700; }
.inv-stock-cell.is-short .inv-pack-note { color: #D64545; }

/* ----------------------------------------------- the customer's desk
 * Five things happen to a customer, and they used to be one screen with
 * different radios set -- which is how a payment got recorded as an invoice.
 * Each act is named, and says under its name what it does to the debt.
 */
.desk {
  border: 1px solid rgb(var(--c-line));
  border-radius: 14px;
  background: rgb(var(--c-surface));
  padding: 18px 20px;
}

.desk-head { margin-bottom: 14px; }

.desk-label {
  margin: 0;
  font-family: var(--font-mono);
  font-size: 10px;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: rgb(var(--c-muted));
}

.desk-owed {
  margin: 2px 0 0;
  font-family: var(--font-mono);
  font-size: 30px;
  font-weight: 700;
  color: rgb(var(--c-ember));
  line-height: 1.1;
}

.desk-note {
  margin: 4px 0 0;
  font-size: 12.5px;
  color: #D42B2B;
  font-weight: 600;
}

.desk-acts {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(min(100%, 180px), 1fr));
  gap: 10px;
}

.desk-act {
  display: flex;
  flex-direction: column;
  gap: 3px;
  text-align: left;
  padding: 12px 14px;
  border: 1px solid rgb(var(--c-line));
  border-left-width: 3px;
  border-radius: 10px;
  background: transparent;
  color: inherit;
  text-decoration: none;
  cursor: pointer;
  font: inherit;
}

.desk-act:hover:not(:disabled) {
  border-color: rgb(var(--c-ember) / 0.55);
  background: rgb(var(--c-ember) / 0.05);
}

.desk-act:disabled { opacity: 0.45; cursor: default; }

/* Which way the debt moves is the thing that was ambiguous, so it is the
   edge of the button: green takes it down, red puts it up. */
.desk-act.is-in { border-left-color: #1F9D63; }
.desk-act.is-out { border-left-color: #D64545; }

.desk-act-name { font-size: 14px; font-weight: 600; }

.desk-act-what {
  font-size: 11.5px;
  color: rgb(var(--c-muted));
  line-height: 1.35;
}

/* The customer's whole account, under his name on a debt row. A man with two
   open bills is two rows, ordered by their due dates -- sometimes on different
   pages -- and the row alone reads as though the rest had gone missing. */
.debt-owed {
  color: inherit;
  text-decoration: none;
  white-space: nowrap;
}

.debt-owed:hover { color: rgb(var(--c-ember)); text-decoration: underline; }

/* ============================================================ stock journal */

/* One line per movement. In and out keep columns of their own so a day is
   read down two edges; the shelf after each line is the third. */
.sj-head {
  display: flex;
  flex-wrap: wrap;
  align-items: flex-start;
  justify-content: space-between;
  gap: 12px;
  margin-bottom: 16px;
}

.sj-pills {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 10px;
  margin-bottom: 14px;
}

.sj-in { color: #1F9D63; }
.sj-out { color: rgb(var(--c-ember)); }

.sj-qty,
.sj-balance {
  font-family: var(--font-mono);
  font-weight: 600;
  white-space: nowrap;
}

.sj-sub {
  display: block;
  margin-top: 2px;
  font-family: var(--font-mono);
  font-size: 10.5px;
  letter-spacing: 0.02em;
  color: rgb(var(--c-muted));
  white-space: nowrap;
}

.sj-nil { color: rgb(var(--c-muted) / 0.45); }

.sj-product a { font-weight: 500; }
.sj-doc { font-size: 12.5px; }
.sj-who { font-size: 12.5px; color: rgb(var(--c-muted)); white-space: nowrap; }

/* A shelf below zero is a booking mistake upstream; it should be found. */
.sj-table tr.sj-neg td { background: rgb(214 69 69 / 0.06); }
.sj-table tr.sj-neg .sj-balance { color: #D64545; }

.sj-table tfoot td {
  padding-top: 12px;
  padding-bottom: 12px;
  font-weight: 600;
  border-top: 2px solid rgb(var(--c-muted) / 0.55);
  background: rgb(var(--c-surface));
}

/* ========================================================= customer journal */

/* The name column is pinned while the payments scroll sideways, so a long
   row never loses whose it is. */
.cj-table th,
.cj-table td { white-space: nowrap; vertical-align: middle; }

.cj-sticky {
  position: sticky;
  left: 0;
  z-index: 2;
  background: rgb(var(--c-surface));
  box-shadow: 1px 0 0 rgb(var(--c-line));
}

.cj-table thead .cj-sticky { z-index: 3; }

.cj-name { min-width: 190px; max-width: 260px; white-space: normal !important; }

.cj-pays-head { border-left: 1px solid rgb(var(--c-line)); }

.cj-pays-hint {
  font-family: var(--font-mono);
  font-size: 10px;
  letter-spacing: 0.06em;
  text-transform: none;
  color: rgb(var(--c-muted));
}

.cj-green { color: #1F9D63; font-family: var(--font-mono); font-weight: 600; }
.cj-owed { color: rgb(var(--c-ember)); font-family: var(--font-mono); font-weight: 700; }

.cj-add-cell,
.cj-pay-cell,
.cj-more-cell { padding: 6px 4px !important; }

.cj-add-cell { padding-left: 12px !important; border-left: 1px solid rgb(var(--c-line)); }

.cj-add {
  width: 38px;
  height: 46px;
  font-size: 20px;
  line-height: 1;
  color: #1F9D63;
  background: transparent;
  border: 1.5px dashed rgb(31 157 99 / 0.55);
  border-radius: 10px;
  cursor: pointer;
  transition: background-color .15s, border-color .15s, transform .15s;
}

.cj-add:hover:not(:disabled) {
  background: rgb(31 157 99 / 0.1);
  border-style: solid;
  transform: translateY(-1px);
}

.cj-add:disabled { opacity: 0.3; cursor: default; }

.cj-pay {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: 2px;
  min-width: 118px;
  padding: 7px 11px;
  text-align: left;
  background: rgb(31 157 99 / 0.07);
  border: 1px solid rgb(31 157 99 / 0.25);
  border-left: 3px solid #1F9D63;
  border-radius: 10px;
  cursor: pointer;
  transition: background-color .15s, transform .15s, box-shadow .15s;
}

.cj-pay:hover { background: rgb(31 157 99 / 0.14); transform: translateY(-1px); }

/* The newest payment is the one asked about; it wears a ring. */
.cj-pay.is-latest { box-shadow: 0 0 0 2px rgb(31 157 99 / 0.22); }

.cj-pay-amount {
  font-family: var(--font-mono);
  font-size: 13.5px;
  font-weight: 700;
  color: #1F9D63;
}

.cj-pay-meta {
  font-family: var(--font-mono);
  font-size: 10.5px;
  color: rgb(var(--c-muted));
}

.cj-more {
  font-family: var(--font-mono);
  font-size: 11px;
  letter-spacing: 0.04em;
  color: rgb(var(--c-ember));
  text-decoration: none;
}

.cj-more:hover { text-decoration: underline; }

.modal-foot .cj-delete { margin-right: auto; color: #D64545; }

/* ============================================================ quick payment */

.qpay-btn {
  margin-left: 8px;
  padding: 3px 9px;
  font-family: var(--font-mono);
  font-size: 10px;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: #1F9D63;
  background: rgb(31 157 99 / 0.08);
  border: 1px solid rgb(31 157 99 / 0.35);
  border-radius: 999px;
  cursor: pointer;
  vertical-align: middle;
  transition: background-color .15s, color .15s;
}

.qpay-btn:hover { color: #fff; background: #1F9D63; }

.qpay-who { margin: 0; font-size: 15px; font-weight: 600; }

.qpay-label-row {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 8px;
}

.qpay-full {
  padding: 0;
  font-family: var(--font-mono);
  font-size: 10.5px;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: #1F9D63;
  background: none;
  border: 0;
  cursor: pointer;
}

.qpay-full:hover { text-decoration: underline; }

/* ============================================================ production cost */

/* Money made is green, money lost is red; the rest of the page stays ink so
   the two stand out where they matter. */
.pc-pos { color: #1F9D63; }
.pc-neg { color: #D64545; }

.pc-section { margin: 22px 0 10px; }

.pc-rates {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(180px, 1fr));
  gap: 12px;
}

.pc-how {
  max-width: 780px;
  font-size: 12.5px;
  line-height: 1.55;
  color: rgb(var(--c-muted));
}

.prod-cost {
  font-family: var(--font-mono);
  font-weight: 600;
  white-space: nowrap;
}
