/* ============================================================================
   iTwin Invoice Studio — app chrome.
   Apple grouped-settings idiom: an off-white ground, white surfaces carrying a
   half-pixel edge and one soft shadow (not hairline borders everywhere), section
   titles outside their cards, filled inputs, and exactly one accent — used only
   for the primary action, focus, links and toggles. Light only, on purpose: a
   professional tool has one look, and the invoice it previews is white paper.
   The invoice document itself is styled in invoice-template.js; it is the PDF.
   ========================================================================== */

:root {
  color-scheme: light;

  /* Ground & surfaces */
  --bg: #f5f5f7;
  --surface: #ffffff;
  --surface-2: #ffffff;
  --desk: #e5e5ea;              /* the grey the preview sheet sits on */

  /* Ink */
  --ink: #1d1d1f;
  --muted: #6e6e73;             /* 4.7:1 on white — #86868b failed AA at 12px */
  --faint: #86868b;             /* decorative only, >= 15px */
  --placeholder: #aeaeb2;

  /* System fills (Apple's translucent greys) */
  --fill-4: rgba(120, 120, 128, 0.08);
  --fill-3: rgba(120, 120, 128, 0.12);
  --fill-2: rgba(120, 120, 128, 0.16);
  --separator: rgba(60, 60, 67, 0.12);

  /* Accent & semantics */
  --accent: #0071e3;
  --accent-d: #0077ed;
  --accent-wash: rgba(0, 113, 227, 0.10);
  --accent-wash-h: rgba(0, 113, 227, 0.16);
  --ring: rgba(0, 113, 227, 0.28);
  --ok: #1a8f43;
  --ok-wash: rgba(52, 199, 89, 0.14);
  --warn: #9a6700;
  --warn-wash: rgba(255, 159, 10, 0.16);
  --err: #d70015;
  --err-wash: rgba(215, 0, 21, 0.09);

  /* Materials */
  --glass: rgba(255, 255, 255, 0.78);
  --glass-blur: saturate(180%) blur(20px);

  --radius: 14px;
  --radius-sm: 10px;
  --radius-lg: 18px;
  --shadow-card: 0 0 0 0.5px rgba(0, 0, 0, 0.06), 0 1px 3px rgba(0, 0, 0, 0.05);
  --shadow-lg: 0 12px 40px rgba(0, 0, 0, 0.16);
  --shadow-paper: 0 1px 2px rgba(0, 0, 0, 0.08), 0 12px 32px rgba(0, 0, 0, 0.12);

  --ease-out: cubic-bezier(0.2, 0.8, 0.2, 1);
  --ease-sheet: cubic-bezier(0.32, 0.72, 0, 1);

  /* 11px padding + 34px controls. The pinned preview sits exactly under it. */
  --topbar-h: 56px;
}

* { box-sizing: border-box; }
/* The `hidden` attribute must win over any display rule that follows it —
   .settings-row{display:flex} was quietly showing both Connect and Disconnect. */
[hidden] { display: none !important; }

html { height: 100%; }
body {
  margin: 0;
  /* One scroller: the document. Two panels scrolling on their own gave a Mac trackpad
     33px of form to rubber-band in at 1440×900, and a different scroller under every
     cursor position — "jiggly" was the word. The form scrolls with the page and the
     paper is pinned beside it. */
  min-height: 100dvh;
  display: flex;
  flex-direction: column;
  font-family: -apple-system, BlinkMacSystemFont, "SF Pro Text", "Segoe UI", "Helvetica Neue", Arial, sans-serif;
  background: var(--bg);
  color: var(--ink);
  font-size: 14px;
  -webkit-font-smoothing: antialiased;
  -webkit-text-size-adjust: 100%;
  text-rendering: optimizeLegibility;
}

/* Icon sprite host — never rendered, but must stay in the layout tree for <use>. */
.sprite { position: absolute; width: 0; height: 0; overflow: hidden; }
svg { fill: none; }

h1, h2 { margin: 0; font-weight: 600; }

/* One focus ring for the whole app, keyboard-only. */
:focus-visible { outline: none; box-shadow: 0 0 0 4px var(--ring); }
.btn:focus-visible, .pill:focus-visible, .icon-btn:focus-visible,
.sheet-item:focus-visible, .client-pick:focus-visible, .add-row:focus-visible,
.summary-row:focus-visible, .status:focus-visible {
  outline: none; box-shadow: 0 0 0 4px var(--ring);
}

/* ---------------------------------------------------------------- top bar */
.topbar {
  flex: 0 0 auto;
  display: flex; align-items: center; gap: 14px;
  padding: 11px 20px;
  background: var(--glass);
  backdrop-filter: var(--glass-blur);
  -webkit-backdrop-filter: var(--glass-blur);
  box-shadow: 0 0.5px 0 var(--separator);
  position: sticky; top: 0; z-index: 20;
  height: var(--topbar-h);
}
.brand { display: flex; align-items: center; gap: 9px; min-width: 0; }
.brand .mark { height: 26px; width: auto; flex: 0 0 auto; }
.brand .t { font-size: 17px; font-weight: 600; letter-spacing: -0.02em; white-space: nowrap; }
.actions { display: flex; align-items: center; gap: 8px; margin-left: auto; }

/* Past two clients a segmented control cannot hold them: at 375px three labels are
   96px wide and all three truncate. Beyond two, the picker becomes one button that
   opens a list, which scales to any number. */
.client-pick {
  /* A client can be named anything now that they are user-created; the top bar's other
     controls are not negotiable, so the name is what gives way. */
  display: inline-flex; align-items: center; gap: 6px; min-width: 0; max-width: 260px;
  padding: 6px 10px 6px 12px; border-radius: 9px; background: var(--fill-3);
  border: 0; font-family: inherit; font-weight: 600; font-size: 13px; line-height: 1.25; color: var(--ink); cursor: pointer;
  transition: background .15s;
}
.client-pick:hover { background: var(--fill-2); }
.client-pick > span { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.client-pick svg { width: 14px; height: 14px; flex: 0 0 auto; color: var(--muted); transform: rotate(90deg); }
.client-list { display: flex; flex-direction: column; }
.client-row {
  display: flex; align-items: center; gap: 12px; width: 100%; text-align: left;
  padding: 14px 12px; border: 0; background: none; border-radius: 10px;
  font-family: inherit; font-weight: 400; font-size: 15px; line-height: 1.3; color: var(--ink); cursor: pointer;
  box-shadow: inset 0 -0.5px 0 var(--separator);
}
.client-list .client-row:last-child { box-shadow: none; }
.client-row:hover { background: var(--fill-4); }
.client-row .tick { width: 18px; flex: 0 0 auto; color: var(--accent); font-weight: 600; }
.client-row[aria-checked="true"] { font-weight: 600; }

/* Gmail status: a dot and a short word, not a banner of prose. */
.pill {
  display: inline-flex; align-items: center; gap: 7px;
  font-size: 12.5px; padding: 6px 12px; border-radius: 999px; font-weight: 500;
  border: none; font-family: inherit; cursor: pointer; max-width: 240px;
  transition: background .15s;
}
.pill > span { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.pill .dot { width: 7px; height: 7px; border-radius: 50%; background: currentColor; flex: 0 0 auto; opacity: .9; }
button.pill:hover { filter: brightness(0.96); }
.pill-muted { background: var(--fill-3); color: var(--muted); }
.pill-ok { background: var(--ok-wash); color: var(--ok); }
.pill-warn { background: var(--warn-wash); color: var(--warn); }

/* ---------------------------------------------------------------- buttons */
.btn {
  display: inline-flex; align-items: center; justify-content: center; gap: 6px;
  border: 1px solid transparent; border-radius: var(--radius-sm);
  padding: 8px 14px; font-family: inherit; font-weight: 500; font-size: 13px; line-height: 1.25; color: var(--ink); cursor: pointer;
  transition: background .15s, color .15s, box-shadow .15s, transform .08s var(--ease-out);
}
.btn svg { width: 16px; height: 16px; flex: 0 0 auto; }
.btn:active { transform: scale(0.97); }
.btn-primary { background: var(--accent); color: #fff; }
.btn-primary:hover { background: var(--accent-d); }
.btn-ghost { background: var(--fill-3); color: var(--ink); }
.btn-ghost:hover { background: var(--fill-2); }
.btn-soft { background: var(--accent-wash); color: var(--accent); }
.btn-soft:hover { background: var(--accent-wash-h); }
/* A filled pill is a call to action. In the form there is only one of those — Send —
   so every other action recedes to text weight; btn-soft stays for dialogs. */
.btn-text { background: transparent; color: var(--accent); padding: 6px 8px; }
.btn-text:hover { background: var(--accent-wash); }
.btn-text.mini { padding: 5px 8px; }
.btn-danger { background: var(--err-wash); color: var(--err); }
.btn-danger:hover { filter: brightness(0.97); }
.btn-danger-solid { background: var(--err); color: #fff; }
.btn-danger-solid:hover { filter: brightness(1.06); }
.btn:disabled { opacity: .5; cursor: default; transform: none; }
.btn.mini { padding: 6px 11px; font-size: 12.5px; }
.btn.mini svg { width: 14px; height: 14px; }

.btn-icon { width: 34px; height: 34px; padding: 0; background: var(--fill-3); color: var(--ink); }
.btn-icon:hover { background: var(--fill-2); }
.btn-icon svg { width: 18px; height: 18px; }

/* ----------------------------------------------------------------- layout */
.layout {
  flex: 1 1 auto;
  display: grid;
  /* A form is a column of fields and reads best around 600px; a fluid split gave it
     width it could not use and took it from the paper. */
  grid-template-columns: minmax(440px, 600px) minmax(460px, 1fr);
}
.form { padding: 20px 22px 40px; }
/* Pinned under the top bar and exactly one viewport tall, so the whole sheet is always
   in view and nothing inside the panel ever needs a scrollbar of its own. */
.preview {
  position: sticky; top: var(--topbar-h); height: calc(100dvh - var(--topbar-h));
  background: var(--desk); display: flex; flex-direction: column;
  box-shadow: inset 0.5px 0 0 var(--separator);
}

/* ------------------------------------------------------- grouped sections */
.group { margin-bottom: 20px; }
.group-h { display: flex; align-items: center; gap: 8px; margin: 0 0 6px 4px; min-height: 22px; }
/* A small tracked uppercase label names the group and gets out of the way. At 13px/600
   sentence case these read as titles, which is what made five of them compete. */
.group-h h2 { font-family: inherit; font-weight: 600; font-size: 11.5px; line-height: 1.2; letter-spacing: .06em; text-transform: uppercase; color: var(--muted); }
.group-h .btn { margin-left: auto; }

.card {
  background: var(--surface); border: 0; border-radius: var(--radius);
  padding: 18px 20px; box-shadow: var(--shadow-card);
}

.row { display: flex; gap: 12px; align-items: flex-end; flex-wrap: wrap; }
.row + .row { margin-top: 12px; }
.field { display: flex; flex-direction: column; gap: 6px; flex: 1 1 130px; min-width: 0; }
.field.grow { flex: 2 1 200px; }
.field.small { flex: 0 0 96px; }
.field > span { font-family: inherit; font-weight: 500; font-size: 12px; line-height: 1.2; color: var(--muted); }

input, select, textarea {
  font: inherit; width: 100%; padding: 9px 12px; color: var(--ink);
  background: var(--fill-4); border: 1px solid transparent; border-radius: var(--radius-sm);
  outline: none; transition: background .15s, border-color .15s, box-shadow .15s;
}
input:hover, select:hover, textarea:hover { background: var(--fill-3); }
input:focus, select:focus, textarea:focus {
  background: var(--surface); border-color: var(--accent); box-shadow: 0 0 0 4px var(--ring);
}
input::placeholder, textarea::placeholder { color: var(--placeholder); }
textarea { resize: vertical; line-height: 1.5; }
select { appearance: none; -webkit-appearance: none; cursor: pointer;
  background-image: linear-gradient(45deg, transparent 50%, var(--muted) 50%), linear-gradient(135deg, var(--muted) 50%, transparent 50%);
  background-position: calc(100% - 15px) 51%, calc(100% - 10px) 51%;
  background-size: 5px 5px, 5px 5px; background-repeat: no-repeat; padding-right: 30px;
}
input[type=number] { -moz-appearance: textfield; }
input[type=number]::-webkit-inner-spin-button, input[type=number]::-webkit-outer-spin-button { -webkit-appearance: none; margin: 0; }
/* Figures line up in a column and don't shift width as you type. */
#year, [data-k="hours"], [data-k="rate"], [data-k="amount"] { text-align: right; font-variant-numeric: tabular-nums; }

/* Period row: month, year, and a single icon for rebuild — no orphan button. */
.period-row { display: grid; grid-template-columns: 1fr 96px 34px; gap: 12px; align-items: end; }
.period-row .btn-icon { margin-bottom: 1px; }
/* The month row is the input and the summary line is its output — one thought, so a
   hairline rather than a second card. */
.period-row + .summary-row { margin-top: 14px; padding-top: 12px; box-shadow: inset 0 0.5px 0 var(--separator); border-radius: 0; }

/* This month's status once it has gone out: when, to whom, whether it has been paid —
   and a warning if the number on screen would repeat one already sent. */
.sent-row { display: flex; align-items: center; gap: 10px; flex-wrap: wrap; margin-top: 12px; font-size: 13px; color: var(--muted); }
.sent-row .sent-text { flex: 1 1 auto; min-width: 0; }
.sent-row .sent-text b { color: var(--ink); font-weight: 500; }
.sent-row .icon-btn { opacity: 1; width: 28px; height: 28px; }
.warn-tag {
  display: inline-block; padding: 2px 8px; border-radius: 999px;
  background: var(--err-wash); color: var(--err); font-size: 11.5px; font-weight: 500;
}

/* ------------------------------------------- invoice details (disclosure) */
/* Five derived fields the monthly job never touches. They collapse to a summary
   line; the grid-rows trick animates height without touching layout elsewhere. */
.summary-row {
  display: flex; align-items: center; gap: 12px; width: 100%;
  background: none; border: 0; padding: 2px 0; cursor: pointer;
  font: inherit; color: var(--ink); text-align: left; border-radius: 8px;
}
.summary-text {
  flex: 1; min-width: 0; font-size: 13px; line-height: 1.4; color: var(--muted);
  font-variant-numeric: tabular-nums; overflow: hidden;
  display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical;
}
.summary-text b { color: var(--ink); font-weight: 500; }
.summary-text .dim { color: var(--faint); }
.summary-edit { display: inline-flex; align-items: center; gap: 3px; color: var(--accent); font-family: inherit; font-weight: 500; font-size: 13px; flex: 0 0 auto; }
.summary-edit svg { width: 15px; height: 15px; transition: transform .24s var(--ease-out); }
.card.details-open .summary-edit svg { transform: rotate(90deg); }
.details-wrap {
  display: grid; grid-template-rows: 0fr; opacity: 0;
  transition: grid-template-rows .26s var(--ease-out), opacity .2s var(--ease-out);
}
.details-inner { overflow: hidden; min-height: 0; visibility: hidden; transition: visibility 0s .26s; }
.card.details-open .details-inner { visibility: visible; transition: visibility 0s; }
.card.details-open .details-wrap { grid-template-rows: 1fr; opacity: 1; }
.details-inner > .row:first-child, .details-inner > .field:first-child { padding-top: 16px; margin-top: 0; }
.details-inner > .field { margin-top: 12px; }

/* ------------------------------------------------------------- line items */
.items-head, .item-row { display: grid; grid-template-columns: 2fr .8fr .9fr 1.1fr 34px; gap: 8px; align-items: center; }
.items-head {
  font-family: inherit; font-weight: 500; font-size: 12px; line-height: 1.2; color: var(--muted);
  margin-bottom: 8px; padding: 0 2px;
}
.items-head span:nth-child(2), .items-head span:nth-child(3), .items-head span:nth-child(4) { text-align: right; }
/* Each input carries its own label so a phone can stack the row into a readable card;
   on desktop the .items-head row names the columns and the labels go visually hidden
   (not display:none — the inputs would then have no accessible name at all). */
.cell { display: flex; flex-direction: column; gap: 5px; min-width: 0; }
.cell-l {
  position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px;
  overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; border: 0;
}
.item-row { margin-bottom: 6px; }
.item-row input { padding: 8px 10px; }
.item-row.is-new { animation: rowIn .18s var(--ease-out); }
@keyframes rowIn { from { opacity: 0; transform: translateY(-4px); } }

.icon-btn {
  display: inline-flex; align-items: center; justify-content: center;
  border: none; background: transparent; color: var(--muted); border-radius: 8px;
  width: 34px; height: 34px; cursor: pointer; padding: 0;
  transition: background .15s, color .15s, opacity .15s;
}
.icon-btn svg { width: 15px; height: 15px; }
.icon-btn:hover { background: var(--err-wash); color: var(--err); }
/* Destructive controls stay out of the way until the row is in play. */
.item-row .icon-btn, .recipient .icon-btn { opacity: 0; }
.item-row:hover .icon-btn, .item-row:focus-within .icon-btn,
.recipient:hover .icon-btn, .recipient:focus-within .icon-btn { opacity: 1; }

/* "Add row" reads as the next line of the list, the way Reminders does it. */
.add-row {
  display: flex; align-items: center; gap: 8px; width: 100%;
  padding: 10px 8px; margin-top: 2px; border: 0; background: none;
  color: var(--accent); font-family: inherit; font-weight: 500; font-size: 13.5px; cursor: pointer; border-radius: var(--radius-sm);
  transition: background .15s;
}
.add-row svg { width: 16px; height: 16px; }
.add-row:hover { background: var(--accent-wash); }

.total-line {
  display: flex; justify-content: space-between; align-items: baseline;
  margin-top: 14px; padding-top: 14px; box-shadow: inset 0 0.5px 0 var(--separator);
}
.total-line > span { font-size: 13px; color: var(--muted); font-weight: 500; }
.total-line strong { font-family: inherit; font-weight: 600; font-size: 22px; line-height: 1; letter-spacing: -0.02em; font-variant-numeric: tabular-nums; }

/* ------------------------------------------------------------- recipients */
.recipient { display: grid; grid-template-columns: 34px 1fr 1.6fr 34px; gap: 8px; align-items: center; margin-bottom: 6px; }
.chk { display: inline-flex; align-items: center; justify-content: center; width: 34px; height: 34px; cursor: pointer; }
.chk input[type=checkbox] { width: 18px; height: 18px; accent-color: var(--accent); margin: 0; padding: 0; }
.rcp-sum { display: none; }
.recipient.needs-email input[data-k="email"] { border-color: var(--warn); background: var(--warn-wash); }
.needs-email-hint { grid-column: 2 / -1; font-size: 12px; color: var(--warn); margin: -2px 0 4px; }

/* --------------------------------------------------------- email & hints */
.ph { margin-top: 12px; }
.ph summary { font-size: 12.5px; color: var(--accent); cursor: pointer; list-style: none; width: fit-content; }
.ph summary::-webkit-details-marker { display: none; }
.ph summary::before { content: "▸ "; display: inline-block; transition: transform .2s var(--ease-out); }
.ph[open] summary::before { content: "▾ "; }
.hint { font-size: 12px; color: var(--muted); margin-top: 8px; line-height: 1.75; }
.hint code { background: var(--fill-3); padding: 2px 6px; border-radius: 5px; font-size: 11px; color: var(--ink); }

.form-foot { display: flex; justify-content: flex-end; }

/* ---------------------------------------------------------------- preview */
.preview-bar {
  display: flex; align-items: baseline; gap: 10px; padding: 14px 24px 2px;
}
.preview-bar .overline { font-family: inherit; font-weight: 600; font-size: 11px; line-height: 1.2; letter-spacing: .07em; text-transform: uppercase; color: var(--muted); }
.preview-bar .note { font-size: 12px; color: var(--muted); margin-left: auto; }
.preview-frame {
  flex: 1; min-height: 0; position: relative; overflow: hidden;
  padding: 16px 24px 24px; display: flex; justify-content: center; align-items: flex-start;
}
/* The paper is a live thumbnail of the PDF; one click opens it full size. The iframe
   swallows clicks, so a transparent button sits over it. */
.preview-open { position: absolute; inset: 0; border: 0; background: transparent; cursor: zoom-in; padding: 0; }
.preview-open:focus-visible { box-shadow: inset 0 0 0 4px var(--ring); }
.preview-frame:has(.preview-open:hover) #preview { box-shadow: var(--shadow-paper), 0 0 0 3px var(--ring); }
#preview, #pmFrame {
  width: 210mm; min-height: 297mm; border: none; background: #fff; border-radius: 2px;
  box-shadow: var(--shadow-paper); transform-origin: top left; flex: 0 0 auto;
}

/* Below this the paper renders under 0.55 and the type is unreadable; the form gives
   up its comfortable width first, because it is the half you are not reading. */
@media (max-width: 1140px) {
  .layout { grid-template-columns: 460px minmax(0, 1fr); }
  /* Measured at 960px: the item row is 361px and four inputs at their natural widths
     ("10 - Rhinokey", "160", "€2,00", "3866.51") need 300 of it. The old fluid split
     cut "Fixed" to "Fixe" and "3866.51" to "3866.!", so the figures get fixed columns
     sized to their longest value, and the description takes what is left. */
  .items-head, .item-row { grid-template-columns: minmax(0, 1fr) 50px 60px 74px 28px; gap: 6px; }
  .items-head { font-size: 11.5px; }
  .item-row input { padding: 8px; }
  .item-row .icon-btn { width: 28px; }
}

/* ----------------------------------------------------------------- modals */
.modal {
  position: fixed; inset: 0; z-index: 50;
  display: flex; align-items: safe center; justify-content: center;
  padding: 24px 16px; overflow-y: auto;
  background: rgba(0, 0, 0, 0.4); backdrop-filter: blur(4px); -webkit-backdrop-filter: blur(4px);
  opacity: 1; visibility: visible;
  transition: opacity .2s var(--ease-out), visibility 0s;
}
.modal.hidden {
  display: flex; opacity: 0; visibility: hidden; pointer-events: none;
  transition: opacity .16s ease-in, visibility 0s .16s;
}
.modal-card {
  background: var(--surface); border-radius: var(--radius-lg); padding: 24px;
  width: 440px; max-width: 100%; margin: auto;
  max-height: calc(100dvh - 48px); overflow-y: auto; overscroll-behavior: contain;
  box-shadow: var(--shadow-lg);
  transition: transform .24s var(--ease-out), opacity .2s var(--ease-out);
}
.modal.hidden .modal-card { transform: scale(.96) translateY(6px); opacity: 0; }
.modal-card.wide { width: 620px; }
.modal-card.narrow { width: 380px; }
/* A confirm is always answering a question asked by whatever is already open, so it has
   to be above all of it — including the menu sheet (55) and the full-screen preview.
   DOM order decided this before, and #confirmModal comes first, so it lost. */
#confirmModal { z-index: 58; }
.modal-h { font-family: inherit; font-weight: 600; font-size: 20px; line-height: 1.25; letter-spacing: -0.02em; }
.modal-sub { color: var(--muted); font-size: 13px; margin: 6px 0 16px; line-height: 1.5; }
.modal-sub b { color: var(--ink); font-weight: 600; }
.modal-actions { display: flex; justify-content: flex-end; gap: 10px; margin-top: 20px; }
/* The card scrolls, so in a long dialog the primary button was below the fold and had to
   be discovered. Sticking it to the bottom of the card keeps it where it is expected. */
.modal-card .modal-actions {
  position: sticky; bottom: -24px; margin: 20px -24px -24px;
  padding: 14px 24px 24px; background: var(--surface);
  box-shadow: 0 -0.5px 0 var(--separator);
}

.send-list { display: flex; flex-direction: column; gap: 8px; margin-bottom: 12px; }
.send-row, .hrow, .settings-row, .connect-row {
  border: 0; background: var(--fill-4); border-radius: 12px;
}
.send-row { display: flex; align-items: center; gap: 10px; padding: 12px 14px; }
.send-row .who { font-weight: 500; }
.send-row .em { color: var(--muted); font-size: 12.5px; margin-left: auto; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.send-row.skipped { opacity: .55; }
.connect-row { display: flex; align-items: center; justify-content: space-between; gap: 14px; padding: 14px; margin-bottom: 12px; }
.send-result { font-size: 13px; }
.send-result .line { padding: 4px 0; }
.send-result .ok { color: var(--ok); }
.send-result .bad { color: var(--err); }
.bcc-line { display: flex; align-items: center; gap: 9px; font-size: 13.5px; margin: 6px 0 14px; cursor: pointer; }
.bcc-line input { width: 17px; height: 17px; accent-color: var(--accent); }

/* ---------------------------------------------------------------- history */
.history-list { display: flex; flex-direction: column; gap: 8px; }
.history-foot {
  display: flex; align-items: center; justify-content: space-between; gap: 12px; flex-wrap: wrap;
  margin-top: 4px; padding: 12px 14px; border-radius: 12px; background: var(--fill-4);
  font-size: 12.5px; color: var(--muted);
}
.history-empty { color: var(--muted); font-size: 13.5px; padding: 28px 0; text-align: center; }
.hrow { display: grid; grid-template-columns: 1.3fr 1fr auto auto; gap: 12px; align-items: center; padding: 12px 14px; }
.hrow .hnum { font-weight: 600; }
.hrow .hsub { font-size: 12px; color: var(--muted); margin-top: 1px; }
.hrow .hamt { font-weight: 600; text-align: right; font-variant-numeric: tabular-nums; }
.hrow .hto { font-size: 12px; color: var(--muted); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.hrow .hact { display: flex; align-items: center; gap: 4px; }
.hrow .icon-btn { opacity: 1; }
.status { font-family: inherit; font-weight: 500; font-size: 12px; padding: 6px 12px; border-radius: 999px; border: none; cursor: pointer; transition: background .15s; }
.status.paid { background: var(--ok-wash); color: var(--ok); }
.status.unpaid { background: var(--fill-3); color: var(--muted); }
.status.unpaid:hover { background: var(--fill-2); }

/* --------------------------------------------------------------- settings */
.settings-group { margin-bottom: 22px; }
.settings-group:last-of-type { margin-bottom: 0; }
.settings-row { display: flex; align-items: center; justify-content: space-between; gap: 16px; padding: 14px; margin: 6px 0; }
.settings-k { font-weight: 500; font-size: 13.5px; }
.settings-v { font-size: 12px; color: var(--muted); margin-top: 2px; line-height: 1.45; }
.settings-btns { display: flex; gap: 8px; flex-shrink: 0; }
/* Group titles were 13px/600 muted above field labels of 12px/500 muted — one point
   apart, same colour, no rule between them, so five groups read as one wall of fields.
   The form's own overline idiom separates them at a glance. */
.settings-label {
  font-family: inherit; font-weight: 600; font-size: 11.5px; line-height: 1.2; letter-spacing: .06em; text-transform: uppercase;
  color: var(--muted); margin: 0 0 10px 4px;
}
.settings-label small { text-transform: none; letter-spacing: 0; font-size: 12px; font-weight: 400; }
.settings-group + .settings-group {
  margin-top: 24px; padding-top: 18px; box-shadow: inset 0 .5px 0 var(--separator);
}
/* Fields stacked straight into a settings group — .row spaces its own siblings, a lone
   field has nothing to space it against. */
.settings-group > .field + .field,
.settings-group > .field + .row,
.settings-group > .row + .field,
.settings-group > .logo-row + .row { margin-top: 12px; }
.narrow-field { max-width: 190px; }

/* Add / edit, under the client list */
.client-manage {
  display: flex; flex-wrap: wrap; gap: 4px; justify-content: space-between;
  margin-top: 10px; padding-top: 8px; border-top: 0.5px solid var(--separator);
}
.client-manage .btn-text svg { width: 15px; height: 15px; }
.client-row-wrap { display: flex; align-items: center; box-shadow: inset 0 -0.5px 0 var(--separator); }
.client-row-wrap:last-child { box-shadow: none; }
.client-row-wrap .client-row { flex: 1 1 auto; box-shadow: none; }
.client-row-wrap .icon-btn { flex: 0 0 auto; color: var(--muted); }

.logo-row { display: flex; gap: 16px; align-items: center; }
.logo-box {
  width: 116px; height: 62px; border-radius: 12px;
  background: #fff; box-shadow: inset 0 0 0 0.5px var(--separator);
  display: flex; align-items: center; justify-content: center; overflow: hidden; flex-shrink: 0;
}
.logo-box.empty { background: var(--fill-4); color: var(--muted); font-size: 12px; text-align: center; padding: 0 8px; line-height: 1.25; }
.logo-box img { max-width: 84%; max-height: 84%; object-fit: contain; }
.logo-actions { display: flex; flex-direction: column; gap: 6px; align-items: flex-start; }

/* ------------------------------------------------------------ menu sheet */
.sheet-wrap {
  position: fixed; inset: 0; z-index: 55; display: flex; align-items: flex-end;
  background: rgba(0, 0, 0, 0.4); backdrop-filter: blur(4px); -webkit-backdrop-filter: blur(4px);
  opacity: 1; visibility: visible;
  transition: opacity .2s var(--ease-out), visibility 0s;
}
.sheet-wrap.hidden {
  display: flex; opacity: 0; visibility: hidden; pointer-events: none;
  transition: opacity .2s ease-in, visibility 0s .2s;
}
.sheet-card {
  background: var(--surface); width: 100%; border-radius: 22px 22px 0 0;
  padding: 6px 12px calc(14px + env(safe-area-inset-bottom, 0px));
  box-shadow: var(--shadow-lg);
  max-height: 88dvh; overflow-y: auto; overscroll-behavior: contain;
  transition: transform .32s var(--ease-sheet);
}
.sheet-wrap.hidden .sheet-card { transform: translateY(100%); }
.sheet-handle { width: 40px; height: 5px; border-radius: 3px; background: var(--fill-2); margin: 8px auto 10px; }
.sheet-item {
  display: flex; align-items: center; gap: 14px; width: 100%; text-align: left;
  background: none; border: none; padding: 15px 12px; font-family: inherit; font-weight: 500; font-size: 16px;
  color: var(--ink); cursor: pointer; border-radius: 12px;
}
.sheet-item svg { width: 21px; height: 21px; flex: 0 0 auto; color: var(--muted); }
.sheet-item small { color: var(--muted); font-weight: 400; }
.sheet-status .dot { width: 9px; height: 9px; border-radius: 50%; background: currentColor; flex: 0 0 auto; margin: 0 6px; }
.sheet-status.pill-ok { color: var(--ok); }
.sheet-status.pill-warn { color: var(--warn); }
.sheet-status.pill-muted { color: var(--muted); }
.sheet-status .sheet-status-t { color: var(--ink); }
.sheet-status small { margin-left: auto; max-width: 50%; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.sheet-item:active { background: var(--fill-4); }
.sheet-cancel { justify-content: center; color: var(--accent); font-weight: 600; margin-top: 2px; }

/* ------------------------------------------------- full-screen preview */
.preview-modal {
  position: fixed; inset: 0; z-index: 55; background: var(--desk);
  display: flex; flex-direction: column;
  opacity: 1; visibility: visible;
  transition: opacity .2s var(--ease-out), visibility 0s;
}
.preview-modal.hidden {
  display: flex; opacity: 0; visibility: hidden; pointer-events: none;
  transition: opacity .18s ease-in, visibility 0s .18s;
}
.pm-bar {
  display: flex; align-items: center; gap: 12px;
  padding: calc(10px + env(safe-area-inset-top, 0px)) 12px 10px;
  background: var(--glass); backdrop-filter: var(--glass-blur); -webkit-backdrop-filter: var(--glass-blur);
  box-shadow: 0 0.5px 0 var(--separator);
}
.pm-title { font-weight: 600; flex: 1; text-align: center; }
.pm-bar .btn { flex: 0 0 auto; }
.pm-body {
  flex: 1; overflow: auto; display: flex; justify-content: center;
  padding: 16px 16px calc(16px + env(safe-area-inset-bottom, 0px));
}

/* ------------------------------------------------------------------ toast */
.toast {
  position: fixed; bottom: 24px; left: 50%; z-index: 60;
  transform: translateX(-50%);
  background: rgba(29, 29, 31, 0.94);       /* no blur: it fades in (see .modal) */
  color: #fff; padding: 12px 20px; border-radius: 12px; font-size: 13.5px;
  box-shadow: var(--shadow-lg); width: max-content; max-width: min(90vw, 420px); text-align: center;
  transition: opacity .22s var(--ease-out), transform .22s var(--ease-out), visibility 0s;
}
.toast.hidden {
  display: block; opacity: 0; visibility: hidden; pointer-events: none;
  transform: translate(-50%, 12px);
  transition: opacity .18s ease-in, transform .18s ease-in, visibility 0s .18s;
}
.toast.ok { background: rgba(26, 143, 67, 0.95); }
.toast.bad { background: rgba(215, 0, 21, 0.95); }

/* Desktop-hidden by default; the IA breakpoint below turns both on. A plain rule
   placed *after* a media query would win on source order and silently undo it. */
.mobilebar { display: none; }
.menu-btn { display: none; }

/* ==========================================================================
   IA breakpoint — below 900px the side-by-side preview cannot exist
   (the grid alone asks for 880px of content), so the whole phone information
   architecture takes over: bottom action bar, hamburger sheet, full-screen
   preview. Density stays desktop until 640.
   ========================================================================== */
@media (max-width: 900px) {
  /* The preview panel is gone, so the form is the page. But the *document* must not be
     the scroller: on an iPhone that collapses the browser toolbar as you move, changes
     the viewport height under a page barely taller than one screen, and rubber-bands at
     both ends — the "jiggle". With html clipped, body becomes an ordinary scroll
     container: the toolbar and the viewport hold still, and the bounce can be turned off. */
  html { overflow: hidden; }
  body { display: block; height: 100%; min-height: 0; overflow-y: auto; overscroll-behavior-y: none; }
  .layout { display: block; }
  .preview { display: none; }
  /* Nothing in the top bar is needed mid-scroll on a phone — the bottom bar has
     the actions — and a sticky bar costs 60px of a short viewport. One row, not two:
     the wordmark is already the tab title and the home-screen icon, and the status
     pill was an amber warning sitting permanently above Send. Status moves into the
     sheet; the Send dialog already carries it where it actually matters. */
  .topbar { position: static; height: auto; padding: 9px 14px; gap: 10px; flex-wrap: nowrap; }
  #profile { min-width: 0; }
  .brand .t { display: none; }
  .client-pick { max-width: 100%; }
  .topbar #emailStatus, .topbar #historyBtn, .topbar #settingsBtn, .topbar #pdfBtn,
  .topbar #sendBtn { display: none; }
  .menu-btn { display: inline-flex; }
  .form-foot { display: none; }          /* it lives in the ☰ sheet on a phone */

  .mobilebar {
    display: flex; gap: 10px;
    position: fixed; left: 0; right: 0; bottom: 0; z-index: 40;
    padding: 10px 14px calc(10px + env(safe-area-inset-bottom, 0px));
    background: var(--glass);
    backdrop-filter: var(--glass-blur); -webkit-backdrop-filter: var(--glass-blur);
    box-shadow: 0 -0.5px 0 var(--separator);
  }
  /* Preview is a sanity check; Send is the job — they were drawn as equals. And the
     number you glance at before sending now rides on the button you press. */
  .mobilebar .btn { padding: 14px; font-size: 15px; border-radius: 12px; }
  .mobilebar .btn svg { width: 20px; height: 20px; }
  .mobilebar #previewBtnM { flex: 0 0 52px; width: 52px; height: 52px; padding: 0; }
  .mobilebar #sendBtnM { flex: 1; gap: 8px; }
  .send-total { font-weight: 500; opacity: .85; font-variant-numeric: tabular-nums; }
  .send-total:not(:empty)::before { content: "·"; margin-right: 8px; opacity: .6; }
  /* The bar is 71px plus whatever the home indicator claims — the old flat 82px
     hid the last rows of the form on a notched iPhone. */
  .layout { padding-bottom: calc(84px + env(safe-area-inset-bottom, 0px)); }
  .toast { bottom: calc(88px + env(safe-area-inset-bottom, 0px)); }

  /* Comfortable targets once there is no mouse. */
  .icon-btn, .menu-btn, #pmClose { min-width: 44px; min-height: 44px; }
  .item-row .icon-btn, .recipient .icon-btn { opacity: 1; }
  .btn.mini { padding: 9px 14px; font-size: 13px; }
  .chk { width: 44px; height: 44px; }
  .chk input[type=checkbox] { width: 22px; height: 22px; }
  .recipient { grid-template-columns: 44px 1fr 1.6fr 44px; }
  .items-head, .item-row { grid-template-columns: 2fr .8fr .9fr 1.1fr 44px; }

}


/* ==========================================================================
   Density breakpoint — a phone. Rows become labelled cards, dialogs become
   bottom sheets, and every control clears 16px so iOS stops zooming on focus.
   ========================================================================== */
@media (max-width: 640px) {
  .form { padding: 12px 14px 24px; }
  .card { padding: 12px 14px; }
  .group { margin-bottom: 16px; }
  .topbar { padding: 8px 12px; }
  .brand { display: none; }               /* the client picker is the title here */
  .client-pick { font-size: 15px; padding: 9px 10px 9px 12px; border-radius: 10px; }

  /* 15px on a 36px box, the size of a field in Settings. iOS would zoom the page on
     focusing any field under 16px, but maximum-scale=1 in the viewport meta suppresses
     that auto-zoom (pinch zoom still works — iOS ignores the cap for it). */
  input, select, textarea { font-size: 15px; padding: 8px 11px; }
  .cell-l { font-size: 11px; }
  .total-line strong { font-size: 20px; }

  /* Two of the editor's date controls share a row; at 375px the longer option text
     truncates to "Last day of the m". */
  .field:has(> #ceIssue), .field:has(> #ceDue) { flex-basis: 100%; }

  /* Save and Cancel are 600px of scroll below the top of a long dialog. */
  .modal-card .modal-actions {
    position: sticky; bottom: calc(-20px - env(safe-area-inset-bottom, 0px));
    margin: 20px -18px 0;
    padding: 12px 18px calc(12px + env(safe-area-inset-bottom, 0px));
    background: var(--surface); box-shadow: 0 -0.5px 0 var(--separator);
  }

  .period-row { grid-template-columns: 1fr 84px 44px; align-items: center; }
  /* "September" and "2026" describe themselves. */
  .period-row .field > span { position: absolute; width: 1px; height: 1px; overflow: hidden; clip: rect(0 0 0 0); }
  .period-row .btn-icon { margin: 0; }

  /* A four-column table on a 375px screen truncates every value to nonsense
     ("Regular wor", "Fixe", "3866"). Each row becomes its own labelled card. */
  .items-head { display: none; }
  .cell-l {
    position: static; width: auto; height: auto; margin: 0; overflow: visible;
    clip: auto; font-family: inherit; font-weight: 500; font-size: 11.5px; line-height: 1.2; color: var(--muted);
  }
  /* A grey card of white inputs inside a white card was the heaviest thing on the
     first screen — the one place the surface stack went four deep. */
  .item-row {
    grid-template-columns: 1fr 1fr 1fr; gap: 8px 8px; position: relative;
    background: none; border: 0; border-radius: 0; padding: 10px 0 12px; margin: 0;
    box-shadow: inset 0 -0.5px 0 var(--separator);
  }
  .item-row:first-child { padding-top: 2px; }
  #items .item-row:last-child { box-shadow: none; }
  .item-row input { background: var(--fill-4); }
  .item-row input:hover { background: var(--fill-3); }
  .item-row .cell-desc { grid-column: 1 / -1; padding-right: 40px; }
  .item-row .icon-btn { position: absolute; top: 6px; right: -8px; }
  .add-row { margin-top: 4px; }

  /* "Who gets this" is a glance, not a form: name and address on one row, tap to edit.
     Ticking never requires opening anything. This also retires a first-pass bug — the
     old nth-of-type counted the checkbox's own label, so the email field auto-placed
     into row 1 and the name fell underneath it. */
  .recipient {
    grid-template-columns: 44px minmax(0, 1fr) 44px; gap: 0 4px; align-items: center;
    background: none; border: 0; border-radius: 0; padding: 0; margin: 0;
    box-shadow: inset 0 -0.5px 0 var(--separator);
  }
  .recipient:last-of-type { box-shadow: none; }
  /* The header row is pinned — tick, name, × — so the fields that open beneath it can
     never push the × onto a row of its own, floating at the bottom left. */
  .recipient .chk { grid-row: 1; grid-column: 1; }
  .recipient .rcp-sum { grid-row: 1; grid-column: 2; }
  .recipient .icon-btn { grid-row: 1; grid-column: 3; }
  .rcp-sum {
    display: flex; flex-direction: column; align-items: flex-start; justify-content: center; gap: 1px;
    min-height: 50px; width: 100%; min-width: 0; padding: 6px 0;
    background: none; border: 0; text-align: left; font: inherit; color: var(--ink); cursor: pointer;
  }
  .rcp-name { font-size: 15px; font-weight: 500; }
  .rcp-mail { font-size: 12.5px; color: var(--muted); max-width: 100%; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
  .recipient.needs-email .rcp-mail { color: var(--warn); }
  .recipient .cell, .recipient .needs-email-hint { display: none; }
  /* Open, a recipient edits the way a contact does: label left, value right, one line
     each, aligned under the name. Two 40px rows where there were two 60px stacks. */
  .recipient.editing .cell {
    display: grid; grid-template-columns: 56px minmax(0, 1fr); align-items: center; gap: 8px;
    grid-column: 2 / -1; margin: 0; padding: 3px 0;
  }
  .recipient.editing .cell:first-of-type { padding-top: 0; }
  .recipient.editing .cell:last-of-type { padding-bottom: 12px; }
  .recipient.editing .cell-l { font-size: 13px; color: var(--muted); }
  .recipient.editing .rcp-mail { display: none; }
  .recipient.editing .rcp-sum { min-height: 44px; }
  .recipient input { background: var(--fill-4); }

  /* Dialogs present as bottom sheets — the native idiom, and it keeps the
     actions in the thumb's reach on a tall phone. The container is clipped, not
     scrollable: the card scrolls on its own, and while it slides up from below the
     screen it overflows the container by its full height — a scrollable fixed layer
     whose overflow is changing every frame is what flashed darker on an iPhone. The
     menu sheet, whose container never scrolls, never flashed. */
  .modal { align-items: flex-end; padding: 0; overflow: hidden; }
  .modal-card, .modal-card.wide, .modal-card.narrow {
    width: 100%; max-width: none; border-radius: 22px 22px 0 0;
    padding: 22px 18px calc(20px + env(safe-area-inset-bottom, 0px));
    max-height: 90dvh;
    /* `margin: auto` from the desktop rule absorbs the free space in both directions
       and re-centres the card, quietly cancelling align-items: flex-end. */
    margin: auto 0 0;
  }
  .modal.hidden .modal-card { transform: translateY(100%); opacity: 1; }
  .modal-actions .btn { flex: 1; padding: 13px; font-size: 15px; }
  .settings-row { flex-direction: column; align-items: stretch; gap: 12px; }
  .settings-btns .btn { flex: 1; }
  /* The app's own touch rule is 44px; these two were 30. */
  .client-manage .btn-text { min-height: 44px; }
  .hrow { grid-template-columns: 1fr auto; row-gap: 8px; }
  .hrow .hto { display: none; }
  .hrow .hact { grid-column: 1 / -1; justify-content: flex-end; }
}

/* Very short viewports (a phone in landscape): sheets would leave nothing
   visible, so centre the dialog again and let it scroll. Keyed on orientation as well
   as height, because Android shrinks the viewport when the keyboard opens — without it
   a 640-tall portrait phone flipped its sheet into a floating card mid-sentence. */
@media (max-width: 640px) and (max-height: 460px) and (orientation: landscape) {
  .modal { align-items: safe center; padding: 12px; }
  .modal-card, .modal-card.wide, .modal-card.narrow {
    border-radius: var(--radius-lg); max-height: calc(100dvh - 24px); padding: 18px;
  }
  .modal.hidden .modal-card { transform: scale(.96); opacity: 0; }
}

/* Width was the wrong question: an iPhone in landscape is 667-932px wide, above every
   phone breakpoint here. Touch devices get the phone's field size whatever the width;
   the viewport's maximum-scale=1 is what keeps iOS from zooming on focus. */
@media (hover: none) and (pointer: coarse) {
  input, select, textarea { font-size: 15px; }
}

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

/* ----------------------------------------------------- email editor (phone)
   Reuses the full-screen preview shell. The subject and message elements are the
   *same* nodes as the inline card's — moved in on open and back on close — so there
   is one set of values and one set of listeners, not a copy to keep in sync. */
.em-tabs {
  display: flex; gap: 4px; padding: 8px 12px 0; background: var(--bg);
}
.em-tab {
  flex: 1; border: 0; background: var(--fill-4); color: var(--muted);
  font-family: inherit; font-weight: 500; font-size: 14px; line-height: 1; padding: 10px; border-radius: 9px; cursor: pointer;
  min-height: 40px;
}
.em-tab.is-on { background: var(--accent-wash); color: var(--accent); font-weight: 600; }
.em-pane { flex: 1; min-height: 0; overflow: auto; padding: 12px 16px; background: var(--bg); }
.em-fields { display: flex; flex-direction: column; gap: 14px; min-height: 100%; }
/* The message is the reason you opened this screen, so it takes the room that is left. */
#emailModal .field:has(> #body) { flex: 1 1 auto; min-height: 0; }
#emailModal #body { flex: 1 1 auto; min-height: 40vh; resize: none; }
#emailModal .ph { display: none; }            /* the chips below replace it */

.em-prev { display: flex; flex-direction: column; gap: 10px; }
.em-prev-sub {
  font-weight: 600; font-size: 15px; padding: 12px 14px;
  background: var(--surface); border-radius: 12px; box-shadow: var(--shadow-card);
}
.em-prev-body {
  flex: 1; white-space: pre-wrap; font-size: 15px; line-height: 1.55;
  padding: 14px; background: var(--surface); border-radius: 12px; box-shadow: var(--shadow-card);
}

/* A toolbar rather than a list: it sits above the keyboard, where a thumb already is. */
.em-chips {
  display: flex; gap: 6px; overflow-x: auto; -webkit-overflow-scrolling: touch;
  padding: 10px 12px calc(10px + env(safe-area-inset-bottom, 0px));
  background: var(--glass); backdrop-filter: var(--glass-blur);
  -webkit-backdrop-filter: var(--glass-blur); box-shadow: 0 -0.5px 0 var(--separator);
}
.em-chip {
  flex: 0 0 auto; border: 0; border-radius: 999px; cursor: pointer;
  background: var(--fill-3); color: var(--ink);
  font: 500 12.5px/1 ui-monospace, SFMono-Regular, Menlo, monospace;
  padding: 10px 12px; min-height: 36px;
}
.em-chip:active { background: var(--accent-wash); color: var(--accent); }
