/* Market data. Layered over b.css, which owns the shell, nav and type.
   Chart style follows the mockup: thin marks, one axis, direct labels at the
   end of each line, a grid that stays out of the way. */

.market{padding-top:clamp(120px,14vh,190px)}
.market .sec-head{margin-bottom:clamp(30px,4vw,52px)}

.stale{margin:0 0 2rem;padding:.85rem 1.1rem;border-radius:12px;
  background:rgba(228,179,99,.10);border:1px solid rgba(228,179,99,.28);
  color:var(--warm);font-size:.88rem}

/* ------------------------------------------------------------- tiles ---- */
.tiles{display:grid;gap:clamp(12px,1.1vw,18px);margin-bottom:clamp(38px,5vw,68px);
  grid-template-columns:repeat(auto-fit,minmax(min(100%,268px),1fr))}
.tile{position:relative;padding:1.5rem 1.5rem 1.6rem;border-radius:18px;
  background:var(--ink-soft);border:1px solid var(--hair-dark);
  display:flex;flex-direction:column;gap:.2rem;overflow:hidden}
.tile::before{content:"";position:absolute;inset:0 auto 0 0;width:3px;background:var(--c)}
.tile-region{font-size:.74rem;letter-spacing:.12em;text-transform:uppercase;
  color:var(--muted-dark)}
.tile-rent{font-size:clamp(2.1rem,3.4vw,2.9rem);line-height:1.05;color:var(--bone);
  font-variant-numeric:tabular-nums;letter-spacing:-.02em}
.tile-sub{font-size:.82rem;color:var(--muted-dark);margin-bottom:.9rem}
.tile-row{display:flex;flex-wrap:wrap;gap:1.4rem;padding-top:.9rem;
  border-top:1px solid var(--hair-dark)}
.delta,.vs{display:flex;flex-direction:column;font-size:1.02rem;
  font-variant-numeric:tabular-nums;color:var(--bone)}
.delta.up{color:var(--fern-glow)}
.delta.down{color:var(--fall)}
.delta-l{font-size:.7rem;letter-spacing:.06em;text-transform:uppercase;
  color:var(--muted-dark);margin-top:.15rem;text-transform:none;letter-spacing:0}
.tile-yield{display:flex;flex-direction:column;margin-top:.9rem;padding-top:.9rem;
  border-top:1px solid var(--hair-dark);font-variant-numeric:tabular-nums}
.tile-yield > span:first-child{font-size:1.02rem;color:var(--bone)}

/* ------------------------------------------------------------ charts ---- */
.charts{display:grid;gap:clamp(30px,3.6vw,54px)}
.chart{margin:0}
.chart figcaption{margin-bottom:1rem;max-width:62ch}
.chart h3{font-size:clamp(1.15rem,1.7vw,1.45rem);color:var(--bone);margin-bottom:.3rem}
.chart figcaption p{font-size:.88rem;color:var(--muted-dark);line-height:1.55}
.chart-empty{color:var(--muted-dark);padding:2rem 0}
.chart-frame{position:relative;background:var(--ink-soft);border:1px solid var(--hair-dark);
  border-radius:18px;padding:1.1rem .9rem .6rem}
.chart svg{display:block;width:100%;height:auto;overflow:visible}

/* Two drawings, one shown. The phone one is drawn at roughly the size it
   is displayed at, so an 11-unit label is 11 device pixels rather than
   the three it was when a 960-unit chart was squeezed into a 260px card
   (23 Sep 2026). Above phone width there is room for the wide one, which
   carries its labels at the end of each line. */
.chart-narrow{display:none}
@media(max-width:720px){
  .chart-wide{display:none}
  .chart-narrow{display:block}
  /* A phone chart earns a little more height than its aspect ratio asks
     for: vertical space is the one thing a phone has. */
  .chart-narrow{padding:.9rem .7rem .5rem}
}

/* The phone's direct labels, moved under the chart: same colour, same end
   value, and room to read both. */
.chart-legend{list-style:none;margin:.7rem 0 0;padding:0;display:flex;
  flex-wrap:wrap;gap:.35rem .9rem}
.chart-legend li{display:flex;align-items:baseline;gap:.35rem;
  font-size:.9rem;line-height:1.4}
.chart-legend .swatch{width:10px;height:10px;border-radius:3px;
  background:var(--c);flex:none;transform:translateY(1px)}
.chart-legend .who{color:var(--text-soft)}
.chart-legend .val{color:var(--bone);font-weight:600;
  font-variant-numeric:tabular-nums}

:root{
  --chart-grid:var(--line-faint);
  --chart-axis:var(--muted-dark);
  --chart-dot:.9;
}
:root[data-theme="light"]{
  --chart-grid:rgba(17,17,16,.16);
  --chart-axis:#55544B;  /* ~5.6:1 on the cream ground, up from ~4:1 */
  --chart-dot:1;
}

/* Chart chrome reads its own tokens, so light mode can strengthen the parts
   that only work on near-black without touching the page palette. A grid and
   an axis label tuned for a dark ground go nearly invisible on cream: the
   axis text was 11px at ~4:1, and the grid at 7% opacity was a rumour. */
.grid{stroke:var(--chart-grid);stroke-width:1}
.zero{stroke:var(--edge);stroke-width:1;stroke-dasharray:3 3}
.axis-y,.axis-x{fill:var(--chart-axis);font-size:11px;
  font-variant-numeric:tabular-nums}
.axis-x{text-anchor:middle}
.axis-y{text-anchor:end}
.line{fill:none;stroke-width:2;stroke-linecap:round;stroke-linejoin:round;
  vector-effect:non-scaling-stroke}
.line.is-benchmark{stroke-width:1.5;stroke-dasharray:4 4;opacity:.85}
/* Full strength in light: the dash pattern already marks it as context, and
   the extra 15% was the difference between recessive and unreadable. */
:root[data-theme="light"] .line.is-benchmark{opacity:1;stroke-width:1.7}
.direct{font-size:12px;font-weight:600}
/* No opacity here: the 400 weight against the label's 600 is the whole
   hierarchy, and dimming put the US benchmark under 4.5:1 on both grounds. */
.direct-v{font-weight:400;font-variant-numeric:tabular-nums}
.dot{opacity:var(--chart-dot)}

/* Lines FADE in; they are not drawn in.
   The draw-in dashed the path and animated the offset, so for 1.5s every chart
   showed a line that stopped partway across — which on a page of time series
   reads as "the data ends here". It was reported as exactly that twice: a zip
   whose values appeared to stop in 2023 when the series runs to Jul 2026.
   It could not even pay for itself: the animation started at page load, so a
   chart below the fold had finished before the reader ever reached it, and the
   only way to see it was to arrive mid-draw and be misled.
   A fade has no state in which the data looks incomplete.
   NEVER dash a chart line by a constant. If a draw-in ever comes back it has
   to use --len, the real path length measured in market.chart() — a hard-coded
   2400 once rendered the quarterly tax series with a five-year hole. */
.js .line{opacity:0;animation:fade .7s ease forwards;animation-delay:var(--d,0ms)}
.js .line.is-benchmark{opacity:0;animation:fade .9s ease forwards}
.js .dot,.js .direct{opacity:0;animation:fade .5s ease forwards;animation-delay:.5s}
@keyframes fade{to{opacity:1}}
@media(prefers-reduced-motion:reduce){
  .js .line,.js .dot,.js .direct{animation:none;opacity:1}
  .js .line.is-benchmark{opacity:.85}
  :root[data-theme="light"] .js .line.is-benchmark{opacity:1}
}

/* Hover readout */
.cursor{stroke:var(--edge);stroke-width:1}
.hit{cursor:crosshair}
.readout{position:absolute;top:.8rem;left:.8rem;pointer-events:none;z-index:3;
  /* Opaque (7 Oct 2026): at 93% a chart's dark lines showed through the
     box on a light page, crossing the very figures it was printing. */
  background:#0E0E0D;border:1px solid var(--hair-dark);border-radius:12px;
  padding:.6rem .8rem;font-size:.8rem;color:var(--bone);
  /* Hug the content. Without a ceiling, a wide label — or both left and right
     being set at once — turns this into a full-width bar and the values end up
     nowhere near the names they belong to. */
  min-width:170px;max-width:min(20rem,60%);width:max-content;
  box-shadow:0 12px 34px rgba(0,0,0,.45)}
.readout[hidden]{display:none}
.readout-m{color:var(--muted-dark);font-size:.72rem;letter-spacing:.06em;
  text-transform:uppercase;margin-bottom:.4rem;display:block}
.readout-r{display:flex;align-items:center;gap:.5rem;justify-content:space-between;
  font-variant-numeric:tabular-nums;line-height:1.7}
/* The value leads and the label follows (7 Oct 2026): the reader already
   has the series and wants the number. A line's key is a short stroke of
   its colour, as the line is, not a box. */
.readout-r i{width:12px;height:2px;border-radius:1px;flex:0 0 auto}
.readout-r b{font-weight:400;flex:1;font-style:normal;color:rgba(250,247,242,.72)}
.readout-v{font-weight:600}
/* Every chart reads from the keyboard too (7 Oct 2026): the figure takes
   focus and the arrow keys walk its values. */
.chart-frame:focus-visible,.calendar:focus-visible{outline:3px solid var(--fern-glow);outline-offset:3px}
.chart-frame:focus:not(:focus-visible),.calendar:focus:not(:focus-visible){outline:none}

.gap-note{font-size:.85rem;color:var(--muted-dark);line-height:1.6;max-width:70ch;
  padding-left:.9rem;border-left:2px solid var(--hair-dark)}

/* -------------------------------------------------- tax check form ----- */
.tf-cols{display:grid;gap:2.4rem;align-items:start;margin:1.4rem 0 2.2rem}
@media(min-width:1080px){.tf-cols{grid-template-columns:minmax(0,46rem) minmax(17rem,24rem);
  gap:clamp(3rem,6vw,7rem)}}
.tax-form{max-width:46rem}
.tf-aside{border-left:2px solid rgba(143,188,164,.4);padding-left:1.3rem;
  display:grid;gap:.9rem;align-content:start}
.tf-aside h3{font-size:.78rem;letter-spacing:.12em;text-transform:uppercase;
  color:var(--muted-dark)}
.tf-aside p{font-size:.9rem;color:var(--muted-dark);line-height:1.7;max-width:44ch}
.tf-grid{display:grid;gap:1rem;margin-bottom:1.3rem}
@media(min-width:640px){.tf-grid{grid-template-columns:1fr 1fr}}
.tax-form label{display:block;font-size:.86rem;color:var(--text-soft)}
.tax-form input,.tax-form select{display:block;width:100%;margin-top:.35rem;
  font:inherit;font-size:.98rem;color:var(--bone);background:none;
  border:1px solid var(--hair-dark);border-radius:12px;padding:.85rem 1rem}
.tax-form input:focus,.tax-form select:focus{outline:none;
  border-color:var(--fern-glow);box-shadow:0 0 0 3px rgba(143,188,164,.15)}
.tax-form small{display:block;margin-top:.35rem;font-size:.76rem;
  color:var(--muted-dark);line-height:1.5}
.tf-opt{font-size:.7rem;letter-spacing:.08em;text-transform:uppercase;
  color:var(--muted-dark);margin-left:.3rem}
.tax-error{border-left:3px solid var(--fall);padding:.7rem 1rem;margin:1rem 0;
  color:var(--text-soft);background:var(--surface-raise);border-radius:0 10px 10px 0}
.tax-result{margin-bottom:1.6rem}

/* Where your tax dollars go — deliberately the plainest block on the page:
   full sentences, larger type, one two-colour bar. The audience includes
   readers for whom the tiles above are already a lot. */
.tax-split{margin:2rem 0 1.8rem;padding:1.5rem 1.6rem;
  background:var(--surface-raise);border:1px solid var(--hair-dark);
  border-radius:16px;max-width:56rem}
.tax-split h4{font-size:.8rem;letter-spacing:.12em;text-transform:uppercase;
  color:var(--muted-dark);margin-bottom:.8rem}
.ts-lead{font-size:1.02rem;line-height:1.75;color:var(--text-soft);
  max-width:62ch;margin-bottom:1.2rem}
.ts-lead strong{color:var(--bone);font-weight:600;
  font-variant-numeric:tabular-nums}
.ts-bar{display:flex;height:30px;border-radius:9px;overflow:hidden;
  margin-bottom:.75rem}
.ts-seg{display:block;min-width:2%}
.ts-seg.ts-services{flex:1}
.ts-schools{background:#8FBCA4}
.ts-services{background:#7FA8D4}
.ts-legend{display:flex;flex-wrap:wrap;gap:.5rem 1.8rem;font-size:.92rem;
  color:var(--text-soft);margin-bottom:1rem}
.ts-legend strong{color:var(--bone);font-variant-numeric:tabular-nums;
  margin-left:.35rem}
.ts-chip{display:inline-block;width:13px;height:13px;border-radius:4px;
  margin-right:.5rem;vertical-align:-1px}
.ts-note{font-size:.8rem;color:var(--muted-dark);line-height:1.6;max-width:70ch}
.ts-detail{margin-top:1.4rem;padding-top:1.2rem;border-top:1px solid var(--hair-dark)}
.ts-detail h5{font-size:.8rem;letter-spacing:.12em;text-transform:uppercase;
  color:var(--muted-dark);margin-bottom:.7rem}
.ts-row{display:flex;justify-content:space-between;align-items:center;
  gap:1rem;padding:.5rem 0;border-bottom:1px dashed var(--hair-dark);
  max-width:44rem;font-size:1.02rem;min-height:3.1rem}
.ts-row:last-of-type{border-bottom:none}
.ts-cat{color:var(--text-soft)}
.ts-cat em{display:block;font-style:normal;font-size:.8rem;color:var(--muted-dark)}
.ts-row strong{color:var(--bone);font-size:1.1rem;font-weight:600;
  font-variant-numeric:tabular-nums;white-space:nowrap}
/* The amount column carries its own share bar, so eleven rows read as
   proportions rather than a ladder of numbers. min-height on the row keeps
   the rhythm even when only some categories name their authorities. */
.ts-amt{display:flex;align-items:center;gap:.7rem;flex-shrink:0}
/* The amount is a numeric column, not a word. Sized and right-aligned so
   every share bar starts at the same x: the row is space-between, so
   without a fixed width here the bar's left edge tracked the width of the
   figure beside it and the bars read as two ragged groups — the
   four-figure rows and the three-figure ones. 7ch of tabular digits holds
   any category of a residential bill. */
.ts-amt strong{min-width:7ch;text-align:right}
.ts-share{display:block;width:92px;height:6px;border-radius:3px;
  background:var(--hair-dark);overflow:hidden}
.ts-share i{display:block;height:100%;background:var(--fern-light);
  border-radius:3px;min-width:2px}
.ts-levies caption{caption-side:top;text-align:left;font-size:.78rem;
  letter-spacing:.1em;text-transform:uppercase;color:var(--muted-dark);
  padding:.9rem 0 .3rem}
.ts-levies table+table{margin-top:.2rem}
@media(max-width:520px){.ts-share{display:none}.ts-row{min-height:0}}
.ts-levies{margin:.9rem 0 1rem}
.ts-levies summary{cursor:pointer;font-size:.88rem;color:var(--fern-light)}
.ts-levies table{width:100%;max-width:44rem;margin-top:.7rem;
  border-collapse:collapse;font-size:.88rem}
.ts-levies td{padding:.4rem .6rem .4rem 0;border-bottom:1px solid var(--hair-dark);
  color:var(--text-soft);vertical-align:top}
.ts-levies td em{font-style:normal;font-size:.78rem;color:var(--muted-dark);
  display:block}
.ts-levies td:nth-child(2){font-size:.8rem;color:var(--muted-dark)}
.ts-levies td:last-child{text-align:right;color:var(--bone);
  font-variant-numeric:tabular-nums;white-space:nowrap}
.tax-cta{display:grid;gap:.6rem;justify-items:start;margin:0 0 2rem}
/* The Ohio explainer. The only long-form page on the site, so it gets an
   editorial layout rather than the split header the chart pages use: there
   is nothing on the right to balance a headline against, and a stranded one
   reads as a mistake. Header over the column, one measure throughout. */
.essay-head{max-width:44rem;margin:0 0 3rem}
.essay-head h2{font-size:clamp(2.4rem,6vw,4rem);line-height:1.02;
  letter-spacing:-.02em;color:var(--bone);margin:.6rem 0 1.4rem;
  text-wrap:balance}
.essay-lede{font-size:clamp(1.1rem,2.1vw,1.32rem);line-height:1.6;
  color:var(--text-soft);max-width:34rem;margin-bottom:1.2rem}
.essay-meta{font-size:.78rem;letter-spacing:.1em;text-transform:uppercase;
  color:var(--muted-dark)}

.essay{max-width:38rem}
/* b.css gives every <section> the page-level padding-block (72-168px).
   These are sections of an article, not of the page, so that reset has to
   be undone or each one carries 150px of dead space above its rule. */
.essay section{padding:2.4rem 0 0;margin-top:2.4rem;
  border-top:1px solid var(--hair-dark)}
.essay section:first-child{padding-top:0;margin-top:0;border-top:none}
.essay h3{font-size:1.3rem;line-height:1.3;color:var(--bone);
  margin:0 0 .9rem;text-wrap:balance;position:relative;padding-left:2.4rem}
/* The numbers encode a real sequence — the lede promises the mechanism
   "in order", and each section depends on the one before it. */
.essay-num{position:absolute;left:0;top:.1em;font-size:.82rem;
  font-variant-numeric:tabular-nums;color:var(--fern-light);
  letter-spacing:.08em}
.essay p{font-size:1.04rem;line-height:1.75;color:var(--text-soft);
  margin:0 0 1rem}
.essay strong{color:var(--bone);font-weight:600}
.essay em{font-style:italic}
.essay-aside{margin:1.4rem 0 0;padding:1rem 1.2rem;border-radius:12px;
  background:var(--surface-raise);border:1px solid var(--hair-dark);
  font-size:.97rem;line-height:1.7;color:var(--text-soft)}

/* The figure that carries the argument: most districts sit low on this
   chart, where a bill does follow its valuation. */
.essay-fig{margin:1.6rem 0;padding:0}
.essay-fig figcaption{font-size:.78rem;letter-spacing:.1em;
  text-transform:uppercase;color:var(--muted-dark);margin-bottom:.8rem}
.dist{display:grid;gap:.28rem}
.dist-row{display:grid;grid-template-columns:4.6rem 1fr 2.4rem;
  align-items:center;gap:.6rem}
.dist-label{font-size:.8rem;color:var(--muted-dark);
  font-variant-numeric:tabular-nums;text-align:right}
.dist-bar{display:block;height:13px;background:var(--hair-dark);
  border-radius:3px;overflow:hidden}
.dist-bar i{display:block;height:100%;background:var(--fern-light);
  border-radius:3px;min-width:2px;opacity:.55}
.dist-bar i.here{background:#E4B363;opacity:1}
.dist-n{font-size:.78rem;color:var(--muted-dark);
  font-variant-numeric:tabular-nums}
.mill-steps{display:grid;gap:.5rem;max-width:30rem}
.mill-steps div{display:flex;justify-content:space-between;align-items:baseline;
  gap:1rem;padding:.55rem .8rem;border-radius:10px;
  background:var(--surface-raise);border:1px solid var(--hair-dark)}
.mill-steps span{font-size:.95rem;color:var(--text-soft)}
.mill-steps strong{color:var(--bone);font-weight:600;white-space:nowrap;
  font-variant-numeric:tabular-nums}
.essay-caption{font-size:.86rem;line-height:1.65;color:var(--muted-dark);
  margin:.9rem 0 0;max-width:34rem}

.essay-end{display:grid;gap:.7rem;justify-items:start;max-width:38rem;
  margin:3rem 0 0;padding-top:2.4rem;border-top:1px solid var(--hair-dark)}
.tax-cta .gap-note{border:none;padding:0}

/* ------------------------------------------------------- provenance ----- */
.provenance{display:grid;gap:2rem;margin-top:clamp(46px,6vw,80px);
  padding-top:2.2rem;border-top:1px solid var(--hair-dark)}
@media(min-width:860px){.provenance{grid-template-columns:1.4fr .9fr;gap:4rem}}
.provenance p{font-size:.9rem;color:var(--muted-dark);line-height:1.7;max-width:60ch}
.provenance a{color:var(--fern-light)}
.provenance dl{display:grid;gap:.7rem;align-content:start}
.provenance dl > div{display:flex;justify-content:space-between;gap:1rem;
  padding-bottom:.7rem;border-bottom:1px solid var(--hair-dark);font-size:.86rem}
.provenance dt{color:var(--muted-dark)}
.provenance dd{color:var(--bone);font-variant-numeric:tabular-nums}

.empty-state{background:var(--ink-soft);border:1px solid var(--hair-dark);
  border-radius:18px;padding:clamp(1.6rem,3vw,2.6rem);max-width:70ch}
.empty-state h3{color:var(--bone);margin-bottom:.7rem}
.empty-state p{color:var(--muted-dark);line-height:1.7;margin-bottom:.9rem}
.empty-state .cmd code{background:var(--line-faint);padding:.5rem .7rem;
  border-radius:8px;display:inline-block;font-size:.82rem;color:var(--fern-glow)}

/* ------------------------------------------------------ free tier ------- */
.tier-banner{display:grid;gap:1.4rem;align-items:center;margin-bottom:clamp(30px,4vw,52px);
  padding:clamp(1.3rem,2.2vw,1.9rem);border-radius:18px;
  background:linear-gradient(120deg,rgba(47,107,79,.20),rgba(47,107,79,.05));
  border:1px solid rgba(143,188,164,.26)}
@media(min-width:820px){.tier-banner{grid-template-columns:1fr auto;gap:2.4rem}}
.tier-banner h3{color:var(--bone);font-size:1.05rem;margin-bottom:.4rem}
.tier-banner p{color:var(--text-soft);font-size:.9rem;line-height:1.65;max-width:66ch}
.tier-cta{display:inline-flex;align-items:center;justify-content:center;white-space:nowrap;
  border:1px solid rgba(143,188,164,.45);color:var(--fern-glow);border-radius:999px;
  padding:.7rem 1.4rem;font-size:.86rem;font-weight:600;text-decoration:none;
  transition:background .3s var(--easing),border-color .3s var(--easing)}
.tier-cta:hover{background:rgba(143,188,164,.12);border-color:var(--fern-glow);
  text-decoration:none}

/* Sign out. A form, not a link — the route is POST + CSRF, so this must post. */
.signout{margin-top:1rem}
.signout button{background:none;border:1px solid var(--hair-dark);color:var(--muted-dark);
  border-radius:999px;padding:.42rem 1rem;font:inherit;font-size:.76rem;
  letter-spacing:.08em;text-transform:uppercase;cursor:pointer;
  transition:color .3s var(--easing),border-color .3s var(--easing)}
.signout button:hover{color:var(--bone);border-color:var(--text-faint)}

/* ---------------------------------------------------- migrated pages ---- */
.subnav{display:flex;flex-wrap:wrap;gap:.5rem;margin-bottom:clamp(26px,3.4vw,44px)}
.subnav a{border:1px solid var(--hair-dark);border-radius:999px;color:var(--muted-dark);
  padding:.5rem 1.1rem;font-size:.82rem;letter-spacing:.04em;text-decoration:none;
  transition:color .3s var(--easing),border-color .3s var(--easing)}
.subnav a:hover{color:var(--bone);border-color:var(--text-faint);text-decoration:none}
.subnav a[aria-current]{color:var(--ink);background:var(--bone);border-color:var(--bone)}

/* The property-tax section's topic buttons, one level below the row above.

   These were underlined text, because on /market/taxes both navs render and
   two identical pill rows read as one control duplicated rather than as a
   hierarchy. Buttons whose edges a reader can see matter more than that
   (8 Sep 2026 — the topics needed consistent buttons), so the
   hierarchy is carried by shape instead: outlined pill above, filled
   card-edged button here. Six labels wrap to two rows in the 52rem shell,
   which is what a menu of topics should look like anyway — and the tap
   targets grow, which the over-55 audience notices more than either. */
/* The tax section's three doors — Your bill, Your county, Ohio — on every
   tax page (24 Sep 2026). Here and not in taxes.css because two of those
   pages, the unpaid series and How Ohio compares, load only this sheet: the
   doors went live unstyled on both, as three words of plain text. Tokens
   only, so light mode is the page's own inversion. */
.v2-doors{display:flex;gap:.3rem;padding:.3rem;margin:0 0 2.4rem;
  border:1px solid var(--line);border-radius:16px;
  background:var(--surface-raise)}
/* On a phone the doors are the tab bar's places again, and the tab bar
   carries them at the thumb (place-first navigation, 29 Sep 2026). */
@media(max-width:720px){.v2-doors{display:none}}
.v2-doors a{flex:1 1 0;display:flex;align-items:center;justify-content:center;
  min-height:2.9rem;padding:0 .3rem;border-radius:12px;text-align:center;
  font-size:1.05rem;font-weight:500;color:var(--text-soft);white-space:nowrap;
  text-decoration:none}
.v2-doors a:hover{color:var(--text-strong)}
.v2-doors a[aria-current="page"]{background:var(--bone);color:var(--ink);
  font-weight:600}
.v2-doors a:focus-visible{outline:3px solid var(--fern-glow);outline-offset:3px}
@media (max-width:640px){.v2-doors a{font-size:1rem}}
.subnav.taxes-tabs{gap:.5rem}
.subnav.taxes-tabs a{border:1px solid var(--line);border-radius:12px;
  background:var(--surface-raise);color:var(--text-soft);
  padding:.7rem 1.05rem;font-size:1rem;line-height:1.2}
.subnav.taxes-tabs a:hover{color:var(--bone);border-color:var(--edge)}
.subnav.taxes-tabs a[aria-current]{background:var(--bone);color:var(--ink);
  border-color:var(--bone);font-weight:600}
/* The way back. It is the only item that is not a sibling of the others —
   every other page hangs off it — so it wears the section's green rather
   than sitting in the row pretending to be one more topic. */
.subnav.taxes-tabs a.is-home{border-color:var(--fern-light);
  color:var(--fern-light);font-weight:600;background:none}
.subnav.taxes-tabs a.is-home:hover{background:rgba(143,188,164,.13);
  color:var(--fern-light)}
.subnav.taxes-tabs a.is-home[aria-current]{background:var(--fern-light);
  color:var(--ink);border-color:var(--fern-light)}

.map-head{display:flex;flex-wrap:wrap;justify-content:space-between;align-items:center;
  gap:1rem;margin-bottom:1.1rem}
.map-switch{display:flex;gap:.4rem}
.map-switch a{font-size:.78rem;color:var(--muted-dark);border:1px solid var(--hair-dark);
  border-radius:999px;padding:.35rem .85rem;text-decoration:none}
.map-switch a[aria-current]{color:var(--fern-glow);border-color:rgba(143,188,164,.5)}
.map-legend{display:flex;align-items:center;gap:.3rem;font-size:.7rem;color:var(--muted-dark)}
.map-legend i{width:22px;height:10px;border-radius:2px}
.map-legend i[data-bucket="0"]{background:var(--tile-0)}
.map-legend i[data-bucket="1"]{background:var(--tile-1)}
.map-legend i[data-bucket="2"]{background:var(--tile-2)}
.map-legend i[data-bucket="3"]{background:var(--tile-3)}
.map-legend i[data-bucket="4"]{background:var(--tile-4)}
.map-midpoint{font-size:.76rem;color:var(--muted-dark);margin:.45rem 0 0}
.map-midpoint strong{color:var(--text-soft);font-weight:600}

/* ---------------------------- the state map ----------------------------
   Four ramps: two scales x two themes. Every bucket is generated to clear
   3:1 against the page it sits on, and the ramp REVERSES between themes —
   on near-black the expensive states are the pale ones, on cream they are
   the dark ones. That reversal is the whole reason the colours live here
   instead of being baked into the tile: the old build wrote one hex per
   tile server-side, chosen for the dark ground, and on cream the top
   bucket came out at 1.19:1. The most expensive states were invisible.

   The diverging ramp (1-yr change, income share) keeps lightness monotonic
   and lets hue carry the sign. A classic diverging palette peaks in
   lightness at the midpoint, which hands a red-green dichromat two
   different values in one colour — measured at 1.08:1 between buckets 1
   and 3 on a red-to-green ramp. These stay >=1.28:1 across every pair
   under both deuteranopia and protanopia. */
:root{
  --tile-0:#376F52; --tile-1:#448B67; --tile-2:#52A67B;
  --tile-3:#80C09F; --tile-4:#B5DBC8;
  /* Solid, not the .82 the tiles used to use: on a saturated tile that
     alpha lightens the glyph onto the fill and cost ~0.8 of a stop,
     which put two buckets under 4.5:1. */
  --tile-ink:#FAF7F2;              /* bucket 0 only, see below */
  --tile-ink-alt:#111110;
}
[data-scale="diverging"]{
  --tile-0:#376F52; --tile-1:#50896C; --tile-2:#99968F;
  --tile-3:#D4AA96; --tile-4:#EFCBB9;
}
:root[data-theme="light"]{
  --tile-0:#4A956F; --tile-1:#3B7959; --tile-2:#306148;
  --tile-3:#244936; --tile-4:#183024;
  --tile-ink:#111110;
  --tile-ink-alt:#FAF7F2;
}
:root[data-theme="light"] [data-scale="diverging"]{
  --tile-0:#4A956F; --tile-1:#46775E; --tile-2:#595751;
  /* Rust rather than amber: to clear 9:1 on cream the warm arm has to be
     dark, and a dark yellow is olive. A rust hue still reads warm down
     there, at the same colour-blind margin. */
  --tile-3:#623621; --tile-4:#48200C;
}

.tilemap{display:grid;grid-template-columns:repeat(12,1fr);gap:4px;
  max-width:820px;margin-bottom:clamp(34px,4.5vw,56px)}
.tile-state{aspect-ratio:1;border-radius:6px;display:grid;
  place-items:center;transition:transform .25s var(--easing)}
.tile-state:hover{transform:scale(1.12);z-index:1}
.tile-state[data-bucket="0"]{background:var(--tile-0)}
.tile-state[data-bucket="1"]{background:var(--tile-1)}
.tile-state[data-bucket="2"]{background:var(--tile-2)}
.tile-state[data-bucket="3"]{background:var(--tile-3)}
.tile-state[data-bucket="4"]{background:var(--tile-4)}
.tile-state[data-bucket="na"]{background:var(--line)}
/* The abbreviation flips once, at the end of the ramp that meets the page.
   Both themes need it and it used to be keyed off a substring of the inline
   style, so in light mode the dark tiles kept dark labels. */
.tile-state span{font-size:clamp(.5rem,1.1vw,.74rem);font-weight:600;
  color:var(--tile-ink-alt)}
.tile-state[data-bucket="0"] span{color:var(--tile-ink)}

.table-scroll{overflow-x:auto;margin-bottom:1.4rem}
.datatable{width:100%;border-collapse:collapse;font-size:.9rem;
  font-variant-numeric:tabular-nums}
.datatable th{font-size:.7rem;letter-spacing:.1em;text-transform:uppercase;
  color:var(--muted-dark);text-align:left;padding:.6rem .7rem;
  border-bottom:1px solid var(--hair-dark);white-space:nowrap}
.datatable th a{color:inherit;text-decoration:none}
.datatable th a:hover{color:var(--bone)}
/* Sortable headers. The caret is always in the DOM so the header never
   changes width when the sort moves — a row of headings that shifts sideways
   on every click reads as a glitch. Inactive carets are invisible until the
   header is hovered or focused, which keeps the row quiet while still saying
   "these are controls" the moment you reach for one. */
.sort-link{display:inline-flex;align-items:center;gap:.32em;cursor:pointer}
.datatable th.num .sort-link{flex-direction:row-reverse}
.sort-caret{font-size:.9em;line-height:1;opacity:0;transition:opacity .2s}
.sort-link:hover .sort-caret,
.sort-link:focus-visible .sort-caret{opacity:.55}
.sort-link.is-sorted{color:var(--fern-glow)}
.sort-link.is-sorted .sort-caret{opacity:1}
.datatable th[aria-sort]:not([aria-sort="none"]){color:var(--fern-glow)}
.sort-link:focus-visible{outline:2px solid var(--fern-glow);outline-offset:3px;
  border-radius:3px}
.datatable td{padding:.55rem .7rem;border-bottom:1px solid var(--line-faint);
  color:var(--text-soft)}
.datatable td a{color:var(--fern-glow);text-decoration:none}
.datatable td a:hover{text-decoration:underline}
.datatable .num,.datatable th.num{text-align:right}
.datatable .rank{color:var(--muted-dark);font-size:.78rem}
.datatable .up{color:var(--fern-glow)}
.datatable .down{color:var(--fall)}
.datatable .swatch{display:inline-block;width:9px;height:9px;border-radius:2px;
  margin-right:.55rem;vertical-align:baseline}
.datatable .swatch[data-bucket="0"]{background:var(--tile-0)}
.datatable .swatch[data-bucket="1"]{background:var(--tile-1)}
.datatable .swatch[data-bucket="2"]{background:var(--tile-2)}
.datatable .swatch[data-bucket="3"]{background:var(--tile-3)}
.datatable .swatch[data-bucket="4"]{background:var(--tile-4)}
.datatable .swatch[data-bucket="na"]{background:var(--line)}

.zip-search{display:flex;gap:.7rem;max-width:520px;margin-bottom:clamp(26px,3.5vw,44px)}
.zip-search input{flex:1;font:inherit;background:var(--ink-soft);color:var(--bone);
  border:1px solid var(--hair-dark);border-radius:999px;padding:.75rem 1.3rem}
.zip-search input:focus{outline:none;border-color:rgba(143,188,164,.5)}
.browse-head{color:var(--bone);font-size:1.05rem;margin:1.6rem 0 .8rem}
.browse-tabs{display:flex;flex-wrap:wrap;gap:.4rem;margin:.2rem 0 1rem}
.browse-tabs .chip{font-size:.82rem;padding:.4rem 1rem;border-radius:999px;
  background:none;                       /* b.css's hero .chip fills itself */
  border:1px solid var(--hair-dark);color:var(--muted-dark);text-decoration:none}
.browse-tabs .chip:hover{color:var(--bone);border-color:var(--text-faint);text-decoration:none}
.browse-tabs .chip.on{color:var(--ink);background:var(--fern-glow);border-color:var(--fern-glow)}
.browse-note{max-width:70ch;margin:-.4rem 0 1.1rem;font-size:.86rem;
  line-height:1.6;color:var(--muted-dark)}
.market .empty{color:var(--muted-dark);margin-bottom:1.6rem}
.map-hint{font-size:.78rem;color:var(--muted-dark);margin:0}
a.tile-state{text-decoration:none;cursor:pointer}
a.tile-state:hover{outline:2px solid var(--text-faint);outline-offset:1px}
a.tile-state:focus-visible{outline:2px solid var(--fern-glow);outline-offset:2px}
.sec-head .kicker a{color:inherit;text-decoration:none}
.sec-head .kicker a:hover{color:var(--bone)}
.radius-switch{display:flex;gap:.4rem;margin-bottom:.9rem}
.radius-switch a{font-size:.78rem;color:var(--muted-dark);border:1px solid var(--hair-dark);
  border-radius:999px;padding:.32rem .8rem;text-decoration:none}
.radius-switch a[aria-current]{color:var(--fern-glow);border-color:rgba(143,188,164,.5)}
.split-note{font-size:.86rem;color:var(--muted-dark);margin:0 0 1.4rem;
  padding-left:.9rem;border-left:2px solid rgba(228,179,99,.45)}
.split-note a{color:var(--fern-light)}
.split-pct{font-variant-numeric:tabular-nums;color:var(--bone)}

/* Housing-type filter — the same affordance as the map/radius switches. */
.type-filter{display:flex;flex-wrap:wrap;gap:.4rem;margin:-.4rem 0 1.6rem}
.type-filter a{font-size:.78rem;color:var(--muted-dark);border:1px solid var(--hair-dark);
  border-radius:999px;padding:.35rem .95rem;text-decoration:none;
  transition:color .3s var(--easing),border-color .3s var(--easing)}
.type-filter a:hover{color:var(--bone);border-color:var(--text-faint);text-decoration:none}
.type-filter a[aria-current]{color:var(--ink);background:var(--fern-glow);
  border-color:var(--fern-glow)}

/* -------------------------------------------- payment hero (A1) --------- */
.pay-hero{display:grid;gap:clamp(16px,2vw,26px);margin-bottom:clamp(34px,4.5vw,56px)}
@media(min-width:1000px){.pay-hero{grid-template-columns:minmax(320px,.85fr) 1.7fr;align-items:start}}
/* OPAQUE stops, not rgba. The old translucent gradient composited against
   whatever was behind it, so the same card rendered near-black on the dark
   page and a washed pale green on the light one — while its text kept the
   dark-surface tokens and became unreadable. These two hexes are those exact
   stops flattened over the dark page, so dark mode is unchanged and the card
   now stays a dark panel in both themes, which is what its text assumes. */
.pay-card{background:linear-gradient(150deg,#18251E,#191917);
  border:1px solid rgba(143,188,164,.28);border-radius:20px;
  padding:clamp(1.4rem,2.4vw,2rem)}
.pay-kicker{display:block;font-size:.72rem;letter-spacing:.12em;
  text-transform:uppercase;color:var(--fern-glow);margin-bottom:.7rem}
.pay-figure{display:block;font-size:clamp(2.6rem,5vw,3.8rem);line-height:1;
  color:var(--bone);letter-spacing:-.03em;font-variant-numeric:tabular-nums}
.pay-figure em{font-style:normal;font-size:.4em;color:var(--muted-dark)}
.pay-line{margin-top:.9rem;font-size:.95rem;line-height:1.6;color:var(--text-soft)}
/* Rising costs are warm, never green — this is a bill, not a return. */
.pay-up{color:var(--warm)}
.pay-down{color:#8FBCA4}
.pay-note{margin-top:.8rem;font-size:.8rem;color:var(--muted-dark);line-height:1.55}
.pay-hero .chart{margin:0}
.pay-hero .chart figcaption p{max-width:none}

/* --------------------------------------- then-vs-now rows (A2) ---------- */
/* Inside the payment card, under a hairline. Two columns per row: the label
   and change on the top line, the two prices and the date beneath — so three
   horizons stack in a narrow column without any of them wrapping. */
.pay-since{margin-top:clamp(1.1rem,2vw,1.5rem);padding-top:clamp(1rem,1.8vw,1.3rem);
  border-top:1px solid rgba(143,188,164,.22)}
.pay-since-head{display:block;font-size:.68rem;letter-spacing:.12em;
  text-transform:uppercase;color:var(--muted-dark);margin-bottom:.7rem}
.tn-list{display:grid;gap:.75rem}
.tn-row{display:grid;grid-template-columns:1fr auto;gap:.15rem .8rem;
  align-items:baseline}
.tn-label{font-size:.72rem;letter-spacing:.1em;text-transform:uppercase;
  color:var(--muted-dark)}
.tn-delta{font-size:clamp(1.05rem,1.5vw,1.25rem);letter-spacing:-.01em;
  font-variant-numeric:tabular-nums;color:var(--bone);text-align:right;
  white-space:nowrap}
.tn-delta.up{color:var(--warm)}
.tn-delta.down{color:#8FBCA4}
.tn-pct{font-size:.78em;opacity:.75;margin-left:.15em}
.tn-pair{grid-column:1/-1;display:flex;flex-wrap:wrap;gap:.15rem .8rem;
  justify-content:space-between;font-size:.76rem;color:var(--muted-dark);
  font-variant-numeric:tabular-nums}
.tn-prices{white-space:nowrap}
.tn-arrow{margin:0 .35em;color:var(--muted-dark)}
.tn-since{color:var(--muted-dark)}
/* The bare variant: a region with no payment series still gets the rows. */
.pay-hero-bare .pay-card{max-width:34rem}
.tn-doubling{font-size:.88rem;color:var(--text-soft);max-width:70ch;
  margin:-.2rem 0 clamp(30px,4vw,48px);
  padding:.7rem 1rem;border-left:2px solid rgba(228,179,99,.45);
  background:rgba(228,179,99,.06);border-radius:0 10px 10px 0}

/* -------------------------------------------------- A5 takeaways -------- */
.takeaways{margin:-.4rem 0 clamp(26px,3.4vw,42px);display:grid;gap:.45rem}
.takeaways p{font-size:.92rem;line-height:1.55;color:var(--text-soft);
  padding-left:.9rem;border-left:2px solid rgba(143,188,164,.4)}

/* ------------------------------------------- A6 state sparklines -------- */
.sparks{display:grid;gap:8px;margin-top:.9rem;
  grid-template-columns:repeat(auto-fill,minmax(min(46%,150px),1fr))}
.spark{position:relative;display:block;background:var(--ink-soft);
  border:1px solid var(--hair-dark);border-radius:10px;padding:.6rem .7rem .5rem;
  text-decoration:none;transition:border-color .3s var(--easing)}
.spark:hover{border-color:rgba(143,188,164,.5);text-decoration:none}
.spark svg{display:block;width:100%;height:34px}
.spark polyline{stroke:var(--fern-glow);stroke-width:1.4;vector-effect:non-scaling-stroke}
.spark-abbr{position:absolute;top:.45rem;left:.7rem;font-size:.68rem;
  font-weight:700;letter-spacing:.08em;color:var(--muted-dark)}
.spark-val{display:block;margin-top:.3rem;font-size:.8rem;color:var(--bone);
  font-variant-numeric:tabular-nums}
.lead{stroke-width:1;opacity:.45}

/* ------------------------------------------------- A8: the mobile pass --- */
/* Filters become one thumbable scroll row; nothing wraps into a wall. */
@media(max-width:720px){
  .subnav,.type-filter,.map-switch,.radius-switch{
    flex-wrap:nowrap;overflow-x:auto;-webkit-overflow-scrolling:touch;
    scrollbar-width:none;margin-inline:calc(-1 * var(--pad));
    padding-inline:var(--pad)}
  .subnav::-webkit-scrollbar,.type-filter::-webkit-scrollbar,
  .map-switch::-webkit-scrollbar,.radius-switch::-webkit-scrollbar{display:none}
  .subnav a,.type-filter a{white-space:nowrap;padding:.62rem 1.1rem;
    min-height:44px;display:inline-flex;align-items:center}
  .map-switch a,.radius-switch a{white-space:nowrap;padding:.5rem .95rem;
    min-height:40px;display:inline-flex;align-items:center}

  /* Tables keep their columns and scroll sideways — but the row's identity
     (first column) stays pinned so you always know what you're reading. */
  .table-scroll{position:relative}
  .datatable th:first-child,.datatable td:first-child{
    position:sticky;left:0;background:var(--ink);z-index:1}
  .table-scroll::after{content:"";position:absolute;inset:0 0 0 auto;width:28px;
    background:linear-gradient(90deg,transparent,var(--ink));pointer-events:none}

  /* The tile map stays tappable: real tile sizes, sideways scroll. */
  .tilemap{grid-template-columns:repeat(12,minmax(38px,1fr));overflow-x:auto;
    -webkit-overflow-scrolling:touch;margin-inline:calc(-1 * var(--pad));
    padding-inline:var(--pad);max-width:none}

  /* Search stays under the thumb while results scroll. */
  .zip-search{position:sticky;top:12px;z-index:5}
  .zip-search input{background:var(--surface-raise);backdrop-filter:blur(10px)}

  .pay-hero{gap:14px}
}

/* ---------------------------------------- the tax section's quiet chrome */
/* No brand, no rentals nav: a muted section label on the left, the theme
   toggle on the right, and a one-line footer that credits the data rather
   than the company. The label is a <span>, not a link — there is no brand
   home to send anyone to, and a dead-looking link would be worse. */
.tax-nav{justify-content:space-between}
/* Colour by inheritance + opacity, the way .nav-links does: the pill's bg
   is --text-soft, which flips ink<->bone with the theme, and the text that
   sits on it flips with the body. A surface token here (--muted-dark) was
   dark-on-dark the moment light mode flipped the pill. */
.tax-nav-name{font-size:.82rem;letter-spacing:.14em;text-transform:uppercase;
  color:inherit;opacity:.62}
.tax-foot{padding:1.6rem 0 2.2rem}
.tax-foot .foot-base{border-top:0;margin-top:0;padding-top:0}
.tax-foot .foot-base span{font-size:.82rem;line-height:1.6;color:var(--muted-dark);
  max-width:66ch;display:block}
.tax-foot-base{display:flex;flex-wrap:wrap;gap:1rem 2rem;align-items:baseline;
  justify-content:space-between}
.tax-foot-base nav{display:flex;gap:1.2rem}
.tax-foot-base nav a{font-size:.82rem;color:var(--muted-dark);
  text-decoration:underline;text-underline-offset:3px}
.tax-foot-base nav a:hover{color:var(--bone)}

/* --------------------------------------------- A3: rent vs buy ---------- */
.rent-buy{margin-bottom:clamp(30px,4vw,48px)}
.rb-verdict{font-size:clamp(1rem,1.4vw,1.15rem);line-height:1.55;color:var(--text-soft);
  max-width:64ch;margin-bottom:1rem;padding-left:.9rem;
  border-left:2px solid var(--warm)}

/* The arithmetic under the verdict. Quieter than the claim it explains, but
   still body-sized: it carries the numbers a reader would otherwise have had
   to tick two boxes to see, and this audience should not have to lean in. */
.rb-breakdown{font-size:clamp(.95rem,1.2vw,1.02rem);line-height:1.6;
  color:var(--muted-dark);max-width:64ch;margin:-.4rem 0 1.2rem;
  padding-left:.9rem}
.rb-notes{margin-top:1rem;max-width:70ch;display:grid;gap:.5rem;
  font-size:.84rem;line-height:1.55;color:var(--muted-dark)}
.rb-notes strong{color:var(--text-soft);font-weight:500}
.rb-caveat{padding-top:.35rem;border-top:1px solid var(--line-faint)}
/* -------------------------------------------- B1/B2: market heat -------- */
.heat-verdict{margin:-.4rem 0 clamp(26px,3.4vw,42px);border-radius:16px;
  padding:1.1rem 1.3rem;font-size:clamp(1.02rem,1.5vw,1.2rem);line-height:1.55}
.heat-verdict p{color:var(--text-strong)}
.heat-cooling{background:rgba(46,94,150,.14);border:1px solid rgba(127,168,212,.35)}
.heat-heating{background:rgba(228,179,99,.12);border:1px solid rgba(228,179,99,.4)}
.heat-balanced{background:var(--ink-soft);border:1px solid var(--hair-dark)}

.calendar{margin-bottom:clamp(30px,4vw,48px);max-width:640px}
.cal-head,.cal-row{display:grid;grid-template-columns:3.2rem repeat(12,1fr);
  gap:3px;align-items:center}
.cal-head span{font-size:.62rem;text-align:center;color:var(--muted-dark);
  letter-spacing:.05em}
.cal-year{font-size:.72rem;color:var(--muted-dark);font-variant-numeric:tabular-nums}
.cal-row{margin-top:3px}
.cal-cell{aspect-ratio:1;border-radius:3px;min-width:0}
/* The month a tap or the arrow keys picked, and its figure under the grid,
   as the maps show a county's (7 Oct 2026). */
.calendar .cal-cell[title]{cursor:pointer}
.cal-cell.is-picked{outline:2px solid var(--text-strong);outline-offset:1px}
.cal-readout{margin:.8rem 0 0;font-size:1rem;line-height:1.5;color:var(--text-strong)}
.cal-readout:empty{display:none}
.cal-readout.on{padding:.6rem .85rem;border:1px solid var(--fern-light);border-radius:10px;
  background:rgba(143,188,164,.09)}
.cal-empty{background:var(--line-faint)}
.cal-legend{display:flex;flex-wrap:wrap;gap:.9rem;margin-top:.8rem;
  font-size:.72rem;color:var(--muted-dark)}
.cal-legend i{display:inline-block;width:10px;height:10px;border-radius:2px;
  margin-right:.35rem;vertical-align:-1px}
.forecast-note{margin:-.4rem 0 1.4rem;font-size:.9rem;line-height:1.6;
  color:var(--text-soft);padding-left:.9rem;border-left:2px solid var(--line)}
.mv-state{font-size:.72rem;color:var(--muted-dark);margin-left:.4rem}
.tile-rent em{font-style:normal;font-size:.45em;color:var(--muted-dark)}

/* ------------------------- chart controls (TD-1) ------------------------
   Real checkboxes, because these are additive series toggles and that is
   what a checkbox means. The custom box is drawn on ::before so the control
   inherits the theme tokens; the native input stays in the DOM and keeps
   keyboard behaviour, focus and screen-reader semantics rather than being
   replaced by a styled <a>. */
.chart-controls{display:flex;flex-wrap:wrap;align-items:flex-start;
  gap:1.2rem 2.2rem;margin:-.4rem 0 1.6rem}
.chart-controls fieldset{border:0;padding:0;margin:0;min-width:0}
.chart-controls legend{padding:0;font-size:.7rem;letter-spacing:.12em;
  text-transform:uppercase;color:var(--muted-dark);margin-bottom:.55rem}
.chart-controls .check{display:flex;align-items:flex-start;gap:.5rem;
  margin-bottom:.35rem;max-width:32rem}
.chart-controls input[type=checkbox]{
  appearance:none;-webkit-appearance:none;flex:0 0 auto;margin:.15rem 0 0;
  width:1rem;height:1rem;border:1px solid var(--text-faint);border-radius:3px;
  background:transparent;cursor:pointer;position:relative;
  transition:background .2s var(--easing),border-color .2s var(--easing)}
.chart-controls input[type=checkbox]:checked{
  background:var(--fern-glow);border-color:var(--fern-glow)}
/* The tick is drawn, not a glyph: a font fallback that lacks the character
   would leave a checked box looking empty. */
.chart-controls input[type=checkbox]:checked::after{
  content:"";position:absolute;left:4px;top:1px;width:4px;height:8px;
  border:solid var(--ink);border-width:0 2px 2px 0;transform:rotate(42deg)}
.chart-controls input[type=checkbox]:focus-visible{
  outline:2px solid var(--fern-glow);outline-offset:2px}
.chart-controls label{font-size:.84rem;line-height:1.45;color:var(--text-soft);
  cursor:pointer}
.chart-controls input[type=checkbox]:checked+label{color:var(--bone)}
.btn-apply{align-self:center;font:inherit;font-size:.78rem;
  letter-spacing:.04em;color:var(--ink);background:var(--bone);border:0;
  border-radius:999px;padding:.45rem 1.1rem;cursor:pointer}
/* With scripting the form submits on change, so the button is redundant —
   and a control that never needs pressing is noise. */
.js .btn-apply{display:none}
/* Touch targets. The label is the clickable partner of the box, so the row
   is what has to be thumb-sized, not the 16px box — comfortably under the
   44px guideline before this. */
@media(max-width:640px){
  .chart-controls{gap:1rem 1.6rem}
  .chart-controls .check{margin-bottom:0}
  .chart-controls label{padding:.78rem 0;min-height:1.6rem}
  .chart-controls input[type=checkbox]{margin-top:.92rem}
}

/* --------------------------- county picker ------------------------------
   Full-width row under the two checkbox groups. 88 counties is too many for
   checkboxes and a native multi-select is a ctrl-click puzzle on desktop and
   unusable on a phone, so: pick one at a time, each becomes a removable
   chip, and the set lives in the URL so it can be shared. */
.chart-controls .county-pick{flex:1 0 100%;border-top:1px solid var(--hair-dark);
  padding-top:1rem;margin-top:.2rem}
.chips{list-style:none;display:flex;flex-wrap:wrap;gap:.4rem;
  margin:0 0 .7rem;padding:0}
.chips li{display:flex;align-items:center;gap:.45rem;font-size:.8rem;
  color:var(--ink);background:var(--fern-glow);border-radius:999px;
  padding:.25rem .35rem .25rem .8rem}
.chips a{color:inherit;text-decoration:none;line-height:1;font-size:1rem;
  width:1.35rem;height:1.35rem;display:flex;align-items:center;
  justify-content:center;border-radius:50%;opacity:.65}
.chips a:hover{opacity:1;background:rgba(0,0,0,.14);text-decoration:none}
.chips .chip-clear{background:transparent;padding:.25rem .2rem}
.chips .chip-clear a{width:auto;height:auto;font-size:.78rem;opacity:1;
  color:var(--muted-dark);text-decoration:underline}
.county-add{display:flex;flex-wrap:wrap;gap:.5rem;align-items:center}
.county-add select{font:inherit;font-size:.82rem;color:var(--text-soft);
  background:transparent;border:1px solid var(--hair-dark);border-radius:6px;
  padding:.42rem 2rem .42rem .7rem;min-width:13rem;cursor:pointer;
  appearance:none;-webkit-appearance:none;
  /* Chevron drawn as a data URI so the control needs no image request and
     inherits nothing that could go stale behind the asset cache. */
  background-image:url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 10 6'%3E%3Cpath fill='none' stroke='%238a8a80' stroke-width='1.4' d='M1 1l4 4 4-4'/%3E%3C/svg%3E");
  background-repeat:no-repeat;background-position:right .7rem center;
  background-size:.6rem}
.county-add select:focus-visible{outline:2px solid var(--fern-glow);
  outline-offset:2px}
/* The options list is painted by the OS, which does not read our tokens —
   without this it renders white-on-white in dark mode on some platforms. */
.county-add select option{color:#1D1D1A;background:#F4F1EA}
.js .btn-add{display:none}
.county-note{font-size:.76rem;color:var(--muted-dark);margin:.6rem 0 0}
@media(max-width:640px){
  .county-add select{flex:1 1 100%;min-width:0}
  /* The select auto-submits on desktop, but a phone's picker fires change
     as the wheel moves on some platforms, so the explicit Add stays. */
  .js .btn-add{display:inline-block}
  /* Thumb-sized remove targets: the × was 21px inside a 30px chip. */
  .chips{gap:.5rem}
  .chips li{padding:.4rem .4rem .4rem .95rem;font-size:.85rem}
  .chips a{width:1.9rem;height:1.9rem;font-size:1.15rem}
}


/* ------------------------- saved-analysis page --------------------------
   The share row is plain .btn pills; only the layout is new. Print rules
   mirror the v2 pages': a saved analysis exists to be handed to someone,
   often on paper. */
.post-suggest{border:1px solid var(--line);border-radius:16px;
  background:var(--surface-raise);padding:1.4rem 1.6rem;margin:2.2rem 0 1rem}
.post-suggest h3{font-size:1.1rem;color:var(--text-strong);margin:0 0 .7rem}
/* pre-line keeps the composed line breaks so what you copy is what you
   read, at a size the 55+ audience can actually proof before posting.
   anywhere is what lets the share links wrap: a permalink is one unbroken
   token with no space to break at, so on a phone it ran straight out of
   the card and off the screen. */
.post-suggest p#post-text{white-space:pre-line;overflow-wrap:anywhere;
  font-size:1.02rem;
  line-height:1.65;color:var(--text-soft);margin:0 0 .8rem;
  user-select:all;-webkit-user-select:all}
.post-suggest .gap-note{margin:0}
.share-actions{display:flex;flex-wrap:wrap;gap:.7rem;margin:1.2rem 0 1rem}
.share-cta{margin-top:2.4rem}
@media print{
  .share-page .nav-wrap,.share-page ~ * .progress,.progress,
  .share-actions,.share-cta,footer,.foot{display:none !important}
  .share-page.on-dark{background:#fff !important;color:#111 !important;
    padding-top:0 !important}
  .share-page *{color:#111 !important}
  .share-page .tile{border-color:#bbb !important;background:#fff !important;
    color:#111 !important;break-inside:avoid}
}

/* ---------------------------- the chip rows on phones ------------------- */
/* Eight topics, the market section's six filters and the check tool's
   filter rows all scroll
   sideways on a phone, and the only sign that more existed was whatever sliver of the third button happened to poke past
   the edge — two pixels, in the screenshot that raised it (Property Tax
   Report, 22 Sep 2026). Apple's rows say "more" three ways, and so does
   this one now: the next button is always visibly cut off (a chip may not
   be wider than 70% of the screen, and the row ends in padding rather
   than flush), the trailing edge fades while there is more to scroll
   (b.js toggles .more/.before from the scroll position), and the row
   snaps to a button when it stops, so it never rests on half a word. b.js
   also opens the row scrolled to the current page's button. A segmented
   control or a tab bar would be wrong here: the guidelines cap both at
   about five items, and the burger menu already lists all eight. */
@media(max-width:720px){
  .subnav,.type-filter,.map-switch,.radius-switch{scroll-snap-type:x proximity;scroll-padding-inline:var(--pad);
    padding-inline-end:calc(var(--pad) + 2.5rem);
    -webkit-mask-image:none;mask-image:none}
  .subnav a,.type-filter a,.map-switch a,.radius-switch a{flex:0 0 auto;max-width:70vw;scroll-snap-align:start;
    white-space:normal;line-height:1.2}
  .subnav.more,.type-filter.more,.map-switch.more,.radius-switch.more{
    -webkit-mask-image:linear-gradient(to right,#000 calc(100% - 2.5rem),transparent);
    mask-image:linear-gradient(to right,#000 calc(100% - 2.5rem),transparent)}
  .subnav.before,.type-filter.before,.map-switch.before,.radius-switch.before{
    -webkit-mask-image:linear-gradient(to right,transparent,#000 var(--pad));
    mask-image:linear-gradient(to right,transparent,#000 var(--pad))}
  .subnav.before.more,.type-filter.before.more,.map-switch.before.more,.radius-switch.before.more{
    -webkit-mask-image:linear-gradient(to right,transparent,#000 var(--pad),#000 calc(100% - 2.5rem),transparent);
    mask-image:linear-gradient(to right,transparent,#000 var(--pad),#000 calc(100% - 2.5rem),transparent)}
}

/* Ohio's place among the states, first under the ranking's heading, and
   its row marked in the table (8 Oct 2026: "How does Ohio compare with
   other states?" lands on the ranking). */
.ranking-answer{font-size:1.08rem;line-height:1.55;color:var(--text-strong);max-width:62ch;margin:.2rem 0 1rem}
.ranking-answer strong{color:var(--text-strong)}
.datatable tr.is-here td{background:rgba(127,168,140,.16);font-weight:600}
.datatable tr.is-here td:first-child{box-shadow:inset 3px 0 0 var(--fern-light)}

/* An Ohio town's property tax and schools, under its heading (8 Oct 2026). */
.town-tax{margin:0 0 1.6rem;padding:.9rem 1.1rem;border-left:3px solid var(--fern-light);
  background:rgba(127,168,140,.08);border-radius:0 10px 10px 0;max-width:72ch}
.town-tax h3{margin:0 0 .35rem;font-size:1.02rem;color:var(--text-strong)}
.town-tax p{margin:0;line-height:1.6;color:var(--text-soft)}

/* ------------------------------------------- living in (22 Oct 2026) ---- */
/* The "Living in" section of a town page (docs/LIVING-IN-SCOPE.md §5):
   the page's own tiles and chart frames, a band of smaller figures, the
   industries as bars, and the weather as one rounded bar a month. Colours
   are tokens, so the chart follows the theme without the attribute remap. */
.living{margin:0 0 clamp(38px,5vw,68px)}
.living-head{font-size:clamp(1.3rem,2vw,1.6rem);margin-top:0}
.living-summary{font-size:1.02rem;line-height:1.7;color:var(--text-soft);max-width:72ch;
  margin:0 0 1.6rem}
.living-tiles{margin-bottom:1.2rem}
.living-tiles .tile-rent{font-size:clamp(1.9rem,3vw,2.5rem)}
.living-tiles .tile-sub{margin-bottom:0;line-height:1.5}
.living-band{list-style:none;margin:0 0 clamp(26px,3vw,40px);padding:1rem 0;
  display:flex;flex-wrap:wrap;gap:.8rem 2.2rem;border-top:1px solid var(--hair-dark);
  border-bottom:1px solid var(--hair-dark)}
.living-band li{font-size:.92rem;color:var(--muted-dark)}
.living-band b{font-size:1.15rem;font-weight:500;color:var(--bone);margin-right:.4rem;
  font-variant-numeric:tabular-nums}
.living-cols{display:grid;gap:clamp(28px,3vw,44px);margin-bottom:1.4rem}
@media(min-width:1000px){.living-cols{grid-template-columns:minmax(0,1fr) minmax(0,1.25fr);
  align-items:start}}
.living h4{color:var(--bone);font-size:1.05rem;margin:0 0 .9rem}
.living-bars{list-style:none;margin:0;padding:0;display:grid;gap:.65rem}
.living-bars li{display:grid;grid-template-columns:minmax(0,10.5rem) 1fr 2.8rem;gap:.8rem;
  align-items:center;font-size:.92rem;line-height:1.3;color:var(--text-soft)}
.living-track{height:.65rem;border-radius:999px;background:var(--line-faint);overflow:hidden}
.living-track i{display:block;height:100%;border-radius:999px;background:var(--fern-light)}
.living-pct{text-align:right;color:var(--bone);font-variant-numeric:tabular-nums}
.living-work .gap-note{margin-top:1.2rem}
.living-weather{margin:0}
.wx .wx-grid{stroke:var(--hair-dark);stroke-width:1}
.wx .wx-freeze{stroke:var(--muted-dark);stroke-width:1;stroke-dasharray:3 4}
.wx .wx-axis,.wx .wx-month{fill:var(--muted-dark);font-size:13px}
.wx .wx-range{fill:var(--fern-light);opacity:.85}
.wx .wx-val{fill:var(--bone);font-size:14px;font-weight:600}
.wx .wx-low{fill:var(--text-soft)}
.living-wx-line{margin:.9rem 0 .4rem;color:var(--text-soft);line-height:1.6}
.living-sources a{color:var(--fern-glow)}
.living-employers,.living-hazards{margin:0 0 1.4rem}
.living-emp{list-style:none;margin:0;padding:0;display:grid;column-gap:2.6rem}
@media(min-width:760px){.living-emp{grid-template-columns:1fr 1fr;grid-auto-flow:column;grid-template-rows:repeat(4,auto)}}
.living-emp li{display:flex;justify-content:space-between;align-items:baseline;gap:1rem;
  padding:.6rem 0;border-bottom:1px solid var(--line-faint)}
.le-name{color:var(--bone);line-height:1.35;min-width:0}
.le-name small{display:block;font-size:.8rem;color:var(--muted-dark);margin-top:.15rem}
.le-staff{font-variant-numeric:tabular-nums;color:var(--bone);white-space:nowrap}
.le-staff abbr{font-size:.7rem;color:var(--muted-dark);text-decoration:none;margin-left:.3rem;letter-spacing:.04em}
.living-employers .gap-note,.living-hazards .gap-note{margin-top:.9rem}
.living-ai{margin:0 0 clamp(26px,3vw,40px);padding:clamp(1.2rem,2.2vw,1.8rem);
  border:1px solid var(--hair-dark);border-radius:18px;background:var(--ink-soft);max-width:78ch}
.living-ai-label{font-size:.88rem;color:var(--muted-dark);line-height:1.55;margin:0 0 .9rem}
.living-ai-chip{display:inline-block;font-size:.72rem;font-weight:600;letter-spacing:.08em;
  text-transform:uppercase;color:var(--ink);background:var(--warm);border-radius:999px;
  padding:.12rem .6rem;margin-right:.4rem}
.living-ai p{color:var(--text-soft);line-height:1.7;margin:0 0 .8rem}
.living-ai p strong{color:var(--bone);font-weight:600}
.living-report{margin-top:.6rem;font-size:.92rem}
.living-report summary{cursor:pointer;color:var(--fern-glow);min-height:44px;display:flex;align-items:center}
.living-report form{display:grid;gap:.5rem;max-width:36rem;margin-top:.4rem}
.living-report textarea{font:inherit;padding:.6rem .7rem;border-radius:10px;border:1px solid var(--hair-dark);
  background:var(--ink);color:var(--text-strong)}
.living-report button{justify-self:start}
.living-report-state{color:var(--text-strong)}
@media(max-width:520px){
  .living-bars li{grid-template-columns:minmax(0,8.5rem) 1fr 2.6rem;font-size:.88rem}
  .living-band{gap:.6rem 1.4rem}
}
