/* Quote tool: design system
   Neutral and entity-agnostic: no brand colour anywhere. Dark rail, light
   canvas, restrained semantic colour used only to convey status. */

:root {
  /* neutrals */
  --ink:        #0F0F10;   /* sidebar, primary actions */
  --ink-soft:   #26262A;
  --bg:         #F1F1F0;
  --surface:    #FFFFFF;
  --sunken:     #F7F7F6;
  --hover:      #ECECEA;
  --border:     #DDDDD9;
  --border-strong: #B4B4AE;

  /* text */
  --text:        #17171A;
  --text-muted:  #46464B;
  --text-subtle: #6A6A70;
  --text-faint:  #8E8E94;

  /* dark rail */
  --rail-bg:      #0F0F10;
  --rail-text:    #A2A2A8;
  --rail-hover:   #1E1E21;
  --rail-active:  #2C2C31;
  --rail-on:      #FFFFFF;

  /* status: functional, not decorative */
  --ok-bg:    #E4F1E8;  --ok-fg:    #1C5334;
  --warn-bg:  #F8EEDA;  --warn-fg:  #74490A;
  --bad-bg:   #F8E5E2;  --bad-fg:   #8A2418;
  --neutral-bg: #E7E7E4; --neutral-fg: #3A3A3E;

  --radius:    6px;
  --radius-sm: 4px;
  --rail-w:    228px;
  --ease: cubic-bezier(.2, 0, .1, 1);
  --font: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
}

* { box-sizing: border-box; }

/* The hidden attribute must beat every display rule in this file. Without
   this, `el.hidden = true` silently does nothing to anything carrying its
   own display (.field, .grid-*, .kv…); the element stays on screen. */
[hidden] { display: none !important; }

body {
  margin: 0;
  font: 13.5px/1.45 var(--font);
  color: var(--text);
  background: var(--bg);
  -webkit-font-smoothing: antialiased;
  -webkit-text-size-adjust: 100%;   /* iPad must not resize text on rotate */
}

.num { font-variant-numeric: tabular-nums; }
.muted { color: var(--text-muted); }
.right { text-align: right; }
a { color: var(--text); }

::selection { background: #D8D8D3; }

/* ── app shell ─────────────────────────────────────────────────────── */

/* dvh, not vh: iOS Safari's toolbars shrink the visible viewport, and vh
   ignores that; the rail would run under the browser chrome */
.app { display: flex; min-height: 100dvh; }

.nav {
  width: var(--rail-w); flex-shrink: 0;
  background: var(--rail-bg);
  display: flex; flex-direction: column;
  padding: 18px 12px;
  position: sticky; top: 0; height: 100dvh;
}
.nav-brand { padding: 2px 8px 22px; }
/* white-on-transparent wordmark, sized to the rail rather than to its own
   pixels so it stays crisp on retina */
.nav-brand img { display: block; width: 100%; max-width: 172px; height: auto; }
.nav-section {
  padding: 18px 8px 6px;
  font-size: 11px; font-weight: 600; letter-spacing: .07em; text-transform: uppercase;
  color: #6E6E76;
}
.nav-item {
  display: flex; align-items: center; gap: 10px;
  padding: 8px; border-radius: var(--radius-sm);
  color: var(--rail-text); text-decoration: none; font-weight: 500;
  transition: background .12s var(--ease), color .12s var(--ease);
}
.nav-item:hover { background: var(--rail-hover); color: #DCDCE0; }
.nav-item.current { background: var(--rail-active); color: var(--rail-on); font-weight: 600; }
/* a screen that does not exist yet: visible so the shape of the app reads,
   but plainly not somewhere you can go */
.nav-soon, .nav-soon:hover { color: #56565D; background: transparent; cursor: default; }
.nav-soon svg { opacity: .6; }
.nav-tag { margin-left: auto; font-size: 9.5px; font-weight: 700; letter-spacing: .06em;
           text-transform: uppercase; color: #6E6E76;
           border: 1px solid #2E2E33; border-radius: 3px; padding: 1px 4px; }
.nav-item svg { width: 16px; height: 16px; flex-shrink: 0; stroke: currentColor;
                stroke-width: 1.6; fill: none; stroke-linecap: round; stroke-linejoin: round; }
.nav-foot { margin-top: auto; padding: 14px 8px 0; border-top: 1px solid #232326;
            font-size: 11.5px; color: #6E6E76; }

/* No entry animation: this is a multi-page app, so it replayed on every
   navigation and made moving between screens feel like a restart. The
   .msg animation below stays; that one marks genuinely new content. */
.main { flex: 1; min-width: 0; padding: 22px 28px 56px; }
/* Centred, not left-hugging. A capped width with no auto margin pins the page
   to the left edge and leaves a growing empty band on the right as the window
   widens: barely visible on the 1140px screens, glaring on the 800px ones. */
.wrap { max-width: 1140px; margin-inline: auto; }
.wrap-narrow { max-width: 800px; }

@keyframes rise { from { opacity: 0; transform: translateY(4px); } to { opacity: 1; transform: none; } }

/* ── page header ───────────────────────────────────────────────────── */

.breadcrumbs { font-size: 12.5px; color: var(--text-subtle); margin-bottom: 6px; }
.breadcrumbs a { color: var(--text-subtle); text-decoration: none; }
.breadcrumbs a:hover { color: var(--text); text-decoration: underline; }

.page-head { display: flex; flex-wrap: wrap; align-items: flex-start; gap: 12px 16px; margin-bottom: 18px; }
.page-head > div:first-child { min-width: 240px; }
.page-head h1 { margin: 0; font-size: 21px; font-weight: 600; letter-spacing: -.015em; }
.page-head p { margin: 4px 0 0; color: var(--text-muted); font-size: 13px; }
.page-head .actions { margin-left: auto; display: flex; gap: 8px; align-items: center; flex-shrink: 0; }

/* ── buttons ───────────────────────────────────────────────────────── */

.btn {
  font: inherit; font-weight: 500; font-size: 13px;
  height: 32px; padding: 0 13px;
  border: 1px solid var(--border-strong); border-radius: var(--radius-sm);
  background: var(--surface); color: var(--text);
  cursor: pointer; white-space: nowrap;
  display: inline-flex; align-items: center; justify-content: center; gap: 6px;
  transition: background .12s var(--ease), border-color .12s var(--ease),
              transform .06s var(--ease), opacity .12s var(--ease);
  text-decoration: none;
}
.btn:hover { background: var(--hover); }
.btn:active { transform: translateY(1px); }
.btn-primary { background: var(--ink); border-color: var(--ink); color: #fff; font-weight: 600; }
.btn-primary:hover { background: var(--ink-soft); border-color: var(--ink-soft); }
.btn-subtle { border-color: transparent; background: transparent; color: var(--text-subtle); }
.btn-subtle:hover { background: var(--hover); color: var(--text); }
.btn-danger:hover { background: var(--bad-bg); border-color: #C89A92; color: var(--bad-fg); }
.btn-sm { height: 28px; font-size: 12.5px; padding: 0 10px; }
.btn:disabled { opacity: .45; cursor: not-allowed; transform: none; }
.btn { position: relative; overflow: hidden; }   /* the working bar clips to the button */
.btn.busy { pointer-events: none; opacity: .8; }
.btn.busy::after {
  content: ''; position: absolute; left: 0; bottom: 0; height: 2px; width: 40%;
  background: currentColor; opacity: .4;
  animation: working 1s var(--ease) infinite;
}
@keyframes working { from { transform: translateX(-110%); } to { transform: translateX(280%); } }

/* keyboard focus only, never a ring on mouse click */
:focus { outline: none; }
:focus-visible { outline: 2px solid var(--ink); outline-offset: 2px; border-radius: var(--radius-sm); }

/* Text fields carry their own focus treatment (ink border + halo, below),
   and browsers match :focus-visible on them even for mouse clicks, so the
   offset ring drew a SECOND ring detached from the field. Suppress it here
   only; checkboxes, buttons and links still get the ring. */
input[type=text]:focus-visible, input[type=email]:focus-visible,
input[type=password]:focus-visible, select:focus-visible,
textarea:focus-visible { outline: none; }

/* ── surfaces ──────────────────────────────────────────────────────── */

.card {
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  padding: 16px;
  margin-bottom: 12px;
}
.card > h2 {
  margin: 0 0 13px; font-size: 13.5px; font-weight: 600;
  display: flex; align-items: baseline; gap: 10px;
}
.card > h2 .note { margin-left: auto; font-size: 12.5px; font-weight: 400; color: var(--text-subtle); }
.card-flush { padding: 0; overflow: hidden; }
/* second heading inside a card, separated from the block above it:
   always an h2 nested deeper than .card > h2 reaches */
h2.rail-section { margin: 20px 0 13px; padding-top: 16px;
                  border-top: 1px solid var(--border); }
.rail-link { display: inline-block; margin-top: 12px; font-size: 12.5px;
             color: var(--text-subtle); text-decoration: none; }
.rail-link:hover { color: var(--text); text-decoration: underline; }

.grid-2 { display: grid; grid-template-columns: 1fr 1fr; gap: 12px; }
.grid-3 { display: grid; grid-template-columns: repeat(3, 1fr); gap: 12px; }
.grid-4 { display: grid; grid-template-columns: repeat(4, 1fr); gap: 12px; }
.split { display: grid; grid-template-columns: 1fr 330px; gap: 12px; align-items: stretch; }
/* The rail ends exactly level with the last card in the column beside it.
   Taking the card out of flow means its own length cannot push the row
   taller: the left column alone sets the height, and the rail fills it.
   Whatever does not fit scrolls inside the card; the headline figures and
   the action stay put while the readout moves. */
.split > div > .card:last-child { margin-bottom: 0; }
.split > aside { position: relative; min-height: 0; }
.split > aside > .card {
  position: absolute; inset: 0;
  display: flex; flex-direction: column;
  margin-bottom: 0; overflow: hidden;
}
/* Grid items default to min-width:auto, so a wide table inside a column widens
   the whole page instead of scrolling in its own .table-scroll. */
.split > *, .grid-2 > *, .grid-3 > *, .grid-4 > * { min-width: 0; }

/* Forms step DOWN a column at a time rather than collapsing straight to one.
   These widths are viewport-wide but the content area is the viewport minus
   the rail, so an iPad in portrait (820) has ~590px to work with: enough for
   two columns, and dropping to one there doubled the scrolling for no gain. */
@media (max-width: 1180px) { .grid-4 { grid-template-columns: repeat(2, 1fr); } }
@media (max-width: 960px) {
  .split { grid-template-columns: 1fr; }
  .grid-3, .grid-4 { grid-template-columns: repeat(2, 1fr); }
  /* stacked: nothing to match height with, so let the rail run its full length */
  .split > div > .card:last-child { margin-bottom: 12px; }
  .split > aside > .card { position: static; height: auto; overflow: visible; margin-bottom: 12px; }
}
@media (max-width: 700px) { .grid-2, .grid-3, .grid-4 { grid-template-columns: 1fr; } }

/* ── forms ─────────────────────────────────────────────────────────── */

.field { display: block; margin-bottom: 12px; }
.field > span { display: block; font-size: 12.5px; font-weight: 500;
                color: var(--text-muted); margin-bottom: 5px; }
.field-hint { font-size: 12px; color: var(--text-subtle); margin-top: 5px; line-height: 1.4; }

/* email and password are here because they are text boxes too. Left out, they
   keep the browser's default size and come out about half width: the
   calculator's unlock field did exactly that, next to a full-width button. */
input[type=text], input[type=email], input[type=password], select, textarea {
  width: 100%; height: 34px; padding: 0 9px;
  font: inherit; color: var(--text);
  background: var(--sunken);
  border: 1px solid var(--border-strong); border-radius: var(--radius-sm);
  transition: background .12s var(--ease), border-color .12s var(--ease), box-shadow .12s var(--ease);
}
textarea { height: auto; padding: 7px 9px; resize: vertical; }
input:hover, select:hover, textarea:hover { border-color: #9C9C95; }
input:focus, select:focus, textarea:focus {
  background: var(--surface); border-color: var(--ink);
  box-shadow: 0 0 0 3px rgba(15, 15, 16, .13);
}
/* Three states a rep can tell apart without reading the contents:
     empty: sunken fill, the field is still waiting
     filled; white fill, something has been entered
     locked: dashed grey, it cannot be typed into (see :disabled below)
   .is-filled is set by app.js, because CSS alone cannot see a value. */
input.is-filled, select.is-filled, textarea.is-filled {
  background: var(--surface); border-color: var(--border-strong);
}
/* an empty field that is holding a step up says so quietly */
.field:has(> span > .req) input:not(.is-filled):not(:disabled),
.field:has(> span > .req) select:not(.is-filled):not(:disabled),
.field:has(> span > .req) textarea:not(.is-filled):not(:disabled) {
  border-color: #C6B79A;
}


/* A disabled control must read as switched off at a glance: the browser
   default barely shows against an already-grey field. The label and any
   affix dim with it, so the whole control reads as one inactive unit. */
input:disabled, select:disabled, textarea:disabled {
  background: var(--hover); color: var(--text-faint);
  border-color: var(--border); border-style: dashed;
  cursor: not-allowed; box-shadow: none;
}
input:disabled:hover, select:disabled:hover { border-color: var(--border); }
.field:has(input:disabled) > span,
.field:has(select:disabled) > span { color: var(--text-faint); }
.affix:has(input:disabled) > .sign { color: var(--text-faint); }


/* currency / percent affixes */
.affix { position: relative; display: block; }
.affix > .sign {
  position: absolute; top: 50%; transform: translateY(-50%);
  color: var(--text-subtle); font-size: 13px; pointer-events: none;
}
.affix.pre > .sign { left: 10px; }
.affix.pre input { padding-left: 22px; }
.affix.post > .sign { right: 10px; }
.affix.post input { padding-right: 24px; }

select {
  appearance: none; -webkit-appearance: none;
  padding-right: 28px;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 12 12'%3E%3Cpath d='M3 4.5 6 8l3-3.5' fill='none' stroke='%236A6A70' stroke-width='1.4' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E");
  background-repeat: no-repeat; background-position: right 9px center; background-size: 12px;
}

.check { display: flex; align-items: center; gap: 8px; font-size: 13px; cursor: pointer; }
.check input { width: auto; height: auto; accent-color: var(--ink); cursor: pointer; }

/* ── tables ────────────────────────────────────────────────────────── */

table { border-collapse: collapse; width: 100%; }
thead th {
  font-size: 11.5px; font-weight: 700; letter-spacing: .05em; text-transform: uppercase;
  color: var(--text-subtle); text-align: left;
  padding: 9px 12px; border-bottom: 1px solid var(--border-strong);
  white-space: nowrap; background: var(--sunken);
}
tbody td { padding: 0 12px; height: 42px; border-bottom: 1px solid var(--border); }
tbody tr:last-child td { border-bottom: 0; }
tbody tr { transition: background .1s var(--ease); }
tbody tr:hover td { background: var(--sunken); }
th.right, td.right { text-align: right; }
.table-scroll { overflow-x: auto; }
.cell-sub { font-size: 12px; color: var(--text-subtle); }


.grid-table td { padding: 3px 5px; height: 36px; }
.grid-table input { height: 28px; border-color: transparent; background: transparent; }
.grid-table input:hover { background: var(--sunken); border-color: var(--border); }
.grid-table input:focus { background: var(--surface); }
.grid-table tbody tr:hover td { background: transparent; }

/* ── lozenges ──────────────────────────────────────────────────────── */

.lozenge {
  display: inline-block; padding: 2px 7px; border-radius: 3px;
  font-size: 11px; font-weight: 700; letter-spacing: .03em; text-transform: uppercase;
  white-space: nowrap;
}
.lozenge-warn    { background: var(--warn-bg);    color: var(--warn-fg); }
.lozenge-bad     { background: var(--bad-bg);     color: var(--bad-fg); }
.lozenge-neutral { background: var(--neutral-bg); color: var(--neutral-fg); }

/* ── readouts ──────────────────────────────────────────────────────── */

.kv { display: flex; justify-content: space-between; gap: 12px; align-items: baseline;
      padding: 7px 0; border-bottom: 1px solid var(--border); font-size: 13px; }
.kv:last-child { border-bottom: 0; }
.kv > span { color: var(--text-muted); }
.kv > b { font-weight: 600; }
.kv-row { border-radius: var(--radius-sm); padding: 8px; margin: 0 -8px;
          cursor: pointer; transition: background .1s var(--ease); }
.kv-row:hover { background: var(--sunken); }
.kv-row.current { background: var(--sunken); }
.kv-row.current > span { color: var(--text); font-weight: 600; }
/* the answer, stated once and large, instead of buried in a list */
.hero { padding: 2px 0 15px; }
.hero > b { display: block; font-size: 29px; font-weight: 700;
            letter-spacing: -.02em; line-height: 1.08; }
.hero > span { display: block; margin-top: 4px; font-size: 12.5px; color: var(--text-subtle); }

/* a row that totals the rows above it */
.kv-sum { border-top: 1px solid var(--border-strong); border-bottom: 0; }
.kv-sum > span { color: var(--text); font-weight: 600; }
.kv-sum > b { font-weight: 700; }

/* group headings inside the readout, mirroring the workbook's own sections */
.kv-group { margin: 14px 0 2px; font-size: 10.5px; font-weight: 700;
            letter-spacing: .07em; text-transform: uppercase; color: var(--text-faint); }
.kv-group:first-child { margin-top: 2px; }
/* the readout is long: tighten rows so the whole picture fits on screen */
aside .kv { padding: 5px 0; font-size: 12.5px; }
/* .kv-row pulls out by 8px for its hover highlight and pays for it with
   matching padding. The rule above resets that padding, so restore it here or
   the system lines hang 8px outside every other row. */
aside .kv-row { padding: 5px 8px; }
.kv-group + .kv { border-top: 0; }
/* the last totals row: the rule below it belongs to .rail-scroll */
.kv-last { border-bottom: 0; }
/* The readout scrolls inside the card. Spans the card's padding so the
   scrollbar sits against its edge, with a stable gutter so rows do not
   shift when the bar appears. */
.rail-scroll {
  flex: 1 1 auto; min-height: 0; overflow-y: auto; overscroll-behavior: contain;
  border-top: 1px solid var(--border);
  margin: 0 -16px; padding: 10px 8px 0 16px;
  scrollbar-gutter: stable; scrollbar-width: thin;
  scrollbar-color: var(--border-strong) transparent;
}
.rail-scroll::-webkit-scrollbar { width: 8px; }
.rail-scroll::-webkit-scrollbar-track { background: transparent; }
.rail-scroll::-webkit-scrollbar-thumb {
  background: var(--border-strong); border-radius: 4px;
}
.rail-scroll > .rail-section:first-child { margin-top: 0; padding-top: 0; border-top: 0; }

/* ── messages & toasts ─────────────────────────────────────────────── */

.msg { padding: 9px 12px; border-radius: var(--radius-sm); font-size: 12.5px;
       margin-bottom: 12px; animation: rise .18s var(--ease); }
.msg-warn { background: var(--warn-bg); color: var(--warn-fg); }
.msg-bad  { background: var(--bad-bg);  color: var(--bad-fg); }
.msg-ok   { background: var(--ok-bg);   color: var(--ok-fg); }

#toasts { position: fixed; right: 20px; bottom: 20px; z-index: 50;
          display: flex; flex-direction: column; gap: 8px; align-items: flex-end; }
.toast {
  background: var(--ink); color: #fff;
  padding: 10px 14px; border-radius: var(--radius);
  font-size: 13px; max-width: 380px;
  box-shadow: 0 6px 20px rgba(0, 0, 0, .22);
  animation: toast-in .22s var(--ease);
  display: flex; align-items: center; gap: 9px;
}
.toast.leaving { animation: toast-out .18s var(--ease) forwards; }
.toast-bad { background: #7B2018; }
.toast-ok::before { content: '✓'; font-weight: 700; }
@keyframes toast-in  { from { opacity: 0; transform: translateY(8px) scale(.98); } to { opacity: 1; transform: none; } }
@keyframes toast-out { to { opacity: 0; transform: translateY(6px) scale(.98); } }

/* ── the quotes list ─────────────────────────────────────────────────── */
.flag { margin-top: 3px; font-size: 11.5px; font-weight: 500; }
.flag-bad  { color: var(--bad-fg); }
tbody tr.row-bad  td:first-child { box-shadow: inset 3px 0 0 var(--bad-fg); }
/* Below this the quotes table becomes one card per quote. Six columns
   cannot fit an iPad held in portrait, and a table you have to scroll
   sideways hides exactly the thing you came to press. Same markup: the
   cells are simply laid out as a card. */
@media (max-width: 1100px) {
  .quotes, .quotes tbody, .quotes tr, .quotes td { display: block; width: auto; }
  .quotes thead { display: none; }
  .quotes tr {
    display: grid; grid-template-columns: 1fr auto; gap: 2px 12px;
    align-items: baseline;
    padding: 12px 14px; margin-bottom: 10px;
    border: 1px solid var(--border); border-radius: var(--radius);
    background: var(--surface);
  }
  .quotes tbody tr:hover td { background: transparent; }
  .quotes td { height: auto; padding: 0; border: 0; }
  .quotes td:nth-child(1) { grid-area: 1 / 1; }             /* id      */
  .quotes td:nth-child(4) { grid-area: 1 / 2; }             /* monthly */
  .quotes td:nth-child(2) { grid-area: 2 / 1 / auto / -1; margin-top: 4px; }
  .quotes td:nth-child(3) { grid-area: 3 / 1 / auto / -1; margin: 8px 0 4px; }
  .quotes td:nth-child(5) { grid-area: 4 / 1 / auto / -1; }  /* updated */
  .quotes td:nth-child(6) { grid-area: 5 / 1 / auto / -1;
    display: flex; flex-wrap: wrap; gap: 8px; margin-top: 12px;
    padding-top: 12px; border-top: 1px solid var(--border); }
  .quotes td:nth-child(6) .btn { flex: 1 1 auto; margin: 0; }
  /* the accent that marked an at-risk row now edges the whole card */
  .quotes tbody tr.row-bad { box-shadow: inset 3px 0 0 var(--bad-fg); }
  .quotes tbody tr.row-bad td:first-child { box-shadow: none; }
  .quotes .cell-sub { display: inline; }
  .quotes td:nth-child(2) .cell-sub { display: block; }
  /* the card IS the row, so the surrounding card chrome would double up */
  .card-flush:has(.quotes) { background: transparent; border: 0; }
}

/* ── returning-customer typeahead (step 1) ─────────────────────────────
   The list moves to whichever field is being typed in, so any field it
   can land on has to be a positioning context. */
.suggest-wrap { position: relative; }
.suggest-row .suggest-sub { flex: 1; }
.suggest {
  position: absolute; top: 100%; left: 0; min-width: 100%; width: max-content;
  max-width: 560px; z-index: 30; margin-top: 4px; overflow: hidden;
  background: var(--surface); border: 1px solid var(--border-strong);
  border-radius: var(--radius-sm); box-shadow: 0 10px 30px rgba(0, 0, 0, .14);
}
.suggest-head { padding: 6px 12px; font-size: 10.5px; font-weight: 700;
  letter-spacing: .07em; text-transform: uppercase; color: var(--text-faint);
  background: var(--sunken); border-bottom: 1px solid var(--border); }
.suggest-row { display: flex; gap: 10px; align-items: baseline;
  padding: 9px 12px; cursor: pointer; }
.suggest-row:hover { background: var(--sunken); }
.suggest-row + .suggest-row { border-top: 1px solid var(--border); }
.suggest-name { font-weight: 600; white-space: nowrap; }
.suggest-sub { font-size: 12px; color: var(--text-subtle); min-width: 0;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.suggest-meta { font-size: 11.5px; color: var(--text-subtle);
  white-space: nowrap; margin-left: auto; }

/* a hint directly under a card heading tucks up against it */
.card > h2 + .field-hint { margin: -6px 0 12px; }
/* the add-row action under a scrolling table */
.table-scroll + .btn { margin-top: 8px; }
/* table cells holding row actions never wrap them */
td.cell-actions { white-space: nowrap; }
/* block-sized skeleton for whole-card placeholders */
.skel-block { height: 70px; }

.empty { padding: 44px 16px; text-align: center; color: var(--text-subtle); font-size: 13.5px; }
.empty b { display: block; color: var(--text); font-size: 15px; margin-bottom: 5px; font-weight: 600; }

/* ── loading ───────────────────────────────────────────────────────── */

.skel { background: linear-gradient(90deg, var(--hover) 25%, var(--sunken) 50%, var(--hover) 75%);
        background-size: 200% 100%; animation: shimmer 1.3s infinite linear;
        border-radius: var(--radius-sm); height: 12px; }
@keyframes shimmer { from { background-position: 200% 0; } to { background-position: -200% 0; } }
.skel-row { display: flex; gap: 12px; align-items: center; padding: 14px 12px;
            border-bottom: 1px solid var(--border); }

.pricing-flag {
  font-size: 12px; color: var(--text-subtle);
  opacity: 0;
  display: inline-flex; align-items: center;
}
.pricing-flag.on { animation: breathe 1.1s ease-in-out infinite alternate; }
.pricing-flag::after { content: '…'; }
@keyframes breathe { from { opacity: .35; } to { opacity: 1; } }

/* ── tabs ──────────────────────────────────────────────────────────── */

.tabs { display: flex; flex-wrap: wrap; gap: 2px; margin-bottom: 15px;
        border-bottom: 1px solid var(--border); }
.tab {
  font: inherit; font-size: 12.5px; font-weight: 500;
  padding: 8px 12px; border: 0; background: none; cursor: pointer;
  color: var(--text-subtle); border-bottom: 2px solid transparent; margin-bottom: -1px;
  display: flex; align-items: center; gap: 7px;
  transition: color .12s var(--ease), border-color .12s var(--ease);
}
.tab:hover { color: var(--text); }
.tab.current { color: var(--text); border-bottom-color: var(--ink); font-weight: 600; }
/* an action, not a tab; sits apart and never takes the active underline */
.tab-add { color: var(--text-subtle); margin-left: 8px; }
.tab-add:hover { color: var(--text); }
.tab-add .plus { font-size: 15px; line-height: 1; font-weight: 400; }

/* actions that operate on the record above them, separated from the fields */
.row-actions {
  display: flex; gap: 8px; align-items: center; flex-wrap: wrap;
  margin-top: 16px; padding-top: 14px; border-top: 1px solid var(--border);
}

/* visual break between navigation-ish actions and record actions */
.action-divider { width: 1px; height: 20px; background: var(--border); margin: 0 2px; }

/* ── toolbar ───────────────────────────────────────────────────────── */

.toolbar { display: flex; gap: 8px; align-items: center; margin-bottom: 12px; flex-wrap: wrap; }
.toolbar input[type=text], .toolbar select { width: auto; min-width: 200px; }

kbd { font: inherit; font-size: 11px; padding: 1px 5px; border: 1px solid var(--border-strong);
      border-bottom-width: 2px; border-radius: 3px; background: var(--surface);
      color: var(--text-subtle); }

@media (prefers-reduced-motion: reduce) {
  *, *::before { animation-duration: .01ms !important; transition-duration: .01ms !important; }
}

/* ── the deal flow bar ──────────────────────────────────────────────
   Every step keeps all four borders and overlaps its neighbour by 1px,
   so a coloured step draws a COMPLETE ring. Dropping border-right
   instead left the active step's outline open on its right edge.     */
.flow { display: flex; gap: 0; margin-bottom: 10px; overflow-x: auto; padding: 1px; }
.flow .step { flex: 1 0 118px; padding: 8px 12px 9px; background: var(--surface);
              border: 1px solid var(--border); margin-right: -1px; position: relative; }
.flow .step:first-child { border-radius: var(--radius) 0 0 var(--radius); }
.flow .step:last-child { margin-right: 0; border-radius: 0 var(--radius) var(--radius) 0; }
.flow .step .n { font-size: 10.5px; font-weight: 700; color: var(--text-faint); }
.flow .step .t { font-size: 12px; font-weight: 600; margin-top: 1px; white-space: nowrap; }
.flow .step .s { font-size: 11px; color: var(--text-subtle); margin-top: 1px; white-space: nowrap; }
.flow .step.done { background: var(--ok-bg); border-color: #B8D9C4; z-index: 1; }
.flow .step.done .n, .flow .step.done .s { color: var(--ok-fg); opacity: .85; }
.flow .step.done .t { color: var(--ok-fg); }
.flow .step.blocked .t, .flow .step.blocked .s { color: var(--text-faint); }

/* the one contextual action under the bar */
.flow-next { display: flex; gap: 8px; align-items: center; flex-wrap: wrap;
             padding: 10px 12px; margin-bottom: 14px; background: var(--surface);
             border: 1px solid var(--border); border-radius: var(--radius); }
.flow-next .hint { font-size: 12.5px; color: var(--text-subtle); }

.flow .step.link { cursor: pointer; }
.flow .step.link:hover { background: var(--hover); }

/* one-line card: a couple of controls that do not warrant full card chrome */
.card-row { display: flex; align-items: center; gap: 8px 20px; flex-wrap: wrap;
            padding: 11px 16px; }
.card-row .field { display: flex; align-items: center; gap: 9px; margin: 0; }
.card-row .field > span { margin: 0; white-space: nowrap; }
.card-row .affix { width: 92px; }
.card-row > .cell-sub { margin-left: auto; }
.card-row .lead-label { flex: 0 0 46px; font-size: 12.5px; font-weight: 500;
                        color: var(--text-muted); }
.card-row .lead-name { flex: 0 0 190px; font-weight: 600; }
.card-row .lead-rest { color: var(--text-muted); }

/* full-width action closing out the summary rail */
.btn-rail { width: 100%; margin-top: 16px; }

/* The customer document, shown in place rather than behind a button. While
   the document renders, the wrapper says so; a blank white box reads as a
   click that did nothing. */
.preview-wrap { position: relative; }
.preview-wrap::before { content: 'Preparing the document…'; position: absolute; inset: 0;
                        display: flex; align-items: center; justify-content: center;
                        color: var(--text-subtle); font-size: 13px; }
.quote-preview { display: block; width: 100%; height: 320px; /* floor; sizePreview() measures the rest */
                 border: 1px solid var(--border); border-radius: var(--radius-sm);
                 background: #fff; position: relative; opacity: 0; transition: opacity .15s var(--ease); }
.quote-preview.loaded { opacity: 1; }

/* marks a field that blocks the step until it is filled */
.req { color: var(--bad-fg); font-weight: 700; margin-left: 3px; }

/* Section headings inside a card body (the pre-qual form): the .card > h2
   rules do not reach them, so the note ran straight into the title. */
#pqInfo h2 { display: flex; align-items: baseline; gap: 10px; }
#pqInfo h2 .note { margin-left: auto; font-size: 12.5px; font-weight: 400;
                   color: var(--text-subtle); }
#pqInfo h2:first-child { margin-top: 4px; padding-top: 0; border-top: 0; }
#pqInfo .msg + h2 { margin-top: 16px; padding-top: 0; border-top: 0; }

/* a step that was done but no longer matches what went out */
.flow .step.stale { background: var(--bad-bg); border-color: #D8B4AC; }
.flow .step.stale .t, .flow .step.stale .s { color: var(--bad-fg); opacity: 1; }
.flow .step.stale .n { color: var(--bad-fg); opacity: .8; }

/* WHERE YOU ARE is a separate fact from what the step's state is, so it is
   drawn on top of any of them: the fill keeps saying done / blocked / stale
   while the ink edge says you are standing here. Must stay below every
   state rule above: equal specificity, so source order decides. */
.flow .step.active { border-color: var(--ink); z-index: 2; }
/* an unfinished step you are working in should not be greyed out */
.flow .step.blocked.active .t, .flow .step.blocked.active .s { color: var(--text); }
.flow-next .hint-warn { color: var(--bad-fg); font-weight: 500; }

/* what is enclosed in the credit packet */
.packet { border: 1px solid var(--border); border-radius: var(--radius-sm);
          padding: 11px 13px; margin-bottom: 8px; }
.packet-head { display: flex; align-items: center; gap: 9px; }
.packet-lines { margin-top: 5px; font-size: 12.5px; color: var(--text-muted); line-height: 1.55; }

/* ── the deal has left ─────────────────────────────────────────────────
   Once the packet is with credit the rep's job is done, so the last step
   stops being a form and becomes a receipt: what happened, then what to
   expect back. */
.done-card { display: flex; gap: 14px; align-items: center;
             padding: 15px 16px; margin-bottom: 4px;
             background: var(--ok-bg); border: 1px solid #B8D9C4;
             border-radius: var(--radius); }
.done-mark { flex: 0 0 32px; height: 32px; border-radius: 50%;
             background: var(--ok-fg); color: #fff; font-size: 17px; font-weight: 700;
             display: flex; align-items: center; justify-content: center; }
.done-card h3 { margin: 0; font-size: 15px; font-weight: 600; color: var(--ok-fg); }
.done-card p { margin: 3px 0 0; font-size: 12.5px; color: var(--ok-fg); opacity: .85; }
/* the same card carries credit's answer; colour says which kind */
.done-card.done-warn { background: var(--warn-bg); border-color: #E0C89A; }
.done-card.done-warn .done-mark { background: var(--warn-fg); }
.done-card.done-warn h3, .done-card.done-warn p { color: var(--warn-fg); }
.done-card.done-bad { background: var(--bad-bg); border-color: #D8B4AC; }
.done-card.done-bad .done-mark { background: var(--bad-fg); }
.done-card.done-bad h3, .done-card.done-bad p { color: var(--bad-fg); }

.outcome { display: flex; gap: 14px; align-items: baseline;
           padding: 9px 0; border-bottom: 1px solid var(--border); font-size: 13px; }
.outcome:last-of-type { border-bottom: 0; }
.outcome b { flex: 0 0 220px; font-weight: 600; }
.outcome span { color: var(--text-muted); }
@media (max-width: 700px) {
  .outcome { display: block; }
  .outcome b { display: block; margin-bottom: 2px; }
}

/* the deal in order */
.timeline { list-style: none; margin: 0; padding: 0; }
.timeline li { display: flex; gap: 14px; padding: 5px 0; font-size: 12.5px;
               border-bottom: 1px solid var(--border); }
.timeline li:last-child { border-bottom: 0; }
.timeline .tw { flex: 0 0 86px; color: var(--text-subtle); font-variant-numeric: tabular-nums; }

/* ═══ DEVICE OVERRIDES: keep this section LAST ═══════════════════════
   Equal-specificity rules resolve by source order, so everything here
   must come after the base rules it adjusts. Moving a base rule below
   this point silently disables the override it fights.

   The tool is used on an iPad, so a finger is the pointer. Two things
   follow, and both are functional rather than cosmetic:

   1. Any field under 16px makes iOS Safari ZOOM THE PAGE when it takes
      focus, and it does not zoom back out. At 13.5px every tap on every
      field threw the layout around.
   2. A 28px button is below Apple's 44pt minimum; the tax-exempt
      checkbox was 13px, which is a coin toss to hit.

   Keyed on `pointer: coarse` so the desktop design is untouched.       */
@media (pointer: coarse) {
  input[type=text], input[type=email], input[type=password],
  select, textarea { font-size: 16px; height: 44px; }
  textarea { height: auto; min-height: 88px; }
  .btn { height: 44px; font-size: 14px; padding: 0 16px; }
  .btn-sm { height: 38px; font-size: 13.5px; padding: 0 12px; }
  .check { padding: 6px 0; }
  .check input { width: 22px; height: 22px; }
  .tab { padding: 12px 14px; font-size: 13.5px; }
  .field > span { font-size: 13px; }
  /* a row of actions needs room between the targets, not just inside them */
  .row-actions, .actions, .page-head .actions { gap: 10px; }
  td.cell-actions .btn { margin-left: 4px; }
  /* the readout is read, not tapped: keep it dense so it still fits.
     .kv-row keeps the horizontal padding its pull-out margin pays for. */
  aside .kv { padding: 7px 0; }
  aside .kv-row { padding: 7px 8px; }
  .suggest-row { padding: 13px 12px; }
  .flow .step { padding: 11px 12px 12px; }
}

/* ── the rail on a tablet ──────────────────────────────────────────────
   228px is 28% of an iPad held in portrait, spent on navigation a rep
   uses twice a day. Below this width it becomes icons, returning ~160px
   to the work. Labels stay in the title attribute. */
@media (max-width: 900px) {
  :root { --rail-w: 76px; }
  .nav { padding: 14px 6px; align-items: center; }
  .nav-brand, .nav-section, .nav-tag, .nav-foot { display: none; }
  .nav-item {
    flex-direction: column; justify-content: center; gap: 3px;
    width: 64px; padding: 9px 2px; margin-bottom: 2px;
  }
  /* a caption, not a tooltip: title/hover does not exist on touch */
  .nav-item span:not(.nav-tag) { font-size: 9px; font-weight: 500; max-width: 60px;
    overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
  .nav-item svg { width: 20px; height: 20px; }
  /* the groups still read as groups, without their headings */
  .nav-section + .nav-item { margin-top: 14px; position: relative; }
  .nav-section + .nav-item::before {
    content: ''; position: absolute; top: -8px; left: 12px; right: 12px;
    height: 1px; background: #26262A;
  }
}

/* stacked layouts scroll the page, not the rail; must follow the
   .rail-scroll base rule to win on source order */
@media (max-width: 960px) {
  .rail-scroll { overflow-y: visible; scrollbar-gutter: auto; }
}

/* Admin credit decision answers */

.answers {
  display: grid;
  grid-template-columns: repeat(4, 1fr);
  gap: 8px;
}

@media (max-width: 860px) {
  .answers {
    grid-template-columns: repeat(2, 1fr);
  }
}

@media (max-width: 480px) {
  .answers {
    grid-template-columns: 1fr;
  }
}

.answer {
  display: flex;
  flex-direction: column;
  gap: 3px;
  text-align: left;
  height: auto;
  padding: 11px 13px;
  white-space: normal;
  align-items: flex-start;
  justify-content: flex-start;
}

  .answer b {
    font-weight: 600;
    font-size: 13px;
    min-height: 2.6em;
  }

  .answer span {
    font-size: 11.5px;
    color: var(--text-subtle);
    line-height: 1.35;
  }

  .answer.current {
    border-color: var(--text);
    box-shadow: inset 0 0 0 1px var(--text);
  }

    .answer.current span {
      color: var(--text-muted);
    }

.danger-zone p {
  font-size: 12.5px;
  color: var(--text-muted);
  margin: 0 0 12px;
}

/* Settings - bad debt by contract year */
.bd-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(56px, 1fr));
  gap: 6px;
}

  .bd-grid .field {
    margin: 0;
  }

  .bd-grid input {
    text-align: center;
    padding: 0 2px;
  }