/* ==========================================================================
   iOR BD Capture - component styles (v3: solid dark professional).
   Built on design tokens (tokens.css). NO gradients, NO glass/backdrop-filter,
   NO glow. Solid surfaces + hairline borders; one warm orange accent used
   sparingly (record button, waveform playhead, one primary CTA); cyan = CRM only.
   Restyles the visual layer only; layout/flow/interaction are unchanged.
   ========================================================================== */

*, *::before, *::after { box-sizing: border-box; }
html, body { margin: 0; padding: 0; height: 100%; }

html {
  /* Chrome/Samsung Internet on Android will "boost" font sizes on pages it
     judges to be desktop-width. width=device-width normally suppresses that,
     but Samsung Internet still inflates text in some reader/accessibility
     modes, which breaks the tabular timer and the fixed-height topbar. */
  -webkit-text-size-adjust: 100%;
  text-size-adjust: 100%;
  /* Set on html AS WELL as body: Chrome reads the viewport's overscroll
     behaviour off html when html declares one, and only falls through to body
     when it does not. Declaring both removes the ambiguity. `contain` stops
     scroll chaining (which is what fires pull-to-refresh on Android) while
     keeping the local overscroll glow, so the gesture still feels alive.
     The x axis matters too: Chrome navigates back on horizontal overscroll. */
  overscroll-behavior: contain;
}

body {
  font-family: var(--font-sans);
  font-size: var(--text-base);
  line-height: var(--leading-normal);
  color: var(--ink);
  background: var(--bg);
  -webkit-font-smoothing: antialiased;
  text-rendering: optimizeLegibility;
  padding-bottom: var(--safe-bottom);
  overscroll-behavior: contain;
  /* Deal names, company names and pasted transcripts are user/CRM data and can
     contain a single unbreakable token longer than a 360px screen. Only kicks
     in when a word genuinely cannot fit, so normal copy is unaffected. */
  overflow-wrap: break-word;
}

button { font-family: inherit; font-size: inherit; cursor: pointer; }

/* Android taps: `manipulation` drops the double-tap-zoom wait, so rapid taps on
   chips and rows register immediately. The tap highlight is retargeted from
   Chrome's default blue-grey (off-brand on this canvas) to a neutral wash that
   still confirms the touch. */
button, a, [role="button"], summary, label, input, select, textarea {
  touch-action: manipulation;
  -webkit-tap-highlight-color: rgba(255, 255, 255, 0.06);
}

/* Hit-target expansion: grows a control's TOUCH area to the 48x48dp Android
   minimum (Material) without changing a pixel of how it looks. Applied to the
   glyph buttons whose visual box is deliberately smaller than 48 and where
   growing the box itself would inflate the row around it. No markup change
   needed, so this stays a pure stylesheet fix. */
.search-clear,
.link-chip .chip-x,
.contact-chip { position: relative; }
.search-clear::after,
.link-chip .chip-x::after,
.contact-chip::after {
  content: "";
  position: absolute;
  top: 50%; left: 50%;
  width: 100%; height: 100%;
  min-width: var(--tap-min);
  min-height: var(--tap-min);
  transform: translate(-50%, -50%);
}
/* Picker contact chips wrap into rows, so a horizontally-grown target would
   collide with the neighbour across an 8px gap. Their width is already fine
   (a name plus 24px of padding clears 48); only the 34px height was short. */
.contact-chip::after { min-width: 0; }

:focus-visible {
  outline: var(--focus-width) solid var(--focus-ring);
  outline-offset: var(--focus-offset);
}

.sr-only {
  position: absolute; width: 1px; height: 1px;
  padding: 0; margin: -1px; overflow: hidden;
  clip: rect(0 0 0 0); white-space: nowrap; border: 0;
}
[hidden] { display: none !important; }

/* ---- Top bar ------------------------------------------------------------- */
.topbar {
  position: sticky;
  top: 0;
  z-index: var(--z-sticky);
  display: flex;
  align-items: center;
  gap: var(--space-2);
  height: calc(56px + var(--safe-top));
  padding: var(--safe-top) var(--space-4) 0;
  background: var(--bg);
  border-bottom: 1px solid var(--border);
}
/* min-width:0 + ellipsis so the title yields to the pill instead of pushing it
   off-screen at 360px; the pill is the only control up here and must stay whole. */
.topbar-title {
  flex: 1 1 auto;     /* soaks up the slack so the actions group on the right */
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  /* Reads as page identity (names the current mode), so a touch less muted than
     a purely decorative label. Neutral ink only (never accent). */
  color: var(--ink-2);
  font-size: var(--text-sm);
  font-weight: var(--weight-semibold);
}
.nav-pill {
  display: inline-flex;
  flex: none;
  align-items: center;
  gap: var(--space-2);
  min-height: var(--tap-min);
  padding: 0 var(--space-4);
  border: 1px solid var(--border);
  border-radius: 0;          /* square by request: no rounded corners on the top bar */
  background: var(--surface-2);
  color: var(--ink-2);
  font-size: var(--text-sm);
  font-weight: var(--weight-semibold);
}
.nav-pill .count {
  display: inline-flex;
  min-width: 20px; height: 20px;
  padding: 0 6px;
  align-items: center; justify-content: center;
  border-radius: var(--radius-pill);
  background: var(--accent);
  color: var(--accent-ink);
  font-size: var(--text-2xs);
  font-weight: var(--weight-bold);
}
.nav-pill svg { width: 18px; height: 18px; }

/* ---- Slim offline / syncing status bar (§17.7), pinned under the top bar --- */
.queue-banner {
  position: sticky;
  top: calc(56px + var(--safe-top));
  z-index: var(--z-banner);
  display: flex;
  align-items: center;
  justify-content: center;
  gap: var(--space-2);
  padding: var(--space-2) var(--space-4);
  background: var(--surface-2);
  border-bottom: 1px solid var(--border);
  color: var(--ink-2);
  font-size: var(--text-xs);
}
.queue-banner .qb-glyph { display: inline-flex; color: var(--ink-3); }
.queue-banner .qb-glyph svg { width: 14px; height: 14px; }
.queue-banner.syncing .qb-glyph { color: var(--accent); }
.queue-banner.syncing .qb-glyph svg { animation: spin 0.9s linear infinite; }
@media (prefers-reduced-motion: reduce) { .queue-banner.syncing .qb-glyph svg { animation: none; } }

/* ---- App / screen scaffold ---------------------------------------------- */
.app { max-width: var(--content-max); margin: 0 auto; padding: 0 var(--space-4); }
.screen { display: block; }

/* ==========================================================================
   Capture screen
   ========================================================================== */
/* Height unit choice matters a lot here on Android.
   dvh tracks the URL bar live, so the moment a receipt makes the page scrollable
   and Chrome collapses the bar, this min-height GROWS mid-scroll and everything
   below the mic (helper line, text fallback) slides down under the thumb.
   svh is the viewport with the bar SHOWN: the largest height guaranteed to be
   visible, so the layout is computed once and never reflows as the bar moves,
   and nothing can be clipped behind it. vh stays first as the fallback for
   engines predating both (Chrome <108, Samsung Internet <21). */
.capture {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: flex-start;   /* orb sits high; the type-it row is pinned to the bottom */
  min-height: calc(100vh - 56px - var(--safe-top));
  min-height: calc(100svh - 56px - var(--safe-top));
  padding-bottom: var(--space-8);
}
.capture-spacer { flex: 0 0 auto; min-height: var(--space-5); }

.receipts { width: 100%; }
.receipts:not(:empty) { padding-top: var(--space-4); }

.mic-wrap { display: flex; flex-direction: column; align-items: center; width: 100%; }

/* Full-width live waveform strip (§17.3): thin white bars + orange playhead. */
.wave-strip { width: 100%; height: var(--wave-strip-h); margin-bottom: var(--space-4); display: none; }
.wave-strip.show { display: block; }
.wave-strip canvas { width: 100%; height: 100%; display: block; }

.mic-timer {
  height: var(--text-timer);
  display: flex;
  align-items: center;
  gap: var(--space-3);
  font-size: var(--text-timer);
  line-height: 1;
  font-weight: var(--weight-semibold);
  font-variant-numeric: var(--numeric-tabular);
  color: var(--ink); /* clean white timer; the orange dot carries "live" */
  margin-bottom: var(--space-5);
  visibility: hidden;
}
.mic-timer.show { visibility: visible; }
.mic-timer.warn { color: var(--warning); }
.mic-timer .dot {
  width: 0.4em; height: 0.4em;
  border-radius: var(--radius-circle);
  background: var(--recording);
  animation: recdot 1.2s ease-in-out infinite;
}
@keyframes recdot { 0%, 100% { opacity: 1; } 50% { opacity: 0.4; } }
@media (prefers-reduced-motion: reduce) { .mic-timer .dot { animation: none; } }

/* ---- The record button: modest solid orange circle, flat (no glow) -------- */
.mic-btn {
  position: relative;
  width: var(--mic-size);
  height: var(--mic-size);
  border: none;
  border-radius: var(--radius-circle);
  background: var(--accent);
  color: var(--accent-ink);
  box-shadow: var(--shadow-orb), 0 0 22px rgba(43, 192, 206, 0.38); /* drop shadow + soft turquoise glow */
  display: grid;
  place-items: center;
  transition: transform var(--dur-fast) var(--ease-emphasis),
              background-color var(--dur-fast) var(--ease-standard);
  touch-action: manipulation;
}
.mic-btn:active { transform: scale(0.97); }
.mic-btn .orb-glyph { width: 46%; height: 46%; }

/* Animated flare: a turquoise highlight sweeps continuously around the button
   border. The mask trick paints only a ring (center cut out) so it frames the
   button without covering the glyph; pointer-events:none keeps it tap-through.
   Idle affordance only -- hidden once the button becomes a stop/busy control. */
.mic-btn::after {
  content: "";
  position: absolute;
  inset: -5px;
  border-radius: inherit;
  padding: 3px;
  background: conic-gradient(from 0deg, transparent 0%, var(--accent) 28%, #8fe8f0 45%, var(--accent) 62%, transparent 88%);
  -webkit-mask: linear-gradient(#000 0 0) content-box, linear-gradient(#000 0 0);
          mask: linear-gradient(#000 0 0) content-box, linear-gradient(#000 0 0);
  -webkit-mask-composite: xor;
          mask-composite: exclude;
  pointer-events: none;
  animation: spin 3s linear infinite;
}
.mic-btn.recording::after, .mic-btn.busy::after, .mic-btn.error-state::after { display: none; }
@media (prefers-reduced-motion: reduce) { .mic-btn::after { animation: none; } }

/* Recording: the button becomes a neutral stop control with an orange square. */
.mic-btn.recording {
  background: var(--surface-2);
  color: var(--accent);
  border: 1px solid var(--border-strong);
  box-shadow: var(--shadow-sm);
}
.mic-btn.busy { background: var(--surface-3); color: var(--ink-2); box-shadow: none; }
.mic-btn.error-state { background: var(--surface-2); color: var(--accent); box-shadow: 0 0 0 3px var(--danger); }
.mic-btn .spinner { width: 40px; height: 40px; }

/* ---- Helper slot: action line + coaching hint (§14.11) ------------------- */
.mic-helper { margin-top: var(--space-5); min-height: 3.2em; color: var(--ink-3); text-align: center; }
.mic-helper.danger { color: var(--danger); max-width: 22rem; }
.mic-helper.warn { color: var(--warning); }
.helper-action { font-size: var(--text-base); color: var(--ink-3); }
.helper-hint { display: block; margin-top: var(--space-1); font-size: var(--text-sm); color: var(--ink-3); opacity: 0.85; }
.helper-hint .lead { color: var(--ink-3); }
.helper-hint .anchor { color: var(--ink-2); }
.helper-hint .ph { color: var(--primary-tint-ink); }
.helper-hint.compact { font-size: var(--text-xs); opacity: 1; color: var(--ink-3); }

.discard-btn {
  margin-top: var(--space-4);
  display: inline-flex;
  align-items: center;
  gap: var(--space-2);
  min-height: var(--tap-min);
  padding: 0 var(--space-4);
  border: none;
  background: transparent;
  color: var(--ink-3);
  font-size: var(--text-sm);
  font-weight: var(--weight-medium);
}

/* ---- Text fallback ------------------------------------------------------- */
.text-fallback { width: 100%; margin-top: auto; padding-top: var(--space-7); }
.text-collapsed {
  display: flex; align-items: center; gap: var(--space-3);
  width: 100%; min-height: var(--field-height);
  padding: 0 var(--space-4);
  border: 1px solid var(--border);
  border-radius: var(--radius-md);
  background: var(--surface-2);
  color: var(--ink-3);
  font-size: var(--text-base);
  text-align: left;
}
.text-collapsed svg { width: 20px; height: 20px; flex: none; }
.text-expanded { display: flex; flex-direction: column; gap: var(--space-3); }
.text-expanded textarea {
  width: 100%;
  min-height: calc(4 * 1.5em + 2 * var(--space-3));
  padding: var(--space-3) var(--space-4);
  border: 1px solid var(--border-strong);
  border-radius: var(--radius-md);
  background: var(--surface-2);
  color: var(--ink);
  caret-color: var(--accent);
  font-family: inherit;
  font-size: var(--text-base);
  line-height: var(--leading-normal);
  resize: vertical;
}
.text-expanded textarea::placeholder { color: var(--ink-3); }

/* ==========================================================================
   Buttons
   ========================================================================== */
.btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: var(--space-2);
  min-height: var(--tap-comfortable);
  padding: 0 var(--space-5);
  border: 1px solid transparent;
  border-radius: var(--radius-pill);
  font-size: var(--text-base);
  font-weight: var(--weight-semibold);
  background: var(--surface-2);
  color: var(--ink);
  transition: background-color var(--dur-fast) var(--ease-standard);
}
.btn svg { width: 22px; height: 22px; }
.btn-primary { background: var(--accent); color: var(--accent-ink); box-shadow: var(--shadow-sm); }
.btn-primary:active { background: var(--accent-press); }
.btn-primary:disabled { background: var(--surface-3); color: var(--ink-3); box-shadow: none; pointer-events: none; }
.btn-secondary { background: var(--surface-2); color: var(--ink); border-color: var(--border); }
.btn-danger { background: transparent; color: var(--danger); border-color: var(--border); }
.btn-block { width: 100%; }

/* 360x640 budget Androids are the low end of the primary device set. Trimming
   the pill's side padding (20px -> 12px) buys 16px per button, which is the
   difference between "Looks right" / "Download CSV" sitting on one line and
   wrapping to two inside their pill. Height, fill and radius are untouched, so
   the v3 look holds; only the internal breathing room tightens. */
@media (max-width: 380px) {
  .btn { padding: 0 var(--space-3); }
}

/* ==========================================================================
   Cards (solid surface + hairline border; no glass)
   ========================================================================== */
.card {
  position: relative;
  width: 100%;
  margin-bottom: var(--space-4);
  padding: var(--space-5);
  border: 1px solid var(--border);
  border-radius: var(--radius-lg);
  background: var(--surface);
  box-shadow: var(--shadow-xs);
  animation: card-reveal var(--dur-slow) var(--ease-out);
}
@keyframes card-reveal { from { opacity: 0; transform: translateY(6px); } to { opacity: 1; transform: translateY(0); } }
.card.accent-warning { border-left: 4px solid var(--warning-line); }
.card.accent-danger  { border-left: 4px solid var(--danger); }

/* "Ready to review" + a timestamp + the 48px kebab plus two 12px gaps comes to
   ~292px against ~304px of inner card width at 360 -- it fits, but with nothing
   spare. The label is the element that can afford to give, so it is the only one
   allowed to shrink and wrap; the time and the kebab hold their size rather than
   the kebab being squeezed below its touch target or pushed out of the card. */
.card-statusrow { display: flex; align-items: center; justify-content: space-between; gap: var(--space-3); margin-bottom: var(--space-4); }
.card-statusrow .status-time, .card-statusrow .kebab { flex: none; }
.status-label { display: inline-flex; align-items: center; gap: var(--space-2); min-width: 0; font-weight: var(--weight-semibold); }
.status-label svg, .status-label .sparkle, .status-label .spinner { flex: none; }
.status-label svg { width: 20px; height: 20px; }
.status-label.success { color: var(--success); }
.status-label.neutral { color: var(--ink-2); }
.status-label.danger  { color: var(--danger); }
.status-label.analysing { color: var(--ink-2); }
.status-time { color: var(--ink-3); font-size: var(--text-sm); font-variant-numeric: var(--numeric-tabular); }

.kebab { border: none; background: transparent; color: var(--ink-3); min-width: var(--tap-min); min-height: var(--tap-min); border-radius: var(--radius-md); font-size: var(--text-xl); line-height: 1; }

/* Analysing sparkle: monochrome, gentle opacity twinkle (no rotate) */
.sparkle { display: inline-flex; color: var(--ink-2); }
.sparkle svg { width: 20px; height: 20px; animation: twinkle 1.8s ease-in-out infinite; }
@keyframes twinkle { 0%, 100% { opacity: 0.55; } 50% { opacity: 1; } }
@media (prefers-reduced-motion: reduce) { .sparkle svg { animation: none; opacity: 1; } }

/* Low-confidence / no-contact banner */
.lowconf-banner {
  display: flex; align-items: flex-start; gap: var(--space-3);
  margin-bottom: var(--space-4);
  padding: var(--space-3) var(--space-4);
  border-radius: var(--radius-md);
  background: var(--warning-tint);
  color: var(--warning-tint-ink);
}
.lowconf-banner svg { width: 20px; height: 20px; flex: none; margin-top: 2px; }
.lowconf-banner strong { display: block; font-weight: var(--weight-semibold); }
.lowconf-banner span { font-size: var(--text-sm); }

.field-summary { font-size: var(--text-lg); font-weight: var(--weight-semibold); line-height: var(--leading-snug); color: var(--ink); margin-bottom: var(--space-4); }

.contact-block {
  position: relative;
  padding: var(--space-4);
  border: 1px solid var(--border);
  border-radius: var(--radius-md);
  background: var(--surface-2);
  margin-bottom: var(--space-4);
  display: flex; gap: var(--space-3); align-items: flex-start;
}
.contact-block svg { width: 28px; height: 28px; color: var(--ink-3); flex: none; margin-top: 4px; }
.contact-block > div { min-width: 0; }
/* At --text-2xl (32px) roughly 9 characters fill the inner width of a card on a
   360px screen, so a run-together CRM company name ("AcmeVisionPartnersLLC")
   overflows the card and, because the card bleeds right, straight off the
   viewport. `anywhere` breaks mid-token only when there is no other option. */
.contact-name { font-size: var(--text-2xl); font-weight: var(--weight-bold); line-height: var(--leading-tight); overflow-wrap: anywhere; }
.contact-company { color: var(--ink-2); font-size: var(--text-base); overflow-wrap: anywhere; }
.placeholder { color: var(--ink-3); font-style: normal; }

/* Model chip: solid quiet pill; tinted when set (no gradient) */
.model-chip {
  display: inline-flex; align-items: center; gap: var(--space-2);
  min-height: var(--tap-min); padding: 0 var(--space-4);
  border: 1px solid var(--border);
  border-radius: var(--radius-pill);
  background: var(--primary-tint);
  color: var(--primary-tint-ink);
  font-size: var(--text-base); font-weight: var(--weight-semibold);
  margin-bottom: var(--space-4);
}
.model-chip.unset { background: var(--surface-2); color: var(--ink-2); }
.model-chip svg { width: 18px; height: 18px; }

.contact-details { display: flex; flex-wrap: wrap; gap: var(--space-2) var(--space-5); margin-bottom: var(--space-4); }
.contact-details .detail { display: inline-flex; align-items: center; gap: var(--space-2); min-height: var(--tap-min); color: var(--ink-2); font-size: var(--text-base); background: none; border: none; padding: 0; }
.contact-details .detail svg { width: 18px; height: 18px; color: var(--ink-3); }

.has-space { display: flex; align-items: center; gap: var(--space-2); padding: var(--space-3) var(--space-4); margin-bottom: var(--space-4); border-radius: var(--radius-md); background: var(--success-tint); color: var(--success-tint-ink); font-weight: var(--weight-semibold); }
.has-space svg { width: 20px; height: 20px; }

.section-label { font-size: var(--text-xs); text-transform: uppercase; letter-spacing: var(--tracking-caps); color: var(--ink-3); margin-bottom: var(--space-2); }
.signals { margin: 0 0 var(--space-4); padding-left: var(--space-5); }
.signals li { font-size: var(--text-sm); color: var(--ink-2); margin-bottom: var(--space-1); }

.followups { margin-bottom: var(--space-4); }
.followup-row { display: flex; align-items: center; gap: var(--space-3); min-height: var(--tap-min); padding: var(--space-1) 0; }
.followup-row .checkbox { width: 20px; height: 20px; flex: none; border: 2px solid var(--border-strong); border-radius: var(--radius-sm); }
/* Both are real buttons. They sat at their text height (~20px) inside a 48px
   row, so the actual targets were ~20px tall -- under half the Android minimum,
   and the date button is the one he taps one-handed to fix a bad due date.
   Filling the row height makes each a full 48px target at no visual cost. */
.followup-desc { flex: 1; min-width: 0; display: inline-flex; align-items: center; min-height: var(--tap-min); color: var(--ink); background: none; border: none; text-align: left; padding: 0; font-size: var(--text-base); }
.followup-date { flex: none; display: inline-flex; align-items: center; justify-content: flex-end; min-height: var(--tap-min); color: var(--ink-2); font-size: var(--text-sm); font-variant-numeric: var(--numeric-tabular); background: none; border: none; padding: 0; white-space: nowrap; }
.followup-date.empty { color: var(--ink-3); }
.add-followup { background: none; border: none; padding: var(--space-2) 0; color: var(--accent); font-weight: var(--weight-semibold); font-size: var(--text-sm); min-height: var(--tap-min); }

/* Meta line + interaction pill */
.meta-line { display: flex; align-items: center; flex-wrap: wrap; gap: var(--space-2); color: var(--ink-3); font-size: var(--text-sm); margin-bottom: var(--space-4); }
.interaction-pill { display: inline-flex; align-items: center; gap: var(--space-1); padding: var(--space-1) var(--space-2); border: 1px solid var(--border); border-radius: var(--radius-pill); background: var(--surface-2); color: var(--ink-2); font-size: var(--text-xs); font-weight: var(--weight-semibold); }
.interaction-pill svg { width: 16px; height: 16px; }

/* At 360px a receipt card has ~304px of inner width. Two actions fit; the error
   card's three ("Play" / "Try again" / "Delete") give each ~93px against ~40px
   of button padding plus a 22px glyph, so the labels were overflowing their
   pills. Wrapping to a second row keeps every action full-size and tappable
   rather than shrinking them below --tap-comfortable. The 8.5rem basis means
   two-up on 360 and single-row on anything wider; --space-3 between them
   satisfies the §9.1 spacing rule for receipt actions in both directions. */
.card-actions { display: flex; flex-wrap: wrap; gap: var(--space-3); }
.card-actions .btn { flex: 1 1 8.5rem; min-width: 0; }

/* Inline edit */
.inline-input {
  width: 100%; font-family: inherit; font-size: var(--text-base);
  padding: var(--space-2) var(--space-3);
  border: 1px solid var(--border-strong);
  border-radius: var(--radius-sm);
  background: var(--surface-2);
  color: var(--ink);
  caret-color: var(--accent);
  animation: fade-in var(--dur-fast) var(--ease-standard);
}
textarea.inline-input { min-height: 4em; line-height: var(--leading-normal); resize: vertical; }
@keyframes fade-in { from { opacity: 0; } to { opacity: 1; } }
.field-editable { cursor: text; }
.save-error { color: var(--danger); font-size: var(--text-sm); margin-top: var(--space-1); }

/* Skeletons: subtle neutral sweep (no violet) */
.skeleton-bar { position: relative; height: 14px; border-radius: var(--radius-sm); background: var(--surface-2); overflow: hidden; margin-bottom: var(--space-3); }
.skeleton-bar::after { content: ""; position: absolute; inset: 0; background: linear-gradient(90deg, transparent, rgba(255,255,255,0.06), transparent); transform: translateX(-100%); animation: shimmer 1.4s linear infinite; }
@keyframes shimmer { to { transform: translateX(100%); } }
@media (prefers-reduced-motion: reduce) { .skeleton-bar::after { display: none; } }

/* Compact reviewed card */
.card-compact { position: relative; display: flex; align-items: center; gap: var(--space-3); width: 100%; padding: var(--space-4); border-radius: var(--radius-lg); background: var(--surface); border: 1px solid var(--border); margin-bottom: var(--space-4); text-align: left; }
.card-compact .check { color: var(--success); flex: none; }
.card-compact .check svg { width: 22px; height: 22px; }
.card-compact .summary { flex: 1; color: var(--ink); font-weight: var(--weight-medium); }
.card-compact-main { flex: 1; min-width: 0; display: flex; align-items: center; gap: var(--space-3); padding: 0; background: none; border: none; text-align: left; color: inherit; cursor: pointer; }
.card-compact-dismiss { flex: none; display: grid; place-items: center; width: var(--tap-min); height: var(--tap-min); border: none; background: none; color: var(--card-ink-3); font-size: var(--text-xl); line-height: 1; border-radius: var(--radius-sm); cursor: pointer; }
.rerecord-banner { display: flex; align-items: center; justify-content: space-between; gap: var(--space-3); padding: var(--space-3) var(--space-4); margin-bottom: var(--space-4); border-radius: var(--radius-lg); background: var(--primary-tint); border: 1px solid var(--accent); }
.rerecord-msg { display: flex; align-items: center; gap: var(--space-2); font-weight: var(--weight-medium); font-size: var(--text-sm); color: var(--ink); }
.rerecord-msg svg { width: 18px; height: 18px; flex: none; color: var(--accent); }
.rerecord-cancel { flex: none; border: none; background: none; color: var(--accent); font-weight: var(--weight-semibold); padding: var(--space-2); cursor: pointer; }

/* Spinner */
.spinner { width: 22px; height: 22px; border: 2.5px solid currentColor; border-right-color: transparent; border-radius: var(--radius-circle); animation: spin 0.7s linear infinite; display: inline-block; }
@keyframes spin { to { transform: rotate(360deg); } }
@media (prefers-reduced-motion: reduce) { .spinner { animation-duration: 1.6s; } }

.audio-player { width: 100%; margin-top: var(--space-3); }

/* ==========================================================================
   Notes list
   ========================================================================== */
/* Scroll clearance for the fixed .export-bar. Expressed as the bar's own height
   plus a gap, so it tracks the bar rather than being a hand-tuned number: the
   last element in the screen (now the setup-link row) ends --space-6 above the
   bar's top edge instead of the 16px it previously happened to land on. */
.notes-screen { padding-bottom: calc(var(--export-bar-h) + var(--space-6) + var(--safe-bottom)); }

/* Sticks BELOW the offline/sync bar rather than under it: --banner-h is 0 unless
   app.js sets it while that bar is showing (see tokens.css). Same
   overscroll-behavior-x reasoning as the deck: this chip row scrolls
   horizontally on a 360px screen and must not chain into browser back. */
.filter-bar {
  position: sticky;
  top: calc(56px + var(--safe-top) + var(--banner-h));
  z-index: var(--z-sticky);
  display: flex;
  gap: var(--space-2);
  padding: var(--space-3) 0;
  background: var(--bg);
  overflow-x: auto;
  overscroll-behavior-x: contain;
  -webkit-overflow-scrolling: touch;
}
.filter-chip { flex: none; min-height: var(--tap-min); padding: 0 var(--space-4); border: 1px solid var(--border); border-radius: var(--radius-pill); background: var(--surface-2); color: var(--ink-2); font-size: var(--text-sm); font-weight: var(--weight-semibold); white-space: nowrap; }
.filter-chip.active { background: var(--primary-tint); color: var(--primary-tint-ink); border-color: transparent; }

/* Cached-data marking (offline) */
.cached-indicator { color: var(--ink-3); font-size: var(--text-xs); padding: var(--space-1) 0 var(--space-3); }

.note-card {
  position: relative;
  display: block;
  width: 100%;
  text-align: left;
  min-height: var(--tap-comfortable);
  padding: var(--space-4);
  margin-bottom: var(--space-3);
  border: 1px solid var(--border);
  border-radius: var(--radius-lg);
  background: var(--surface);
  box-shadow: var(--shadow-xs);
}
.note-card .line1 { font-size: var(--text-base); font-weight: var(--weight-semibold); color: var(--ink); padding-right: var(--space-6); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.note-card .line2 { display: flex; align-items: center; gap: var(--space-2); font-size: var(--text-sm); color: var(--ink-2); overflow: hidden; margin-top: 4px; }
.note-card .line2 .l2text { min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.note-card .line3 { display: flex; align-items: center; justify-content: space-between; margin-top: var(--space-2); }
.note-card .time { color: var(--ink-3); font-size: var(--text-xs); }
.note-card .flag { position: absolute; top: var(--space-3); right: var(--space-3); color: var(--warning); }
.note-card .flag svg { width: 18px; height: 18px; }

/* List-card link cue (muted cyan chain glyph) */
.link-cue { display: inline-flex; vertical-align: middle; margin-left: var(--space-1); }
.link-cue svg { width: 15px; height: 15px; color: var(--link); }

/* Status badges */
.badge { display: inline-flex; align-items: center; gap: 4px; padding: 3px var(--space-2); border-radius: var(--radius-pill); font-size: var(--text-xs); font-weight: var(--weight-semibold); white-space: nowrap; }
.badge svg { width: 14px; height: 14px; }
.badge .spinner { width: 12px; height: 12px; border-width: 2px; }
.badge-working  { background: var(--neutral-tint); color: var(--neutral-tint-ink); }
.badge-pending  { background: var(--neutral-tint); color: var(--neutral-tint-ink); } /* static; clock glyph */
.badge-error    { background: var(--danger-tint);  color: var(--danger-tint-ink); }
.badge-review   { background: var(--warning-tint); color: var(--warning-tint-ink); }
.badge-reviewed { background: var(--success-tint); color: var(--success-tint-ink); }
.badge-exported { background: var(--neutral-tint); color: var(--neutral-tint-ink); }

/* Export bar */
.export-bar { position: fixed; left: 0; right: 0; bottom: 0; z-index: var(--z-sticky); padding: var(--space-3) var(--space-4) calc(var(--space-3) + var(--safe-bottom)); background: var(--bg); border-top: 1px solid var(--border); }
.export-bar .btn { max-width: var(--content-max); margin: 0 auto; }

/* Setup-link recovery row (key escape hatch). NOT IN DESIGN-SPEC.md -- styled
   conservatively in the existing v3 language pending spec blessing; see the
   report. Deliberately introduces no new visual pattern: the button reuses the
   .discard-btn tertiary treatment verbatim (transparent, --ink-3, --text-sm,
   no fill, no border) and the field reuses the .firstrun input treatment, which
   is the one place the app already asks for a key. */
.setup-link-row {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: var(--space-3);
  /* Sits directly above the fixed export bar. --space-6 here plus the screen's
     matching bottom padding keeps a rare-use escape hatch from crowding the
     primary Export action, well past the §9.1 8px adjacent-target minimum. */
  margin-top: var(--space-6);
  padding-top: var(--space-5);
  border-top: 1px solid var(--border);
}

/* Tertiary. Orange is reserved for record/live/primary (§17.8), and the primary
   Export CTA is inches below this, so it stays neutral gray and text-only. */
.setup-link-btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-height: var(--tap-min);
  padding: 0 var(--space-4);
  border: none;
  border-radius: var(--radius-pill);
  background: transparent;
  color: var(--ink-3);
  font-size: var(--text-sm);
  font-weight: var(--weight-medium);
}
.setup-link-btn:active { background: var(--surface-2); }

/* Readonly fallback field for insecure origins / old WebViews with no clipboard
   API. The value is a full URL plus a key, so it is always wider than a 360px
   screen: width:100% + min-width:0 keeps the OVERFLOW INSIDE the input (which
   scrolls its own value natively) so the page itself never scrolls sideways.
   --text-base rather than --text-sm because Android zooms on focusing any field
   under 16px, and this one is focused and select()ed the moment it appears. */
.setup-link-text {
  width: 100%;
  min-width: 0;
  min-height: var(--field-height);
  padding: 0 var(--space-3);
  border: 1px solid var(--border-strong);
  border-radius: var(--radius-md);
  background: var(--surface-2);
  color: var(--ink-2);
  font-family: var(--font-mono);
  font-size: var(--text-base);
  text-overflow: clip;
}

/* Empty state */
.empty { display: flex; flex-direction: column; align-items: center; text-align: center; padding: var(--space-9) var(--space-4); color: var(--ink-3); }
.empty svg { width: 56px; height: 56px; color: var(--ink-3); margin-bottom: var(--space-4); }
.empty p { color: var(--ink-2); margin: 0 0 var(--space-5); max-width: 22rem; }

/* ==========================================================================
   Sync status row (notes-list header)
   ========================================================================== */
.sync-row { display: flex; align-items: center; justify-content: space-between; gap: var(--space-3); padding: var(--space-1) 0 var(--space-3); }
.sync-status { color: var(--ink-3); font-size: var(--text-xs); display: inline-flex; align-items: center; gap: var(--space-2); }
.sync-status.failed { color: var(--warning); }
.sync-btn { display: inline-flex; align-items: center; justify-content: center; min-width: var(--tap-min); min-height: var(--tap-min); border: 1px solid var(--border); border-radius: var(--radius-pill); background: var(--surface-2); color: var(--ink-2); }
.sync-btn:disabled { opacity: 0.5; pointer-events: none; }
.sync-btn svg { width: 20px; height: 20px; }
.sync-btn.spinning svg { animation: spin 0.9s linear infinite; }
@media (prefers-reduced-motion: reduce) { .sync-btn.spinning svg { animation-duration: 2s; } }

/* Export sheet unlinked warning line */
.export-unlinked { color: var(--warning); font-size: var(--text-sm); margin-bottom: var(--space-3); display: flex; align-items: center; gap: var(--space-2); }
.export-unlinked svg { width: 16px; height: 16px; flex: none; }
/* Inline text action inside a sheet body (the follow-up tasks download). Keeps the
   48dp tap target the rest of the UI uses without looking like a second CTA next
   to the primary Download button. */
.linklike { background: none; border: none; padding: var(--space-1) 0; min-height: var(--tap-min);
  color: var(--link-tint-ink); font-size: var(--text-sm); font-weight: var(--weight-semibold);
  text-decoration: underline; }
.linklike:active { opacity: 0.7; }
/* Picker truncation notice: the list is capped so a 2,000-row roster cannot be
   rebuilt on every keystroke. Quiet, since it is a hint and not a result. */
.picker-trunc { color: var(--ink-3); font-size: var(--text-xs); padding: var(--space-2) var(--space-3); }

/* Set-up-another-phone sheet. The QR sits on white regardless of theme: scanners
   want a light quiet zone, and the app canvas is near-black. */
.setup-link { display: block; width: 100%; background: none; border: none; min-height: var(--tap-min);
  color: var(--ink-2); font-size: var(--text-sm); text-decoration: underline;
  text-underline-offset: 3px; margin-top: var(--space-2); }
.qr-frame { display: grid; place-items: center; background: #fff; border-radius: var(--radius-md);
  padding: var(--space-4); margin: var(--space-3) 0; min-height: 200px; }
.qr-img { width: 200px; height: 200px; display: block; }
.qr-loading { color: #5b7293; font-size: var(--text-sm); }
.qr-warn { color: var(--warning); }

/* ==========================================================================
   Deal deck (§16) - solid dark cards, hairline border, no glow
   ========================================================================== */
.deck { width: 100%; margin-top: var(--space-2); }
.deck-label { color: var(--ink-3); font-size: var(--text-sm); font-weight: var(--weight-semibold); margin: 0 0 var(--space-2); }
/* overscroll-behavior-x is the load-bearing one: without it, flicking the deck
   past its last card chains to the document and Chrome/Samsung Internet read it
   as the horizontal overscroll back-navigation gesture, so swiping the deck can
   leave the app mid-capture. `contain` keeps the scroll local. The y value stops
   a slightly-diagonal swipe from chaining upward into pull-to-refresh.
   padding-top gives the white deck cards' --shadow-sm somewhere to render:
   overflow-x:auto forces overflow-y to auto, which was clipping their top edge. */
.deck-track {
  display: flex;
  gap: var(--space-3);
  overflow-x: auto;
  overscroll-behavior-x: contain;
  overscroll-behavior-y: contain;
  scroll-snap-type: x mandatory;
  -webkit-overflow-scrolling: touch;
  padding-top: var(--space-1);
  padding-bottom: var(--space-2);
  scrollbar-width: none;
}
.deck-track::-webkit-scrollbar { display: none; }

/* Pointer-device drag affordance. Touch drags the native scroller for free; a
   mouse cannot, so app.js adds pointer-drag and this advertises it. Gated on
   hover+fine so no touch device ever evaluates it -- a coarse pointer would
   otherwise pick up the grabbing state on tap and, worse, the user-select:none.
   :active is the primary state because it needs no JS coordination. .dragging is
   an OPTIONAL hook: :active is dropped by Chrome once the pointer leaves the
   element, so on a drag that wanders off the track vertically the cursor snaps
   back to grab mid-drag. If the drag handler already tracks that state, toggling
   .dragging on the track fixes it; without it everything still works.
   user-select:none stops the drag from selecting deal names across cards (a
   no-op if the handler already preventDefaults mousedown). */
@media (hover: hover) and (pointer: fine) {
  .deck-track { cursor: grab; }
  .deck-track:active,
  .deck-track.dragging {
    cursor: grabbing;
    user-select: none;
    -webkit-user-select: none;
  }
}

/* Focus ring: the global :focus-visible rule at the top of this file applies as
   written and is NOT clipped -- an element's own overflow clips its descendants,
   never its own outline, and no ancestor of the track (.deck, .capture, .app,
   body, html) sets overflow at all. The 5px ring (--focus-width 3 + --focus-
   offset 2) clears the 16px .app side padding and the 8px .deck-label margin
   above, so no inset treatment is needed. Left as the global rule deliberately:
   the deck should not look different from every other focusable thing.
   :focus-visible (not :focus) is also what makes this correct for the new mouse
   drag -- Chrome does not match :focus-visible when a tabindex div is focused by
   pointer, so dragging never flashes a ring, while Tab always shows one. */
.deck-card {
  flex: 0 0 var(--deck-card-w);
  scroll-snap-align: start;
  display: flex; flex-direction: column; gap: var(--space-2);
  min-height: 150px;
  padding: var(--space-4);
  border: 1px solid var(--border);
  border-radius: var(--radius-lg);
  background: var(--surface);
  box-shadow: var(--shadow-xs);
}
/* -webkit-line-clamp caps the number of LINES; it does nothing about a single
   token wider than the card, which then overflows horizontally and, since the
   track is a scroll-snap container, misaligns every snap point after it. */
.deck-deal { color: var(--ink); font-weight: var(--weight-semibold); line-height: var(--leading-snug); display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; overflow: hidden; overflow-wrap: anywhere; }
.deck-contact { color: var(--ink-2); font-size: var(--text-sm); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.deck-meta { display: flex; align-items: center; gap: var(--space-2); flex-wrap: wrap; margin-top: auto; }
.stage-chip { display: inline-flex; align-items: center; padding: 2px var(--space-2); border-radius: var(--radius-pill); background: var(--surface-2); color: var(--ink-2); font-size: var(--text-2xs); font-weight: var(--weight-semibold); }
.nodeal-tag { display: inline-flex; align-items: center; padding: 2px var(--space-2); border-radius: var(--radius-pill); background: var(--surface-2); color: var(--ink-3); font-size: var(--text-2xs); font-weight: var(--weight-semibold); }
.staleness { display: inline-flex; align-items: center; gap: 4px; color: var(--ink-3); font-size: var(--text-xs); }
.staleness.stale { color: var(--warning); }
.staleness svg { width: 14px; height: 14px; }
/* Card Record control: outlined, not a second big orange fill */
.deck-record {
  display: inline-flex; align-items: center; justify-content: center; gap: var(--space-2);
  min-height: var(--tap-comfortable);
  margin-top: var(--space-2);
  border: 1px solid var(--accent);
  border-radius: var(--radius-pill);
  background: transparent;
  color: var(--accent);
  font-size: var(--text-base); font-weight: var(--weight-semibold);
}
.deck-record svg { width: 20px; height: 20px; }
.deck-card.find-another { align-items: center; justify-content: center; text-align: center; color: var(--ink-2); }
.deck-card.find-another .find-btn { display: inline-flex; align-items: center; gap: var(--space-2); background: none; border: none; color: var(--link-tint-ink); font-weight: var(--weight-semibold); font-size: var(--text-base); min-height: var(--tap-comfortable); }
.deck-card.find-another .find-btn svg { width: 18px; height: 18px; color: var(--link); }

/* Recording link chip (§16.3) - muted cyan CRM identity */
.link-chip-host { width: 100%; margin-bottom: var(--space-4); }
.link-chip { display: flex; align-items: center; gap: var(--space-2); width: 100%; padding: var(--space-3) var(--space-4); border: 1px solid var(--link); border-left: 4px solid var(--link); border-radius: var(--radius-md); background: var(--link-tint); color: var(--link-tint-ink); }
.link-chip .chip-glyph { color: var(--link); flex: none; }
.link-chip .chip-glyph svg { width: 18px; height: 18px; }
.link-chip .chip-text { flex: 1; min-width: 0; font-size: var(--text-sm); font-weight: var(--weight-semibold); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.link-chip .chip-x { background: none; border: none; color: var(--link-tint-ink); font-size: var(--text-lg); line-height: 1; min-width: 36px; min-height: 36px; border-radius: var(--radius-sm); }

/* ==========================================================================
   Note detail extras
   ========================================================================== */
.transcript-block { margin: var(--space-4) 0; }
.transcript-head { display: flex; align-items: center; justify-content: space-between; margin-bottom: var(--space-2); }
.transcript-head .link { background: none; border: none; color: var(--accent); font-weight: var(--weight-semibold); min-height: var(--tap-min); }
/* pre-wrap preserves the transcript's line breaks but will not break a long
   token (a dictated URL, a run-on from Whisper), so pair it with anywhere. */
.transcript-body { padding: var(--space-4); border-radius: var(--radius-md); background: var(--surface-2); color: var(--ink-2); font-size: var(--text-sm); line-height: var(--leading-normal); white-space: pre-wrap; overflow-wrap: anywhere; }
.danger-zone { margin-top: var(--space-6); padding-top: var(--space-5); border-top: 1px solid var(--border); }

/* ==========================================================================
   Association block (§15) - muted cyan --link, solid surfaces
   ========================================================================== */
.assoc-block { position: relative; margin-bottom: var(--space-4); padding: var(--space-3) var(--space-4); border: 1px solid var(--border); border-left: 3px solid var(--link-line); border-radius: var(--radius-md); background: var(--surface-2); }
.assoc-block .assoc-line1 svg, .assoc-ghost-row svg.link-glyph { color: var(--link); width: 16px; height: 16px; flex: none; }
.assoc-body { display: block; width: 100%; text-align: left; background: none; border: none; padding: var(--space-1) 0; cursor: pointer; }
/* padding-right clears the 48px .assoc-clear target that now reaches the block's
   padding edge (48 target - 16 block padding = 32 = --space-7). */
.assoc-line1 { display: flex; align-items: center; gap: var(--space-2); padding-right: var(--space-7); min-width: 0; }
.assoc-deal { min-width: 0; color: var(--ink); font-weight: var(--weight-semibold); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.assoc-sub { color: var(--ink-2); font-size: var(--text-sm); margin-top: 2px; margin-left: calc(16px + var(--space-2)); }
.assoc-caption-row { display: flex; align-items: center; gap: var(--space-2); margin-top: var(--space-1); margin-left: calc(16px + var(--space-2)); }
.assoc-caption { color: var(--link-tint-ink); font-size: var(--text-xs); }
.assoc-change { background: none; border: none; padding: var(--space-1); min-height: var(--tap-min); color: var(--link-tint-ink); font-size: var(--text-xs); font-weight: var(--weight-semibold); }
/* 32px at 8/8 inset -> 48px at 0/0 inset. The block's own 12/16 padding means
   the glyph's centre lands in exactly the same place as before; only the touch
   area grows, from 32x32 (well under the 48dp Android minimum) to 48x48. */
.assoc-clear { position: absolute; top: 0; right: 0; display: grid; place-items: center; width: var(--tap-min); height: var(--tap-min); border: none; background: none; color: var(--ink-3); font-size: var(--text-lg); line-height: 1; border-radius: var(--radius-sm); }
.assoc-clear:active { background: var(--surface-3); }
.assoc-block.ghost { border-left: 3px dashed var(--border-strong); background: transparent; }
.assoc-ghost-row { display: flex; align-items: center; gap: var(--space-2); width: 100%; min-height: var(--tap-min); background: none; border: none; padding: 0; color: var(--ink-2); font-size: var(--text-base); text-align: left; }
.assoc-ghost-row svg.link-glyph { color: var(--ink-2); }
.assoc-ghost-label { flex: 1; }
.assoc-ghost-row svg.chev { width: 18px; height: 18px; color: var(--ink-3); }

/* Match candidates ("Did you mean?") - dashed ghost, suggestion not a link */
.assoc-block.candidates { border-left: 3px dashed var(--border-strong); background: transparent; }
.candidates-head { display: flex; align-items: center; gap: var(--space-2); margin-bottom: var(--space-2); color: var(--link-tint-ink); font-size: var(--text-sm); font-weight: var(--weight-semibold); }
.candidates-head svg { width: 16px; height: 16px; color: var(--link); }
.candidate-row { display: flex; align-items: center; gap: var(--space-3); width: 100%; min-height: var(--tap-comfortable); padding: var(--space-2) var(--space-3); margin-bottom: var(--space-2); border: 1px solid var(--border); border-radius: var(--radius-md); background: var(--surface-2); color: var(--ink); text-align: left; }
.candidate-row.best { background: var(--surface-3); }
.candidate-row .cand-main { flex: 1; min-width: 0; }
.candidate-row .cand-line1 { font-weight: var(--weight-semibold); color: var(--ink); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.candidate-row .cand-reason { color: var(--ink-3); font-size: var(--text-xs); margin-top: 2px; }
.candidate-row .linkplus { color: var(--link); flex: none; }
.candidate-row .linkplus svg { width: 18px; height: 18px; }
.candidate-else { display: flex; align-items: center; justify-content: space-between; width: 100%; min-height: var(--tap-min); background: none; border: none; padding: var(--space-2) var(--space-1); color: var(--ink-2); font-size: var(--text-sm); }
.candidate-else svg { width: 18px; height: 18px; color: var(--ink-3); }

/* ==========================================================================
   Overlays: scrim, bottom sheets, first-run, toast (all solid, no blur)
   ========================================================================== */
.scrim { position: fixed; inset: 0; z-index: var(--z-overlay); background: var(--scrim); animation: fade-in var(--dur-base) var(--ease-standard); }
.sheet {
  position: fixed; left: 0; right: 0; bottom: 0;
  z-index: var(--z-overlay);
  max-width: var(--content-max);
  margin: 0 auto;
  background: var(--surface-glass-strong); /* solid #101013 in v3 */
  border-top: 1px solid var(--border);
  border-radius: var(--radius-xl) var(--radius-xl) 0 0;
  padding: var(--space-4) var(--space-4) calc(var(--space-5) + var(--safe-bottom));
  box-shadow: var(--shadow-lg);
  animation: sheet-up var(--dur-slow) var(--ease-out);
}
.sheet::before { content: ""; display: block; width: 40px; height: 4px; margin: 0 auto var(--space-4); border-radius: var(--radius-pill); background: var(--border-strong); }
@keyframes sheet-up { from { transform: translateY(100%); } to { transform: translateY(0); } }
@media (prefers-reduced-motion: reduce) { .sheet { animation: fade-in var(--dur-base); } }
.sheet h2 { margin: 0 0 var(--space-4); font-size: var(--text-lg); }
.sheet-body { color: var(--ink-2); font-size: var(--text-sm); margin-bottom: var(--space-4); }

.sheet-row { display: flex; align-items: center; gap: var(--space-3); width: 100%; min-height: var(--tap-comfortable); padding: 0 var(--space-3); border: none; background: none; border-radius: var(--radius-md); color: var(--ink); font-size: var(--text-base); text-align: left; }
.sheet-row .row-label { flex: 1; }
.sheet-row svg { width: 20px; height: 20px; color: var(--ink-2); }
.sheet-row:active { background: var(--surface-2); }
.sheet-row .check { color: var(--accent); margin-left: auto; }
.sheet-row .check svg { width: 22px; height: 22px; color: var(--accent); }
.sheet-row.clear { color: var(--ink-3); }
.sheet-row.clear svg { color: var(--ink-3); }
.sheet-actions { display: flex; gap: var(--space-3); margin-top: var(--space-4); }
.sheet-actions .btn { flex: 1; }

.toggle-row { display: flex; align-items: center; gap: var(--space-3); min-height: var(--tap-comfortable); cursor: pointer; }
.toggle-row input { width: 22px; height: 22px; accent-color: var(--accent); }

/* Picker sheet */
/* vh first, svh overrides where supported. Without the fallback, engines that
   reject the new units (Safari <15.4, Chrome <108, Firefox <101) drop max-height
   entirely: this flex column then has no height bound, .picker-list never
   scrolls, and the sheet grows past the top of the viewport with its rows
   clipped and unreachable.
   svh rather than dvh: the sheet is position:fixed and can be opened while the
   URL bar is collapsed. dvh would size it to that taller viewport, and the bar
   re-expanding (which any scroll gesture triggers) would then push the sheet's
   top rows off-screen. svh caps it at the always-visible height, so every row
   stays reachable no matter what the bar does. */
.picker-sheet { max-height: 90vh; max-height: 90svh; display: flex; flex-direction: column; }
.picker-search { position: sticky; top: 0; display: flex; align-items: center; gap: var(--space-2); margin-bottom: var(--space-3); padding: 0 var(--space-3); min-height: var(--field-height); border: 1px solid var(--border-strong); border-radius: var(--radius-pill); background: var(--surface-2); }
.picker-search svg.search-glyph { width: 18px; height: 18px; color: var(--ink-3); flex: none; }
.picker-search input { flex: 1; border: none; background: none; outline: none; color: var(--ink); caret-color: var(--accent); font-family: inherit; font-size: var(--text-base); min-height: var(--tap-min); }
.picker-search input::placeholder { color: var(--ink-3); }
/* The visual box stays 32px (it has to fit inside the --field-height pill); the
   48x48 touch area comes from the shared ::after expander near the top of this
   file. flex:none stops it collapsing when a long query fills the input. */
.picker-search .search-clear { flex: none; background: none; border: none; color: var(--ink-3); font-size: var(--text-lg); min-width: 32px; min-height: 32px; }
/* contain: the list is a scroller inside a fixed sheet. Without it, flicking it
   past either end chains to the document behind the scrim, which on Android
   both scrolls the page under the sheet and can fire pull-to-refresh. */
.picker-list { overflow-y: auto; overscroll-behavior: contain; -webkit-overflow-scrolling: touch; }
.picker-section-label { font-size: var(--text-xs); text-transform: uppercase; letter-spacing: var(--tracking-caps); color: var(--ink-3); margin: var(--space-3) 0 var(--space-2); }
.picker-row { display: block; width: 100%; text-align: left; min-height: var(--tap-comfortable); padding: var(--space-3); margin-bottom: var(--space-2); border: 1px solid var(--border); border-radius: var(--radius-md); background: var(--surface-2); color: var(--ink); }
.picker-row.current { border-color: var(--link); }
.picker-row .row-top { display: flex; align-items: center; gap: var(--space-2); }
.picker-row .deal-name { flex: 1; min-width: 0; font-weight: var(--weight-semibold); color: var(--ink); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.picker-row .deal-company { color: var(--ink-2); font-size: var(--text-sm); margin-top: 2px; }
.picker-row .cyan-check { color: var(--link); flex: none; }
.picker-row .cyan-check svg { width: 20px; height: 20px; }
/* Row gap widened to 16px so the chips' expanded 48px touch areas (7px above and
   below each 34px chip) clear each other when the list wraps. Column gap stays
   at 8px per §9.1; the chips do not grow horizontally. */
.contact-chips { display: flex; flex-wrap: wrap; gap: var(--space-4) var(--space-2); margin-top: var(--space-2); }
.contact-chip { display: inline-flex; align-items: center; min-height: 34px; padding: 0 var(--space-3); border: none; border-radius: var(--radius-pill); background: var(--surface-3); color: var(--ink-2); font-size: var(--text-xs); font-weight: var(--weight-semibold); }
.contact-chip.match { background: var(--link-tint); color: var(--link-tint-ink); }
.picker-empty { text-align: center; color: var(--ink-2); padding: var(--space-6) var(--space-2); }
.picker-empty p { margin: 0 0 var(--space-4); }
.picker-link-nodeal { background: none; border: none; color: var(--link-tint-ink); font-weight: var(--weight-semibold); min-height: var(--tap-min); }

/* First-run key overlay */
.firstrun { position: fixed; inset: 0; z-index: var(--z-toast); background: var(--bg); display: flex; flex-direction: column; align-items: center; justify-content: center; text-align: center; padding: var(--space-6); padding-top: calc(var(--space-6) + var(--safe-top)); }
.firstrun .keyicon { margin-bottom: var(--space-5); color: var(--accent); }
.firstrun .keyicon svg { width: 48px; height: 48px; }
.firstrun h1 { font-size: var(--text-3xl); margin: 0 0 var(--space-3); color: var(--ink); }
.firstrun p { color: var(--ink-2); margin: 0 0 var(--space-6); max-width: 22rem; }
.firstrun-form { width: 100%; max-width: 22rem; display: flex; flex-direction: column; gap: var(--space-4); }
.firstrun input { width: 100%; min-height: var(--field-height); padding: 0 var(--space-4); border: 1px solid var(--border-strong); border-radius: var(--radius-md); background: var(--surface-2); color: var(--ink); caret-color: var(--accent); font-family: inherit; font-size: var(--text-base); }
.firstrun input::placeholder { color: var(--ink-3); }
.firstrun .err { color: var(--danger); font-size: var(--text-sm); min-height: 1.4em; }

/* Toast */
.toast-host { position: fixed; left: 0; right: 0; bottom: calc(var(--space-6) + var(--safe-bottom)); z-index: var(--z-toast); display: flex; flex-direction: column; align-items: center; gap: var(--space-2); pointer-events: none; padding: 0 var(--space-4); }
/* The export toast fires on the one screen that also has the fixed export bar,
   and at 24px up it landed on top of that bar, covering the primary action and
   its own "Undo". Lift above the bar only while the bar exists. :has() is a
   progressive enhancement (Chrome 105+, Samsung Internet 20+); older engines
   keep the previous position, which is the current behaviour, not a regression. */
body:has(.export-bar) .toast-host {
  bottom: calc(var(--tap-comfortable) + var(--space-7) + var(--safe-bottom));
}
.toast { display: flex; align-items: center; gap: var(--space-4); max-width: var(--content-max); width: 100%; padding: var(--space-3) var(--space-4); border-radius: var(--radius-md); background: var(--surface-3); border: 1px solid var(--border); color: var(--ink); box-shadow: var(--shadow-lg); pointer-events: auto; animation: toast-in var(--dur-base) var(--ease-out); }
@keyframes toast-in { from { opacity: 0; transform: translateY(10px); } to { opacity: 1; transform: translateY(0); } }
.toast .msg { flex: 1; font-size: var(--text-sm); }
.toast .undo { background: none; border: none; color: var(--accent); font-weight: var(--weight-bold); font-size: var(--text-sm); min-height: var(--tap-min); padding: 0 var(--space-2); }

/* ==========================================================================
   §17.9 White cards. Content cards go white (physical sheets on the dark
   recording UI) and drop the outlined-box look: no outer border, soft shadow
   only, and list/receipt/detail cards bleed flush to the right screen edge
   (left corners rounded, right corners square). Deck cards go white too but
   keep four rounded corners (swipe peek). The dark shell — canvas, top bar,
   capture/recording, offline bar, ALL bottom sheets — is unchanged.
   Every on-card element re-colors to the --card-* family.
   ========================================================================== */

/* -- White surfaces + geometry -------------------------------------------- */
.card, .note-card, .card-compact {
  background: var(--card-surface);
  color: var(--card-ink);
  border: none;
  border-radius: var(--radius-lg) 0 0 var(--radius-lg); /* right corners square */
  box-shadow: var(--shadow-md);
  margin-right: calc(-1 * var(--space-4));               /* bleed to the right edge */
}
.card.accent-warning { border-left: 4px solid var(--card-warning-line); }
.card.accent-danger  { border-left: 4px solid var(--card-danger-line); }
.deck-card {
  background: var(--card-surface);
  color: var(--card-ink);
  border: none;
  box-shadow: var(--shadow-sm); /* keeps four rounded corners from the base rule */
}

/* -- On-card text hierarchy ------------------------------------------------ */
.card .field-summary, .card .contact-name,
.card .followup-desc,
.note-card .line1, .card-compact .summary,
.deck-card .deck-deal { color: var(--card-ink); }
.card .contact-company, .card .signals li, .card .followup-date,
.note-card .line2, .note-card .line2 .l2text,
.deck-card .deck-contact { color: var(--card-ink-2); }
.card .status-time, .card .meta-line, .card .section-label, .card .placeholder,
.card .kebab, .card .followup-date.empty,
.note-card .time,
.deck-card .staleness, .deck-card .nodeal-tag { color: var(--card-ink-3); }
.card .status-label.neutral, .card .status-label.analysing { color: var(--card-ink-2); }
.card .status-label.success { color: var(--card-success-ink); }
.card .status-label.danger { color: var(--card-danger-ink); }

/* -- Insets, fields, transcript ------------------------------------------- */
.card .contact-block { background: var(--card-surface-2); border-color: var(--card-border); }
.card .contact-block svg, .card .contact-details .detail svg { color: var(--card-ink-3); }
.card .contact-details .detail { color: var(--card-ink-2); }
.card .inline-input { background: var(--card-surface-2); border-color: var(--card-border-strong); color: var(--card-ink); caret-color: var(--accent); }
.card .save-error { color: var(--card-danger-ink); }
.card .followup-row .checkbox { border-color: var(--card-border-strong); }
.card .add-followup { color: var(--card-accent-ink); }

/* -- Chips / badges / pills (card variants) -------------------------------- */
.card .model-chip { background: var(--card-primary-tint); color: var(--card-primary-tint-ink); border-color: var(--card-border); }
.card .model-chip.unset { background: var(--card-surface-2); color: var(--card-ink-2); }
.card .interaction-pill, .note-card .interaction-pill { background: var(--card-surface-2); color: var(--card-ink-2); border-color: var(--card-border); }
.deck-card .stage-chip { background: var(--card-surface-2); color: var(--card-ink-2); }
.deck-card .nodeal-tag { background: var(--card-surface-2); }
.deck-card .staleness.stale { color: var(--card-warning-ink); }
.card .has-space { background: var(--card-success-tint); color: var(--card-success-ink); }
.note-card .flag { color: var(--card-warning-ink); }
.link-cue svg { color: var(--card-link); }

.card .badge-working, .note-card .badge-working,
.card .badge-exported, .note-card .badge-exported { background: var(--card-neutral-tint); color: var(--card-neutral-ink); }
.card .badge-pending, .note-card .badge-pending { background: var(--card-neutral-tint); color: var(--card-ink-3); }
.card .badge-error, .note-card .badge-error { background: var(--card-danger-tint); color: var(--card-danger-ink); }
.card .badge-review, .note-card .badge-review { background: var(--card-warning-tint); color: var(--card-warning-ink); }
.card .badge-reviewed, .note-card .badge-reviewed, .card-compact .badge-reviewed { background: var(--card-success-tint); color: var(--card-success-ink); }
.card-compact .check { color: var(--card-success-ink); }

/* -- Analysing (light shimmer) + confidence banner ------------------------ */
.card .sparkle { color: var(--card-accent-ink); }
.card .skeleton-bar { background: var(--card-surface-2); }
.card .skeleton-bar::after { background: linear-gradient(90deg, transparent, rgba(0,0,0,0.05), transparent); }
.card .lowconf-banner { background: var(--card-warning-tint); color: var(--card-warning-ink); }

/* -- Association block + candidates (cyan, recolored for white) ----------- */
.card .assoc-block { background: var(--card-surface-2); border-color: var(--card-border); border-left-color: var(--card-link-line); }
.card .assoc-block.ghost, .card .assoc-block.candidates { background: transparent; border-left-color: var(--card-border); }
.card .assoc-line1 svg, .card .assoc-block .candidates-head svg, .card .candidate-row .linkplus { color: var(--card-link); }
.card .assoc-deal { color: var(--card-ink); }
.card .assoc-sub { color: var(--card-ink-2); }
.card .assoc-caption, .card .assoc-change, .card .candidates-head { color: var(--card-link); }
.card .assoc-clear { color: var(--card-ink-3); }
.card .assoc-ghost-row { color: var(--card-ink-2); }
.card .assoc-ghost-row svg.link-glyph { color: var(--card-ink-2); }
.card .assoc-ghost-row svg.chev { color: var(--card-ink-3); }
.card .candidate-row { background: var(--card-surface-2); border-color: var(--card-border); color: var(--card-ink); }
.card .candidate-row.best { background: var(--card-surface-3); }
.card .candidate-row .cand-line1 { color: var(--card-ink); }
.card .candidate-row .cand-reason { color: var(--card-ink-3); }
.card .candidate-else { color: var(--card-ink-2); }
.card .candidate-else svg { color: var(--card-ink-3); }

/* -- Buttons on a white card: primary stays orange; others go card-neutral - */
.card .btn-secondary, .card-compact .btn-secondary { background: var(--card-surface-2); color: var(--card-ink); border-color: var(--card-border); }
.card .btn-danger { color: var(--card-danger-ink); border-color: var(--card-border); }
.card .btn-primary:disabled { background: var(--card-surface-3); color: var(--card-ink-3); }
/* Deck card record control: orange outline, darkened orange text (readable on white) */
.deck-card .deck-record { color: var(--card-accent-ink); border-color: var(--accent); }
.deck-card.find-another .find-btn { color: var(--card-link); }
.deck-card.find-another .find-btn svg { color: var(--card-link); }

/* ==========================================================================
   Reactive orb (capture) + Contacts screen
   The follow-up deck moved off the capture screen onto its own route, so
   capture is now one thing to look at and one thing to tap.
   ========================================================================== */

/* The canvas is decorative and sits behind the button; the button keeps the tap.
   A fixed square keeps the bloom from reflowing the column as it pulses. */
/* The stage reserves the orb's full square so the absolutely-positioned canvas
   cannot sit on top of the helper text below it. */
.orb-stage {
  position: relative;
  display: grid;
  place-items: center;
  width: 100%;
  min-height: min(78vw, 320px);
}
.orb-canvas {
  position: absolute;
  width: min(78vw, 320px);
  height: min(78vw, 320px);
  pointer-events: none;
  z-index: 0;
}
.orb-stage .mic-btn { position: relative; z-index: 1; }
/* The ring is the light source now, so the button becomes the DARK CORE it
   surrounds. A solid accent-filled disc here reads as a second, competing light
   and flattens the whole effect. */
.orb-stage .mic-btn::after { display: none; }
.orb-stage .mic-btn {
  background: rgba(6, 14, 28, 0.78);
  color: var(--ink);
  box-shadow: inset 0 0 0 1px rgba(150, 240, 255, 0.22), 0 0 30px rgba(6, 14, 28, 0.55);
}
.orb-stage .mic-btn:active { background: rgba(10, 22, 42, 0.9); }
/* Recording keeps its own treatment: the stop control must stay unmistakable. */
.orb-stage .mic-btn.recording { background: var(--danger, #e5484d); color: #fff; }

/* Contacts screen -------------------------------------------------------- */
.contacts { display: flex; flex-direction: column; gap: var(--space-3); padding-top: var(--space-3); padding-bottom: var(--space-6); }
.contacts-head { display: flex; align-items: baseline; justify-content: space-between; gap: var(--space-3); }
.contacts-title { color: var(--ink); font-size: var(--text-lg); font-weight: var(--weight-bold); }
.contacts-count { color: var(--ink-3); font-size: var(--text-sm); }

.contacts-search {
  width: 100%;
  min-height: var(--tap-comfortable);
  padding: 0 var(--space-3);
  background: var(--surface-2);
  border: 1px solid var(--border);
  border-radius: var(--radius-md);
  color: var(--ink);
  font-size: var(--text-base);
}
.contacts-search::placeholder { color: var(--ink-3); }

/* Small tiles, two across on a phone and wider as space allows. Scrolling is
   the page's own; the grid never becomes a nested scroller. */
.tile-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(150px, 1fr));
  gap: var(--space-3);
}
.tile {
  position: relative;
  display: flex;
  flex-direction: column;
  gap: 2px;
  min-height: 108px;
  padding: var(--space-3);
  padding-right: calc(var(--space-3) + 34px); /* clear of the record control */
  background: var(--card-surface);
  border: 1px solid var(--card-border, var(--border));
  border-radius: var(--radius-md);
}
.tile-name { color: var(--card-ink); font-weight: var(--weight-semibold); line-height: var(--leading-snug);
  display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; overflow: hidden; overflow-wrap: anywhere; }
.tile-company { color: var(--card-ink-2); font-size: var(--text-sm); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.tile-meta { margin-top: auto; padding-top: var(--space-2); }
.tile-quiet { color: var(--card-ink-3); font-size: var(--text-xs); }
.tile-quiet.stale { color: var(--card-warning-ink); font-weight: var(--weight-semibold); }

.tile-rec {
  position: absolute;
  top: var(--space-2);
  right: var(--space-2);
  width: 34px; height: 34px;
  display: grid; place-items: center;
  border: 1px solid var(--accent);
  border-radius: var(--radius-circle);
  background: none;
  color: var(--card-accent-ink);
}
.tile-rec svg { width: 17px; height: 17px; }
.tile-rec:active { background: var(--card-surface-2); }
.tile-empty { grid-column: 1 / -1; color: var(--ink-3); font-size: var(--text-sm); padding: var(--space-4) 0; }

/* Second nav pill, capture screen only. */
.nav-pill.nav-contacts { display: inline-flex; align-items: center; gap: var(--space-2); }
.nav-pill.nav-contacts svg { width: 16px; height: 16px; flex: none; }

/* A phone notch is a fixed exclusion zone across the CENTRE of the status area,
   and --safe-top only pads vertically. On a 393px screen a title plus two text
   buttons cannot clear it, so the Contacts label slid under the cutout. Below
   that width the pill collapses to its icon, which empties the centre entirely.
   The button keeps its aria-label, so its accessible name is unchanged. */
@media (max-width: 430px) {
  .nav-pill.nav-contacts { padding: 0 var(--space-3); }
  .nav-pill.nav-contacts .nav-label { display: none; }
}

/* The orb is decoration. Under reduced motion it paints one still frame (see
   startOrb) and the canvas simply stops updating; nothing else to disable. */
