/* ============================================================================
   SKIN (2026-08-06) — the shadcn/Linear look, carried by tokens.css.
   Loads AFTER styles.css and wins ties: this file owns how components LOOK;
   styles.css keeps owning layout. Recipes follow Basecoat/shadcn conventions
   (vendored in public/vendor/ for the markup-level migration). Rules per
   design/UI-STANDARD.md §4: two weights, three text colors, tint badges,
   one primary per screen, semantic color reserved.
   ========================================================================== */

/* ---- base ------------------------------------------------------------------ */
body {
  background: var(--background);
  color: var(--foreground);
  font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
  -webkit-font-smoothing: antialiased;
}
.topbar { border-bottom: 1px solid var(--border); padding-bottom: var(--space-3); }
.brand h1 { color: var(--foreground); font-size: var(--text-lg); letter-spacing: -0.01em; }
.tagline { color: var(--muted-foreground); font-size: var(--text-xs); }

/* ---- cards: one quiet recipe everywhere ------------------------------------ */
.card, .inv-card, .deal-card, .connect-card, .stat-tile, .blast-tab-card {
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--radius-lg);
  box-shadow: var(--shadow-sm);
}
.inv-card:hover, .deal-card:hover { box-shadow: var(--shadow-md); border-color: var(--border-strong); }

/* ---- buttons: three tiers, one look ---------------------------------------- */
.primary, button.primary {
  background: var(--primary); color: var(--primary-foreground);
  border: 1px solid var(--primary);
  border-radius: var(--radius); padding: 8px var(--space-4);
  font-size: var(--text-sm); font-weight: var(--weight-bold);
  box-shadow: var(--shadow-sm); cursor: pointer;
  transition: background .12s ease;
}
.primary:hover:not(:disabled) { background: var(--primary-hover); }
.primary:disabled { opacity: .55; cursor: default; }
.ghost-btn {
  background: var(--surface); color: var(--foreground);
  border: 1px solid var(--border-strong);
  border-radius: var(--radius); padding: 7px var(--space-3);
  font-size: var(--text-sm); font-weight: var(--weight-bold); cursor: pointer;
}
.ghost-btn:hover { background: var(--gray-1); border-color: var(--gray-5); }
.blast-btn {
  background: var(--primary); color: var(--primary-foreground);
  border: 1px solid var(--primary); border-radius: var(--radius);
  font-weight: var(--weight-bold); box-shadow: var(--shadow-sm);
}
.blast-btn:hover:not(:disabled) { background: var(--primary-hover); }
.blast-btn:disabled { opacity: .55; }

/* focus visible, always (rulebook E7 + accessibility floor) */
button:focus-visible, input:focus-visible, select:focus-visible, [tabindex]:focus-visible {
  outline: 2px solid var(--ring); outline-offset: 2px;
}

/* ---- inputs & selects ------------------------------------------------------- */
input[type="text"], input[type="password"], input:not([type]), select, textarea, .addr-input {
  background: var(--surface); color: var(--foreground);
  border: 1px solid var(--border-strong); border-radius: var(--radius);
  font-size: var(--text-sm); padding: 8px var(--space-3);
}
input::placeholder { color: var(--faint-foreground); }
select { cursor: pointer; }
input:focus, select:focus, textarea:focus {
  border-color: var(--ring);
  outline: 2px solid color-mix(in srgb, var(--ring) 25%, transparent);
  outline-offset: 0;
}

/* ---- badges: soft tint + dark text of the same hue (E10) -------------------- */
.badge { border-radius: 999px; font-size: var(--text-xs); font-weight: var(--weight-bold);
  padding: 2px 9px; border: 1px solid transparent; }
.badge.inst { background: var(--warning-soft); color: var(--warning-strong); border-color: var(--warning); }
.badge.yours { background: var(--success-soft); color: var(--success-strong); border-color: var(--success); }
.badge.teaser { background: var(--primary-soft); color: var(--primary-hover); border-color: var(--primary); }

/* ---- spaces bar (top nav) --------------------------------------------------- */
.spaces { border-bottom: 1px solid var(--border); }
.space { color: var(--muted-foreground); font-weight: var(--weight-bold); }
.space:hover { color: var(--foreground); }
.space.active { color: var(--foreground); border-bottom-color: var(--foreground); }
.space .n { background: var(--gray-2); color: var(--muted-foreground); }
/* An unfilled count badge is a grey blob sitting where a number should be, which reads as
   a dash or a broken value. No number yet means no badge (2026-08-07 ui-check finding). */
.space .n:empty { display: none; }
.space.active .n { background: var(--primary); color: var(--primary-foreground); }

/* ---- deal header ------------------------------------------------------------ */
/* The stage-chevron rules that lived here targeted #dh-stepper-slot, a slot that was
   never in the markup — so they styled nothing from the day they were written. The
   stage bar is retired; the header's one stage pill (#dc-stage) is the whole story. */
.deal-header { box-shadow: var(--shadow-sm); }

/* facts chips in the header */
.fact {
  background: var(--gray-1); color: var(--muted-foreground);
  border: 1px solid var(--border); border-radius: 999px;
  font-size: var(--text-xs);
}
.fact b { color: var(--foreground); font-weight: var(--weight-bold); }
.free-tag { background: var(--success-soft); color: var(--success-strong);
  border: 1px solid var(--success); border-radius: 999px; font-size: var(--text-xs);
  font-weight: var(--weight-bold); }

/* ---- deals board ------------------------------------------------------------- */
.deals-tab {
  background: var(--surface); color: var(--muted-foreground);
  border: 1px solid var(--border-strong); border-radius: 999px;
  font-weight: var(--weight-bold); font-size: var(--text-sm);
}
.deals-tab.active { background: var(--primary); color: var(--primary-foreground); border-color: var(--primary); }
.deals-board .stage-col-title, .deals-board .group-title { color: var(--muted-foreground); }
.deal-card { cursor: pointer; }
.deal-card .deal-price { color: var(--success); font-weight: var(--weight-bold); }

/* ---- drawers / modals -------------------------------------------------------- */
.drawer { background: var(--surface); border-left: 1px solid var(--border); box-shadow: var(--shadow-lg); }
.modal, .review-modal { border-radius: var(--radius-lg); box-shadow: var(--shadow-lg); }

/* ---- group titles ------------------------------------------------------------ */
.group-title {
  font-size: var(--text-xs); font-weight: var(--weight-bold);
  letter-spacing: .06em; text-transform: uppercase; color: var(--muted-foreground);
}
.group-sub { text-transform: none; letter-spacing: 0; font-weight: var(--weight-normal);
  color: var(--faint-foreground); }

/* ---- map pins ---------------------------------------------------------------- */
.comp-pin {
  background: var(--surface); color: var(--foreground);
  border: 1px solid var(--border-strong); border-radius: 999px;
  font-size: var(--text-xs); font-weight: var(--weight-bold);
  box-shadow: var(--shadow-md);
}

/* comps buttons are SECONDARY (one primary per screen — the header owns it) */
/* Load comps is the map's ONE call to action and stays SOLID (Gabe 2026-07-20, re-stated
   2026-08-07: "more in your face so people know to click it"). This block used to restyle
   it back to a quiet white ghost button, silently reverting that decision because skin.css
   loads after styles.css — so the rule in styles.css that implements it never applied.
   Deliberate exception to "one primary per screen": the header's primary acts on the whole
   DEAL, this one acts on the panel you are looking at. */
.comps-bar .ghost-btn { border-radius: var(--radius); }

/* ============================================================================
   TYPE & BOARD PASS (Gabe 2026-08-06: "modern, bold, in your face, large" —
   repeated small-text complaint, now a standing rule: body 15px floor, key
   names 16-17px, numbers bold, stage titles strong, columns distinct).
   ========================================================================== */
body { font-size: 15px; }
.view-sub, .pipeline-sub { font-size: var(--text-sm); }

/* board: separated columns on the gray page, strong titles, bigger cards */
.board-cols { gap: var(--space-4); }
.stage-col {
  background: var(--gray-2);
  border: 1px solid var(--border);
  border-radius: var(--radius-lg);
  padding: var(--space-3);
  min-width: 250px;
}
.stage-head {
  font-size: var(--text-base); font-weight: var(--weight-bold);
  letter-spacing: 0; text-transform: none; color: var(--foreground);
  display: flex; align-items: center; gap: var(--space-2);
  padding: var(--space-1) var(--space-1) var(--space-3);
}
.stage-count {
  background: var(--surface); color: var(--muted-foreground);
  border: 1px solid var(--border-strong);
  border-radius: 999px; font-size: var(--text-xs); padding: 1px 8px;
}
.deal-card { padding: var(--space-4); }
.deal-card .deal-name { font-size: var(--text-base); font-weight: var(--weight-bold); color: var(--foreground); }
.deal-card .deal-addr { font-size: var(--text-sm); color: var(--muted-foreground); margin-top: 2px; }
.deal-card .deal-value { font-size: var(--text-base); font-weight: var(--weight-bold); color: var(--success); margin-top: var(--space-2); }
.deal-chips { margin-top: var(--space-2); }
.deals-tab { font-size: var(--text-base); padding: 8px var(--space-4); }
.stage-empty { color: var(--faint-foreground); padding: var(--space-3); }

/* workspace type floor */
.flowtab { font-size: var(--text-base); }
.task-label { font-size: var(--text-base); }
.task-hint { font-size: var(--text-sm); }
.task-meta { font-size: var(--text-sm); }
#dc-name { font-size: var(--text-xl); }
.dc-addr { font-size: var(--text-base); }
.fact { font-size: var(--text-sm); padding: 5px 12px; }
.comps-bar { display: flex; align-items: center; gap: var(--space-2); flex-wrap: wrap; }
/* The radius picker only ever holds "1 mi". It was inheriting .picker-compact's
   `flex: 1 1 260px; min-width: 240px`, which beat the `width: auto` here and stretched it
   most of the way across the map (Gabe 2026-08-07). Size it to its content and give the
   space back to the button beside it. */
.comps-bar .comps-radius-sel {
  flex: 0 0 auto; width: auto; min-width: 0; max-width: none;
  padding: 8px var(--space-2); font-weight: var(--weight-bold);
}
/* Bigger and bolder than the surrounding controls — it is what you came here to press. */
.comps-bar #comps-btn { font-size: var(--text-base); font-weight: var(--weight-bold); padding: 10px 18px; }

/* the "Use as ARV" forward button rides inline with the note */
.use-arv-btn { margin-left: var(--space-2); padding: 6px var(--space-3); font-size: var(--text-sm); }
#comps-note { font-size: var(--text-sm); }

/* unlock pill: elongated single line; unlocked = solid green (Gabe) */
.dh-facts .unlock-report-btn {
  flex-direction: row; align-items: center; gap: var(--space-2);
  padding: 8px var(--space-4); font-size: var(--text-sm); white-space: nowrap;
}
/* ONE colour language for money (Gabe, 2026-08-08): anything not yet unlocked is AMBER,
   matching the comp cards' locked pills; anything paid for is green. The old blue read as
   just another primary button, so the "this costs money" signal was carried by words alone. */
.dh-facts .unlock-report-btn:not(.unlocked) {
  background: var(--amber-1); border: 1px solid var(--amber-6); color: var(--amber-8);
  box-shadow: none;
}
.dh-facts .unlock-report-btn:not(.unlocked):hover {
  background: var(--amber-6); border-color: var(--amber-6); color: #fff; filter: none;
}
.dh-facts .unlock-report-btn:not(.unlocked) .ic { stroke: var(--amber-8); }
.dh-facts .unlock-report-btn:not(.unlocked):hover .ic { stroke: #fff; }
.dh-facts .unlock-report-btn.unlocked {
  background: var(--success); border-color: var(--success);
  color: #fff;
}
.dh-facts .unlock-report-btn.unlocked:hover { background: var(--green-8); border-color: var(--green-8); }
.dh-facts .unlock-report-btn.unlocked .ic { stroke: #fff; }

/* stage headers: their own highlighted bar, separate from the card stack (Gabe) */
.stage-col { padding: 0 0 var(--space-3); overflow: hidden; }
.stage-head {
  background: var(--primary-soft); color: var(--primary-hover);
  border-bottom: 1px solid var(--border);
  padding: var(--space-3) var(--space-4);
  margin: 0 0 var(--space-3);
}
.stage-count { background: var(--surface); color: var(--primary-hover); border-color: transparent; }
.stage-col .deal-card { margin: 0 var(--space-3) var(--space-3); }
.stage-col .stage-empty { padding-left: var(--space-4); }

/* comp selection */
.comp-pick {
  display: inline-flex; align-items: center; gap: 6px;
  font-size: var(--text-xs); font-weight: var(--weight-bold);
  color: var(--muted-foreground); cursor: pointer; margin-bottom: var(--space-1);
}
.comp-pick input { accent-color: var(--primary); width: 15px; height: 15px; }
.use-arv-btn:disabled { opacity: .55; }

/* stage header bars: uniform height whether the name takes one line or two —
   wrapped names ("Ai Re-Engaged | Needs Review") were making ragged columns */
/* FIXED height — min-height still left a 1px subpixel gap between wrapped and
   unwrapped names (measured 64 vs 65). Fixed box, content centered, ends it. */
.stage-head { height: 66px; align-items: center; line-height: 1.25; overflow: hidden; }

/* ============================================================================
   Overview = comps LIST beside the MAP (Gabe: the list below a tall map was
   invisible; the old Discover side-by-side with hover->pin highlight was right).
   Task list spans the top; once comps load, list left / map right; while there
   are no comps the map takes the full row.
   ========================================================================== */
#tab-overview { display: grid; grid-template-columns: 420px minmax(0, 1fr); gap: var(--space-4); align-items: start; }
#tab-overview .task-card { grid-column: 1 / -1; margin-bottom: 0; }
#tab-overview #comps-group { grid-column: 1; grid-row: 2; padding-right: var(--space-1); }
/* SQUARE map (Gabe 2026-08-07). Not just a preference — the thing being displayed is a
   CIRCLE (the search radius), and a square is the only shape that frames a circle with no
   wasted space. In a wide box the circle is limited by height and the sides go empty; in a
   tall one the reverse. Square also means the fit is the same in both directions, so a
   radius that fits vertically always fits horizontally. */
#tab-overview .mapcol {
  grid-column: 2; grid-row: 2; margin: 0;
  aspect-ratio: 1 / 1; height: auto; /* stays sticky, just no longer full-viewport tall */
}
#tab-overview #map { min-height: 0; }
/* no comps yet -> the map spreads across both columns */
#tab-overview:has(#comps-group.hidden) .mapcol { grid-column: 1 / -1; }
#tab-overview:has(#comps-group.hidden) #map { min-height: 420px; }
#tab-overview #comps-group .cards { grid-template-columns: 1fr; display: grid; gap: var(--space-3); }

@media (max-width: 980px) {
  #tab-overview { grid-template-columns: 1fr; }
  #tab-overview #comps-group, #tab-overview .mapcol { grid-column: 1; }
  #tab-overview #comps-group { max-height: none; }
}

/* merged comps: CASH keeps the classic green pill Gabe liked; financed solds neutral */
.comp-pin.cash {
  background: linear-gradient(180deg, #22c55e, #16a34a); color: #fff;
  border: 2px solid #fff; box-shadow: 0 3px 10px rgba(22, 163, 74, .45);
}
.comp-price-label.is-cash { color: var(--success-strong); font-weight: var(--weight-bold); }

/* WHO GETS THIS DEAL — the audience block (2026-08-08, replaces the two-stat hero).
   Every pool wears the SAME number treatment on purpose: the old page gave the paid pool
   48px and the free ready-to-send pool 12px grey, which is what made it unreadable. Rank
   comes from ORDER (free first) and from one accent on the row you act on, not from size. */
/* styles.css sets `.summary { display: flex }` for the old two-stat hero, which laid the
   heading and the rows out as SIDE-BY-SIDE flex siblings — heading stranded left, rows
   shoved right, a dead gap between them. Reset it to a column here (skin.css loads last). */
.summary { display: block; }

.aud-head {
  display: flex; flex-wrap: wrap; align-items: baseline; gap: var(--space-2) var(--space-3);
  font-size: var(--text-sm); font-weight: var(--weight-bold);
  text-transform: uppercase; letter-spacing: .05em; color: var(--muted-foreground);
  margin-bottom: var(--space-3);
}
.aud-radius { font-size: var(--text-xs); font-weight: var(--weight-normal); text-transform: none; letter-spacing: 0; }
/* Two columns once there is room (Gabe, 2026-08-08: "a lot of white space side to side").
   Free pools land in the left column and the paid/skip ones in the right, because the rows
   are ordered free-first and a 2-column grid fills column-major with the row count we have.
   One column below 760px so the label never crushes against the tag. */
.aud-rows { display: grid; grid-template-columns: 1fr; gap: var(--space-4); align-items: start; }
@media (min-width: 760px) {
  .aud-rows { grid-template-columns: 1fr 1fr; gap: var(--space-6); }
}
.aud-col { display: flex; flex-direction: column; gap: 2px; min-width: 0; }
.aud-coltitle {
  margin: 0 0 var(--space-1) var(--space-2);
  font-size: var(--text-xs); font-weight: var(--weight-bold);
  text-transform: uppercase; letter-spacing: .05em; color: var(--faint-foreground);
}

.aud-row {
  display: grid; grid-template-columns: 3.5rem 1fr auto; align-items: center;
  gap: var(--space-3); padding: var(--space-2) var(--space-2);
  border-radius: var(--radius); text-decoration: none; color: inherit;
  border: 1px solid transparent;
}
/* Hover is a TINT, not a fill. `var(--muted)` here is this codebase's muted TEXT color
   (#64748b), so the first version painted a dark slate pill straight over the words —
   Gabe's screenshot, 2026-08-08. Same class of mistake as using total sqft for $/sqft:
   right-sounding token, wrong meaning. */
.aud-row:hover { background: var(--primary-soft); border-color: var(--border); }
.aud-row:focus-visible { outline: 2px solid var(--primary); outline-offset: 2px; }
/* Bigger and coloured (Gabe): the counts are the content of this block, so they read at a
   glance — and the colour IS the meaning. Purple = yours/free, amber = costs money,
   grey = skip. One glance tells you which numbers are spendable attention and which are
   spendable dollars. */
.aud-n {
  font-size: 30px; font-weight: var(--weight-bold); line-height: 1;
  letter-spacing: -.02em; text-align: right; font-variant-numeric: tabular-nums;
  color: var(--primary);
}
.aud-text { display: flex; flex-direction: column; gap: 1px; min-width: 0; }
.aud-label { font-style: normal; font-size: var(--text-base); font-weight: var(--weight-bold); }
.aud-sub { font-style: normal; font-size: var(--text-sm); color: var(--muted-foreground); }
.aud-tag {
  font-size: var(--text-xs); font-weight: var(--weight-medium); color: var(--muted-foreground);
  white-space: nowrap;
}
.aud-row.is-paid .aud-n { color: var(--amber-8); }
.aud-row.is-paid .aud-tag { color: var(--amber-8); font-weight: var(--weight-bold); }
.aud-row.is-skip .aud-n { color: var(--faint-foreground); }
/* The one row you are meant to act on first keeps its green go-signal. */
.aud-row.is-ready .aud-tag { color: var(--success-strong); font-weight: var(--weight-bold); }

.aud-todo {
  display: block; margin-top: var(--space-3); padding-top: var(--space-3);
  border-top: 1px solid var(--border);
  font-size: var(--text-xs); color: var(--muted-foreground); text-decoration: underline;
}
.aud-todo:hover { color: var(--foreground); }
.aud-empty { font-size: var(--text-sm); color: var(--muted-foreground); }

/* Numbers and the Blast button stay on screen while you work down the lists — the point of
   the block is that you never have to scroll back up to act on it. Desktop only: on a phone
   pinning this would eat the list it is meant to introduce. */
/* Base first, override second — same specificity, so source order decides. */
#mine-group, #local-group, #inst-group, #mine-unmatched-toggle { scroll-margin-top: var(--space-3); }
@media (min-width: 900px) {
  /* BELOW the topbar, not behind it. `top: 0` pinned this at the viewport edge while
     .topbar (z-index 30) sits there too, so at z-index 5 it slid underneath and vanished.
     71px is the topbar's measured height; the extra 36px in the GHL iframe is the band the
     header is already pushed down by. */
  .results-bar { position: sticky; top: 71px; z-index: 5; }
  .in-iframe .results-bar { top: 107px; }
  /* A jump target must clear the pinned bar, or the heading you asked for lands underneath
     it and the link reads as broken. */
  #mine-group, #local-group, #inst-group, #mine-unmatched-toggle { scroll-margin-top: 260px; }
}

/* buy → sell → keep, the sentence a wholesaler would say out loud (2026-08-08). It sits
   ABOVE the supporting rows because it is the answer; everything under it is the working.
   Wraps rather than scrolls — on a phone the three steps stack and still read in order. */
.an-chain {
  display: flex; flex-wrap: wrap; align-items: baseline; gap: var(--space-2) var(--space-3);
  padding: var(--space-3) 0 var(--space-3); border-bottom: 1px solid var(--border);
  margin-bottom: var(--space-3);
}
.an-chain .ch-step { display: flex; flex-direction: column; gap: 2px; }
.an-chain .ch-step i {
  font-style: normal; font-size: var(--text-xs); font-weight: var(--weight-medium);
  color: var(--muted-foreground); text-transform: uppercase; letter-spacing: .04em;
}
.an-chain .ch-step b { font-size: var(--text-xl); font-weight: var(--weight-bold); letter-spacing: -.02em; }
.an-chain .ch-arrow { color: var(--faint-foreground); font-size: var(--text-lg); }
/* The paycheck is the point of the line, so it is the only part that carries colour. */
.an-chain .ch-keep b.pos { color: var(--success-strong); }
.an-chain .ch-keep b.neg { color: var(--danger-strong, #b91c1c); }

/* PRICE IT THREE WAYS (2026-08-08) — fast / market / top dollar, each a real button that
   fills the price box. The left stripe is the buyer verdict at that price: green = cash
   buyers take it, amber = thin, red = flippers pass (landlord money). "You make" carries
   its own colour because a NEGATIVE number at the market price is the row that matters. */
.an-po-head {
  font-size: var(--text-sm); font-weight: var(--weight-bold);
  margin-bottom: var(--space-2);
}
.an-po-rows { display: flex; flex-direction: column; gap: var(--space-2); margin-bottom: var(--space-4); }
.an-po {
  display: grid; grid-template-columns: minmax(7.5rem, auto) auto 1fr auto;
  align-items: center; gap: var(--space-3);
  padding: var(--space-2) var(--space-3);
  background: var(--surface); border: 1px solid var(--border);
  border-left-width: 4px; border-radius: var(--radius);
  font: inherit; text-align: left; cursor: pointer;
}
.an-po:hover { background: var(--primary-soft); }
.an-po.is-on { border-color: var(--primary); border-left-color: var(--primary); background: var(--primary-soft); }
.an-po.t3 { border-left-color: var(--success); }
.an-po.t2, .an-po.t1 { border-left-color: var(--amber-6); }
.an-po.t0 { border-left-color: var(--danger-strong, #b91c1c); }
.an-po .po-label { font-weight: var(--weight-bold); font-size: var(--text-sm); }
.an-po .po-label i { font-style: normal; font-weight: var(--weight-normal); color: var(--muted-foreground); font-size: var(--text-xs); display: block; }
.an-po .po-price { font-size: var(--text-lg); font-weight: var(--weight-bold); font-variant-numeric: tabular-nums; }
.an-po .po-who { font-size: var(--text-xs); color: var(--muted-foreground); min-width: 0; }
.an-po .po-make { font-variant-numeric: tabular-nums; font-weight: var(--weight-bold); }
.an-po .po-make.pos { color: var(--success-strong); }
.an-po .po-make.neg { color: var(--danger-strong, #b91c1c); }
@media (max-width: 640px) {
  .an-po { grid-template-columns: 1fr auto; }
  .an-po .po-who { display: none; }
}

/* Coming back to SAVED comps must not read as "this is all the comps there are"
   (Gabe, 2026-08-08). One banner, one story: what these are, when they were kept,
   and that Load comps pulls today's sales without losing the picks. */
.comps-saved-banner {
  display: flex; gap: var(--space-2); align-items: baseline;
  background: var(--primary-soft); border: 1px solid var(--border);
  border-radius: var(--radius); padding: var(--space-2) var(--space-3);
  font-size: var(--text-sm); margin-bottom: var(--space-2);
}
.comps-saved-banner b { white-space: nowrap; }

/* A price the user typed over the computed one. Amber = "yours, on purpose", the same
   language the locked/unlocked comp pills use — not an error, just not automatic. */
.an-inwrap.an-overridden { border-color: var(--warning-strong, #b45309); }
.an-linkbtn {
  background: none; border: 0; padding: 0; font: inherit; color: var(--primary);
  text-decoration: underline; cursor: pointer;
}
.an-ceiling { margin-top: var(--space-2); max-width: 52ch; line-height: 1.4; }

/* An address the geocoder could not vouch for. Muted, not an error colour: nothing is
   broken and no money was lost, the comp simply cannot be looked up. */
.comp-unconfirmed {
  font-size: var(--text-xs); color: var(--muted-foreground);
  margin-top: var(--space-1); max-width: 34ch; line-height: 1.35;
}

/* A comp SAVED with this deal, plotted from a healed address (2026-08-08). Deliberately
   not green: green means "an investor paid cash here", which is a claim about the market.
   These are the user's own picks, so they read as chosen rather than as evidence — the
   dashed ring is the same "yours, not the market's" language as the saved cards. */
.comp-pin.saved {
  background: var(--surface); color: var(--primary);
  border: 2px dashed var(--primary); box-shadow: var(--shadow-md);
}

/* arrowed progression tabs (the mockup look Gabe asked back for) */
.ft-arrow { color: var(--faint-foreground); font-size: var(--text-base); padding: 0 var(--space-1); align-self: center; }
.flowtabs { align-items: center; }

/* in the workspace the deal IS the address — the form's address row is redundant
   (header shows it); radius/months/Find buyers stay for pulling MORE buyers */
#tab-buyers .dealhead-row { display: none; }

/* ============================================================================
   Progression tabs, IN YOUR FACE (Gabe: big, bold, unmissable — a thin underline
   wasn't enough). Active step = solid pill; arrows carry the sequence.
   ========================================================================== */
.flowtabs { border-bottom: none; gap: var(--space-2); margin-bottom: var(--space-4); }
.flowtab {
  font-size: 19px; font-weight: 700; padding: 12px 26px;
  border: 1px solid transparent; border-radius: 999px; margin-bottom: 0;
  color: var(--muted-foreground);
}
.flowtab:hover { color: var(--foreground); background: var(--gray-2); }
.flowtab.active {
  background: var(--primary); color: var(--primary-foreground);
  border-color: var(--primary); box-shadow: var(--shadow-sm);
}
.ft-arrow { font-size: 20px; font-weight: 700; color: var(--gray-4); padding: 0 2px; }

/* the empty-selection ARV control reads as a HINT, not a broken button */
.use-arv-btn:disabled {
  background: transparent; color: var(--muted-foreground);
  border: 1px dashed var(--border-strong); box-shadow: none; opacity: 1;
}

/* the pipeline BOARD is the one full-width screen (Gabe: see more stages without
   scrolling); the deal workspace and every other space stay centered at 1280 */
.shell .content.wide { max-width: none; }

/* comps summary lives in the SIDEBAR now (Gabe: not overlaid on the map), with air
   between the words and the ARV control */
.comps-summary { font-size: var(--text-sm); color: var(--muted-foreground);
  padding: var(--space-2) var(--space-1) var(--space-3); line-height: 1.5; }
.comps-summary .use-arv-btn { display: block; margin: var(--space-3) 0 0; width: 100%; text-align: center; }

/* inactive steps read as REAL pills all the time (Gabe liked the hover grey — keep it),
   hover a shade deeper, active stays solid primary */
.flowtab { background: var(--gray-2); border-color: var(--border); }
.flowtab:hover { background: var(--gray-3); color: var(--foreground); }

/* spaces sized up to match the bigger steps */
.space { font-size: 17px; padding: 12px 20px; }

/* stage as ONE pill beside the name — the full chevron bar was dead space (stages
   move in GHL; here we only show where the deal sits) */
.dc-namerow { display: flex; align-items: center; gap: var(--space-3); flex-wrap: wrap; }
.dc-stage {
  background: var(--primary-soft); color: var(--primary-hover);
  border: 1px solid var(--primary); border-radius: 999px;
  font-size: var(--text-sm); font-weight: var(--weight-bold);
  padding: 4px 14px; white-space: nowrap;
}
/* Kanban columns: CONSTANT width (Gabe 2026-08-06 — stretching to fill the screen made
   3 columns absurdly wide). Few stages -> the row centers; many stages -> it scrolls. */
.board-cols {
  width: max-content; max-width: 100%;
  margin: 0 auto; overflow-x: auto;
}
.stage-col { flex: 0 0 320px; width: 320px; min-width: 320px; }

/* comps sidebar (rewritten 2026-08-08): the column GROWS with its cards and the PAGE
   scrolls. The old fixed height left the list ~450px — two cards behind their own
   scrollbar (Gabe: "you shouldn't have to scroll that early — three, maybe five"). The
   map column is sticky, so it rides along as the page scrolls; nothing is lost by
   letting the list be as tall as it is. */
#tab-overview #comps-group {
  display: flex; flex-direction: column;
  height: auto;
  overflow: visible;
}
#tab-overview #comps-group .group-title,
#tab-overview #comps-group .comps-summary { flex: none; }
#tab-overview #comps-group .comps-summary {
  border-bottom: 1px solid var(--border);
  padding-bottom: var(--space-4); margin-bottom: var(--space-2);
}
#tab-overview #comps-group .cards { padding-top: var(--space-2); }
#tab-overview #comps-group .cards { overflow-y: auto; flex: 1; min-height: 0; }

/* saved comps: the line that tells you what is stored vs unsaved, and the comps that were
   saved earlier but are not in today's results (kept visible, never silently dropped) */
.comps-saved-line {
  font-size: var(--text-sm); font-weight: var(--weight-bold);
  color: var(--muted-foreground); padding: var(--space-2) 0 0;
}
.saved-only-title { margin-top: var(--space-4); }
.comp-saved-only { border-style: dashed; }

/* deal search: find one deal by address across every pipeline (2026-08-07, Ted) */
.deals-find { display: flex; gap: var(--space-2); align-items: center; margin: 0 0 var(--space-1); flex-wrap: wrap; }
.deals-search { flex: 1 1 420px; max-width: 520px; min-height: 44px; }
#scratch-btn { min-height: 44px; white-space: nowrap; }

/* "How this ARV is worked out" — two methods, side by side, one of them chosen.
   Cards rather than a dropdown on purpose: the comparison IS the insight, and a dropdown
   would hide the number you are not currently using, which is the one that tells you
   whether your comps are the wrong size. */
.arv-methods { margin: var(--space-2) 0 var(--space-3); }
.am-head { font-size: 13px; font-weight: 800; color: var(--ink); margin-bottom: 6px; }
.am-sub { font-weight: 600; color: var(--muted); font-size: 11.5px; margin-left: 6px; }
.am-grid { display: grid; grid-template-columns: 1fr 1fr; gap: var(--space-2); }
@media (max-width: 620px) { .am-grid { grid-template-columns: 1fr; } }
.am-card { text-align: left; background: var(--card); border: 2px solid var(--line);
  border-radius: 12px; padding: 12px 14px; cursor: pointer; transition: border-color .12s, background .12s; }
.am-card:hover:not(:disabled) { border-color: var(--primary); }
.am-card.is-on { border-color: var(--primary); background: #f5f3ff; }
.am-card.is-off, .am-card:disabled { cursor: default; opacity: 1; background: var(--bg); }
.am-name { font-size: 12px; font-weight: 800; text-transform: uppercase; letter-spacing: .04em; color: var(--muted); }
.am-card.is-on .am-name { color: var(--primary); }
.am-value { font-size: 24px; font-weight: 800; letter-spacing: -0.02em; margin: 2px 0 4px; }
.am-work { font-size: 12px; color: var(--muted); line-height: 1.45; }
.am-blocked { font-size: 12px; color: var(--muted); line-height: 1.5; margin-top: 4px; }
.am-warn { font-size: 12px; color: var(--amber-8); background: var(--amber-1);
  border-radius: 6px; padding: 6px 8px; margin-top: 6px; line-height: 1.45; }
.am-foot { font-size: 11.5px; color: var(--muted); margin-top: 6px; }

/* Locked vs unlocked, as two clearly different things rather than one grey pill.
   NOT red for locked: red is the app's error colour, and a paid action the client may
   perfectly reasonably want is not an error. Amber is the honest signal — "this costs
   something, your call". Green is "yours forever", which is the actual promise. */
.comp-detail-btn { font-weight: 700; border-radius: 999px; transition: background .12s, border-color .12s, color .12s; }
.comp-detail-btn.is-locked {
  color: var(--amber-8); border-color: var(--amber-6); background: var(--amber-1);
}
.comp-detail-btn.is-locked:hover { background: var(--amber-6); border-color: var(--amber-6); color: #fff; }
.comp-detail-btn.is-unlocked {
  color: var(--green-8); border-color: var(--green-6); background: var(--green-1);
}
.comp-detail-btn.is-unlocked:hover { background: var(--green-6); border-color: var(--green-6); color: #fff; }
.deals-find-note { margin: 0 0 var(--space-3); }
/* Scratch address: the header badge earns its keep by being visible from every tab, so the
   "none of this saves" state can't be forgotten halfway through working a property. */
body.scratchpad #deal-warn { border-left: 3px solid var(--amber); }
.deal-hits { display: grid; gap: var(--space-3); max-width: 720px; }
.deal-hits .deal-card { cursor: pointer; padding: var(--space-3) var(--space-4); }
.dh-row { display: flex; justify-content: space-between; gap: var(--space-3); align-items: baseline; }
.dh-stage { font-size: var(--text-xs); color: var(--muted-foreground); white-space: nowrap; }
.dh-addr { font-size: var(--text-sm); color: var(--muted-foreground); margin-top: 2px; }

/* Missing/unreadable property address (2026-08-07). Amber, not red: nothing is broken and
   nothing was lost — the deal simply can't be worked until it has an address. Sits in the
   deal header so it is met on arrival, not after Load comps fails. */
.deal-warn {
  background: var(--warning-soft); color: var(--warning-strong);
  border: 1px solid var(--warning); border-radius: var(--radius);
  padding: var(--space-3) var(--space-4); margin-top: var(--space-3);
  font-size: var(--text-sm); line-height: 1.45;
}
.deal-warn strong { display: block; font-size: var(--text-base); margin-bottom: 2px; }
/* The address row is hidden in the workspace because the deal IS the address — which only
   holds when there is one. When there isn't, reveal it so the fix is reachable. */
#tab-buyers .dealhead-row.show-addr { display: flex; }

/* Per-comp $/sqft. Only appears once a comp is unlocked, which makes the value of the
   unlock visible on the card itself rather than described in a tooltip. */
.comp-psf { font-size: 11.5px; font-weight: 700; color: var(--muted); margin-top: 2px; }

/* Scratch-address bar. Lives in the deal header because for a scratch address there is no
   deal — the address IS the subject, so it belongs where a deal's address would be, visible
   from every tab rather than buried in the buyer-search form. */
.scratch-bar { display: flex; gap: var(--space-2); align-items: center; margin-top: var(--space-2); }
.scratch-bar input { flex: 1 1 auto; min-width: 0; min-height: 44px; }
.scratch-bar .primary { min-height: 44px; white-space: nowrap; }

/* Scratch-bar autocomplete needs a positioned parent for its dropdown. */
.scratch-wrap { position: relative; flex: 1 1 auto; min-width: 0; }
.scratch-wrap input { width: 100%; }

/* Comp cards were rendering ~20px taller than their content, which read as dead white space
   on saved comps (Gabe, 2026-08-07). It was NOT the card: fit-content on a card measures
   112px against a rendered 132px. #comps-list is a GRID with a definite height (it matches
   the map beside it), and a grid's default align-content is STRETCH — so the leftover space
   in that fixed box gets distributed into the auto-sized rows. Three 112px rows plus gaps
   is 360px inside 428px, so every row silently gained ~23px.
   Pack the rows at the top and let the spare space stay spare. */
#comps-list { align-content: start; }

/* Load comps hover: it carries .ghost-btn but is painted SOLID by `.comps-bar .ghost-btn`,
   and `.ghost-btn:hover` then swapped the background to near-white while leaving the text
   white — the label vanished on hover (Gabe, 2026-08-07). Give the solid button a solid
   hover instead of inheriting the ghost one. */
.comps-bar .ghost-btn:hover,
.comps-bar #comps-btn:hover {
  background: var(--primary-hover, #1d4ed8);
  border-color: var(--primary-hover, #1d4ed8);
  color: #fff;
}

/* Motivation flags, on the front of the card. Solid orange pill with DARK text — the same
   costume as .fact.flag in the report popup (Gabe 2026-08-17: one fact, one look; the loud
   version survives because these flags are the thing the unlock is sold on). Dark text is
   what keeps the loud fill AA-legible at this size. */
.flag-chip { background: var(--amber-5); color: var(--foreground); border-color: var(--amber-6);
  font-weight: 700; font-size: 11px; padding: 2px 8px; border-radius: 999px; }
.comp-flags { display: flex; flex-wrap: wrap; gap: 4px; justify-content: flex-end; margin-top: 4px; }
