/* ============================================================================
   styles.css  —  The look of the app, in one place.

   Colours are defined once as variables below. Change a value here and it
   updates everywhere (e.g. change --accent to re-brand the buttons/links).
   ========================================================================== */

:root {
  /* --- The original ten. Values unchanged: this is the app's identity. ----- */
  --bg:         #111827;  /* page background      (gray-900) */
  --panel:      #1f2937;  /* cards, sidebar       (gray-800) */
  --panel-2:    #374151;  /* inputs, hover        (gray-700) */
  --border:     #374151;  /* dividers             (gray-700) */
  --text:       #e5e7eb;  /* main text            (gray-200) */
  --text-muted: #9ca3af;  /* secondary text       (gray-400) */
  --accent:     #2563eb;  /* buttons, links       (blue-600) */
  --accent-2:   #1d4ed8;  /* button hover         (blue-700) */
  --danger:     #dc2626;  /* delete / errors      (red-600)  */
  --ok:         #16a34a;  /* success              (green-600)*/
  --radius: 0.5rem;

  /* --- Named at last -------------------------------------------------------
     Not new colours. Every one of these was already in the app, typed as a
     literal in two to thirty-four places. Naming them is what lets the next
     edit reach all of them.                                                 */
  --neutral:       #6b7280;  /* no status / unassigned / archived  (gray-500) */
  --on-accent:     #ffffff;  /* text on any filled colour                     */
  --border-strong: #4b5563;  /* input + dashed borders             (gray-600) */
  --well:          #0b1220;  /* inset code block, one step below --bg         */
  --well-text:     #cbd5e1;  /* text inside that well              (slate-300)*/

  /* --- Readable-on-dark pairs ---------------------------------------------
     A 600-weight fill is not a legible text colour on #111827. --ok and
     --danger stay the FILL; these are their text-side twins. Keeping both
     is why .roi-pos and .toast-ok can share a meaning without sharing a hex. */
  --link:        #60a5fa;  /* text links, in-cell links          (blue-400)  */
  --ok-text:     #4ade80;  /* positive figures                   (green-400) */
  --danger-text: #f87171;  /* negative figures, field errors     (red-400)   */
  --warn:        #d97706;  /* attention, not failure             (amber-600) */

  /* --- Elevation -----------------------------------------------------------
     --panel is the card. These are the two steps above it, so a hero tile can
     sit forward of the cards around it without inventing a hex.             */
  --surface-1:   #202b3c;  /* raised card                                    */
  --surface-2:   #263349;  /* the hero tile, one step further forward        */

  /* --- The status ramp -----------------------------------------------------
     ONE home for "what colour does Paid / Overdue / Low stock mean". This
     rule previously lived in FIVE files, and client-details.js carried a
     second copy of both the invoice map and the order map. Feature code reads
     these through js/palette.js and never retypes them; deep-check §20 fails
     if a raw hex reappears in a feature file.                               */
  --st-neutral:  #6b7280;  /* Draft, Completed campaign, Archived, none      */
  --st-progress: #2563eb;  /* Sent, Processing — in flight                   */
  --st-good:     #2e9e5b;  /* Paid, Completed, In stock                      */
  --st-bad:      #c0392b;  /* Overdue, Cancelled, Out of stock               */
  --st-pending:  #e5892e;  /* Pending, Low stock, awaiting invoicing         */
  --st-info:     #6366f1;  /* Shipped, Scheduled                             */
  --st-alt:      #a855f7;  /* Orders layer, uninvoiced value                 */

  /* --- Chart ink -----------------------------------------------------------
     Chart.js paints to a canvas and cannot read a CSS variable, so these are
     read once through palette.js. They are here, not there, so the stylesheet
     stays the single source.                                                */
  --chart-grid:  #374151;  /* gridlines                                      */
  --chart-tick:  #9ca3af;  /* axis labels                                    */
  --chart-slice-1: #2563eb;
  --chart-slice-2: #16a34a;
  --chart-slice-3: #8b5cf6;
  --chart-slice-4: #f59e0b;
  --chart-slice-5: #ec4899;
  --chart-slice-6: #14b8a6;

  /* --- Spacing -------------------------------------------------------------
     Card padding had SEVEN homes: .card had none at all (content flush to the
     border), while .seq-card, .arch-card, .dash-card, .set-card, .chart-card and
     the client-page cards each picked their own, and .cd-identity had its two
     values the wrong way round. Same fault as the 105 hexes, different property.
     Scoped to the client page for now; see HANDOVER for the app-wide pass.     */
  --pad-card: 1.15rem 1.4rem;

  /* --- Type ----------------------------------------------------------------
     The weight ladder, so a label and a figure are never the same weight.
     Most of "designed rather than default" is here and it costs nothing.    */
  --w-label: 400;
  --w-num:   600;

  /* --- Paper ---------------------------------------------------------------
     The invoice preview is deliberately LIGHT — it is a document that gets
     printed, not a screen. Its palette is separate on purpose, and naming it
     is also what makes a light mode a token swap rather than a rewrite.     */
  --paper:        #ffffff;
  --paper-ink:    #111827;
  --paper-ink-2:  #374151;
  --paper-muted:  #6b7280;
  --paper-rule:   #d1d5db;  /* header rule under table headings              */
  --paper-rule-2: #eceff3;  /* hairline between rows                         */

  /* --- PSYNC'D (KICKOFF_NETWORKING.md decision 9) ------------------------------
     Taken from the PSYNC'D logo: black, and a champagne-to-bronze gold. Used by
     the card preview now, and the public scan page in slice 3.             */
  --brand-ink:    #000000;  /* PSYNC'D black                                  */
  --brand-panel:  #121110;  /* the card on black                              */
  --brand-rule:   #2C2925;  /* a hairline on that card                        */
  --brand-text:   #F2ECE4;  /* warm white                                     */
  --brand-muted:  #BDB3A6;  /* secondary text on black                        */
  --brand-gold:   #C9A27F;  /* rules and borders                              */
  --brand-gold-1: #DFC5A6;  /* champagne: the gradient's start                */
  --brand-gold-2: #BB9170;  /* bronze: the gradient's end                     */
}

/* Numbers that sit in a column, or next to each other, line up. Applied as a
   utility so it can reach a <span> without a bespoke class per screen. */
.num, .stat-value, .dash-tile-v, .rep-num, .li-total, .li-price,
.stock-now-value, .stock-delta, .roi-pos, .roi-neg, .roi-none {
  font-variant-numeric: tabular-nums;
  font-feature-settings: "tnum" 1;
}
/* Large figures only: tightens the gap that big type opens up. */
.stat-value, .dash-tile-v, .dash-hero-v, .stock-now-value {
  letter-spacing: -0.015em;
}

* { box-sizing: border-box; }
html, body { height: 100%; margin: 0; }
body {
  font-family: 'Inter', system-ui, sans-serif;
  background: var(--bg);
  color: var(--text);
}

.hidden { display: none !important; }
.view, .page { transition: opacity 0.25s ease-in-out; }

/* --- The shell: sidebar, top bar, main area ---------------------------------
   This used to live in style="" attributes in index.html, where no stylesheet
   rule could reach it — which is why the phone got a 240px sidebar and about
   96px of page. Layout lives here now so the phone block below can move it.

   min-width:0 on .app-col is the other half, and it is load-bearing: a flex
   item will not shrink below its content by default, so without it a wide
   table makes the column WIDER than the window and the whole page scrolls
   sideways — measured at 800px (a phone held sideways), Invoicing went 436px
   past the edge. With it, an over-wide thing has to deal with that itself (a
   table scrolls inside its own card). <main> needs no copy: overflow-y:auto
   already lets it shrink.                                                  */
.app-view { height: 100%; display: flex; }
.app-col { flex: 1; min-width: 0; display: flex; flex-direction: column; }
.app-main { flex: 1; overflow-y: auto; padding: 1.5rem; }

.sidebar {
  width: 240px;
  flex-shrink: 0;
  display: flex;
  flex-direction: column;
  background: var(--panel);
  border-right: 1px solid var(--border);
  overflow-x: hidden;
}
.sidebar-head { display: flex; align-items: center; justify-content: space-between; padding: 1rem 0.9rem; }
.sidebar-brand { font-weight: 700; color: var(--on-accent); }
.sidebar-nav { flex: 1; overflow-y: auto; }
.sidebar-logout { border: none; cursor: pointer; text-align: left; padding: 0.75rem 0.9rem; margin: 0.5rem; }
/* A way out to Claude Code, not a screen: quiet like the screen links, set
   apart from them, and marked \2197 because it leaves the app. */
.sidebar-claude { display: block; margin: 0.25rem 0.5rem 0; padding: 0.6rem 0.9rem;
  border-top: 1px solid var(--border); color: var(--text-muted); text-decoration: none; }
.sidebar-claude:hover { color: var(--text); background: var(--panel-2); border-radius: var(--radius); }

/* Phone-only furniture. Hidden on a computer, where the sidebar never closes.
   (The old desktop "collapse" button is gone: the links are words with no
   icons, so collapsing left an empty strip and one blue blob.)              */
.topbar, .nav-close, .nav-scrim { display: none; }
.nav-open, .nav-close { border: none; cursor: pointer; font-size: 1.15rem; line-height: 1;
  padding: 0.5rem 0.65rem; border-radius: var(--radius); }
.topbar-title { font-weight: 700; color: var(--on-accent); min-width: 0;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }

/* THE PHONE. A media query, not a container query, and deliberately so:
   Standard 8 says a CARD responds to its own box, but the shell IS the window,
   so the window is the right thing to ask. 760px matches every other phone
   breakpoint in this file; a phone held sideways (~830px) keeps the sidebar,
   which the owner's landscape screenshot showed working.

   The sidebar leaves the flow (position:fixed) — it no longer takes width from
   the page at all — and slides in over it when .nav-open is set on the shell. */
@media (max-width: 760px) {
  .app-view { flex-direction: column; }
  .topbar {
    display: flex; align-items: center; gap: 0.5rem; flex-shrink: 0;
    padding: 0.4rem 0.75rem; background: var(--panel); border-bottom: 1px solid var(--border);
  }
  .app-main { padding: 1rem 0.75rem; }
  .sidebar {
    position: fixed; top: 0; bottom: 0; left: 0; z-index: 41;
    width: min(280px, 85vw);
    transform: translateX(-100%); transition: transform 0.2s ease-out;
    visibility: hidden;
  }
  .app-view.nav-open .sidebar { transform: none; visibility: visible; }
  .nav-close { display: inline-block; }
  .app-view.nav-open .nav-scrim {
    display: block; position: fixed; inset: 0; z-index: 40;
    background: color-mix(in srgb, var(--bg) 70%, transparent);
  }
  .nav-link { padding: 0.75rem 0.9rem; }          /* a finger, not a cursor */
}
@media (max-width: 760px) and (prefers-reduced-motion: reduce) {
  .sidebar { transition: none; }
}

.nav-link {
  display: flex;
  align-items: center;
  gap: 0.75rem;
  padding: 0.6rem 0.9rem;
  margin: 0.1rem 0.5rem;
  border-radius: var(--radius);
  color: var(--text-muted);
  text-decoration: none;
  white-space: nowrap;
  cursor: pointer;
  transition: background 0.15s, color 0.15s;
}
.nav-link:hover { background: var(--panel-2); color: var(--text); }
.nav-link.nav-active { background: var(--accent); color: var(--on-accent); }
.sidebar-category {
  padding: 0.75rem 0.9rem 0.25rem;
  font-size: 0.7rem;
  text-transform: uppercase;
  letter-spacing: 0.05em;
  color: var(--text-muted);
}

/* --- Forms ----------------------------------------------------------------- */
.form-input {
  width: 100%;
  background: var(--panel-2);
  border: 1px solid var(--border-strong);
  border-radius: 0.375rem;
  color: var(--text);
  padding: 0.5rem 0.75rem;
  font-size: 0.875rem;
  /* Browsers give a textarea a typewriter font and a select their own; every
     box in the app types in the app's font. */
  font-family: inherit;
}
.form-input:focus {
  outline: none;
  border-color: var(--accent);
  box-shadow: 0 0 0 1px var(--accent);
}
.form-input::placeholder { color: var(--text-muted); }
.field-error { color: var(--danger-text); font-size: 0.8rem; margin-top: 0.2rem; }

/* --- Buttons --------------------------------------------------------------- */
.btn {
  display: inline-flex; align-items: center; justify-content: center; gap: 0.4rem;
  padding: 0.5rem 1rem;
  border: none; border-radius: 0.375rem;
  font-weight: 600; font-size: 0.875rem;
  cursor: pointer; transition: background 0.15s;
}
.btn-primary { background: var(--accent); color: var(--on-accent); }
.btn-primary:hover { background: var(--accent-2); }
.btn-danger  { background: var(--danger); color: var(--on-accent); }
.btn-ghost   { background: transparent; color: var(--text-muted); }
.btn-ghost:hover { background: var(--panel-2); color: var(--text); }

/* --- Panels / tables ------------------------------------------------------- */
/* `.card` carries padding. It did not for the whole build, so every bare
   `class="card"` rendered its content flush against the border — sixteen of
   them across five files. Only client-details.js escaped, because
   `.cd-flow > .card` supplies it there; that is why the design-passed screens
   looked right and the rest did not.

   The fix was deferred pending an "opt-out list". That list turns out to have
   ONE entry. Every variant that sets its own padding — set-card, rep-pad,
   chart-card, dash-card, cal-wrap, cal-bar, cal-empty, cal-agenda,
   comms-payload, playbook, filters, pad, product-card — is declared LATER in
   this file and so already wins on source order. Deferring cost five screens;
   the opt-out list was cheap once anyone counted.

   Known debt, deliberately not fixed here: those variants use six different
   padding values between them rather than --pad-card. Normalising them changes
   the spacing on six more screens and wants its own pass. */
.card { background: var(--panel); border: 1px solid var(--border); border-radius: var(--radius);
  padding: var(--pad-card); }
/* The one opt-out: a table sets its own inset through `th, td` below, so a
   padded card would double it and push the rules away from the card edge. */
.card.table-wrap { padding: 0; }
table { width: 100%; border-collapse: collapse; }
th, td { text-align: left; padding: 0.7rem 1rem; border-bottom: 1px solid var(--border); }
th { color: var(--text-muted); font-size: 0.75rem; text-transform: uppercase; letter-spacing: 0.03em; }

/* --- Toasts (corner pop-up messages) -------------------------------------- */
.toast-host {
  position: fixed; bottom: 1.25rem; right: 1.25rem;
  display: flex; flex-direction: column; gap: 0.5rem; z-index: 60;
}
.toast {
  min-width: 220px; padding: 0.75rem 1rem; border-radius: var(--radius);
  color: var(--on-accent); font-size: 0.875rem; box-shadow: 0 8px 24px rgba(0,0,0,0.35);
  opacity: 0; transform: translateY(8px); transition: opacity 0.25s, transform 0.25s;
}
.toast-show { opacity: 1; transform: translateY(0); }
.toast-ok    { background: var(--ok); }
.toast-error { background: var(--danger); }

/* --- Modal ----------------------------------------------------------------- */
.modal-backdrop {
  position: fixed; inset: 0; background: rgba(0,0,0,0.6);
  display: flex; align-items: center; justify-content: center; padding: 1rem; z-index: 50;
}
.modal {
  background: var(--panel); border: 1px solid var(--border);
  border-radius: var(--radius); width: 100%; max-width: 560px;
  max-height: 90vh; overflow-y: auto; padding: 1.5rem;
}

/* ============================================================================
   Clients screen
   ========================================================================== */
.page-title{ font-size:1.6rem; font-weight:700; color:var(--on-accent); margin:0; }
.clients-head{ display:flex; justify-content:space-between; align-items:center; gap:1rem; margin-bottom:1.25rem; flex-wrap:wrap; }
.clients-actions{ display:flex; gap:.5rem; flex-wrap:wrap; }

.filters{ display:grid; grid-template-columns:1fr 1fr 1fr; gap:1rem; padding:1rem; margin-bottom:1.25rem; }
.filters-4{ grid-template-columns:repeat(4,1fr); }   /* Orders: + the invoicing filter */
.filter-field label{ display:block; font-size:.8rem; color:var(--text-muted); margin-bottom:.25rem; }
@media (max-width:1100px){ .filters-4{ grid-template-columns:1fr 1fr; } }
@media (max-width:760px){ .filters, .filters-4{ grid-template-columns:1fr; } }

.table-wrap{ overflow-x:auto; }

/* THE PHONE, for any list table that opts in with class="stack-table"
   (Invoicing, Orders, Campaigns). Clients has its own version above because
   it was first and its rows need no labels; these do — "8 Oct 2026" means
   nothing until it says whether it is the issue date or the due date.

   Each row becomes a small card: who it is (.st-lead) and the money (.st-amt)
   across the top, then one "Label  value" line per remaining cell, read from
   the cell's data-label, then the buttons. A cell whose value is the table's
   "no value" dash (.st-none) is left out: with no column to sit in, a dash
   says nothing. The money is placed by .st-amt and never shrinks, so on the
   money screen the amount is the one thing that is always on screen — before
   this, it started off the right edge of the card. */
@media (max-width:760px){
  .stack-table, .stack-table tbody{ display:block; }
  .stack-table thead{ display:none; }
  .stack-table tr{ display:grid; grid-template-columns:minmax(0,1fr) auto; align-items:center;
    gap:.3rem .75rem; padding:.8rem .9rem; border-bottom:1px solid var(--border); }
  .stack-table tr:last-child{ border-bottom:none; }
  .stack-table td{ display:block; padding:0; border-bottom:none; min-width:0; }
  .stack-table td.st-lead{ grid-column:1; order:-2; font-weight:600; overflow-wrap:anywhere; }
  .stack-table td.st-amt{ grid-column:2; order:-1; text-align:right; white-space:nowrap; }
  .stack-table td[data-label]{ grid-column:1 / -1; display:flex; justify-content:space-between;
    align-items:center; gap:.75rem; flex-wrap:wrap; }
  .stack-table td[data-label]::before{ content:attr(data-label); color:var(--text-muted);
    font-size:.75rem; font-weight:var(--w-label); }
  .stack-table td.st-none{ display:none; }
  .stack-table td.cell-actions{ grid-column:1 / -1; display:flex; flex-wrap:wrap; justify-content:flex-end;
    gap:.4rem; white-space:normal; padding-top:.3rem; }
  .stack-table td.empty{ grid-column:1 / -1; padding:.4rem 0; }
}
.cell-name .strong{ font-weight:600; display:block; }
.cell-name .sub{ font-size:.78rem; color:var(--link); }
.muted{ color:var(--text-muted); }
.empty{ text-align:center; color:var(--text-muted); padding:2rem; }

.chip{ display:inline-block; padding:.15rem .55rem; border-radius:999px; font-size:.72rem; font-weight:600;
  color:var(--on-accent); background:var(--chip,var(--neutral)); white-space:nowrap; margin-right:.35rem; }
/* A chip for a state that is a to-do rather than a fact ("Not invoiced") —
   outlined instead of filled, so it reads as quieter than a status chip. */
.chip-quiet{ color:var(--text-muted); background:transparent; border:1px solid var(--border); }

.cell-actions{ white-space:nowrap; text-align:right; }
.row-btn{ background:transparent; border:1px solid var(--border); color:var(--text-muted); border-radius:6px;
  padding:.25rem .6rem; font-size:.8rem; cursor:pointer; margin-left:.25rem; transition:background .15s,color .15s; }
.row-btn:hover{ background:var(--panel-2); color:var(--text); }
/* Delete previously differed from View only on :hover, so at rest the three row
   buttons were identical and the destructive one had no affordance at all.
   The owner spotted this from a screenshot; deep-check section 22 now fails if
   .danger carries no resting rule of its own. */
.row-btn.danger{ color:var(--danger-text); border-color:color-mix(in srgb, var(--danger) 45%, transparent); }
.row-btn.danger:hover{ background:var(--danger); color:var(--on-accent); border-color:var(--danger); }

/* ============================================================================
   Clients screen — the craft pass.

   Everything here is SCOPED to .cl-* or to .clients-head on purpose. .chip and
   .btn-ghost are app-wide and eleven screens have not been reviewed since the
   design pass began, so retinting them globally would be the same mistake the
   app-wide .card padding fix was deliberately held back from. When those
   screens get their pass, these variants are what they adopt.
   ========================================================================== */

/* The filter row is a TOOLBAR, not a card. Three controls in a tall bordered
   box with a 490px search field read as unfinished; inline and sized to their
   content, they read as chrome. */
.cl-toolbar{ display:flex; gap:.5rem; align-items:center; flex-wrap:wrap; margin-bottom:.9rem; }
.cl-search{ flex:1 1 200px; max-width:20rem; }
.cl-toolbar .form-input{ padding:.4rem .6rem; font-size:.85rem; }
.cl-filter{ flex:0 0 auto; min-width:9.5rem; }

/* The summary strip. Gives a sparse page top-weight without pretending to fill
   it — four clients cannot fill a 1080px viewport and anything sized in pixels
   to try would break at fifteen, which is the hand-balancing fault again. */
.cl-summary{ display:flex; gap:.5rem; flex-wrap:wrap; margin-bottom:.9rem; }
.cl-stat{ flex:1 1 6rem; background:var(--panel); border-radius:var(--radius); padding:.55rem .8rem; }
.cl-stat-k{ display:block; font-size:.72rem; color:var(--text-muted); font-weight:var(--w-label); }
.cl-stat-v{ display:block; font-size:1.15rem; color:var(--text); font-weight:var(--w-num); }
/* Missing data is a prompt, not a state. A blank stage used to render as an
   "N/A" chip that looked like a configured stage; it is counted here instead. */
.cl-stat-warn .cl-stat-v{ color:var(--warn); }

.cl-count{ font-size:.9rem; color:var(--text-muted); font-weight:var(--w-label); }

/* Row rules recede below the card edge. Every border in this app was one
   weight, so nothing receded and everything competed. */
.cl-table th{ border-bottom:1px solid var(--border); }
.cl-table td{ border-bottom:1px solid color-mix(in srgb, var(--border) 55%, transparent); }
.cl-table tr:last-child td{ border-bottom:none; }
.cl-table td{ padding:.6rem 1rem; }

.cl-who{ display:flex; align-items:center; gap:.6rem; min-width:0; }
/* Same derivation as the Dashboard and the client page — initials(), one home
   in utils.js. This is the third screen to want an avatar and the point at
   which two private copies became one shared one. */
.cl-av{ flex-shrink:0; width:1.9rem; height:1.9rem; border-radius:999px;
  display:inline-flex; align-items:center; justify-content:center;
  font-size:.7rem; font-weight:var(--w-num); letter-spacing:.02em;
  color:var(--on-accent); background:var(--av,var(--neutral)); overflow:hidden; }
.cl-who-body{ display:flex; flex-direction:column; gap:.05rem; min-width:0; }
/* The name opens the client. A BUTTON, not a clickable <tr>: a row handler is
   unreachable by keyboard and would swallow the mailto link sitting inside it. */
.cl-name{ background:none; border:0; padding:0; font:inherit; text-align:left;
  font-weight:var(--w-num); color:var(--text); cursor:pointer; overflow-wrap:anywhere; }
.cl-name:hover{ color:var(--link); text-decoration:underline; }
/* The email was link-COLOURED on a <span> — it looked clickable and was not.
   It is a real mailto now, which is what the colour was already promising. */
.cl-mail{ font-size:.78rem; color:var(--link); text-decoration:none; overflow-wrap:anywhere; }
.cl-mail:hover{ text-decoration:underline; }

/* Tinted rather than filled. A solid pill with white text was the loudest thing
   on the page and it was carrying the least important column. --ok-text and
   friends exist for exactly this and the chips never used them. */
.cl-chip{ display:inline-block; padding:.15rem .5rem; border-radius:5px; font-size:.75rem;
  font-weight:var(--w-num); white-space:nowrap;
  background:color-mix(in srgb, var(--chip,var(--neutral)) 20%, transparent);
  color:color-mix(in srgb, var(--chip,var(--neutral)) 45%, var(--text)); }
.cl-arch{ font-size:.85rem; color:color-mix(in srgb, var(--chip,var(--neutral)) 40%, var(--text)); }
/* Empty is not a value. A dash, muted, never a chip. */
.cl-none{ color:var(--text-muted); }
.cl-phone{ font-variant-numeric:tabular-nums; color:var(--text-muted); font-size:.85rem; }
/* toE164() already knows this number cannot be dialled; the screen just never
   asked. Amber marks it as something to fix, not something that is fine. */
.cl-phone-bad{ color:var(--warn); }

/* THE PHONE: a table cannot be five columns in 360px. Each client becomes a
   card-like row — who they are across the top, then archetype, stage and phone
   as a wrapped line, then the two buttons — and the header row goes, because
   with no columns it labels nothing. The SAME markup: the table is still the
   table on a computer, and nothing about the data is decided by the screen.
   A dash is how the table says "no value in this column"; with no column to
   belong to, a lone dash means nothing, so on a phone it is simply left out. */
@media (max-width:760px){
  .cl-table, .cl-table tbody{ display:block; }
  .cl-table thead{ display:none; }
  .cl-table tr{ display:flex; flex-wrap:wrap; align-items:center; gap:.35rem .75rem; padding:.75rem .9rem;
    border-bottom:1px solid color-mix(in srgb, var(--border) 55%, transparent); }
  .cl-table tr:last-child{ border-bottom:none; }
  .cl-table td{ display:block; padding:0; border-bottom:none; }
  .cl-table td.cl-td-who{ flex:1 0 100%; min-width:0; }
  .cl-table td:has(> .cl-none){ display:none; }
  .cl-table td.cell-actions{ margin-left:auto; }
  .cl-table td.empty{ flex:1 0 100%; padding:.4rem 0; }
  .cl-search{ flex:1 0 100%; max-width:none; }
  .cl-filter{ flex:1 1 0; min-width:0; }
}

/* Modal form */
.modal-title{ font-size:1.25rem; font-weight:700; color:var(--on-accent); margin:0 0 1.25rem; }
.form-grid{ display:grid; grid-template-columns:1fr 1fr 1fr; gap:1rem; }
@media (max-width:760px){ .form-grid{ grid-template-columns:1fr; } }
.ff label{ display:block; font-size:.8rem; color:var(--text-muted); margin-bottom:.25rem; }
/* Fields stacked one after another in a dialog get the same 1rem the grid
   gives its own rows — without it a label sits on the box above it. */
/* Direct children only: fields INSIDE a .form-grid are already spaced by its
   gap, and a margin there would double it. */
.modal > .ff + .ff, .modal > .ff + .form-grid, .modal > .form-grid + .ff, .modal > .form-grid + .form-grid{ margin-top:1rem; }
.modal-actions{ display:flex; justify-content:flex-end; gap:.75rem; margin-top:1.5rem; }

/* Details view */
.link{ background:none; border:none; color:var(--link); cursor:pointer; padding:0; margin-bottom:1rem; font-size:.9rem; }
.link:hover{ text-decoration:underline; }
.details-head{ padding:1.25rem 1.5rem; margin-bottom:1rem; }
.dh-top{ display:flex; justify-content:space-between; align-items:center; gap:1rem; margin-bottom:1rem; flex-wrap:wrap; }
.info-grid{ display:grid; grid-template-columns:1fr 1fr; gap:.5rem 1.5rem; }
@media (max-width:600px){ .info-grid{ grid-template-columns:1fr; } }
.info{ display:flex; gap:.5rem; font-size:.9rem; }
.info-k{ color:var(--text-muted); min-width:110px; }
.info-v{ color:var(--text); }

/* Communication playbook */
/* The archetype leads the page: full width, ABOVE the flow. Nothing sits above
   it, so it has no tallest column to wait for — which is why this placement is
   safe where a trailing band and a column-span were not. It is a sibling of the
   flow now, so it needs its own padding rather than inheriting the flow's. */
.playbook{
  border-left:4px solid var(--accent); container-type:inline-size;
  padding:var(--pad-card); margin-bottom:16px;
}
.pb-head{ display:flex; align-items:center; gap:.5rem; margin-bottom:.35rem; }
.pb-name{ font-weight:600; color:var(--text); }
.pb-tag{ color:var(--text-muted); font-style:italic; margin:.1rem 0 1rem; }
.pb-field{ font-size:.9rem; margin-bottom:.6rem; }
.pb-k{ display:inline-block; font-size:.7rem; letter-spacing:.06em; text-transform:uppercase; color:var(--accent); font-weight:700; margin-right:.4rem; }
.pb-cols{ display:grid; grid-template-columns:1fr 1fr; gap:1.75rem; margin-top:.75rem; }
/* Only collapse to one column when genuinely narrow. In the old left-hand
   column this never triggered, so two columns fought over 20rem. */

.playbook .pb-cols li{ font-size:.9rem; }
/* A CONTAINER query, not a media query. The DO/DON'T columns need to collapse
   when the CARD is narrow, which has nothing to do with the window width \u2014 a
   media query never fires for a squeezed card inside a wide viewport, which is
   exactly how this ended up unreadable in the left-hand column. */
@container (max-width:44rem){ .pb-cols{ grid-template-columns:1fr; } }
@media (max-width:600px){ .pb-cols{ grid-template-columns:1fr; } }
.pb-cols ul{ margin:.35rem 0 0; padding-left:0; list-style:none; }
.pb-cols li{ position:relative; padding-left:15px; margin-bottom:.35rem; font-size:.88rem; }
.pb-cols li::before{ content:""; position:absolute; left:0; top:.55em; width:6px; height:6px; border-radius:2px; background:var(--accent); }
.pb-cols ul.dont li::before{ background:var(--text-muted); }

/* ============================================================================
   Client detail page
   ========================================================================== */
/* Three columns, by QUESTION: who they are / what happened / what to act on.
   The old two-column .details-grid is kept below because nothing else was
   ported off it. */
/* A FLOW, not a grid. See the comment in client-details.js: a grid plus a
   trailing band opens a void under the short columns the moment they are uneven,
   and nothing about the data guarantees they will be even. Multi-column balances
   the cards so the columns END LEVEL whatever they contain, so the hole is not
   possible rather than merely absent today.
   This is the pattern for any card-stack screen from here on. */
.cd-flow{ column-count:3; column-gap:16px; }
@media (max-width:1400px){ .cd-flow{ column-count:2; } }
@media (max-width:1000px){ .cd-flow{ column-count:1; } }

/* A card must never be sawn in half across a column break. */
.cd-flow > .card{
  break-inside:avoid; -webkit-column-break-inside:avoid; page-break-inside:avoid;
  margin:0 0 16px; padding:var(--pad-card);
}

/* NOTHING SPANS. This card used to set column-span:all so the playbook could be
   full width — and a spanner has to clear the TALLEST column above it, which is
   the exact constraint the trailing band had. The band was removed and its
   behaviour reintroduced one line later, so the void came straight back.
   A balancing flow only balances if every item is an ordinary item in it. The
   playbook is readable at a third of the width because .pb-cols collapses on a
   container query — that is what pays for not spanning. */

/* --- Identity ------------------------------------------------------------- */
.cd-identity{ display:flex; flex-direction:column; align-items:center; text-align:center; gap:.6rem; padding-top:1.5rem; }
/* Initials, not a photo: a client record has no photo field, and adding one is
   a Storage feature with a delete path and a GDPR question, not a design pass.
   Tinted by archetype, so the page carries it from the very top. */
.cd-av{
  width:5.5rem; height:5.5rem; border-radius:999px;
  display:flex; align-items:center; justify-content:center;
  font-size:1.75rem; font-weight:var(--w-num); letter-spacing:.02em;
  color:var(--on-accent); background:var(--av,var(--neutral));
  overflow:hidden; flex-shrink:0;
}
.cd-name{ font-size:1.5rem; font-weight:700; color:var(--on-accent); margin:0; overflow-wrap:anywhere; }
.cd-arch{ margin:0; }
.cd-facts{ display:grid; grid-template-columns:1fr 1fr; gap:.4rem; margin-top:.3rem; width:100%; }
.cd-fact{
  display:flex; flex-direction:column; gap:.1rem; align-items:center; min-width:0;
  background:var(--panel-2); border:1px solid var(--border); border-radius:var(--radius);
  padding:.35rem .6rem;
}
.cd-fact-k{ font-size:.62rem; text-transform:uppercase; letter-spacing:.05em; color:var(--text-muted); font-weight:var(--w-num); }
.cd-fact-v{ font-size:.85rem; font-weight:var(--w-num); font-variant-numeric:tabular-nums; text-align:center; overflow-wrap:anywhere; }
.cd-identity-controls{ display:flex; gap:.5rem; width:100%; margin-top:.5rem; }
.cd-identity-controls .form-input{ flex:1; }

/* --- Action bar ----------------------------------------------------------- */
/* Inside the identity card now, separated by a rule rather than a card border. */
.cd-actions{
  display:flex; flex-direction:column; gap:.5rem; width:100%;
  margin-top:.9rem; padding-top:.9rem; border-top:1px solid var(--border);
}
.cd-actions .btn{ width:100%; }

.cd-contact .info-grid{ grid-template-columns:1fr; }

/* --- Timeline ------------------------------------------------------------
   A history that does not LOOK like a history reads as another list of rows,
   which is why the old activity panel disappeared into the page. The rail and
   the per-kind icon do that work; the data is unchanged. */
.cd-tl{ display:flex; flex-direction:column; max-height:34rem; overflow-y:auto; }
.cd-tl-item{ display:flex; gap:.85rem; position:relative; padding:0 0 1.1rem; }
.cd-tl-item:last-child{ padding-bottom:0; }
/* The connecting rail, drawn behind the dots and stopped at the last one. */
.cd-tl-item::before{
  content:""; position:absolute; left:1.05rem; top:2.2rem; bottom:0;
  width:1px; background:var(--border);
}
.cd-tl-item:last-child::before{ display:none; }
.cd-tl-dot{
  position:relative; z-index:1; flex-shrink:0;
  width:2.1rem; height:2.1rem; border-radius:999px;
  display:flex; align-items:center; justify-content:center; font-size:.9rem;
  background:var(--panel-2); border:1px solid var(--border);
}
.cd-tl-session{ border-color:var(--st-progress); }
.cd-tl-email{   border-color:var(--st-info); }
.cd-tl-note{    border-color:var(--neutral); }
.cd-tl-call{    border-color:var(--st-pending); }
.cd-tl-payment{ border-color:var(--st-good); }
.cd-tl-body{ min-width:0; flex:1; padding-top:.15rem; }
.cd-tl-top{ display:flex; gap:.75rem; align-items:baseline; justify-content:space-between; flex-wrap:wrap; }
.cd-tl-title{ font-weight:var(--w-num); font-size:.9rem; overflow-wrap:anywhere; }
.cd-tl-when{ font-size:.74rem; color:var(--text-muted); white-space:nowrap; font-variant-numeric:tabular-nums; }
.cd-tl-text{ margin:.2rem 0 0; font-size:.85rem; color:var(--text-muted); white-space:pre-wrap; overflow-wrap:anywhere; }

/* --- Overflow: clamped content and its floating panel ---------------------
   The point of the panel being FIXED rather than inline: an expand-in-place
   fixes the truncation and reintroduces the layout shift it existed to prevent.
   Open or shut, the card underneath does not move by a pixel. */
.ovf{ display:flex; flex-direction:column; align-items:flex-start; gap:.15rem; min-width:0; }
.ovf-plain{ margin:.2rem 0 0; white-space:pre-wrap; overflow-wrap:anywhere; }
/* Explicit, not left to the browser's [hidden] default. This element holds the
   FULL text, so if anything ever set display on it the untruncated content would
   appear on the page and undo the whole point. Deep-check section 7 flagged it as
   an unstyled hook, which is exactly the kind of thing that list is for. */
.ovf-full{ display:none; }
.ovf-clamp{
  margin:.2rem 0 0; white-space:pre-wrap; overflow-wrap:anywhere;
  display:-webkit-box; -webkit-box-orient:vertical;
  -webkit-line-clamp:var(--ovf-lines,3); line-clamp:var(--ovf-lines,3);
  overflow:hidden;
}
.ovf-more{
  background:none; border:0; padding:.1rem 0; cursor:pointer;
  color:var(--link); font-size:.76rem; font-weight:var(--w-num);
  font-family:inherit; text-align:left;
}
.ovf-more:hover{ text-decoration:underline; }
.ovf-more:focus-visible{ outline:2px solid var(--link); outline-offset:2px; border-radius:2px; }

.ovf-panel{
  position:fixed; z-index:1200; width:min(32rem, calc(100vw - 2rem));
  background:var(--surface-1); border:1px solid var(--border-strong);
  border-radius:var(--radius); box-shadow:0 12px 32px rgba(0,0,0,0.45);
  display:flex; flex-direction:column; overflow:hidden;
}
.ovf-panel-head{
  display:flex; align-items:center; justify-content:space-between; gap:1rem;
  padding:.6rem .85rem; border-bottom:1px solid var(--border);
  background:var(--panel);
}
.ovf-panel-title{ font-size:.8rem; font-weight:var(--w-num); color:var(--text-muted);
  text-transform:uppercase; letter-spacing:.05em; overflow-wrap:anywhere; }
.ovf-panel-x{
  background:none; border:0; color:var(--text-muted); font-size:1.25rem;
  line-height:1; cursor:pointer; padding:0 .2rem; font-family:inherit;
}
.ovf-panel-x:hover{ color:var(--text); }
/* Capped, and it scrolls itself — the second half of the owner's requirement.
   An uncapped panel on a client with forty invoices is the same problem again,
   just floating. */
.ovf-panel-body{ padding:.85rem; overflow-y:auto; max-height:min(60vh, 26rem); }
/* Inside the panel the content is no longer clamped, and nested lists must not
   grow their own scrollbar inside the panel's. */
.ovf-panel-body .lines{ max-height:none; overflow:visible; }
.ovf-panel-body .cd-tl{ max-height:none; overflow:visible; }

/* --- Financial summary ---------------------------------------------------- */
.cd-money .card-h2{ margin-bottom:.75rem; }
/* One hero figure, the same reasoning as the Dashboard's overdue tile: the
   number you look up is bigger than the numbers you scan past. */
.cd-money-hero{
  display:flex; flex-direction:column; gap:.15rem;
  background:var(--surface-1); border:1px solid var(--border);
  border-radius:var(--radius); padding:.85rem 1rem; margin-bottom:.9rem;
}
.cd-money-ltv{ font-size:1.9rem; font-weight:700; letter-spacing:-0.015em; font-variant-numeric:tabular-nums; }
.cd-money-sub{ font-size:.74rem; color:var(--text-muted); }
.cd-money-row{ display:flex; align-items:baseline; justify-content:space-between; gap:1rem; padding:.35rem 0; }
.cd-money-k{ font-size:.72rem; text-transform:uppercase; letter-spacing:.05em; color:var(--text-muted); font-weight:var(--w-num); }
.cd-money-v{ font-size:1rem; font-weight:var(--w-num); font-variant-numeric:tabular-nums; }
.cd-money-bad{ color:var(--danger-text); }
.cd-money-warn{ color:var(--warn); }
.cd-money-note{ margin:.6rem 0 0; font-size:.78rem; color:var(--text-muted); }

/* --- Call modal ----------------------------------------------------------- */
/* The archetype's own phone guidance, surfaced at the moment it is useful. */
.cd-call-tip{
  border-left:3px solid var(--accent); padding:.6rem .85rem; margin-bottom:1rem;
  background:var(--panel-2);                                     /* fallback */
  background:color-mix(in srgb, var(--accent) 10%, var(--panel));
  border-radius:var(--radius);
}
.cd-call-tip-text{ margin:.25rem 0 0; font-size:.88rem; }
.cd-call-dial{ margin-bottom:1rem; }
.cd-call-dial .btn{ width:100%; }
.cd-call-nodial{ font-size:.85rem; display:block; }

.details-grid{ display:grid; grid-template-columns:1.3fr 1fr; gap:16px; align-items:start; }
@media (max-width:900px){ .details-grid{ grid-template-columns:1fr; } }
.details-col{ display:flex; flex-direction:column; gap:16px; min-width:0; }

.card-h2{ font-size:1.05rem; font-weight:700; color:var(--on-accent); margin:0 0 .9rem; }
.dh-title{ display:flex; align-items:center; gap:.6rem; flex-wrap:wrap; }
.dh-controls{ display:flex; align-items:center; gap:.5rem; }
.dh-controls .form-input{ width:auto; }
.link-inline{ color:var(--link); text-decoration:none; }
/* A link-styled BUTTON (Reports' campaign names, Social's "Start a new one")
   must shed the browser's button chrome, or it renders as a white box. */
button.link-inline{ background:none; border:0; padding:0; font:inherit; cursor:pointer; text-align:left; }
.link-inline:hover{ text-decoration:underline; }

/* Generic line rows (sessions, files, linked records) */
.lines{ display:flex; flex-direction:column; gap:.5rem; max-height:420px; overflow-y:auto; }
.line{ display:flex; align-items:center; justify-content:space-between; gap:.75rem;
  background:var(--panel-2); border:1px solid var(--border); border-radius:8px; padding:.5rem .7rem; font-size:.88rem; }
.line.done{ opacity:.6; }
.line.done .line-main span{ text-decoration:line-through; }
.line-main{ display:flex; align-items:center; gap:.55rem; min-width:0; flex:1; }
.line-main > span{ min-width:0; }
.icon-btn{ background:transparent; border:none; color:var(--text-muted); font-size:1.2rem; line-height:1;
  cursor:pointer; padding:0 .25rem; border-radius:4px; }
.icon-btn.danger:hover{ color:var(--on-accent); background:var(--danger); }

/* Add rows */
.add-row{ margin-top:.9rem; display:flex; gap:.5rem; }
.add-row-top, .add-row-bottom{ display:flex; gap:.5rem; }
.add-row-top{ margin-bottom:.5rem; }
[data-add-session]{ margin-top:.9rem; display:block; }
.add-note{ margin-bottom:.9rem; }
.add-note .btn{ margin-top:.5rem; }

/* Notes / activity / questionnaire items */
.note{ background:var(--panel-2); border:1px solid var(--border); border-radius:8px; padding:.65rem .75rem; }
.note + .note{ margin-top:.5rem; }
.note-date{ font-size:.72rem; color:var(--text-muted); margin:0 0 .25rem; }
.note-subject{ font-weight:600; color:var(--text); margin:0 0 .2rem; }
.note-body{ white-space:pre-wrap; font-size:.86rem; color:var(--text); margin:0; }
.qa{ font-size:.78rem; margin:.2rem 0 0 .5rem; }
.qa-k{ color:var(--text-muted); }
.qa-v{ color:var(--text); }

/* Package tracker */
.package-tracker{ margin-bottom:1rem; }
.pt-row{ display:flex; justify-content:space-between; font-size:.82rem; color:var(--text-muted); margin-bottom:.35rem; }
.progress{ background:var(--panel-2); border-radius:999px; height:8px; overflow:hidden; }
.progress-fill{ background:var(--accent,var(--accent)); height:100%; width:0; transition:width .3s ease; }

.hint-note{ font-size:.75rem; color:var(--text-muted); margin:.6rem 0 0; font-style:italic; }

/* ============================================================================
   Settings screen
   ========================================================================== */
.settings-grid{ display:grid; grid-template-columns:repeat(auto-fill, minmax(min(300px, 100%), 1fr)); gap:1.25rem; align-items:start; }
.set-wide{ grid-column:1 / -1; }

.set-card{ padding:1.25rem 1.5rem; display:flex; flex-direction:column; }
.set-h2{ font-size:1.1rem; font-weight:700; color:var(--on-accent); margin:0 0 1rem; }
.set-label{ display:block; font-size:.78rem; color:var(--text-muted); margin-bottom:.3rem; }

.set-list{ display:flex; flex-direction:column; gap:.5rem; min-height:120px; max-height:280px; overflow-y:auto; margin-bottom:1rem; }
.set-empty{ font-size:.85rem; padding:.5rem 0; }
.set-row{ display:flex; align-items:center; justify-content:space-between; gap:.75rem;
  background:var(--panel-2); border:1px solid var(--border); border-radius:8px; padding:.55rem .75rem; }
.set-row-name{ display:flex; align-items:center; gap:.5rem; font-size:.9rem; color:var(--text);
  min-width:0; overflow:hidden; text-overflow:ellipsis; white-space:nowrap; }
.stage-dot{ display:inline-block; width:.9rem; height:.9rem; border-radius:999px; flex-shrink:0; border:2px solid var(--panel); }

.set-add{ display:flex; gap:.5rem; margin-top:auto; }
.set-add .form-input{ flex:1; }
.set-add-stack{ display:flex; flex-direction:column; gap:.6rem; margin-top:auto; }
.set-color{ width:2.6rem; height:2.4rem; padding:2px; background:var(--panel-2);
  border:1px solid var(--border-strong); border-radius:.375rem; cursor:pointer; flex-shrink:0; }
.set-day{ width:5rem; flex:0 0 auto; }
.set-inline{ display:flex; align-items:center; gap:.5rem; }

/* Sequences */
.seq-list{ display:flex; flex-direction:column; gap:.75rem; margin-bottom:1rem; }
.seq-card{ background:var(--panel-2); border:1px solid var(--border); border-radius:8px; padding:.9rem 1rem; }
.seq-head{ display:flex; align-items:center; justify-content:space-between; margin-bottom:.6rem; }
.seq-name{ font-size:1rem; font-weight:600; color:var(--on-accent); margin:0; }
.seq-steps{ display:flex; flex-direction:column; gap:.4rem; margin-bottom:.75rem; }
.seq-step{ display:flex; align-items:center; justify-content:space-between; gap:.5rem;
  background:var(--panel); border:1px solid var(--border); border-radius:6px; padding:.4rem .6rem; font-size:.85rem; color:var(--text); }
.seq-add-step{ display:flex; gap:.5rem; align-items:center; }
.seq-add-step .form-input{ min-width:0; }
.seq-add-step select{ flex:1; }

/* Automation rules */
.rule-list{ display:flex; flex-direction:column; gap:.6rem; margin-bottom:1.25rem; max-height:340px; overflow-y:auto; }
.rule-item{ display:flex; align-items:center; justify-content:space-between; gap:.75rem;
  background:var(--panel-2); border:1px solid var(--border); border-radius:8px; padding:.65rem .85rem; }
.rule-text{ font-size:.86rem; color:var(--text); line-height:1.5; }
.rule-text strong{ color:var(--on-accent); }
.rule-k{ display:inline-block; font-size:.68rem; font-weight:700; letter-spacing:.06em;
  color:var(--accent); margin-right:.35rem; }

.automation-rule{ display:grid; grid-template-columns:1fr auto 1fr auto; gap:1rem; align-items:center;
  background:var(--panel-2); border:1px solid var(--border); border-radius:8px; padding:1rem; }
.automation-rule .trigger-card, .automation-rule .action-card{
  background:var(--panel); border:1px dashed var(--border-strong); padding:.75rem; border-radius:.375rem; }
.automation-rule .arrow{ font-size:1.5rem; color:var(--text-muted); }
.set-suboptions{ margin-top:.5rem; display:flex; flex-direction:column; gap:.5rem; }
.automation-rule .btn{ align-self:end; }
@media (max-width:760px){
  .automation-rule{ grid-template-columns:1fr; }
  .automation-rule .arrow{ transform:rotate(90deg); justify-self:center; }
}

/* Archetype playbook editors */
.arch-grid{ display:grid; grid-template-columns:repeat(auto-fill, minmax(min(340px, 100%), 1fr)); gap:1rem; }
.arch-card{ background:var(--panel-2); border:1px solid var(--border); border-radius:8px; padding:1rem 1.1rem; }
.arch-head{ display:flex; align-items:center; gap:.5rem; flex-wrap:wrap; }
/* Fold-down playbooks and businesses: the heading is the whole tap target,
   with a chevron that turns when open. */
summary.arch-head{ list-style:none; cursor:pointer; min-height:2.25rem; }
summary.arch-head::-webkit-details-marker{ display:none; }
summary.arch-head::after{ content:'\203A'; margin-left:auto; font-size:1.35rem; line-height:1; color:var(--text-muted); transition:transform .15s; }
details[open] > summary.arch-head{ margin-bottom:.9rem; }
details[open] > summary.arch-head::after{ transform:rotate(90deg); }
.arch-name{ font-weight:600; color:var(--on-accent); }
.arch-badge{ font-size:.65rem; text-transform:uppercase; letter-spacing:.05em; font-weight:700;
  color:var(--accent); border:1px solid var(--accent); border-radius:999px; padding:.05rem .5rem; }
.arch-field{ margin-bottom:.7rem; }
/* Boxes grow to fit their text, so nothing scrolls inside a box (owner,
   20 Oct 2026). field-sizing is Chrome's; elsewhere they stay resizable. */
.arch-field textarea{ resize:vertical; field-sizing:content; min-height:2.5rem; }
.arch-field textarea.arch-oneline{ resize:none; }
.arch-cols{ display:grid; grid-template-columns:1fr 1fr; gap:.75rem; }
@media (max-width:540px){ .arch-cols{ grid-template-columns:1fr; } }
.set-intro{ margin:-.4rem 0 1rem; }
.biz-add{ margin-top:1rem; }
/* A business's invoice numbers: its own section under the voice, with its own Save. */
.biz-numbers{ border-top:1px solid var(--border); padding-top:.8rem; margin-top:.9rem; }
.biz-numbers .set-inline .form-input{ min-width:0; }
.biz-numbers .set-inline [data-biz-num="prefix"]{ flex:0 0 6.5rem; text-transform:uppercase; }
.biz-num-status{ margin:.35rem 0 0; font-size:.85rem; }
.set-problems{ margin:.2rem 0 .6rem; padding-left:1.1rem; color:var(--st-bad); font-size:.85rem; display:flex; flex-direction:column; gap:.25rem; }
.arch-actions{ display:flex; justify-content:flex-end; gap:.6rem; margin-top:.4rem; }
.arch-actions .btn{ white-space:nowrap; }

/* Website integration */
.set-code{ font-family:ui-monospace, SFMono-Regular, Menlo, monospace; font-size:.78rem;
  background:var(--well); color:var(--well-text); resize:vertical; }

/* -------------------------------------------------------------------------
   Tasks screen
   ------------------------------------------------------------------------- */
.tasks-head{ display:flex; align-items:center; justify-content:space-between; gap:1rem; margin-bottom:1.25rem; flex-wrap:wrap; }
/* minmax(0,1fr), not 1fr: a bare 1fr track never shrinks below its widest
   content, so one long task title made the column — and the page — wider than
   a phone. */
.tasks-cols{ display:grid; grid-template-columns:repeat(2, minmax(0,1fr)); gap:1.25rem; align-items:start; }
@media (max-width:820px){ .tasks-cols{ grid-template-columns:minmax(0,1fr); } }

.card-head{ display:flex; align-items:center; justify-content:space-between; gap:.5rem 1rem; margin-bottom:.9rem; flex-wrap:wrap; }
.card-head .card-h2{ margin:0; }
.btn-sm{ padding:.3rem .7rem; font-size:.8rem; }

.task-check{ width:1.05rem; height:1.05rem; flex-shrink:0; accent-color:var(--accent); cursor:pointer; }
.task-body{ display:flex; flex-direction:column; gap:.15rem; min-width:0; }
.task-title{ min-width:0; overflow:hidden; text-overflow:ellipsis; white-space:nowrap; }
/* On a phone one line holds three words, so a task read "Send the program…".
   Up to three lines there, still clamped (Standard 6) so a pasted essay
   cannot take over the list. */
@media (max-width:760px){
  .task-title{ white-space:normal; overflow-wrap:anywhere; display:-webkit-box;
    -webkit-box-orient:vertical; -webkit-line-clamp:3; line-clamp:3; }
}
.task-sub{ display:flex; align-items:center; gap:.6rem; font-size:.78rem; flex-wrap:wrap; }
/* Wraps rather than running off the card: on a phone a money row's chip and
   four buttons are wider than the card, and Delete was cut off at "De". */
.task-acts{ display:flex; gap:.25rem; flex-shrink:0; flex-wrap:wrap; max-width:100%; }
.task-acts .icon-btn{ font-size:.8rem; font-weight:600; padding:.25rem .55rem; }
.task-acts .icon-btn:hover{ color:var(--text); background:var(--panel-2); }

/* Grouping inside the Pending card. The list used to be a flat date sort, so a
   task three weeks late looked exactly like one due next month. --gk carries
   the group's colour from palette.js; no colour is typed here (section 20). */
.task-group{ display:flex; flex-direction:column; gap:.5rem; }
.task-group + .task-group{ margin-top:.9rem; }
.task-group-k{ display:flex; align-items:center; gap:.4rem; margin:0;
  font-size:.72rem; font-weight:700; letter-spacing:.06em; text-transform:uppercase;
  color:var(--gk,var(--text-muted)); }
/* A left edge rather than a fill: the row must stay readable, and a whole row
   painted red is louder than the information warrants. */
.line.is-late{ border-left:3px solid var(--danger); padding-left:.6rem; }
.task-late{ color:var(--danger-text); font-weight:600; }

/* A row whose headline is MONEY. The amount must never be the thing that gets
   cut off: "£60,…" could be £60 or £60,000. It sits in its own box that does
   not shrink, the identifier beside it is the part allowed to give way, and
   when the row is too narrow the actions wrap underneath rather than eating the
   figure. Caught by screenshot on the client page (section 27). */
.line-money{ flex-wrap:wrap; }
.line-money .line-main{ flex:1 0 auto; }
.line-head{ display:flex; align-items:baseline; gap:.5rem; min-width:0; }
.line-amt{ flex-shrink:0; white-space:nowrap; font-weight:600; font-variant-numeric:tabular-nums; }

.ff-wide{ grid-column:1 / -1; }

/* -------------------------------------------------------------------------
   Invoicing screen
   ------------------------------------------------------------------------- */
.stat-cards{ display:grid; grid-template-columns:repeat(3,1fr); gap:1rem; margin-bottom:1.25rem; }
.stat-cards-4{ grid-template-columns:repeat(4,1fr); }   /* Orders: + awaiting invoicing */
@media (max-width:1100px){ .stat-cards-4{ grid-template-columns:repeat(2,1fr); } }
@media (max-width:760px){ .stat-cards, .stat-cards-4{ grid-template-columns:1fr; } }

/* Delivery address: the one-line summary under an order's status, and the
   "reuse the last address" offer in the order editor. */
.row-addr{ display:block; font-size:.72rem; margin-top:.2rem; max-width:16rem;
  overflow:hidden; text-overflow:ellipsis; white-space:nowrap; }
.addr-reuse{ display:flex; align-items:center; gap:.75rem; flex-wrap:wrap;
  margin-top:.6rem; padding:.5rem .75rem; border:1px dashed var(--border); border-radius:var(--radius); }
.addr-reuse .muted{ font-size:.8rem; }
.stat-card{ background:var(--panel); border:1px solid var(--border); border-left:3px solid var(--stat,var(--neutral));
  border-radius:var(--radius); padding:1rem 1.15rem; display:flex; flex-direction:column; gap:.15rem; }
.stat-label{ font-size:.75rem; text-transform:uppercase; letter-spacing:.04em; color:var(--text-muted); }
.stat-value{ font-size:1.5rem; font-weight:700; color:var(--on-accent); font-variant-numeric:tabular-nums; }
.stat-sub{ font-size:.75rem; color:var(--text-muted); }

/* .row-btn.danger:hover was declared here a second time, identical to its
   declaration in the buttons block, so an edit to one silently did nothing.
   The rule has one home now, up with .row-btn. */

/* Editor modal */
.modal-lg{ max-width:920px; }
.inv-modal-head{ display:flex; align-items:baseline; justify-content:space-between; gap:1rem; margin-bottom:1.25rem; }
.inv-modal-head .modal-title{ margin:0; }
.inv-number{ font-size:.85rem; color:var(--text-muted); }
.inv-number strong{ color:var(--text); font-variant-numeric:tabular-nums; }
.inv-sub{ font-size:.95rem; font-weight:600; color:var(--on-accent); margin:1.25rem 0 .5rem; }

.li-table th, .li-table td{ padding:.35rem .5rem; border-bottom:none; }
.li-table thead th{ border-bottom:1px solid var(--border); }
.li-table th:first-child, .li-table td:first-child{ width:48%; }
.li-num{ text-align:right; white-space:nowrap; }
.li-table .li-qty, .li-table .li-price{ text-align:right; }
.li-total, .li-price{ font-variant-numeric:tabular-nums; }
/* Orders line-item picker: advisory stock note under the product select */
.li-table .li-stock{ display:block; font-size:.72rem; margin-top:.25rem; }
/* THE PHONE: four columns of inputs in a 350px dialog left the description a
   stub, the quantity unreadable, the product picker reading "Ch" and the
   remove button off the edge. Each line becomes two rows — what it is, full
   width; then Qty, Unit price and Amount, each labelled from data-label since
   the header row is gone, with the remove button at the end. */
@media (max-width:760px){
  .li-table, .li-table tbody{ display:block; }
  .li-table thead{ display:none; }
  .li-table tr{ display:grid; grid-template-columns:4.5rem minmax(0,1fr) auto auto; gap:.35rem .6rem;
    align-items:end; padding:.6rem 0; border-bottom:1px solid var(--border); }
  .li-table td{ display:block; padding:0; }
  .li-table th:first-child, .li-table td:first-child{ grid-column:1 / -1; width:auto; }
  .li-table td[data-label]::before{ content:attr(data-label); display:block; margin-bottom:.2rem;
    font-size:.72rem; color:var(--text-muted); text-align:left; }
}

.inv-grid-2{ display:grid; grid-template-columns:1fr 1fr; gap:1.5rem; margin-top:1.25rem; align-items:start; }
@media (max-width:760px){ .inv-grid-2{ grid-template-columns:1fr; } }
.inv-status-label{ display:block; font-size:.8rem; color:var(--text-muted); margin:.75rem 0 .25rem; }
/* The business an invoice is from, and why it can't change once numbered. */
.inv-biz-note{ display:block; font-size:.78rem; margin-top:.25rem; }
/* The reassign card: invoices with no business yet, given to one in one go. */
.inv-assign{ margin-bottom:1rem; }
.inv-assign-text{ margin:0 0 .75rem; }
.inv-assign-text:last-child{ margin-bottom:0; }
.inv-assign-row{ display:flex; flex-wrap:wrap; gap:.6rem; align-items:center; }
.inv-assign-row select{ flex:1 1 12rem; min-width:0; }

.inv-totals{ background:var(--panel-2); border:1px solid var(--border); border-radius:var(--radius); padding:1rem 1.15rem;
  display:flex; flex-direction:column; gap:.5rem; }
.inv-row{ display:flex; align-items:center; justify-content:space-between; gap:1rem; font-size:.9rem; }
.inv-row > span:last-child{ font-variant-numeric:tabular-nums; }
.inv-control > span:last-child{ display:flex; align-items:center; gap:.4rem; }
.inv-discount{ display:flex; gap:.4rem; }
.inv-discount select{ width:auto; padding:.3rem .4rem; }
.inv-discount input{ width:90px; padding:.3rem .5rem; text-align:right; }
.inv-tax{ display:flex; align-items:center; gap:.35rem; }
.inv-tax input{ width:70px; padding:.3rem .5rem; text-align:right; }
.inv-pct{ color:var(--text-muted); }
.inv-total{ border-top:1px solid var(--border); padding-top:.6rem; margin-top:.2rem; font-size:1.1rem; font-weight:700; color:var(--on-accent); }

/* Printable preview ("paper") */
.inv-preview-shell{ padding:1rem; }
.inv-preview-actions{ display:flex; justify-content:flex-end; gap:.75rem; margin-bottom:1rem; }
.inv-paper{ background:var(--paper); color:var(--paper-ink); border-radius:6px; padding:2.25rem; }
.pp-top{ display:flex; justify-content:space-between; align-items:flex-start; gap:1rem; border-bottom:2px solid var(--paper-ink); padding-bottom:1rem; margin-bottom:1.25rem; }
.pp-from-name{ font-size:1.1rem; font-weight:700; }
.pp-title{ text-align:right; }
.pp-inv{ font-size:1.4rem; font-weight:800; letter-spacing:.08em; }
.pp-num{ font-variant-numeric:tabular-nums; color:var(--paper-ink-2); margin-bottom:.35rem; }
.pp-muted{ color:var(--paper-muted); font-size:.85rem; }
/* Wraps: on paper there is room for all three blocks side by side, so print
   is unchanged; on a phone the dates were pushed past the right edge of the
   page and cut off. A long email breaks rather than widening the page. */
.pp-meta{ display:flex; flex-wrap:wrap; justify-content:space-between; gap:1rem; margin-bottom:1.5rem; }
.pp-meta > div{ min-width:0; overflow-wrap:anywhere; }
.pp-k{ font-size:.72rem; text-transform:uppercase; letter-spacing:.05em; color:var(--paper-muted); }
.pp-strong{ font-weight:600; }
.pp-dates{ text-align:right; }
.pp-dates > div{ margin-bottom:.2rem; }
.pp-table{ width:100%; border-collapse:collapse; margin-bottom:1.25rem; }
.pp-table th{ text-align:left; font-size:.72rem; text-transform:uppercase; letter-spacing:.04em; color:var(--paper-muted); border-bottom:1px solid var(--paper-rule); padding:.5rem; }
.pp-table td{ padding:.5rem; border-bottom:1px solid var(--paper-rule-2); color:var(--paper-ink); }
.pp-table .li-num{ text-align:right; }
/* THE PHONE: the document keeps its four columns — it is a document, and the
   owner is checking what the client will see — but gives up the padding that
   pushed the Amount column off the page. `screen and` is NOT optional: an A4
   page is about 720px wide, so a bare max-width:760px query would also fire
   when the owner PRINTS on a computer, and change the invoice on paper. */
@media screen and (max-width:760px){
  .inv-preview-shell{ padding:.5rem; }
  .inv-paper{ padding:1.1rem; }
  .pp-table th, .pp-table td{ padding:.4rem .25rem; }
  .pp-table th{ font-size:.62rem; letter-spacing:.02em; }
  .pp-table .li-num{ white-space:nowrap; }
}
.pp-totals{ margin-left:auto; width:min(320px,100%); display:flex; flex-direction:column; gap:.4rem; }
.pp-trow{ display:flex; justify-content:space-between; font-size:.9rem; }
.pp-trow > span:last-child{ font-variant-numeric:tabular-nums; }
.pp-grand{ border-top:2px solid var(--paper-ink); padding-top:.5rem; margin-top:.25rem; font-size:1.15rem; font-weight:700; }
.pp-notes{ margin-top:1.5rem; border-top:1px solid var(--paper-rule-2); padding-top:.75rem; }
.pp-notes p{ margin:.25rem 0 0; white-space:pre-wrap; }

/* Printing is only ever the invoice (Print / Save PDF in its preview).
   Everything else is taken OUT of the layout with display:none. It used to be
   hidden with visibility:hidden, which hides a thing but keeps the space it
   takes — so the whole app, at full page height, sat invisibly on page one and
   every printed invoice began with a BLANK PAGE. #invoice-preview is appended
   straight to <body>, which is what makes the one selector below enough. */
@media print{
  body{ background:var(--paper); }
  body > *:not(#invoice-preview){ display:none !important; }
  #invoice-preview{ position:static; background:var(--paper); padding:0; display:block; }
  #invoice-preview .inv-preview-shell{ box-shadow:none; border:none; max-width:none; padding:0; }
  #invoice-preview .modal{ border:none; max-height:none; overflow:visible; }
  .inv-preview-actions{ display:none !important; }
  .inv-paper{ padding:0; }
}

/* Placeholder shown on screens not yet rebuilt (overwritten by a feature's render once built). */
.rebuild-note{ color:var(--text-muted); padding:2.5rem; text-align:center; background:var(--panel);
  border:1px dashed var(--border); border-radius:var(--radius); font-size:.95rem; }

/* -------------------------------------------------------------------------
   Products (catalog)
   ------------------------------------------------------------------------- */
.prod-toggle{ display:flex; align-items:center; gap:.45rem; color:var(--text); font-size:.9rem; padding:.5rem 0; cursor:pointer; }
.prod-toggle input{ width:auto; }

.product-grid{ display:grid; grid-template-columns:repeat(auto-fill, minmax(240px, 1fr)); gap:1.25rem; }
.product-card{ display:flex; flex-direction:column; overflow:hidden; padding:0; }
.product-card.product-archived{ opacity:.6; }
.product-img{ width:100%; height:150px; object-fit:cover; background:var(--panel-2); display:block; }
.product-img-ph{ display:flex; align-items:center; justify-content:center; font-size:2.5rem; font-weight:700;
  color:var(--text-muted); background:linear-gradient(135deg, var(--panel-2), var(--panel)); }
.product-body{ padding:1rem 1.1rem; display:flex; flex-direction:column; gap:.55rem; flex:1; }
.product-top{ display:flex; align-items:flex-start; justify-content:space-between; gap:.5rem; }
.product-name{ font-size:1.05rem; font-weight:700; color:var(--on-accent); margin:0; }
.product-biz{ font-size:.78rem; color:var(--text-muted); margin:.15rem 0 0; text-transform:uppercase; letter-spacing:.04em; }
.product-desc{ font-size:.85rem; color:var(--text-muted); margin:0; flex:1;
  display:-webkit-box; -webkit-line-clamp:3; -webkit-box-orient:vertical; overflow:hidden; }
.product-meta{ display:flex; align-items:center; justify-content:space-between; gap:.5rem; flex-wrap:wrap; }
.product-price{ font-size:1.3rem; font-weight:700; color:var(--on-accent); }
.product-acts{ display:flex; align-items:center; justify-content:flex-end; gap:.35rem; flex-wrap:wrap;
  margin-top:.35rem; padding-top:.75rem; border-top:1px solid var(--border); }
.product-acts .chip{ margin-right:auto; }

/* --- Stock ledger (Update stock modal + locked fields) ------------------- */
.ff-hint{ display:block; font-size:.72rem; color:var(--text-muted); margin-top:.3rem; }
.input-locked{ opacity:.65; cursor:not-allowed; }
.stock-now{
  display:flex; align-items:center; justify-content:space-between;
  background:var(--panel-2); border:1px solid var(--border);
  border-radius:.5rem; padding:.7rem .9rem; margin:.9rem 0;
}
.stock-now-label{ font-size:.8rem; color:var(--text-muted); }
.stock-now-value{ font-size:1.5rem; font-weight:700; font-variant-numeric:tabular-nums; }
.stock-preview{ font-size:.85rem; color:var(--text-muted); margin:.6rem 0 0; min-height:1.2em; }
.stock-preview-bad{ color:var(--danger); }
.stock-delta{ font-weight:700; font-variant-numeric:tabular-nums; }
.stock-up{ color:var(--st-good); }
.stock-down{ color:var(--st-bad); }
.stock-still{ color:var(--text-muted); }

/* --- Campaigns -------------------------------------------------------------
   The campaigns list reuses the stat cards, filters and table styles above.
   These are the pieces the analytics page adds: the two chart panels, the
   figures they fall back to when Chart.js can't load, and the housekeeping
   notice bar. ROI gets its own colour classes because it is the one number on
   the screen whose SIGN is the message.                                     */

.chart-grid{ display:grid; grid-template-columns:1fr 1fr; gap:1rem; margin-bottom:1rem; }
@media (max-width:1000px){ .chart-grid{ grid-template-columns:1fr; } }
.chart-card{ padding:1.25rem 1.5rem; }

/* Chart.js needs a parent with a real height: with maintainAspectRatio off and
   an auto-height parent, the canvas grows on every resize event. */
.chart-box{ position:relative; height:20rem; }

/* Same figures as the chart, in text, for when Chart.js isn't there. Laid out
   with the .info rows the detail pages already use. */
.chart-fallback{ display:flex; flex-direction:column; gap:.4rem; padding:.5rem 0; }

/* A quiet, actionable strip: something needs tidying, nothing is broken. */
.notice-bar{ display:flex; align-items:center; justify-content:space-between; gap:1rem;
  flex-wrap:wrap; background:var(--panel); border:1px solid var(--border);
  border-left:3px solid var(--warn); border-radius:var(--radius);
  padding:.75rem 1rem; margin-bottom:1rem; font-size:.9rem; }

.roi-pos{ color:var(--ok-text); font-weight:700; font-variant-numeric:tabular-nums; }
.roi-neg{ color:var(--danger-text); font-weight:700; font-variant-numeric:tabular-nums; }
.roi-none{ color:var(--text-muted); font-variant-numeric:tabular-nums; }

/* --- Accessibility floor ---------------------------------------------------
   A visible keyboard focus ring everywhere (the sheet only styled focus on
   text inputs), and honouring the OS "reduce motion" setting. Chart.js
   animations are switched off separately, in campaigns.js, because they are
   drawn to a canvas and CSS can't reach them.                               */

:where(a, button, select, textarea, input, [tabindex]):focus-visible{
  outline:2px solid var(--link); outline-offset:2px; border-radius:4px;
}
@media (prefers-reduced-motion: reduce){
  *, *::before, *::after{
    animation-duration:.01ms !important; animation-iteration-count:1 !important;
    transition-duration:.01ms !important; scroll-behavior:auto !important;
  }
}

/* ============================================================================
   CALENDAR
   Month grid + agenda + day view + appointment editor.

   The layout problem this solves: a month grid has to stay scannable on a busy
   week. Every row is the same height because a day cell shows at most three
   events and collapses the rest into "+N more" — without that cap, one busy
   Tuesday stretches its whole row and the month stops being readable at a
   glance. Below 900px the grid becomes a single scrollable column, which is
   also what the Agenda view is for.
   ========================================================================== */

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

.cal-bar{
  display:flex; flex-wrap:wrap; align-items:center; gap:1rem 1.5rem;
  padding:.85rem 1rem; margin-bottom:1.25rem;
}
.cal-nav{ display:flex; align-items:center; gap:.5rem; }
.cal-month{ font-size:1.1rem; font-weight:700; color:var(--on-accent); margin:0; min-width:11ch; text-align:center; }
.cal-arrow{ font-size:1.2rem; line-height:1; padding:.25rem .7rem; }

.cal-modes{ display:inline-flex; border:1px solid var(--border); border-radius:var(--radius); overflow:hidden; }
.cal-mode{
  background:transparent; border:none; color:var(--text-muted);
  padding:.4rem .9rem; font-size:.85rem; font-weight:600; cursor:pointer;
}
.cal-mode:hover{ background:var(--panel-2); color:var(--text); }
.cal-mode-on{ background:var(--accent); color:var(--on-accent); }

.cal-tools{ display:flex; align-items:center; gap:1rem; flex-wrap:wrap; }
.cal-client{ min-width:12rem; padding:.4rem .6rem; }
.cal-toggle{ display:inline-flex; align-items:center; gap:.4rem; font-size:.85rem; color:var(--text-muted); cursor:pointer; }

.cal-layers{ display:flex; flex-wrap:wrap; align-items:center; gap:.25rem 1rem; }
.cal-layer{ display:inline-flex; align-items:center; gap:.4rem; font-size:.8rem; color:var(--text-muted); cursor:pointer; }
.cal-swatch{ width:.7rem; height:.7rem; border-radius:2px; display:inline-block; }

/* --- Month grid ----------------------------------------------------------- */
.cal-wrap{ padding:1rem; }
.cal-dow-row,
.cal-grid{ display:grid; grid-template-columns:repeat(7, minmax(0,1fr)); }
.cal-dow-row{ margin-bottom:.4rem; }
.cal-dow{ text-align:center; font-size:.75rem; font-weight:700; letter-spacing:.04em;
  text-transform:uppercase; color:var(--text-muted); padding:.2rem 0; }

.cal-grid{ gap:1px; background:var(--border); border:1px solid var(--border); border-radius:var(--radius); overflow:hidden; }
.cal-cell{ background:var(--panel); min-height:118px; padding:.4rem; }
.cal-blank{ background:var(--bg); }
.cal-day{ cursor:pointer; transition:background .15s ease; display:flex; flex-direction:column; gap:.3rem; }
.cal-day:hover{ background:var(--panel-2); }
.cal-daynum{
  display:block; width:100%; background:none; border:none; padding:0;
  font:inherit; font-size:.8rem; font-weight:600; color:var(--text-muted);
  text-align:right; cursor:pointer; border-radius:3px;
}
.cal-daynum:hover{ color:var(--text); }
.cal-daynum:focus-visible{ outline:2px solid var(--accent); outline-offset:2px; }
.cal-today{ box-shadow:inset 0 0 0 2px var(--accent); }
.cal-today .cal-daynum{ color:var(--on-accent); }

.cal-evs{ display:flex; flex-direction:column; gap:3px; min-width:0; }
.cal-ev{
  display:block; width:100%; text-align:left;
  font-size:.72rem; line-height:1.3; padding:2px 6px; border-radius:4px;
  border:none; border-left:3px solid var(--ev,var(--neutral));
  background:var(--panel-2);   /* fallback where color-mix() is unsupported */
  background:color-mix(in srgb, var(--ev,var(--neutral)) 22%, var(--panel));
  color:var(--text); cursor:pointer;
  overflow:hidden; text-overflow:ellipsis; white-space:nowrap;
  transition:opacity .15s ease;
}
.cal-ev:hover{ opacity:.8; }
.cal-ev-done{ opacity:.55; text-decoration:line-through; }
.cal-more{
  background:none; border:none; color:var(--text-muted); cursor:pointer;
  font-size:.7rem; font-weight:600; text-align:left; padding:1px 6px;
}
.cal-more:hover{ color:var(--text); text-decoration:underline; }

/* --- Agenda --------------------------------------------------------------- */
.cal-agenda{ padding:1rem; }
.cal-empty{ padding:2rem 1rem; text-align:center; }
.cal-agenda-day{ padding:.65rem 0; border-bottom:1px solid var(--border); }
.cal-agenda-day:last-child{ border-bottom:none; }
.cal-agenda-date{
  background:none; border:none; color:var(--on-accent); font-weight:700; font-size:.9rem;
  cursor:pointer; padding:0 0 .4rem; text-align:left;
}
.cal-agenda-date:hover{ text-decoration:underline; }
.cal-agenda-items{ display:flex; flex-direction:column; gap:.3rem; }
.cal-agenda-item{
  display:flex; align-items:baseline; gap:.6rem; width:100%; text-align:left;
  background:var(--panel-2); border:none; border-left:3px solid var(--ev,var(--neutral));
  border-radius:4px; padding:.4rem .6rem; color:var(--text); cursor:pointer; font-size:.85rem;
}
.cal-agenda-item:hover{ filter:brightness(1.12); }
.cal-agenda-label{ font-weight:600; }
.cal-agenda-detail{ color:var(--text-muted); font-size:.8rem; min-width:0; overflow:hidden; text-overflow:ellipsis; white-space:nowrap; }

/* --- Day view modal ------------------------------------------------------- */
.cal-day-body{ max-height:60vh; overflow-y:auto; display:flex; flex-direction:column; gap:.5rem; }
.cal-day-item{
  display:flex; align-items:flex-start; gap:.75rem;
  background:var(--panel-2); border-left:3px solid var(--ev,var(--neutral));
  border-radius:4px; padding:.6rem .75rem;
}
.cal-day-tick{ margin-top:.15rem; flex-shrink:0; }
.cal-day-main{ flex:1; min-width:0; }
.cal-day-line{ display:flex; gap:.6rem; align-items:baseline; flex-wrap:wrap; }
.cal-day-when{ font-size:.78rem; font-weight:700; color:var(--link); white-space:nowrap; }
.cal-day-what{ font-weight:600; min-width:0; overflow-wrap:anywhere; }
.cal-day-sub{ display:flex; gap:.5rem; align-items:center; flex-wrap:wrap; margin-top:.25rem; font-size:.8rem; }
.cal-day-acts{ display:flex; gap:.35rem; flex-shrink:0; }
.cal-day-actions{ justify-content:space-between; }
.cal-appt-actions{ align-items:center; }
.cal-spacer{ flex:1; }

@media (max-width: 900px){
  .cal-bar{ gap:.75rem 1rem; }
  .cal-cell{ min-height:96px; }
  .cal-ev{ font-size:.68rem; }
}
@media (max-width: 640px){
  .cal-dow-row, .cal-grid{ grid-template-columns:1fr; }
  .cal-blank{ display:none; }
  .cal-dow{ display:none; }
  .cal-cell{ min-height:auto; }
  .cal-daynum{ text-align:left; }
}

/* ==========================================================================
   COMMUNICATIONS
   Two views behind one header: the archetype composer and the sent log.
   ========================================================================== */

.comms-modes{ display:flex; gap:.35rem; }
.comms-mode{
  background:var(--panel); color:var(--text-muted); border:1px solid var(--border);
  border-radius:var(--radius); padding:.45rem .9rem; cursor:pointer;
  font:inherit; font-size:.85rem; font-weight:600;
}
.comms-mode:hover{ color:var(--text); }
.comms-mode-on{ background:var(--accent); border-color:var(--accent); color:var(--on-accent); }

.comms-grid{ display:grid; grid-template-columns:minmax(0,1fr) minmax(0,1fr); gap:1.25rem; align-items:start; }
.comms-col{ display:flex; flex-direction:column; gap:1.25rem; min-width:0; }

.comms-actions{
  display:flex; align-items:center; justify-content:space-between;
  gap:.75rem; flex-wrap:wrap; margin-top:.75rem; font-size:.82rem;
}

.comms-error{
  margin-top:.75rem; padding:.7rem .85rem; border-radius:var(--radius);
  /* rgba(220,38,38,…) was --danger typed out a third time, and it did not move
     when --danger did. Derived instead, with the two-declaration fallback the
     calendar already uses for browsers without color-mix(). */
  background:var(--panel-2);
  background:color-mix(in srgb, var(--danger) 12%, var(--panel));
  border:1px solid var(--border);
  border:1px solid color-mix(in srgb, var(--danger) 40%, var(--panel));
  color:var(--danger-text); font-size:.85rem;
}

/* --- What gets sent ------------------------------------------------------- */
.comms-payload{ padding:1rem 1.25rem; }
.comms-payload > summary{ cursor:pointer; font-weight:600; font-size:.9rem; }
.comms-pay-body{
  margin:.6rem 0 0; padding:.75rem; max-height:16rem; overflow:auto;
  background:var(--bg); border:1px solid var(--border); border-radius:4px;
  font-family:ui-monospace, SFMono-Regular, Menlo, monospace;
  font-size:.75rem; line-height:1.5; color:var(--text-muted);
  white-space:pre-wrap; overflow-wrap:anywhere;
}

.comms-out{ font-family:inherit; line-height:1.6; }

/* --- Sent ----------------------------------------------------------------- */
.comms-sent{ display:grid; grid-template-columns:minmax(0,20rem) minmax(0,1fr); gap:1.25rem; align-items:start; }
.comms-list{
  display:flex; flex-direction:column; gap:.4rem;
  max-height:calc(100vh - 18rem); overflow-y:auto;
}
.comms-item{
  display:flex; flex-direction:column; gap:.2rem; width:100%; text-align:left;
  background:var(--panel); border:1px solid var(--border); border-left:3px solid transparent;
  border-radius:var(--radius); padding:.7rem .85rem; cursor:pointer; font:inherit; color:inherit;
}
.comms-item:hover{ background:var(--panel-2); }
.comms-item-on{ border-left-color:var(--accent); background:var(--panel-2); }
.comms-item-top{ display:flex; justify-content:space-between; gap:.6rem; align-items:baseline; }
.comms-item-who{ font-weight:700; font-size:.85rem; overflow-wrap:anywhere; }
.comms-item-when{ font-size:.72rem; color:var(--text-muted); white-space:nowrap; }
.comms-item-sub{ font-size:.82rem; font-weight:600; overflow-wrap:anywhere; }
.comms-item-pre{ font-size:.75rem; color:var(--text-muted); overflow-wrap:anywhere; }

.comms-hint{ margin:-.2rem 0 1rem; }

/* "What changed" — the alignment with its working shown. Tinted, not filled:
   this sits inside a card that is already the loudest thing in the column, and
   a solid red/green block would read as an error state rather than an edit. */
.comms-diff{ margin-top:.75rem; padding:.7rem .85rem; background:var(--bg);
  border:1px solid var(--border); border-radius:var(--radius); }
.comms-diff-key{ font-size:.75rem; margin:0 0 .5rem; }
.comms-diff-body{ margin:0; font-size:.85rem; line-height:1.7;
  white-space:pre-wrap; overflow-wrap:anywhere; }
.comms-del{ background:color-mix(in srgb, var(--danger) 18%, transparent);
  color:var(--danger-text); text-decoration:line-through; border-radius:3px; }
.comms-ins{ background:color-mix(in srgb, var(--ok) 18%, transparent);
  color:var(--ok-text); text-decoration:none; border-radius:3px; }
.comms-read{ min-width:0; }
.comms-read-body{
  white-space:pre-wrap; overflow-wrap:anywhere; line-height:1.65;
  font-size:.9rem; color:var(--text);
}

@media (max-width: 900px){
  .comms-grid, .comms-sent{ grid-template-columns:1fr; }
  .comms-list{ max-height:22rem; }
}

/* ==========================================================================
   DASHBOARD
   Every row is a button, because every row goes somewhere.
   ========================================================================== */

.dash-strip{ display:grid; grid-template-columns:repeat(auto-fit,minmax(11rem,1fr)); gap:1rem; margin-bottom:1.5rem; }

/* Mockup B's hero. The overdue figure is the one genuinely urgent number on
   this page, so it is physically larger and sits one surface forward of the
   three ambient tiles rather than being a fourth equal one. It spans two
   columns of the auto-fit grid so it reads as a different KIND of thing. */
.dash-hero{
  grid-column:span 2; display:flex; flex-direction:column; gap:.2rem;
  background:var(--surface-2); border:1px solid var(--border);
  border-left:3px solid var(--neutral);
  border-radius:var(--radius); padding:1.1rem 1.25rem;
}
.dash-hero-v{ font-size:2.4rem; font-weight:700; line-height:1.05; }
/* Warmer only when there is something to be warm about: a red hero over £0.00
   trains the owner to ignore it. */
.dash-hero-bad{
  border-left-color:var(--danger);
  background:var(--surface-2);                                  /* fallback */
  background:color-mix(in srgb, var(--danger) 10%, var(--surface-2));
}
.dash-hero-bad .dash-hero-v{ color:var(--danger-text); }
@media (max-width:760px){ .dash-hero{ grid-column:span 1; } }

/* Mockup A's ordering: the column that gets acted on is wider and first.
   align-items:stretch (the grid default) is what gives the shared bottom
   edge — the ragged three-column bottom was the thing that read as unfinished. */
.dash-main{ display:grid; grid-template-columns:1.6fr 1fr; gap:1.25rem; align-items:start; }
.dash-col-wide{ min-width:0; }
@media (max-width:1000px){ .dash-main{ grid-template-columns:1fr; } }

/* The urgent card carries a coloured hairline; the ambient ones stay quiet. */
.dash-card-urgent{ border-top:3px solid var(--danger); }

/* Rows grouped by WHY. The label is quiet and the group does the work. */
.dash-group-k{
  font-size:.7rem; text-transform:uppercase; letter-spacing:.06em;
  color:var(--text-muted); font-weight:var(--w-num);
  margin:1rem 0 .4rem;
}
.dash-group-k:first-child{ margin-top:0; }

/* An avatar turns a list of strings into a list of people. Initials are
   derived in dashboard.js and escaped by html`` — never innerHTML'd. */
.dash-av{
  flex-shrink:0; width:1.9rem; height:1.9rem; border-radius:999px;
  display:inline-flex; align-items:center; justify-content:center;
  font-size:.7rem; font-weight:var(--w-num); letter-spacing:.02em;
  color:var(--on-accent); background:var(--av,var(--neutral));
  overflow:hidden;
}
.dash-row-body{ display:flex; flex-direction:column; gap:.15rem; min-width:0; flex:1; text-align:left; }
/* The age badge. Tabular so a column of them lines up. */
.dash-age{
  flex-shrink:0; font-size:.72rem; font-weight:var(--w-num);
  color:var(--danger-text); font-variant-numeric:tabular-nums;
}
.dash-tile{ background:var(--panel); border:1px solid var(--border); border-left:3px solid var(--accent);
  border-radius:var(--radius); padding:.9rem 1rem; display:flex; flex-direction:column; gap:.15rem; }
.dash-tile-k{ font-size:.75rem; text-transform:uppercase; letter-spacing:.04em; color:var(--text-muted); font-weight:700; }
.dash-tile-v{ font-size:1.5rem; font-weight:700; }
.dash-tile-sub{ font-size:.75rem; color:var(--text-muted); }
.dash-tile.dash-bad{ border-left-color:var(--danger); }
.dash-tile.dash-warn{ border-left-color:var(--warn); }

.dash-grid{ display:grid; grid-template-columns:repeat(auto-fit,minmax(18rem,1fr)); gap:1.25rem; align-items:start; }
.dash-col{ display:flex; flex-direction:column; gap:1.25rem; min-width:0; }
.dash-card{ padding:1.1rem 1.25rem; }
.dash-h2{ display:flex; align-items:center; gap:.5rem; }
.dash-count{ background:var(--panel-2); border:1px solid var(--border); border-radius:999px;
  padding:.05rem .5rem; font-size:.72rem; font-weight:700; color:var(--text-muted); }

.dash-list{ display:flex; flex-direction:column; gap:.35rem; }
.dash-scroll{ max-height:19rem; overflow-y:auto; }
.dash-empty{ color:var(--text-muted); font-size:.85rem; margin:.2rem 0 0; }

.dash-row{ display:flex; flex-direction:column; gap:.15rem; width:100%; text-align:left;
  background:var(--panel-2); border:1px solid var(--border); border-left:3px solid transparent;
  border-radius:var(--radius); padding:.55rem .7rem; cursor:pointer; font:inherit; color:inherit; }
.dash-row:hover{ border-color:var(--accent); }
.dash-row-bad{ border-left-color:var(--danger); }
.dash-row-main{ font-size:.85rem; font-weight:600; display:flex; align-items:center; gap:.4rem;
  flex-wrap:wrap; overflow-wrap:anywhere; }
.dash-row-meta{ font-size:.74rem; color:var(--text-muted); overflow-wrap:anywhere; }
.dash-row-split{ flex-direction:row; align-items:center; justify-content:space-between; gap:.5rem; cursor:default; }
.dash-row-split:hover{ border-color:var(--border); }
.dash-row-open{ display:flex; flex-direction:column; gap:.15rem; flex:1; min-width:0; text-align:left;
  background:none; border:0; padding:0; cursor:pointer; font:inherit; color:inherit; }
/* A row with an avatar beside its text. It MUST come after .dash-row and
   .dash-row-open: it changes their flex-direction, and between equal selectors
   the later one wins. It used to sit 26 lines above them, so every avatar row
   on the Dashboard stacked its circle over the text, centred, on every screen
   size — found on the owner's phone (deep-check section 28 now checks the
   order for every pair of classes used together). */
.dash-row-av{ flex-direction:row; align-items:center; gap:.6rem; }
.dash-chip{ font-size:.62rem; padding:.05rem .35rem; }
.dash-actions{ display:flex; flex-direction:column; gap:.4rem; }
.dash-actions .btn{ width:100%; text-align:left; }

.dash-pipeline-h{ margin:2rem 0 1rem; font-size:1.25rem; }
.dash-pipeline{ display:grid; grid-template-columns:repeat(auto-fit,minmax(12rem,1fr)); gap:1rem; align-items:start; }
.dash-stage{ background:var(--panel); border:1px solid var(--border); border-top:3px solid var(--stage);
  border-radius:var(--radius); padding:.8rem .9rem; min-width:0; }
.dash-stage-h{ display:flex; align-items:center; gap:.4rem; margin:0 0 .6rem; font-size:.85rem;
  font-weight:700; color:var(--stage); }
.dash-stage-list{ display:flex; flex-direction:column; gap:.3rem; max-height:20rem; overflow-y:auto; }
.dash-chip-card{ display:flex; align-items:center; gap:.4rem; flex-wrap:wrap; width:100%; text-align:left;
  background:var(--panel-2); border:1px solid var(--border); border-radius:var(--radius);
  padding:.4rem .55rem; cursor:pointer; font:inherit; font-size:.8rem; color:inherit; overflow-wrap:anywhere; }
.dash-chip-card:hover{ border-color:var(--stage); }

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

   Reuses the invoicing stat cards, the campaigns chart grid and the standard
   table. The only new pieces are the two block headings that keep the "money
   received" figures visually apart from the "invoices raised" ones — they are
   scoped by two different dates and a single undivided row of tiles would
   imply they add up to something.
   ------------------------------------------------------------------------- */
.rep-period-label{ font-size:.8rem; color:var(--text-muted); }
.rep-bounds{ margin:0 0 1.25rem; font-size:.85rem; color:var(--text-muted); }
.rep-block-head{ font-size:.8rem; text-transform:uppercase; letter-spacing:.06em;
  color:var(--text-muted); margin:0 0 .6rem; }
.rep-note{ margin:-.35rem 0 1.5rem; font-size:.8rem; color:var(--text-muted); }
.rep-pad{ padding:1.25rem 1.5rem; }
/* Canary for deep-check section 7: `.styled-only-in-a-comment` is named here
   and defined nowhere. If the check ever reads CSS comments as rules again, its
   self-test sees this and fails.

   `.pad` was USED by social.js on two cards and styled by nothing, so the
   composer and the posts list had their content flush against the card border
   while the loading and empty states beside them (which use .rep-pad) looked
   fine. Deep-check section 7 should have caught it and did not: its hook
   heuristic asked whether the string ".pad" appeared anywhere in JS, and
   `String(n).padStart(2,'0')` answered yes, so an unstyled class hid behind a
   method name for the whole design pass.

   This is opt-IN and touches only the two elements that already ask for it. It
   is NOT the app-wide `.card { padding }` fix, which stays deferred: that one
   changes twelve screens and needs an opt-out list drawn up first. */
.pad{ padding:var(--pad-card); }
.rep-num{ text-align:right; font-variant-numeric:tabular-nums; }

/* The WhatsApp hand-off's refusal state. A dead button that does nothing when
   clicked is worse than a plain explanation of why there isn't one. */
.comms-wa-why{ font-size:.8rem; cursor:help; border-bottom:1px dotted var(--border); }

.page-sub{ color:var(--text-muted); margin:.15rem 0 1.1rem; max-width:60ch; font-size:.9rem; }

/* -------------------------------------------------------------------------
   Social studio (Marketing → Social)

   Reuses the standard card, form-input, button and page-head styles. The new
   pieces are the composer's control row, the post list rows and the status /
   voice chips. No new colours — chips take theirs inline from the archetype
   palette and the status map, the same way the Reports tiles do.
   ------------------------------------------------------------------------- */
/* auto-fit with a floor, not repeat(3,1fr): three fields in a phone-width
   card squeezed the selects to "Fa" and "— c". The fields now wrap when the
   card is too narrow for three — decided by the card's own width, with no
   breakpoint to get wrong (Standard 8). */
.social-composer .social-controls{ display:grid; grid-template-columns:repeat(auto-fit, minmax(min(9.5rem,100%),1fr)); gap:.75rem; margin:.5rem 0 .75rem; }
.social-composer .ff{ display:flex; flex-direction:column; gap:.25rem; }
.social-composer .ff span{ font-size:.8rem; color:var(--text-muted); }
.social-text{ width:100%; resize:vertical; min-height:8rem; font-family:inherit; }
.social-actions{ display:flex; align-items:center; justify-content:space-between; margin-top:.75rem; gap:.5rem; }
.social-note{ margin:.5rem 0 0; font-size:.8rem; }
.social-error{ color:var(--danger,var(--danger)); font-size:.85rem; margin:.5rem 0 0; }

.social-list{ display:flex; flex-direction:column; gap:.75rem; }
/* Wraps, and the text asks for 14rem before it will share its line: on a
   phone the three buttons used to keep their place and squeeze the post into
   a column three words wide. Now they drop under it instead. */
.social-item{ display:flex; flex-wrap:wrap; gap:.5rem 1rem; align-items:flex-start; justify-content:space-between;
  border:1px solid var(--border); border-radius:var(--radius); padding:.85rem 1rem; }
.social-item-main{ min-width:0; flex:1 1 14rem; }
.social-item-meta{ display:flex; align-items:center; gap:.5rem; flex-wrap:wrap; font-size:.8rem; margin-bottom:.4rem; }
.social-item-text{ margin:0; white-space:pre-wrap; overflow-wrap:anywhere; color:var(--text); font-size:.9rem;
  display:-webkit-box; -webkit-line-clamp:4; -webkit-box-orient:vertical; overflow:hidden; }
.social-item-actions{ display:flex; gap:.35rem; flex-shrink:0; }
.social-chip{ display:inline-block; padding:.1rem .5rem; border-radius:999px; font-size:.72rem; font-weight:600;
  color:var(--on-accent); background:var(--chip,var(--neutral)); }
.social-overdue{ color:var(--danger,var(--danger)); font-weight:600; }

/* -------------------------------------------------------------------------
   Networking: "Met someone" (KICKOFF_NETWORKING.md slice 1)
   ------------------------------------------------------------------------- */
/* One tap from every screen. Under the drawer (40) and every dialog (50). */
.met-fab{ position:fixed; right:1.25rem; bottom:1.5rem; z-index:30; height:3.5rem; padding:0 1.4rem 0 1.1rem;
  border:0; border-radius:1.75rem; background:var(--accent); color:var(--on-accent); font:inherit; font-size:1rem;
  font-weight:600; display:flex; align-items:center; gap:.6rem; box-shadow:0 8px 24px rgba(0,0,0,.45); cursor:pointer; }
.met-fab:hover{ background:var(--accent-2); }
/* The button floats over the page: the last row of any screen must clear it. */
.app-main{ padding-bottom:6rem; }

.met-modal{ max-width:480px; }
.met-where{ display:flex; align-items:center; gap:.75rem; background:var(--bg); border:1px solid var(--border);
  border-radius:var(--radius); padding:.75rem .9rem; }
.met-where-body{ flex:1; min-width:0; display:flex; flex-direction:column; gap:.1rem; }
.met-k{ font-size:.78rem; color:var(--text-muted); }
.met-where-v{ font-size:1rem; font-weight:600; overflow-wrap:anywhere; }
.met-stale{ font-size:.8rem; color:var(--warn); }
.met-picker{ margin-top:.6rem; display:flex; flex-direction:column; gap:.35rem; }
.met-inline{ display:flex; gap:.5rem; }
.met-inline .form-input{ flex:1; min-width:0; }
.met-time{ font-size:.82rem; color:var(--text-muted); margin:.5rem 0 1rem; }
.met-grid{ display:grid; grid-template-columns:repeat(2, minmax(0, 1fr)); gap:.75rem 1rem; margin-bottom:1rem; }
.met-modal > .ff{ margin-bottom:1rem; }
.met-consent{ display:flex; align-items:center; gap:.7rem; min-height:2.75rem; font-size:.95rem; color:var(--text); margin-top:.5rem; flex-wrap:wrap; }
.met-consent input{ width:1.35rem; height:1.35rem; accent-color:var(--accent); margin:0; flex-shrink:0; }
.met-actions{ margin-top:1rem; }
.met-due{ font-size:.82rem; color:var(--text-muted); text-align:right; margin:.5rem 0 0; }

/* "Is this the Ada Lovelace you already have?" */
.met-same{ max-width:440px; display:flex; flex-direction:column; gap:.9rem; }
.met-same .modal-title{ margin:0; }
.met-same p{ margin:0; }
.met-same-card{ background:var(--bg); border:1px solid var(--border); border-radius:var(--radius); padding:.85rem;
  display:flex; flex-direction:column; gap:.3rem; }
.met-same-name{ font-weight:600; }
.met-same-actions{ display:flex; flex-direction:column; gap:.6rem; }
.met-same-actions .btn{ min-height:3rem; }
.met-same-actions .btn-ghost{ border:1px solid var(--border-strong); }
.met-same-note{ font-size:.82rem; }

/* The client page: where you met, and marketing consent. */
.cd-met-list{ display:flex; flex-direction:column; gap:.8rem; }
.cd-met-row{ display:flex; flex-direction:column; gap:.15rem; }
.cd-met-where{ font-weight:600; overflow-wrap:anywhere; }
.cd-met-when{ font-size:.85rem; color:var(--text-muted); }
.cd-met-note{ font-size:.88rem; overflow-wrap:anywhere; }
.cd-consent{ display:flex; flex-direction:column; align-items:flex-start; gap:.25rem; }
.cd-consent-history{ font-size:.82rem; color:var(--text-muted); }
.cd-consent-history summary{ cursor:pointer; }
.cd-consent-past{ display:block; margin-top:.2rem; }

/* Settings → Networking */
.net-block{ margin-bottom:1.25rem; }
.net-days{ width:5.5rem; flex:0 0 auto; }
.net-note{ font-size:.85rem; margin:.4rem 0 0; }
.net-events, .net-held-list{ display:flex; flex-direction:column; margin-top:.75rem; }
.net-ev, .net-held-row{ display:flex; flex-wrap:wrap; align-items:center; gap:.5rem; padding:.7rem 0; border-top:1px solid var(--border); }
.net-ev-main{ flex:1 1 12rem; min-width:0; display:flex; flex-direction:column; gap:.1rem; }
.net-ev-name{ font-weight:600; overflow-wrap:anywhere; }
.net-ev-sub{ font-size:.82rem; }
.net-badge{ font-size:.75rem; font-weight:600; color:var(--on-accent); background:var(--accent); border-radius:999px; padding:.2rem .6rem; }
.net-rename{ flex-basis:100%; display:flex; gap:.5rem; }
.net-rename .form-input{ flex:1; min-width:0; }
.net-clear{ margin-top:.6rem; }
.net-held-count{ font-size:.85rem; margin-left:auto; }

/* The Dashboard's held-emails note, and the current event on Clients. */
.dash-held{ display:flex; align-items:center; gap:.6rem; width:100%; margin:0 0 1.25rem; padding:.8rem 1rem; text-align:left;
  background:var(--panel); border:1px solid var(--warn); border-radius:var(--radius); color:var(--text); font:inherit; cursor:pointer; }
.dash-held-go{ margin-left:auto; color:var(--link); font-weight:600; white-space:nowrap; }
.net-strip{ display:flex; align-items:center; gap:.6rem; background:var(--panel); border:1px solid var(--border);
  border-radius:var(--radius); padding:.7rem .9rem; margin-bottom:1rem; font-size:.92rem; }

/* Your card (Settings → Businesses): the preview, in the PSYNC'D look the scan
   page will wear (KICKOFF_NETWORKING.md decision 9). Colours: --brand-* in :root. */
.biz-card .met-grid{ margin-bottom:.75rem; }
.biz-card > .ff{ margin-bottom:.75rem; }
.card-prev{ background:var(--brand-ink); color:var(--brand-text); border-radius:var(--radius); padding:1rem;
  display:flex; flex-direction:column; gap:.8rem; margin-top:.25rem; }
.card-prev-pic{ width:4rem; height:4rem; border-radius:50%; align-self:center; display:block; }
.card-prev-box{ background:var(--brand-panel); border:1px solid var(--brand-gold); border-radius:.9rem; padding:1rem 1.1rem;
  display:flex; flex-direction:column; gap:.2rem; }
.card-prev-name{ font-family:Georgia, 'Times New Roman', serif; font-size:1.15rem; overflow-wrap:anywhere; }
.card-prev-role{ font-size:.85rem; color:var(--brand-muted); }
.card-prev-rows{ display:grid; grid-template-columns:3.5rem minmax(0, 1fr); gap:.35rem .5rem; font-size:.85rem;
  border-top:1px solid var(--brand-rule); padding-top:.7rem; margin-top:.5rem; overflow-wrap:anywhere; }
.card-prev-k{ color:var(--brand-muted); }
.card-prev-msg{ margin:0; text-align:center; font-family:Georgia, 'Times New Roman', serif; color:var(--brand-gold-1); line-height:1.45; }
.card-prev-link{ text-align:center; border:1px solid var(--brand-gold); border-radius:.7rem; padding:.6rem; font-weight:600; font-size:.9rem; }
