/* ============================================================
   SaskTaxSales — the signed-in listing panel (docs/growth/18)
   Loaded ONLY on the members shell, after styles.css. Drives both
   signed-in tiers; the public shell and the operator console never
   see it and keep the original panel styling.

   The handoff specified Spectral + Figtree and #2f6b4f. We map its
   ROLES onto our own tokens instead: numbers / titles / stat values
   to --font-serif, everything else to --font-sans, legal
   descriptions to --font-mono, green = --green. Adding two webfonts
   for one panel would cost real LCP on a shell we just optimised.
   ============================================================ */

/* --- Shell -------------------------------------------------
   ONE scroll container for everything below the title bar.

   The handoff kept the stat group, watch-outs and tab strip as fixed chrome
   above a smaller scroll area. Measured on our 452px panel that cost 479px of
   an 884px panel — 54% furniture, leaving the tab content 406px, which is what
   made the panes feel like a letterbox at the bottom. So only the TITLE BAR is
   pinned (it holds the close button and says which parcel you are in); the
   stats, actions, watch-outs and tabs scroll with the content, and the tab
   strip is `sticky` so it re-pins the moment it reaches the top and is never
   more than a flick away.

   That sticky is a deliberate departure from the handoff's "tabs are normal
   flow, never sticky inside the scroller" rule. That rule was written to stop
   the strip changing height mid-scroll; the underline here is an inset
   box-shadow, so it cannot.

   .mp-body also carries .detail-body: the mobile bottom-sheet gesture code
   queries that class for scrollTop and the sheet CSS flips its touch-action. */
.mp-shell .mp-body {
  /* `scroll` (not `auto`) + a stable gutter: the scrollbar must not appear and
     disappear between tabs, which would shift every row by ~15px. */
  overflow-y: scroll;
  scrollbar-gutter: stable;
  padding: 0;
  /* Makes this the offsetParent, so .mp-tabs.offsetTop reads as the strip's
     offset within the SCROLL CONTENT (sticky does not move layout position).
     showTab() scrolls to exactly that. Without it the value would be measured
     from the panel and be off by the title bar's height. */
  position: relative;
}
.mp-spacer { height: 44px; }   /* the save FAB never covers the last row */

.mp-hd { padding: 11px var(--s5) 10px; border-bottom: 1px solid var(--line); }
.mp-eyebrow-hd { color: var(--ink-3); font-size: var(--fs-xs); font-weight: 600; margin-bottom: 2px; }
.mp-legal { font-family: var(--font-serif); font-size: 21px; }

.mp-top { padding: 10px var(--s5) 12px; border-bottom: 1px solid var(--line); }
.mp-top:has(+ .mp-wstrip) { border-bottom: none; padding-bottom: 4px; }

/* --- Stat group -------------------------------------------
   Deliberately tight. This is reference furniture above the tab content, and
   every pixel it takes is a pixel the pane below does not get. The serif
   numerals carry their own optical leading, so the label sits almost flush
   under them rather than a line-height away. */
.mp-statgrp { border: 1px solid var(--line-2); border-radius: 10px; overflow: hidden; margin-bottom: var(--s2); }

/* Every cell is a CENTRED flex column, and the rows stretch their cells to a
   common height. Two things fall out of that: each label/value pair sits
   optically centred in its own box rather than hanging off the top, and the
   dividers run the full height of the row instead of only as tall as whichever
   cell happened to be tallest. `line-height: 1` on the labels is what closes
   the gap under them -- at 9.5px the default leading alone was ~5px of dead
   air between a label and the number it belongs to. */
.mp-cdrow { display: flex; align-items: stretch; background: var(--surface-2); padding: 14px 14px 14px 6px; }
.mp-cd { flex: 0 0 98px; text-align: center; display: flex; flex-direction: column; justify-content: center; }
/* A serif digit fills maybe two thirds of its line box, so a "normal"
   line-height leaves ~5px of invisible slack under the numeral and the label
   reads as adrift from it. Clamping the box to just over the cap height puts
   the two back on speaking terms; the margin below is then the whole gap, and
   is the only number to change if it needs another pixel. */
.mp-cd b { display: block; font-family: var(--font-serif); font-size: 29px; line-height: .78; font-weight: 700; color: var(--green); }
.mp-cd span { display: block; font-size: 9.5px; line-height: 1; font-weight: 600; letter-spacing: .07em; color: var(--green); margin-top: 2px; }
.mp-cdr { flex: 1; padding-left: 16px; border-left: 1px solid var(--line-2); display: flex; flex-direction: column; justify-content: center; }
.mp-cdr--wide { padding-left: 14px; border-left: none; }
.mp-cdr span { display: block; font-size: 9.5px; line-height: 1; font-weight: 600; letter-spacing: .07em; color: var(--ink-3); margin-bottom: 4px; }
.mp-cdr b { font-family: var(--font-serif); font-size: 19px; line-height: 1.02; font-weight: 700; color: var(--ink); }
.mp-strow { display: flex; align-items: stretch; border-top: 1px solid var(--line-2); background: var(--surface); }
/* Generous horizontal padding is what separates the three facts: it holds each
   label/value pair well clear of the dividers instead of letting them crowd
   the hairline, so the row reads as three things rather than one run-on. */
.mp-st {
  flex: 1; min-width: 0; padding: 13px 15px; border-left: 1px solid var(--line-2);
  display: flex; flex-direction: column; justify-content: center;
}
.mp-st:first-child { border-left: none; }
.mp-st span { display: block; font-size: 9.5px; line-height: 1; font-weight: 600; letter-spacing: .06em; color: var(--ink-3); margin-bottom: 4px; }
.mp-st b { font-family: var(--font-serif); font-size: 15px; line-height: 1.05; font-weight: 700; color: var(--ink); overflow-wrap: anywhere; }
.mp-st--hi b { color: var(--green); }
.mp-st--muted b { color: var(--ink-disabled); }

/* --- Action stack ----------------------------------------- */
.mp-actstack { display: flex; flex-direction: column; gap: 6px; }
.mp-actrow { display: flex; gap: 6px; }
.mp-abtn {
  flex: 1; display: flex; align-items: center; justify-content: center; gap: 6px;
  text-align: center; white-space: nowrap; text-decoration: none;
  border: 1px solid var(--line-2); border-radius: 8px; padding: 7px 4px;
  font-family: var(--font-sans); font-size: var(--fs-sm); font-weight: 600;
  color: var(--ink); background: var(--surface); cursor: pointer;
}
.mp-abtn:hover { border-color: var(--ink-3); color: var(--ink); }
.mp-abtn .ic { width: 15px; height: 15px; }
.mp-abtn--pri { background: var(--green); color: #fff; border-color: var(--green); padding: 9px 4px; font-size: 14.5px; }
.mp-abtn--pri:hover { background: var(--green-700); border-color: var(--green-700); color: #fff; }

/* --- Watch-outs strip -------------------------------------
   Only UNCERTAIN signals (plus a neutral factual chip) reach this,
   capped at three, and the whole strip is absent when empty. */
.mp-wstrip { display: flex; flex-wrap: wrap; align-items: center; gap: 7px; padding: 12px var(--s5) 14px; background: var(--surface); }
.mp-chip {
  display: inline-flex; align-items: center; gap: 5px; cursor: pointer;
  font-size: 12px; font-weight: 600; color: var(--ink);
  background: var(--surface); border: 1px solid var(--line-2); border-radius: 20px; padding: 4px 11px;
}
.mp-chip--risk { border-color: #e0b3a6; color: #b1442b; background: #fdf6f4; }
.mp-chip:hover { border-color: var(--ink-3); }

/* --- Diligence bar ---------------------------------------- */
.mp-diligbar {
  display: flex; align-items: center; gap: 10px; width: 100%;
  padding: 10px var(--s5) 11px; border: none; border-top: 1px solid var(--line);
  border-bottom: 1px solid var(--line); background: var(--surface); cursor: pointer; text-align: left;
}
.mp-dl { font-size: var(--fs-xs); font-weight: 600; color: var(--ink); white-space: nowrap; }
.mp-trk { flex: 1; height: 5px; border-radius: 3px; background: var(--line-2); overflow: hidden; }
.mp-trk i { display: block; height: 100%; background: var(--green); transition: width .18s ease; }
.mp-go { font-size: var(--fs-xs); font-weight: 600; color: var(--green); white-space: nowrap; }

/* --- Tabs --------------------------------------------------
   Sticky at the top of the scroll container (see the shell note): scroll into
   the pane and the strip pins, so switching tabs is always one click. The
   active underline is an inset box-shadow, so a tab going active can never
   change the strip's height and jitter the sticky edge. */
.mp-tabs {
  position: sticky; top: 0; z-index: 4;
  display: flex; align-items: center; gap: 22px;
  padding: 11px var(--s5) 0; border-bottom: 1px solid var(--line-2);
  background: var(--surface);
}
.mp-tab {
  padding: 0 1px 9px; border: none; background: none; cursor: pointer;
  font-family: var(--font-sans); font-size: 14.5px; font-weight: 600; color: var(--ink-2);
  display: flex; align-items: center; gap: 5px;
}
.mp-tab.is-on { color: var(--ink); box-shadow: inset 0 -2.5px 0 var(--green); }
.mp-rf { width: 6px; height: 6px; border-radius: 50%; background: #c0392b; }

/* The panes carry the body padding now that .mp-body itself has none (the
   stat group, strip and tab bar bring their own, full-bleed edges included). */
.mp-pane { display: none; padding: var(--s4) var(--s5) 0; }
.mp-pane.is-on { display: block; }

/* --- Tiles ------------------------------------------------- */
.mp-tiles { display: grid; grid-template-columns: 1fr 1fr; gap: 9px; margin-bottom: var(--s3); }
.mp-tile { border: 1px solid var(--line); border-radius: 11px; padding: 11px 12px; background: var(--surface-2); min-width: 0; }
.mp-tile-l { font-size: 10.5px; font-weight: 600; letter-spacing: .06em; color: var(--ink-2); }
.mp-tile-v { font-family: var(--font-serif); font-size: 28px; line-height: 1.05; font-weight: 700; color: var(--ink); margin: 5px 0 3px; overflow-wrap: anywhere; }
.mp-tile-n { font-size: 11.5px; font-weight: 500; color: var(--ink-2); }
.mp-tile--green { background: var(--green-tint); border-color: var(--green-tint-2); }
.mp-tile--green .mp-tile-l { color: #4f7a62; }
.mp-tile--green .mp-tile-v { color: var(--green); }
.mp-tile--amber { background: var(--amber-bg); border-color: var(--amber-line); }
.mp-tile--amber .mp-tile-l { color: var(--amber-ink); }
.mp-tile--amber .mp-tile-v { color: var(--amber-ink); }
.mp-tile--locked {
  border: 1px dashed var(--line-2);
  background: repeating-linear-gradient(135deg, var(--surface-2), var(--surface-2) 7px, #efece2 7px, #efece2 14px);
}
.mp-tile--locked .mp-tile-v { color: var(--ink-disabled); letter-spacing: .08em; }

/* --- Cards + evidence blocks ------------------------------- */
.mp-card { background: var(--surface-2); border-radius: 11px; padding: 13px 14px; margin-bottom: var(--s3); }
.mp-card--quiet { background: var(--surface-3); }
.mp-eyebrow { font-size: 11.5px; font-weight: 600; letter-spacing: .07em; text-transform: uppercase; color: var(--ink-3); margin-bottom: var(--s2); }
.mp-lead { margin: 0; font-family: var(--font-serif); font-size: 15px; line-height: 1.4; color: var(--ink); text-wrap: pretty; }
.mp-quiet { margin: 0; font-size: var(--fs-xs); line-height: 1.45; color: var(--ink-2); }

.mp-block { border: 1px solid var(--line); border-radius: 10px; padding: 0; margin-bottom: var(--s3); background: var(--surface); }
.mp-block > summary {
  list-style: none; cursor: pointer; user-select: none;
  padding: 11px 12px; display: flex; justify-content: space-between; align-items: center; gap: var(--s2);
  font-size: 12.5px; font-weight: 600; letter-spacing: .05em; text-transform: uppercase; color: var(--ink-2);
}
.mp-block > summary::-webkit-details-marker { display: none; }
.mp-block .mp-chev .ic { width: 15px; height: 15px; color: var(--ink-3); transition: transform .16s; }
.mp-block[open] .mp-chev .ic { transform: rotate(180deg); }
.mp-bd { padding: 0 12px 11px; }
.mp-block[open] .mp-bd { border-top: 1px solid var(--line-3); padding-top: 9px; }
/* A re-homed legacy block (valueBlock, comps) brings its own styling. */
.mp-block--plain .mp-bd > .value-block { margin: 0; padding: 0; border: none; background: none; }

.mp-kv { display: flex; justify-content: space-between; gap: var(--s3); padding: 5px 0; border-bottom: 1px solid var(--line-3); font-size: 13.5px; font-weight: 600; }
.mp-kv:last-of-type { border-bottom: none; }
.mp-kv span:first-child { color: var(--ink-3); font-weight: 500; flex: none; max-width: 47%; }
/* min-width:0 is what lets a long legal description ("Lot 8 Block 12 Plan
   77B05398") wrap instead of overflowing: a flex item defaults to min-width
   auto, so overflow-wrap alone never gets the chance to break it. */
.mp-kv span:last-child { color: var(--ink); text-align: right; overflow-wrap: anywhere; min-width: 0; }
.mp-mono { font-family: var(--font-mono); font-size: 12.5px; }

/* Every evidence block ends in one of these. No exceptions. */
.mp-src { margin-top: var(--s2); font-size: 11.5px; font-weight: 500; line-height: 1.4; color: var(--ink-3); }

.mp-note { display: flex; gap: 9px; align-items: flex-start; background: var(--surface-2); border-radius: 10px; padding: 10px 12px; margin: 0 0 var(--s3); font-size: 13px; line-height: 1.42; color: var(--ink); }
.mp-note .ic { flex: none; width: 16px; height: 16px; color: var(--amber-ink); margin-top: 1px; }
.mp-guide { margin: 0 0 var(--s3); font-size: var(--fs-sm); }
.mp-inline-actions { margin-bottom: var(--s3); }

/* --- Derived signals --------------------------------------
   Three states, and NONE of them defaults to clear. Green appears
   only where a check ran on this parcel and returned a value. */
.mp-sigrow { display: flex; justify-content: space-between; align-items: center; gap: 10px; padding: 7px 0; border-bottom: 1px solid var(--line-3); font-size: 13.5px; font-weight: 600; color: var(--ink); }
.mp-sigrow:last-of-type { border-bottom: none; }
.mp-sig { display: inline-flex; align-items: center; gap: 5px; font-size: 13px; font-weight: 600; text-align: right; }
.mp-sig--risk { color: #b1442b; }
.mp-sig--ok { color: var(--green); }
.mp-sig--na { color: var(--ink-3); }
.mp-sig--lk { color: var(--ink-disabled); }
.mp-legend { display: flex; flex-direction: column; gap: 4px; padding-top: 9px; font-size: 11.5px; line-height: 1.4; color: var(--ink-3); }
.mp-legend b { font-weight: 700; }

/* --- Locks + the report offer ------------------------------ */
/* A withheld-data disclosure: one quiet line, deliberately NOT a block.
   It replaces the lock blocks on the report-only shell. Same type size and
   colour as a block's source line, because that is what it is: a note about
   what this page does and does not contain, not an offer. No border, no bars,
   no button -- the whole point is that the panel stops looking like a wall. */
.mp-withheld {
  margin: var(--s3) 0 0;
  font-size: var(--fs-xs);
  line-height: 1.55;
  color: var(--ink-3);
}
.mp-pane > .report-offer { margin-bottom: var(--s3); }

/* The FREE-account card. It borrows the lock's SHAPE so the panel stays
   cohesive, and none of its "something is hidden" signals: no lockbars, no
   dashed border, no diagonal hatch. Those say there is content behind this you
   are not being shown, which is false -- an account buys utility, not facts,
   and the listings stay free either way. A solid tint reads as an offer; the
   hatch reads as a wall. Same green family as the report card, so the two asks
   on this panel look like siblings rather than two different systems. */
.mp-lock--free { border: 1px solid #d7e6dd; background: #f4faf6; }
.mp-lock--free .mp-freelist { margin: var(--s2) 0 var(--s3); padding-left: 18px; }
.mp-lock--free .mp-freelist li { font-size: var(--fs-sm); line-height: 1.5;
  color: var(--ink-2); padding: 2px 0; }
.mp-lock--free .mp-freefine { margin: var(--s2) 0 0; font-size: var(--fs-xs);
  color: var(--ink-3); }

.mp-lock {
  border: 1px dashed var(--line-2); border-radius: 11px; padding: 12px 13px; margin-bottom: var(--s3);
  background: repeating-linear-gradient(135deg, var(--surface-2), var(--surface-2) 7px, #efece2 7px, #efece2 14px);
}
.mp-lock-h { font-size: 12px; font-weight: 600; letter-spacing: .06em; text-transform: uppercase; color: var(--ink-2); margin-bottom: 5px; }
.mp-lock p { margin: 0 0 9px; font-size: 13px; line-height: 1.45; color: var(--ink-2); }
.mp-lockbars { display: flex; flex-direction: column; gap: 6px; margin-bottom: 10px; }
.mp-lockbars i { display: block; height: 9px; border-radius: 5px; background: var(--line-2); }
.mp-ctas { display: flex; flex-wrap: wrap; gap: 7px; }
.mp-cta { background: var(--green); color: #fff; border: 1px solid var(--green); border-radius: 8px; padding: 8px 13px; font-family: var(--font-sans); font-size: 13px; font-weight: 600; cursor: pointer; }
.mp-cta:hover { background: var(--green-700); border-color: var(--green-700); }
/* .mp-cta sits on BOTH <button> and <a> ("Open the report" is a link). The
   global `a:hover { color: var(--blue-700) }` is specificity (0,1,1) and beats
   the class (0,1,0), so the label turned blue on a solid green button on hover
   -- unreadable, and only on the anchor variants. Pin the colour across every
   link state, and drop the underline a bare <a> would otherwise carry onto
   something shaped like a button. */
.mp-cta:link, .mp-cta:visited, .mp-cta:hover, .mp-cta:focus, .mp-cta:active {
  color: #fff; text-decoration: none;
}
.mp-cta--ghost { background: var(--surface); color: var(--green); }
.mp-cta--ghost:hover { background: var(--green-tint); }
.mp-cta--ghost:link, .mp-cta--ghost:visited, .mp-cta--ghost:hover,
.mp-cta--ghost:focus, .mp-cta--ghost:active { color: var(--green); }

.mp-orlbl { display: flex; align-items: center; gap: 9px; margin: var(--s3) 0 var(--s2); font-size: 11px; font-weight: 600; letter-spacing: .06em; color: var(--ink-3); }
.mp-orlbl::after { content: ""; flex: 1; height: 1px; background: var(--line); }
.mp-rep { border: 1px solid var(--line-2); border-radius: 11px; padding: 13px 13px 12px; background: var(--surface); margin-bottom: var(--s3); }
.mp-rep-h { display: flex; align-items: baseline; justify-content: space-between; gap: 10px; margin-bottom: 5px; }
.mp-rep-t { font-size: 14.5px; font-weight: 600; color: var(--ink); }
.mp-rep-p { font-size: 14.5px; font-weight: 600; color: var(--green); white-space: nowrap; }
.mp-rep-p i { font-style: normal; font-size: 11.5px; font-weight: 500; color: var(--ink-3); }
.mp-rep p { margin: 0 0 9px; font-size: 12.8px; line-height: 1.45; color: var(--ink-2); }
.mp-rep ul { margin: 0 0 11px; padding: 0; list-style: none; display: flex; flex-direction: column; gap: 4px; }
.mp-rep li { font-size: 12.5px; font-weight: 500; color: var(--ink); display: flex; gap: 7px; }
.mp-rep li::before { content: "\2713"; color: var(--green); font-weight: 700; }
.mp-rep-n { margin-top: 9px; font-size: 11.5px; line-height: 1.4; color: var(--ink-3); }
/* Offer visible, purchase not yet possible (no Stripe report price configured). */
.mp-cta--inert { background: var(--line-2); border-color: var(--line-2); color: var(--surface); cursor: default; }
.mp-rep-soon { margin-left: 9px; font-size: 11.5px; font-weight: 600; color: var(--ink-3); }

/* --- Checklist + notes ------------------------------------- */
.mp-check { display: flex; align-items: center; gap: 10px; padding: 8px 0; border-bottom: 1px solid var(--line-3); font-size: 14px; font-weight: 600; color: var(--ink); cursor: pointer; }
.mp-check:last-of-type { border-bottom: none; }
.mp-check input { position: absolute; opacity: 0; width: 0; height: 0; }
.mp-box { width: 19px; height: 19px; border: 1.5px solid var(--line-2); border-radius: 5px; flex: none; display: grid; place-items: center; font-size: 12px; color: #fff; background: var(--surface); }
.mp-check.is-done .mp-box { background: var(--green); border-color: var(--green); }
.mp-check input:focus-visible + .mp-box { outline: 2px solid var(--green); outline-offset: 2px; }
.mp-check-l { flex: 1; min-width: 0; }
.mp-check-link { margin-left: auto; font-size: 12px; color: var(--green); white-space: nowrap; }
.mp-check-flag { margin-left: auto; font-size: 12px; color: #b1442b; white-space: nowrap; }

.mp-note-field {
  width: 100%; box-sizing: border-box; resize: vertical;
  border: 1px dashed var(--line-2); border-radius: 9px; padding: 9px 10px;
  font-family: var(--font-sans); font-size: 13px; line-height: 1.45; color: var(--ink);
  background: var(--surface);
}
.mp-note-field:focus { outline: none; border-style: solid; border-color: var(--green); box-shadow: 0 0 0 3px rgba(29, 107, 74, .12); }
.mp-notesave { margin-top: 6px; display: flex; justify-content: space-between; gap: var(--s2); font-size: 11.5px; font-weight: 600; color: var(--ink-3); }
.mp-notestate.is-ok { color: var(--green); }

/* --- Re-homed legacy blocks --------------------------------
   These come from app.js unchanged (their wording carries honesty
   guarantees); only their outer spacing is normalised to the pane. */
.mp-pane > .value-block,
.mp-pane > .contact-block,
.mp-pane > .lookup-block,
.mp-pane > .bid-block,
.mp-pane > .soil-card,
.mp-pane > .usability-card,
.mp-pane > .comps-block,
.mp-pane > .locked-block,
.mp-pane > .detail-resource,
.mp-pane > .detail-parcel-wrap { margin-bottom: var(--s3); }

/* --- Mobile ------------------------------------------------
   The peek snap needs no special collapsing any more: with one scroll
   container, the peek simply shows the top of it (title bar, stats, primary
   action) and everything else is below the fold, which is exactly the
   behaviour the old display:none rules were faking. The peek height stays at
   SHEET_PEEK so the map pin remains visible.

   The sheet's touch-action rules key off .detail-body, which .mp-body also
   carries — keep both classes together or the sheet stops scrolling at full. */
@media (max-width: 920px) {
  .mp-pane, .mp-hd, .mp-top, .mp-wstrip, .mp-diligbar, .mp-tabs {
    padding-left: var(--s4); padding-right: var(--s4);
  }
  body:not(.admin-console) .detail.detail-sheet .mp-hd { flex: none; padding-top: 6px; padding-bottom: 10px; }
  .mp-tabs { gap: 18px; }
  .mp-tab { font-size: 14px; }
}
/* --- Set up your board: the four-question flow a new member answers once ----
   Two approved layouts from ONE DOM: a 700x472 desktop modal with a green step
   rail, and a bottom sheet under 720px. Both chromes are always rendered and
   the breakpoint decides which is visible -- one template, no duplicate state.

   The handoff pins a palette of its own; every value below is the OUR-token
   equivalent of that intent (rail green -> --green-800, primary -> --green,
   selected tint -> --green-tint, warm panel -> --surface-2, and so on), so the
   flow inherits the brand and light/dark with the rest of the shell. Sizes,
   spacing and radii are the handoff's.

   THE FRAME HEIGHT IS FIXED ON PURPOSE: the modal must not resize between
   questions, so the question column scrolls internally instead. */
.stf-scrim {
  position: fixed; inset: 0; z-index: 2000;
  background: rgba(20, 30, 26, .55);
  display: flex; align-items: center; justify-content: center;
  padding: 16px;
  overscroll-behavior: contain;
}
.stf-card {
  /* On-dark rail tones. Local, because the palette has no on-green scale and
     the rail is the only surface that needs one. */
  --stf-rail-2: #a8c4b0; --stf-rail-3: #8fb199; --stf-rail-4: #7ea589;
  /* 512, not the handoff's 472. The frame still must not resize between
     questions, and at 472 exactly one question did not fit: question one runs
     18px long, because its explainer has to carry the imagery-is-years-stale
     caveat before anyone filters on a building. Question three cleared by a
     single pixel. Shortening the copy to hit a round number would be trading
     the one thing that stops a member trusting a stale satellite read for 40px
     of whitespace on the other three screens. Any new question has to fit
     THIS height -- measure before you add a sentence. */
  display: flex; width: 700px; max-width: calc(100vw - 32px); height: 512px;
  max-height: calc(100vh - 32px);
  /* No border. The handoff specifies a 1px warm-grey one, which works against
     the white card it was drawn on -- but our rail is deep green, so the same
     hairline reads as a white outline traced around the dark edge. The card
     sits on a dark scrim and the shadow already separates it. */
  background: var(--surface, #fff);
  border-radius: 14px; box-shadow: 0 14px 40px rgba(40, 50, 40, .16); overflow: hidden;
}

/* --- left rail (desktop) --- */
.stf-rail {
  flex: 0 0 206px; width: 206px; background: var(--green-800, #14502f);
  padding: 24px 22px; display: flex; flex-direction: column;
}
/* The production wordmark, knocked out to solid white for the dark rail. The
   filter beats shipping a white copy of the file: one asset, and a brand tweak
   never has to be made twice. */
.stf-mark {
  /* Subordinate to the 21px title below it, as the handoff has it -- the
     wordmark identifies the panel, it is not the headline. */
  display: block; width: 98px; height: auto;
  filter: brightness(0) invert(1);
}
.stf-rail h2 { font: 600 21px/1.2 var(--font-serif); color: #fff; margin: 20px 0 0; }
.stf-rail p.sub { font: 500 12px/1.5 var(--font-sans); color: var(--stf-rail-2); margin: 6px 0 0; }
.stf-steps { display: flex; flex-direction: column; gap: 2px; margin-top: 22px; }
.stf-stepbtn {
  display: flex; align-items: flex-start; gap: 9px; width: 100%; text-align: left;
  padding: 8px 9px; border: 0; border-radius: 8px; background: none; cursor: pointer;
}
.stf-stepbtn.is-now { background: rgba(255, 255, 255, .12); }
.stf-mk {
  flex: 0 0 19px; width: 19px; height: 19px; border-radius: 50%; margin-top: 1px;
  display: flex; align-items: center; justify-content: center;
  border: 1px solid rgba(255, 255, 255, .35); color: #fff; font: 600 10.5px/1 var(--font-sans);
}
.stf-stepbtn.is-now .stf-mk { border-color: #fff; }
.stf-stepbtn.is-done .stf-mk {
  background: var(--green-tint-2, #bcd9c8); border-color: var(--green-tint-2, #bcd9c8);
  color: var(--green-800, #14502f);
}
.stf-sl { min-width: 0; }
.stf-sl b { display: block; font: 600 12.5px/1.3 var(--font-sans); color: var(--stf-rail-2); }
.stf-stepbtn.is-now .stf-sl b, .stf-stepbtn.is-done .stf-sl b { color: #fff; }
.stf-sl span {
  display: block; font: 500 11px/1.35 var(--font-sans); color: var(--stf-rail-3);
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis; max-width: 138px;
}
.stf-foot-note { margin-top: auto; font: 500 11px/1.5 var(--font-sans); color: var(--stf-rail-4); }

/* --- right column --- */
.stf-col { flex: 1; min-width: 0; display: flex; flex-direction: column; }
.stf-body { flex: 1; min-height: 0; overflow-y: auto; padding: 26px 28px 10px; }
.stf-kicker {
  font: 600 10.5px/1 var(--font-sans); letter-spacing: .09em; text-transform: uppercase;
  color: var(--ink-3, #8a929a); margin: 0;
}
.stf-title { font: 600 23px/1.22 var(--font-serif); color: var(--ink, #1f2a30); margin: 8px 0 0; }
.stf-note { font: 500 12.5px/1.55 var(--font-sans); color: var(--ink-2, #5d6b72); margin: 9px 0 0; }
.stf-why { display: none; }

.stf-opts { display: flex; flex-direction: column; gap: 6px; margin-top: 14px; }
.stf-opt {
  display: flex; align-items: center; gap: 10px; width: 100%; text-align: left;
  padding: 11px 13px; border: 1px solid var(--line, #e7e4dc); border-radius: 10px;
  background: var(--surface, #fff); cursor: pointer;
}
.stf-opt:hover { background: var(--surface-3, #f7f5ef); }
.stf-opt.is-on { border-color: var(--green, #1d6b4a); background: var(--green-tint, #e8f1ec); }
.stf-dot { display: none; }
.stf-optmain { flex: 1; min-width: 0; }
.stf-optmain b { display: block; font: 600 14px/1.3 var(--font-sans); color: var(--ink, #1f2a30); }
.stf-optmain span { display: block; font: 500 12px/1.4 var(--font-sans); color: var(--ink-2, #5d6b72); margin-top: 2px; }
.stf-n { font: 600 12px/1 var(--font-sans); color: var(--ink-3, #8a929a); font-style: normal; white-space: nowrap; }
.stf-n0 { color: var(--ink-disabled, #b0a89a); }

.stf-input {
  width: 100%; padding: 11px 13px; border: 1px solid var(--line-2, #d4d0c6); border-radius: 10px;
  background: var(--surface, #fff); font: 500 14px/1.3 var(--font-sans); color: var(--ink, #1f2a30);
}
.stf-card:focus { outline: none; }
.stf-input:focus-visible, .stf-opt:focus-visible, .stf-stepbtn:focus-visible,
.stf-bar:focus-visible, .stf-lnk:focus-visible, .stf-go:focus-visible, .stf-why:focus-visible {
  outline: 2px solid var(--green, #1d6b4a); outline-offset: 2px;
}
/* --- town typeahead (Q3) ---
   A real list rather than a native <datalist>, which the main board uses but
   which cannot show anything beside the name. Where is the LAST question and
   the one that narrows hardest, so it is the one place a count matters most --
   each row carries what picking that town would leave, the other answers and
   the current radius included. Only municipalities that actually hold
   inventory are offered. */
.stf-tw { position: relative; }
.stf-sug {
  display: none; position: absolute; left: 0; right: 0; top: calc(100% + 4px); z-index: 3;
  background: var(--surface, #fff); border: 1px solid var(--line-2, #d4d0c6);
  border-radius: 10px; box-shadow: 0 8px 20px rgba(40, 50, 40, .12); overflow: hidden;
}
.stf-sug.is-open { display: block; }
.stf-sugrow {
  display: flex; align-items: center; justify-content: space-between; gap: 12px;
  width: 100%; text-align: left; padding: 10px 13px; border: 0;
  border-bottom: 1px solid var(--line-3, #f0eee7); background: none; cursor: pointer;
  font: 600 13.5px/1.3 var(--font-sans); color: var(--ink, #1f2a30);
}
.stf-sugrow:last-child { border-bottom: 0; }
.stf-sugrow:hover, .stf-sugrow.is-on { background: var(--green-tint, #e8f1ec); }
.stf-sugrow span { min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }

.stf-opt-go { justify-content: center; background: var(--green, #1d6b4a); border-color: var(--green, #1d6b4a); }
.stf-opt-go b { color: #fff; }
.stf-opt-go:hover { background: var(--green-700, #16573b); }

/* --- radius control (Q3) --- */
.stf-rad {
  margin-top: 10px; border: 1px solid var(--line, #e7e4dc); border-radius: 10px;
  padding: 12px 13px 10px; opacity: .55; transition: opacity .15s ease;
}
.stf-rad.is-live { opacity: 1; }
.stf-radhead { display: flex; align-items: baseline; justify-content: space-between; gap: 10px; }
.stf-radhead b { font: 600 12.5px/1.3 var(--font-sans); color: var(--ink, #1f2a30); }
.stf-radhead span { font: 600 13px/1.3 var(--font-sans); color: var(--green-ink, #1d6b4a); }
.stf-range { width: 100%; margin: 8px 0 0; accent-color: var(--green, #1d6b4a); }
.stf-radends { display: flex; justify-content: space-between; font: 500 11px/1.3 var(--font-sans); color: var(--ink-3, #8a929a); }
.stf-radhint { font: 500 11.5px/1.45 var(--font-sans); color: var(--ink-3, #8a929a); margin: 7px 0 0; }

/* --- footer (pinned, never moves) --- */
.stf-foot {
  display: flex; align-items: center; gap: 0;
  padding: 13px 28px; border-top: 1px solid var(--line-3, #f0eee7); background: var(--surface-3, #f7f5ef);
}
/* Desktop keeps the handoff's arrangement -- Back alone on the left, the tally
   and Skip grouped 16px apart on the right -- from three flat siblings, so the
   mobile grid below can re-place them without a second DOM. */
.stf-tally { margin-left: auto; }
.stf-skip { margin-left: 16px; }
.stf-lnk {
  background: none; border: 0; padding: 2px; cursor: pointer;
  font: 600 12.5px/1 var(--font-sans); color: var(--ink, #1f2a30);
}
.stf-lnk[disabled] { color: var(--ink-disabled, #b0a89a); cursor: default; }
.stf-tally { font: 500 12px/1 var(--font-sans); color: var(--ink-3, #8a929a); }
.stf-skip { color: var(--ink-3, #8a929a); }
.stf-skip.is-done { color: var(--green-ink, #1d6b4a); }

/* --- completion --- */
.stf-sum { margin-top: 14px; }
.stf-sumrow {
  display: flex; align-items: baseline; justify-content: space-between; gap: 14px;
  padding: 9px 0; border-bottom: 1px solid var(--line-3, #f0eee7);
}
.stf-sumrow b { font: 500 12.5px/1.4 var(--font-sans); color: var(--ink-2, #5d6b72); }
.stf-sumrow span { font: 600 12.5px/1.4 var(--font-sans); color: var(--ink, #1f2a30); text-align: right; }
.stf-chips { display: none; flex-wrap: wrap; gap: 6px; margin-top: 14px; }
.stf-chip {
  background: var(--green-tint, #e8f1ec); border: 1px solid var(--green-tint-2, #bcd9c8);
  border-radius: 20px; padding: 7px 13px; font: 600 13px/1.2 var(--font-sans); color: var(--green-800, #14502f);
}
.stf-go {
  margin-top: 16px; background: var(--green, #1d6b4a); color: #fff; border: 0; border-radius: 9px;
  padding: 10px 18px; cursor: pointer; font: 600 13px/1.2 var(--font-sans);
}
.stf-go:hover { background: var(--green-700, #16573b); }
.stf-warn {
  color: var(--ink-2, #5d6b72); background: var(--surface-2, #f4f2ec); border-radius: 10px;
  padding: 10px 12px; margin-top: 12px;
}

/* --- 2b: bottom sheet ------------------------------------------------------ */
@media (max-width: 719px) {
  .stf-scrim { align-items: flex-end; padding: 0; }
  .stf-card {
    /* CONSTANT ACROSS QUESTIONS, like the desktop frame. `height: auto` sized
       the sheet to each question's content, so answering one made the whole
       sheet jump taller or shorter under the reader's thumb -- the tallest
       (question one) and the shortest (question three, before a town is typed)
       differ by over a hundred pixels. Derived from the viewport so a short
       phone still shows some board above it, which is the point of a sheet. */
    width: 100%; max-width: none; height: min(640px, calc(100vh - 88px));
    border: 0; border-top: 1px solid var(--green-tint-2, #bcd9c8); border-radius: 20px 20px 0 0;
    box-shadow: 0 -10px 30px rgba(40, 50, 40, .14);
  }
  .stf-rail { display: none; }
  .stf-handle { display: block; padding-top: 9px; }
  .stf-handle i { display: block; width: 38px; height: 4px; border-radius: 2px; background: var(--line-2, #d4d0c6); margin: 0 auto; }
  .stf-shead { display: flex; align-items: center; justify-content: space-between; gap: 12px; padding: 12px 20px 0; }
  .stf-shead .stf-kicker { font-size: 11px; letter-spacing: .08em; color: var(--green-ink, #1d6b4a); }
  .stf-bars { display: flex; gap: 5px; }
  .stf-bar { width: 22px; height: 4px; border-radius: 2px; border: 0; padding: 0; background: var(--line, #e7e4dc); cursor: pointer; }
  .stf-bar.is-done { background: var(--green-tint-2, #bcd9c8); }
  .stf-bar.is-now { background: var(--green, #1d6b4a); }
  .stf-body { padding: 12px 20px 14px; }
  .stf-body .stf-kicker { display: none; }
  .stf-title { font-size: 23px; line-height: 1.25; margin-top: 0; }
  /* The explainer goes behind a disclosure so the options stay above the fold
     on Q1 and Q4, whose explainers are the long ones. */
  .stf-why {
    display: inline-block; background: none; border: 0; padding: 8px 0 0; cursor: pointer;
    font: 600 12.5px/1 var(--font-sans); color: var(--green-ink, #1d6b4a);
  }
  .stf-note {
    display: none; background: var(--surface-2, #f4f2ec); border-radius: 10px;
    padding: 12px 13px; font-size: 13px; line-height: 1.6;
  }
  .stf-note.is-open { display: block; }
  .stf-opts { gap: 7px; }
  .stf-opt { padding: 14px; border-radius: 12px; min-height: 48px; }
  .stf-sugrow { padding: 13px 14px; min-height: 48px; font-size: 14.5px; }
  .stf-dot {
    display: block; flex: 0 0 17px; width: 17px; height: 17px; border-radius: 50%;
    border: 1.5px solid var(--line, #e7e4dc); background: var(--surface, #fff);
  }
  .stf-opt.is-on .stf-dot { border-color: var(--green, #1d6b4a); background: var(--green, #1d6b4a); }
  .stf-optmain b { font-size: 15px; }
  .stf-optmain span { font-size: 12.5px; line-height: 1.4; }
  /* 16px is a hard floor on a text field, not a style choice: iOS Safari zooms
     the whole page in when it focuses an input smaller than this, and the reader
     is then left mid-question on a page they have to pinch back out of. */
  .stf-input { font-size: 16px; }
  .stf-rad { background: var(--surface-2, #f4f2ec); border: 0; border-radius: 11px; padding: 13px 14px 11px; }
  .stf-radhead b { font-size: 13.5px; }
  .stf-radhead span { font-size: 14px; }
  .stf-radends { font-size: 11.5px; }
  /* Back | tally | Skip, as three equal-weight columns: the outer 1fr pair are
     the same width whatever the buttons measure, so the tally is genuinely
     centred rather than merely between them. Each cell centres its own content,
     so a tally that wraps on a narrow phone no longer drags Back out of line
     with Skip. */
  .stf-foot {
    display: grid; grid-template-columns: 1fr auto 1fr; align-items: center;
    gap: 10px; padding: 14px 20px 20px;
  }
  .stf-tally { margin-left: 0; text-align: center; }
  .stf-skip { margin-left: 0; justify-self: end; }
  .stf-foot > .stf-lnk:first-child { justify-self: start; }
  .stf-lnk { font-size: 14px; }
  .stf-tally { font-size: 12.5px; }
  .stf-sum { display: none; }
  .stf-chips { display: flex; }
  .stf-go { width: 100%; border-radius: 11px; padding: 15px 0; font-size: 15px; }
}
@media (min-width: 720px) { .stf-handle, .stf-shead { display: none; } }
@keyframes stf-rise { from { transform: translateY(100%); } to { transform: none; } }
/* Entrance only, and keyed off the SCRIM. render() replaces .stf-card wholesale
   on every answer, back, step-jump and disclosure toggle, so an animation on
   the card replays on each of those -- the sheet slid back down and up again
   just to open "Why we ask". The scrim outlives the rebuilds; setup-flow.js
   removes .is-entering once the slide has run. */
@media (max-width: 719px) {
  .stf-scrim.is-entering .stf-card { animation: stf-rise .22s ease-out; }
}
/* Must match the entrance rule's specificity or it loses to it and the slide
   plays for a reader who asked the OS for no motion. */
@media (prefers-reduced-motion: reduce) {
  .stf-scrim.is-entering .stf-card, .stf-card { animation: none; }
}
