/* ============================================================
   VIBES SHEET — page-type components
   Loads after vibes.css. Same tokens, no new ones except
   the verdict ramp, which is semantic and must survive theming.
   ============================================================ */

/* ---------- BREADCRUMB ---------- */
/* The title block's nav treatment, applied to the generated pages'
   breadcrumb: hairline underlines at the sheet's own scale, not the
   coloured link row it inherited from style.css. Supersedes the three
   .seo-breadcrumb rules still sitting in vibes.css. */

.seo-breadcrumb {
  display: flex; flex-wrap: wrap; align-items: baseline; gap: 4px 12px;
  font-size: 14px; color: var(--muted); margin: 0 0 24px;
}
.seo-breadcrumb a {
  display: inline-flex; align-items: center; min-height: 44px; color: var(--ink);
  text-decoration: underline;
  text-decoration-color: color-mix(in srgb, var(--rule) 26%, transparent);
  text-decoration-thickness: 1px; text-underline-offset: 4px;
}
.seo-breadcrumb a:hover { color: var(--signal); }
.seo-breadcrumb__sep { color: var(--faint); }

/* ---------- VERDICT (BUY / WAIT / SELL) ---------- */
/* Semantic, so it does NOT follow --signal. It has to read the
   same on both domains or the meaning shifts between sites. */

:root {
  --verdict-buy:   #3D6B2E;
  --verdict-wait:  #8A5E0E;
  --verdict-sell:  #A83A19;
}
@media (prefers-color-scheme: dark) {
  :root { --verdict-buy: #8CBB6E; --verdict-wait: #E0AA45; --verdict-sell: #E0703F; }
}

.verdict {
  display: inline-flex;
  align-items: baseline;
  gap: 8px;
  border: 2px solid currentColor;
  padding: 4px 12px;
  font-family: var(--font-data);
  font-size: 14px;
  letter-spacing: 0.1em;
  text-transform: uppercase;
}
.verdict--buy  { color: var(--verdict-buy); }
.verdict--wait { color: var(--verdict-wait); }
.verdict--sell { color: var(--verdict-sell); }
.verdict small { font-size: 11px; letter-spacing: 0.06em; opacity: 0.85; }

/* The verdict is a reading of public data, not advice. Always
   pair it with .verdict-basis in the markup — never let it stand alone. */
.verdict-basis { font-size: 14px; color: var(--muted); margin: 8px 0 0; }

/* The same reading at row scale, for .list__item asides, ledger cells and
   ranked rows. Colour and a hairline underline, never a pill — the coloured
   background it had came from style.css:3871 and _base.html's inline block,
   and both leave in 5.4 / 8.5. Without this rule every signal_badge() call
   on the site renders as unstyled body text, and 8.5's sweep cannot see it. */
.signal-badge {
  font-family: var(--font-data);
  font-size: 12px;
  font-weight: 400;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  white-space: nowrap;
  border-bottom: 2px solid currentColor;
  padding: 0 0 1px;
  border-radius: 0;
}
.signal-badge.signal-buy  { color: var(--verdict-buy);  background: none; }
.signal-badge.signal-wait { color: var(--verdict-wait); background: none; }
.signal-badge.signal-sell { color: var(--verdict-sell); background: none; }

/* ---------- SCORE BAR (vibe score, confidence) ---------- */
/* A surveyor's scale bar, not a donut. Position on a line is the
   most accurately read encoding there is. */

.score { margin: 0 0 4px; }
.score__head { display: flex; justify-content: space-between; align-items: baseline; font-size: 14px; }
.score__val { font-family: var(--font-data); font-size: 15px; color: var(--signal); }
.score__track { position: relative; height: 26px; border-bottom: var(--hair); margin-top: 4px; }
.score__track::before,
.score__track::after {
  content: ""; position: absolute; bottom: 0; width: 1px; height: 7px; background: var(--rule); opacity: 0.5;
}
.score__track::before { left: 0; }
.score__track::after  { right: 0; }
.score__tick {
  position: absolute; bottom: 0; width: 2px; height: 20px; background: var(--signal);
  transform: translateX(-1px);
}
.score__scale { display: flex; justify-content: space-between; font-family: var(--font-data); font-size: 11px; color: var(--faint); margin-top: 4px; }

/* ---------- FACT GRID (replaces the four stat boxes) ---------- */

.facts { display: grid; grid-template-columns: repeat(auto-fit, minmax(160px, 1fr)); border-top: var(--hair); border-left: var(--hair); }
.facts__cell { border-right: var(--hair); border-bottom: var(--hair); padding: 12px 16px; }
.facts__label { font-size: 12px; letter-spacing: 0.07em; text-transform: uppercase; color: var(--muted); margin: 0 0 4px; }
.facts__value { font-family: var(--font-display); font-variation-settings: "wdth" 108; font-weight: 700; font-size: 26px; line-height: 1.1; margin: 0; }
.facts__note  { font-size: 13px; color: var(--muted); margin: 3px 0 0; }
.facts__value--up   { color: var(--verdict-buy); }
.facts__value--down { color: var(--verdict-sell); }

/* ---------- CHAPTER NAV (Vibe / Money / Living / Risks) ---------- */
/* Sticky, hairline, no pills. */

/* The full-bleed margin/padding pair assumed a container with 24px of side
   padding to cancel against. Its parent is an unpadded <main>, so it bled 24px
   past both edges and every one of the 132 suburb pages scrolled the document
   sideways at every width — 1464 against 1440, 414 against 390. The internal
   overflow-x stays: it is how four chapter links fit at 390px. */
.chapters {
  position: sticky; top: 0; z-index: 20;
  display: flex; gap: 0; overflow-x: auto;
  background: var(--paper);
  border-top: var(--hair); border-bottom: var(--hair);
  padding: 0 24px;
  scrollbar-width: none;
}
.chapters::-webkit-scrollbar { display: none; }
.chapters a {
  flex: none; padding: 12px 16px 11px; text-decoration: none;
  font-size: 14px; white-space: nowrap;
  border-bottom: 2px solid transparent; margin-bottom: -1px;
}
.chapters a[aria-current="true"] { border-bottom-color: var(--signal); color: var(--signal); }

/* ---------- MAP ---------- */

.map {
  position: relative;
  aspect-ratio: 4 / 3;
  border: var(--hair);
  background: var(--paper-2);
}
@media (min-width: 900px) { .map { aspect-ratio: 16 / 9; } }

.map__canvas { position: absolute; inset: 0; }

/* MapLibre chrome, de-branded to hairlines */
.maplibregl-ctrl-group { border-radius: 0 !important; box-shadow: none !important; border: var(--hair) !important; background: var(--paper) !important; }
.maplibregl-ctrl-group button { border-radius: 0 !important; }
.maplibregl-ctrl-attrib { background: color-mix(in srgb, var(--paper) 85%, transparent) !important; font-family: var(--font-data); font-size: 11px; }
.maplibregl-popup-content {
  border-radius: 0; box-shadow: none; border: var(--hair);
  background: var(--paper); color: var(--ink);
  padding: 12px 12px; font-family: var(--font-body); font-size: 14px;
}
.maplibregl-popup-tip { display: none; }
.popup__dist { font-family: var(--font-data); font-size: 13px; color: var(--signal); }
.popup__title { font-weight: 500; margin: 2px 0 0; }
.popup__meta { font-size: 13px; color: var(--muted); margin: 4px 0 0; }

/* Map legend sits under the map, never floating over it */
.map-legend { display: flex; flex-wrap: wrap; gap: 8px 24px; padding: 12px 0; border-bottom: var(--hair); font-size: 14px; }
.map-legend span { display: inline-flex; align-items: center; gap: 8px; }
.map-legend i { width: 11px; height: 11px; flex: none; background: var(--signal); font-style: normal; }
/* Shape carries meaning as well as colour — never colour alone. */
.mk-approval      { background: var(--signal); }                                   /* square   */
.mk-contamination { background: var(--verdict-sell); clip-path: polygon(50% 0,100% 100%,0 100%); } /* triangle */
.mk-venue         { background: var(--signal-2); border-radius: 50%; }              /* circle   */
.mk-noise         { background: var(--signal-2); clip-path: polygon(50% 0,100% 50%,50% 100%,0 50%); } /* diamond */
.mk-hazard        { background: var(--muted); clip-path: polygon(50% 0,100% 38%,82% 100%,18% 100%,0 38%); }
.mk-origin        { background: var(--ink); }                                      /* the address itself */

/* ---------- ARTICLE (guides, insights, stories) ---------- */

.article { max-width: 720px; margin: 0 auto; padding: 0 24px; }
.article__head { padding: 48px 0 24px; border-bottom: var(--hair); }
.article__kicker { font-family: var(--font-data); font-size: 12px; letter-spacing: 0.09em; text-transform: uppercase; color: var(--signal); margin: 0 0 12px; }
.article__meta { display: flex; flex-wrap: wrap; gap: 4px 16px; font-family: var(--font-data); font-size: 12px; color: var(--muted); margin: 16px 0 0; }
.article__body { padding: 32px 0; font-size: 17px; line-height: 1.68; }
.article__body > * { max-width: 100%; }
.article__body p  { margin: 0 0 16px; }
.article__body ul, .article__body ol { margin: 0 0 16px; padding-left: 24px; }
.article__body li { margin-bottom: 8px; }
.article__body blockquote { margin: 24px 0; padding-left: 16px; border-left: 2px solid var(--signal); font-size: 19px; line-height: 1.4; }
.article__body a { text-decoration-color: color-mix(in srgb, currentColor 40%, transparent); }

/* Contents — an index to a set of notes, so numbering is honest */
.contents { border-top: var(--hair); border-bottom: var(--hair); padding: 16px 0; margin: 0 0 32px; }
.contents h2 { font-size: 12px !important; font-family: var(--font-data); font-weight: 400; letter-spacing: 0.09em; text-transform: uppercase; color: var(--muted); margin: 0 0 8px; }
.contents ol { list-style: none; margin: 0; padding: 0; counter-reset: c; }
.contents li { counter-increment: c; display: grid; grid-template-columns: 34px 1fr; padding: 4px 0; font-size: 15px; }
.contents li::before { content: counter(c, decimal-leading-zero); font-family: var(--font-data); font-size: 12px; color: var(--faint); padding-top: 3px; }
.contents a { text-decoration: none; border-bottom: var(--hair-soft); }

/* ---------- CARD LIST (insights index, rankings, neighbours) ---------- */
/* Rows, not cards. Dense lists read better ruled than boxed. */

.list { border-top: var(--hair); }
.list__item { display: grid; grid-template-columns: 1fr auto; gap: 12px 16px; padding: 16px 0; border-bottom: var(--hair-soft); align-items: baseline; }
.list__item h3 { margin: 0 0 4px; }
.list__item h3 a { text-decoration: none; }
.list__item p { margin: 0; font-size: 15px; color: var(--muted); max-width: 66ch; }
.list__aside { font-family: var(--font-data); font-size: 13px; color: var(--muted); text-align: right; white-space: nowrap; }
@media (max-width: 620px) {
  .list__item { grid-template-columns: 1fr; }
  /* nowrap keeps "$1.3M · +11.6% YoY" on one line while the aside is its own
     column. Once the row stacks there is no column to protect, and an aside
     carrying prose rather than numbers — the project stage on
     /major-projects/, 60+ characters — held the grid open to 624px and pushed
     the whole page sideways at 390. */
  .list__aside { text-align: left; white-space: normal; }
}

/* ---------- SOURCE LINE ---------- */
/* Every data section ends with one. This is the credibility engine
   of the whole design — never omit it. */

.source {
  display: flex; flex-wrap: wrap; gap: 4px 12px;
  font-family: var(--font-data); font-size: 12px; color: var(--faint);
  padding-top: 8px; margin-top: 16px; border-top: var(--hair-soft);
}
.source a { color: inherit; }

/* ============================================================
   EXPLORATION MODE
   For the reader who has no address yet — moving up from Sydney,
   knows the city, knows no suburb. The data pages assume knowledge
   this person doesn't have. These three components are the way in.
   ============================================================ */

/* ---------- SECOND DOOR ---------- */
/* Sits directly under the address field on the homepage. One line.
   Half the traffic can't answer "what's your address?" — this is
   where they go instead. */

/* Above the fold it is a framed block, not a footnote. §2b called for one line,
   but a line under a search field is what the orienting reader's eye skips —
   and that reader is the half of the traffic who cannot answer the question the
   hero just asked. Framed in hairlines, no fill, no radius: it earns attention
   from weight and position, not decoration. The one-line form survives as
   .door--inline for the footer register. */
.door {
  border: var(--hair); margin-top: 24px; padding: 24px;
}
.door__q {
  font-family: var(--font-display); font-variation-settings: "wdth" var(--wdth-display);
  font-weight: 700; font-size: 22px; line-height: 1.15; margin: 0 0 8px; max-width: 26ch;
}
.door__a { font-size: 16px; color: var(--muted); margin: 0 0 16px; max-width: 48ch; }
.door__link {
  display: inline-flex; align-items: center; min-height: 44px;
  font-family: var(--font-data); font-size: 13px; letter-spacing: 0.08em;
  text-transform: uppercase; color: var(--signal); text-decoration: none;
  border-bottom: 2px solid var(--signal);
}
.door__link:hover { color: var(--ink); border-bottom-color: var(--ink); }

/* The footer repeat. One line, as §2b asked — a second framed block there would
   compete with the one above the fold instead of supporting it. */
.door--inline {
  display: flex; flex-wrap: wrap; align-items: baseline; gap: 4px 12px;
  border: 0; border-top: var(--hair-soft); margin-top: 16px; padding: 12px 0 0;
  font-size: 15px;
}
.door--inline span { color: var(--muted); }
.door--inline a { text-decoration: none; border-bottom: 1px solid var(--signal); color: var(--signal); padding-bottom: 1px; }

/* ---------- LENSES ---------- */
/* Ways into 130 suburbs for someone who can't name one. Real links to
   real pages — no JS filtering, this is a static site. Each lens is a
   question a mover actually asks, not a data facet. */

.lenses { display: grid; grid-template-columns: repeat(auto-fit, minmax(230px, 1fr)); border-top: var(--hair); border-left: var(--hair); }
.lens {
  border-right: var(--hair); border-bottom: var(--hair);
  padding: 16px 16px 16px; text-decoration: none; display: block;
}
.lens:hover { background: var(--paper-2); }
.lens h3 { margin: 0 0 4px; }
.lens p { margin: 0; font-size: 14px; color: var(--muted); max-width: 34ch; }
.lens__count { font-family: var(--font-data); font-size: 12px; color: var(--faint); display: block; margin-top: 8px; }

/* ---------- PLATE ---------- */
/* The only images in the system. Hairline frame, mono caption naming
   the place and the date it was taken. Documentary, not marketing.
   NEVER stock photography — it destroys the one thing this design buys.
   Suburb and walk pages only. Never on a data page. */

.plate { margin: 0 0 24px; }
.plate img { display: block; width: 100%; height: auto; border: var(--hair); }
.plate figcaption {
  display: flex; flex-wrap: wrap; justify-content: space-between; gap: 4px 16px;
  font-family: var(--font-data); font-size: 12px; color: var(--muted);
  padding-top: 8px;
}
.plate--wide { grid-column: 1 / -1; }

/* A strip of three, for a suburb's character section */
.plates { display: grid; grid-template-columns: repeat(auto-fit, minmax(220px, 1fr)); gap: 16px; margin: 0 0 24px; }
.plates .plate { margin: 0; }
.plates figcaption { display: block; }

/* ---------- CHARACTER ---------- */
/* The plain-language read on a place. On the current site this is the
   warmest thing on the page and it sits buried under fifteen data
   sections. It belongs at the top. */

.character { font-size: 19px; line-height: 1.55; max-width: 46ch; margin: 0 0 24px; }
.character strong { font-weight: 500; }

.fit { display: grid; grid-template-columns: repeat(auto-fit, minmax(240px, 1fr)); gap: 0; border-top: var(--hair); border-left: var(--hair); }
.fit__cell { border-right: var(--hair); border-bottom: var(--hair); padding: 16px 16px; }
.fit__cell h4 { font-size: 12px; letter-spacing: .07em; text-transform: uppercase; color: var(--muted); font-weight: 500; margin: 0 0 8px; }
.fit__cell ul { list-style: none; margin: 0; padding: 0; font-size: 15px; }
.fit__cell li { padding: 3px 0 3px 16px; position: relative; }
.fit__cell li::before { content: ""; position: absolute; left: 0; top: 11px; width: 7px; height: 1px; background: var(--muted); }

/* ---------- SUBURB LIST ROW ---------- */
/* The suburbs index is an orientation surface, not a directory.
   Character sentence first, numbers second. */

.suburb-row { display: grid; grid-template-columns: 1fr auto; gap: 8px 24px; padding: 16px 0; border-bottom: var(--hair-soft); align-items: baseline; }
.suburb-row h3 { margin: 0 0 4px; }
.suburb-row h3 a { text-decoration: none; }
.suburb-row p { margin: 0; font-size: 15px; color: var(--muted); max-width: 60ch; }
.suburb-row__nums { font-family: var(--font-data); font-size: 13px; color: var(--muted); text-align: right; white-space: nowrap; }
.suburb-row__nums b { display: block; font-weight: 400; color: var(--ink); font-size: 15px; }
@media (max-width: 620px) {
  .suburb-row { grid-template-columns: 1fr; }
  .suburb-row__nums { text-align: left; }
}

/* ---------- NOTES / FAQ ---------- */
/* Every FAQ on the site, sitewide (PAGE-TYPES §1). A disclosure list on
   hairlines — the rules that styled it lived in _base.html's inline block
   with a coloured summary and a +/− glyph, and left with it in 5.4.
   The markup is faq_section() in _components.html and is not 5.4's to change,
   so the class names stay and take the .notes treatment. */

.faq-section { border-top: var(--hair); margin-top: 48px; }
.faq-section > h2 {
  font-family: var(--font-data); font-size: 12px; font-weight: 400;
  letter-spacing: 0.09em; text-transform: uppercase; color: var(--muted);
  margin: 16px 0 0;
}
.faq-item { border-bottom: var(--hair-soft); }
.faq-item summary {
  padding: 16px 0; cursor: pointer; font-size: 17px; list-style: none;
  display: grid; grid-template-columns: 1fr 20px; gap: 16px; align-items: baseline;
}
.faq-item summary::-webkit-details-marker { display: none; }
.faq-item summary::after {
  content: "+"; font-family: var(--font-data); font-size: 15px;
  color: var(--muted); text-align: right;
}
.faq-item[open] summary::after { content: "\2212"; }
.faq-item summary:hover { color: var(--signal); }
.faq-answer { padding: 0 0 16px; }
.faq-answer p { margin: 0; font-size: 16px; color: var(--muted); max-width: 66ch; }

/* ---------- SUBURB PAGE SCAFFOLDING ---------- */
/* The suburb template's content moved onto the primitives in phase 3, but its
   structural wrappers — .container for width, .section for vertical rhythm —
   were left on style.css, which 5.4 removes. Without these two rules all 132
   suburb pages render full-bleed to the viewport edge.
   They are the .sheet primitive under the names the markup already uses.
   The markup should become .sheet / .block when a task next owns suburb.html;
   107 further classes on that page are still unstyled — see BLOCKED.md. */

.container { width: 100%; max-width: var(--sheet-max); margin: 0 auto; padding: 0 24px; }
.section   { padding: 32px 0; }
.section:first-child { padding-top: 16px; }

/* ---------- SUBURB PAGE: STRUCTURAL RULES style.css USED TO CARRY ---------- */
/* Task 5.4 removed style.css and I recorded the 107 orphaned classes as
   "mostly redundant wrappers … the visual loss is small". That was wrong about
   one of them: .suburb-map carried `height: 400px`, and without it MapLibre
   initialised into a zero-height box on all 132 suburb pages — canvas present,
   markers present, no tiles ever requested, nothing visible. The map looked
   deleted. These are the rules that do structural work, in the sheet
   vocabulary: hairlines, no radius, no shadow. */

.map-container-suburb { position: relative; width: 100%; }
.suburb-map { height: 420px; width: 100%; border: var(--hair); overflow: hidden; }
@media (max-width: 620px) { .suburb-map { height: 300px; } }

.chapter { position: relative; }
.suburb-title-row { display: flex; align-items: baseline; gap: 16px; flex-wrap: wrap; }
.suburb-actions { display: flex; gap: 16px; flex-wrap: wrap; }
.suburb-da-list { display: flex; flex-direction: column; }
.da-item { display: grid; grid-template-columns: 1fr auto; gap: 4px 16px; align-items: baseline; padding: 12px 0; border-bottom: var(--hair-soft); }
.da-item-desc { margin: 0; }
.da-item-meta { font-family: var(--font-data); font-size: 13px; color: var(--muted); white-space: nowrap; }
.spillover-card { padding: 12px 0; border-bottom: var(--hair-soft); }
.spillover-card-header { display: flex; justify-content: space-between; align-items: baseline; gap: 16px; }
.spillover-card-body { display: flex; align-items: baseline; gap: 12px; font-size: 15px; color: var(--muted); }
.section-title { font-family: var(--font-display); font-variation-settings: "wdth" 106; font-weight: 700; }
.section-subtitle { color: var(--muted); font-size: 15px; margin-top: 4px; }

/* ============================================================
   ADDRESS RESULTS (/check.html)
   The commercial page, and the last consumer of style.css. Its
   report is rendered by js/app.js, which emits ~370 class names
   across 50 families — renaming them all would be a rewrite of
   the paid deliverable, so the page's SKELETON moves onto the
   primitives (title block, .field, .facts, .map, .rows, .notes)
   and the interior detail is restyled here in the same
   vocabulary: hairlines, no radius, no shadow, mono for numbers.
   Everything below is scoped under .report-section or an id, so
   none of it can leak onto another page type.
   ============================================================ */

/* ---------- report shell ---------- */
.report-section { padding-bottom: 32px; }
.report-header { padding: 32px 0 16px; border-bottom: var(--hair); }
.report-address { font-family: var(--font-display); font-variation-settings: "wdth" 106; font-weight: 700; font-size: clamp(26px, 4vw, 40px); line-height: 1.08; margin: 0; }
.report-framing, .report-generated, .report-trust-line { font-family: var(--font-data); font-size: 12px; letter-spacing: .06em; text-transform: uppercase; color: var(--muted); margin: 8px 0 0; }
.report-suburb-link { display: inline-block; margin-top: 8px; font-size: 15px; }

/* Each report section is a ruled block, not a card */
.report-section-card { border-bottom: var(--hair); padding: 0; margin: 0; background: none; }
.section-header { display: flex; align-items: baseline; gap: 12px; padding: 24px 0 12px; cursor: pointer; }
.section-title { font-family: var(--font-display); font-variation-settings: "wdth" 106; font-weight: 700; font-size: 22px; margin: 0; display: flex; align-items: baseline; gap: 8px; }
.section-count { font-family: var(--font-data); font-size: 13px; color: var(--muted); font-weight: 400; }
.section-subtitle { font-size: 15px; color: var(--muted); margin: 0 0 12px; }
.section-content { padding-bottom: 24px; }
.section-content.collapsed { display: none; }
.collapse-icon { margin-left: auto; font-family: var(--font-data); font-size: 13px; color: var(--faint); }
.section-alert, .section-recommendation { border-left: 2px solid var(--signal); padding: 8px 0 8px 12px; margin: 12px 0; font-size: 15px; }

/* ---------- findings: the distance-row primitive ---------- */
.item-card, .gated-headline-item, .related-da-card, .hazard-card, .safety-card, .outlook-card, .livability-grade-card, .alert-body {
  display: grid; grid-template-columns: 1fr auto; gap: 4px 16px;
  align-items: baseline; padding: 12px 0; border-bottom: var(--hair-soft);
  background: none; border-radius: 0; box-shadow: none;
}
.item-header, .gated-headline-content, .hazard-header, .safety-header, .outlook-header { display: contents; }
.item-name, .hazard-title, .gated-headline-content strong { font-weight: 500; font-size: 16px; }
.item-distance, .item-badge, .hazard-severity, .gated-headline-meta, .item-details, .item-description, .related-da-meta {
  font-family: var(--font-data); font-size: 13px; color: var(--muted);
}
.item-icon, .hazard-icon, .section-icon, .livability-icon, .traffic-icon, .railway-icon,
.outlook-icon, .timeline-icon, .gated-clear-icon, .hazards-clear-icon, .subsection-icon,
.show-more-icon, .btn-icon, .signal-icon, .cat-icon { display: none; }

/* ---------- stat rows: the fact grid ---------- */
.gated-stat-row, .trajectory-display, .safety-categories, .pulse-categories, .crime-intel-strip {
  display: grid; grid-template-columns: repeat(auto-fit, minmax(150px, 1fr));
  border-top: var(--hair); border-left: var(--hair); margin: 12px 0;
}
.gated-stat, .trajectory-metric, .safety-category, .crime-intel-chip, .pulse-cat-chip {
  border-right: var(--hair); border-bottom: var(--hair); padding: 12px 16px; background: none; border-radius: 0;
}
.gated-stat-value, .trajectory-value, .score-value, .chip-number, .hero-number {
  display: block; font-family: var(--font-display); font-variation-settings: "wdth" 108;
  font-weight: 700; font-size: 24px; line-height: 1.1;
}
.gated-stat-label, .trajectory-label, .score-label, .chip-label, .cat-label {
  display: block; font-size: 12px; letter-spacing: .07em; text-transform: uppercase; color: var(--muted); margin-bottom: 3px;
}

/* ---------- paywall: a ruled row, never a card ---------- */
.gated-more, .gated-report-link, .da-overflow, .show-more-label {
  font-size: 15px; color: var(--muted); margin: 12px 0 0;
}
.gated-clear, .hazards-clear { display: block; font-size: 15px; color: var(--muted); padding: 12px 0; }
.gated-summary { margin: 0; }

/* ---------- the offer ---------- */
.offer-section, .offer-card { border-top: var(--hair); padding: 32px 0 0; margin-top: 32px; background: none; }
.offer-header h3, .offer-header h2 { font-family: var(--font-display); font-variation-settings: "wdth" 106; font-weight: 700; }
.offer-tiers, .offer-features { display: grid; grid-template-columns: repeat(auto-fit, minmax(210px, 1fr)); border-top: var(--hair); border-left: var(--hair); margin: 16px 0; }
.offer-tier { border-right: var(--hair); border-bottom: var(--hair); padding: 16px 16px; background: none; border-radius: 0; }
.offer-btn, .report-sticky-cta-btn, .check-btn, .alert-action-btn, .modal-action-btn {
  border: 0; background: var(--ink); color: var(--paper); border-radius: 0;
  padding: 12px 24px; font-family: var(--font-data); font-size: 13px;
  letter-spacing: .08em; text-transform: uppercase; cursor: pointer; text-decoration: none; display: inline-block;
}
.offer-btn:hover, .report-sticky-cta-btn:hover, .check-btn:hover, .alert-action-btn:hover, .modal-action-btn:hover { background: var(--signal); }
.offer-btn--professional { background: var(--signal); }
.offer-data-line, .offer-anchor-text, .offer-intro-badge { font-family: var(--font-data); font-size: 12px; color: var(--muted); }

/* ---------- sticky purchase bar ---------- */
.report-sticky-cta {
  position: fixed; left: 0; right: 0; bottom: 0; z-index: 30;
  display: flex; align-items: center; justify-content: space-between; gap: 16px;
  background: var(--paper); border-top: var(--hair); padding: 12px 24px;
}
.report-sticky-cta.hidden { display: none; }

/* ---------- modal ---------- */
/* Visibility is driven by .active (js/app.js:3107), NOT by .hidden. Defaulting
   this to display:flex left the item modal open over the address field on every
   load — caught by screenshotting the page, not by reading the diff. */
.modal { position: fixed; inset: 0; z-index: 60; display: none; align-items: center; justify-content: center; padding: 24px; }
.modal.active { display: flex; }
.modal-backdrop { position: absolute; inset: 0; background: color-mix(in srgb, var(--ink) 55%, transparent); }
.modal-content { position: relative; background: var(--paper); border: var(--hair); border-radius: 0; box-shadow: none; max-width: 560px; width: 100%; max-height: 86vh; overflow-y: auto; padding: 24px; }
.modal-header { display: flex; align-items: baseline; justify-content: space-between; gap: 16px; border-bottom: var(--hair); padding-bottom: 12px; margin-bottom: 16px; }
.modal-close {
  border: 0; background: none; font-size: 21px; line-height: 1;
  cursor: pointer; color: var(--muted);
  min-width: 44px; min-height: 44px;  /* touch target, not just a glyph */
}
.modal-actions { display: flex; flex-wrap: wrap; gap: 12px; border-top: var(--hair); padding-top: 16px; margin-top: 16px; }

/* ---------- everything else the report renders ---------- */
.story-block, .crime-intel, .livability-item, .traffic-roads-summary, .pulse-bar-row,
.timeline-event, .developer-info, .insight-details, .vibe-list, .data-sources-summary,
.data-disclaimer-card, .hazards-advice, .signal-item, .alert-header, .subsection-title {
  background: none; border-radius: 0; box-shadow: none;
}
.story-heading, .subsection-title, .alert-header { font-weight: 500; font-size: 16px; margin: 16px 0 4px; }
.story-text, .story-context, .livability-detail, .traffic-text, .railway-text, .hazard-description,
.crime-intel-tip, .pulse-coverage-note, .hazards-clear-note, .data-disclaimer-card, .verdict-disclaimer {
  font-size: 15px; color: var(--muted); max-width: 66ch;
}
.story-signal-positive { color: var(--verdict-buy); }
.story-signal-negative { color: var(--verdict-sell); }
.trust-badge, .status-chip, .signal-tag, .hazard-detail-chip, .traffic-road-tag, .vibe-badge, .pulse-badge {
  font-family: var(--font-data); font-size: 12px; letter-spacing: .06em;
  text-transform: uppercase; color: var(--muted); border-radius: 0; background: none;
}
.trust-dot, .verdict-dot { display: none; }
.pulse-bar-track { height: 3px; background: var(--paper-2); border-radius: 0; }
.pulse-bar-fill { height: 3px; background: var(--signal); border-radius: 0; }
.spinner { display: none; }
.no-data { color: var(--faint); font-size: 15px; }

@media (max-width: 620px) {
  .item-card, .gated-headline-item, .related-da-card, .hazard-card, .safety-card,
  .outlook-card, .livability-grade-card, .alert-body { grid-template-columns: 1fr; }
  .report-sticky-cta { padding: 8px 16px; flex-wrap: wrap; }
}

/* ---------- BACK / SECONDARY LINK ---------- */
/* .btn.btn-secondary survives on the suburb page's footer link and was one of
   the 107 classes orphaned when 5.4 removed style.css. It is a link, not a
   button: hairline underline, ink, and a 44px target. */
.btn, .back-link {
  display: inline-flex; align-items: center; min-height: 44px;
  font-size: 15px; color: var(--ink);
  text-decoration: underline;
  text-decoration-color: color-mix(in srgb, var(--rule) 26%, transparent);
  text-decoration-thickness: 1px; text-underline-offset: 4px;
}
.btn:hover, .back-link:hover { color: var(--signal); }
