/* ==========================================================================
   BOLO Texas — components
   Requires tokens.css and base.css. Class names are BEM-ish:
   .block, .block__element, .block--modifier, plus .is-state for JS states.
   Each block starts with a comment showing the expected markup.
   Full class reference: docs/DESIGN-SYSTEM.md

   Contents
     1. Buttons and icon buttons     9. Forms
     2. Chips and badges            10. Tabs and stepper
     3. Site header                 11. Drawer, chat, modal, toast
     4. City / zone picker          12. Price, scoreboard, map
     5. Bottom tab bar              13. Footer
     6. Cards                       14. Shared fixes and small modifiers
     7. Rail and story tiles        15. Page furniture (breadcrumbs, page hero,
     8. Ad slots                        section nav, CTA panel, empty state)
                                    16. Content blocks (callout, alert bar, box,
                                        accordion, avatar, lists, table, tiles)
                                    17. Listing and media pieces (gallery,
                                        sticky actions, chat, upload, range)
                                    18. Map additions
   ========================================================================== */

/* ==========================================================================
   1. BUTTONS
   <a class="btn btn--primary" href>Advertise</a>
   <button class="btn btn--ghost btn--small">Save</button>
   Modifiers: --primary (orange CTA), --ghost (outline), --ink (navy solid),
   --small, --block (full width). Default is a quiet filled button.
   <button class="icon-btn" aria-label="Search"><svg…></button> is a 40px
   square icon-only control.
   ========================================================================== */
.btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: var(--sp-2);
  min-height: 44px;
  padding: 0 var(--sp-5);
  background: var(--surface-2);
  color: var(--ink);
  border: 1px solid transparent;
  border-radius: var(--r-pill);
  font-size: var(--fs-sm);
  font-weight: 600;
  line-height: 1.2;
  text-align: center;
  white-space: nowrap;
  cursor: pointer;
  transition: background var(--dur-fast), border-color var(--dur-fast), transform var(--dur-fast);
}

.btn:hover { color: var(--ink); background: var(--line); }
.btn:active { transform: scale(0.98); }
.btn[disabled], .btn[aria-disabled="true"] { opacity: 0.5; pointer-events: none; }
.btn svg { width: 1.1em; height: 1.1em; flex: none; }

.btn--primary { background: var(--accent); color: var(--accent-ink); }
.btn--primary:hover { background: color-mix(in srgb, var(--accent) 86%, #000); color: var(--accent-ink); }
.btn--ink { background: var(--ink); color: var(--bg); }
.btn--ink:hover { background: var(--ink-2); color: var(--bg); }
.btn--ghost { background: transparent; border-color: var(--line-strong); }
.btn--ghost:hover { background: var(--surface-2); border-color: var(--ink); }
.btn--small { min-height: 34px; padding: 0 var(--sp-3); font-size: var(--fs-xs); }
.btn--block { display: flex; width: 100%; }

.icon-btn {
  display: inline-grid;
  place-items: center;
  flex: none;
  width: 40px;
  height: 40px;
  border-radius: var(--r-pill);
  color: var(--ink);
  transition: background var(--dur-fast);
}

.icon-btn:hover { background: var(--surface-2); color: var(--ink); }
.icon-btn svg { width: 20px; height: 20px; }
.icon-btn--solid { background: var(--surface); border: 1px solid var(--line); box-shadow: var(--shadow-sm); }

/* ==========================================================================
   2. CHIPS AND BADGES
   Chip: tappable filter or live-data pill.
     <button class="chip is-active">Furniture</button>
     <span class="chip chip--data"><svg…> <strong>94°</strong> Dallas</span>
   Modifiers: --data (non-interactive live number), --live (orange dot),
   --ok, --alert. State: .is-active or [aria-pressed="true"].
   .chip-row makes a single-line, horizontally scrolling row of chips.

   Badge: small status label, usually over a card image.
     <span class="badge badge--live">Live</span>
   Modifiers: --live (pulsing dot), --ok, --alert, --sponsored, --ink.
   ========================================================================== */
.chip {
  display: inline-flex;
  align-items: center;
  gap: var(--sp-2);
  min-height: 36px;
  padding: 0 var(--sp-3);
  background: var(--surface);
  color: var(--ink);
  border: 1px solid var(--line);
  border-radius: var(--r-pill);
  font-size: var(--fs-sm);
  font-weight: 500;
  white-space: nowrap;
  transition: background var(--dur-fast), border-color var(--dur-fast);
}

.chip svg { width: 16px; height: 16px; flex: none; }
button.chip:hover, a.chip:hover { border-color: var(--ink); color: var(--ink); }
.chip.is-active, .chip[aria-pressed="true"], .chip[aria-current="true"] { background: var(--ink); border-color: var(--ink); color: var(--bg); }
.chip--data { background: var(--surface-2); border-color: transparent; font-variant-numeric: tabular-nums; }
.chip--data strong { font-family: var(--font-mono); font-weight: 700; }
.chip--ok { background: var(--ok-soft); border-color: transparent; color: var(--ok-text); }
.chip--alert { background: var(--alert-soft); border-color: transparent; color: var(--alert-text); }
.chip--live::before { content: ""; width: 8px; height: 8px; border-radius: 50%; background: var(--live); animation: pulse 1.6s ease-out infinite; }

.chip-row {
  display: flex;
  gap: var(--sp-2);
  overflow-x: auto;
  margin-inline: calc(var(--gutter) * -1);
  padding: 2px var(--gutter);
  scrollbar-width: none;
}

.chip-row::-webkit-scrollbar { display: none; }
.chip-row > * { flex: none; }

.badge {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  padding: 3px 8px;
  background: var(--surface);
  color: var(--ink);
  border-radius: var(--r-sm);
  font-family: var(--font-mono);
  font-size: 0.6875rem;
  font-weight: 700;
  line-height: 1.4;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  white-space: nowrap;
}

.badge--ink { background: var(--ink); color: var(--bg); }
.badge--ok { background: var(--ok-soft); color: var(--ok-text); }
.badge--alert { background: var(--alert); color: #fff; }
.badge--sponsored { background: var(--surface-2); color: var(--ink-2); border: 1px solid var(--line-strong); }
.badge--live { background: var(--accent); color: var(--accent-ink); }
.badge--live::before { content: ""; width: 7px; height: 7px; border-radius: 50%; background: currentColor; animation: pulse 1.6s ease-out infinite; }

@keyframes pulse {
  0% { box-shadow: 0 0 0 0 color-mix(in srgb, var(--pulse-color, var(--live)) 60%, transparent); }
  70%, 100% { box-shadow: 0 0 0 7px transparent; }
}

/* The dot inside an orange badge is dark, so its ring must be dark too. */
.badge--live::before { --pulse-color: var(--accent-ink); }

/* ==========================================================================
   3. SITE HEADER (rendered by app.js into <header id="site-header">)
   <header class="site-header" id="site-header">
     <div class="site-header__inner container">
       <a class="site-header__logo" href="index.html">
         <span class="site-header__logo-mark">BOLO</span>
         <span class="site-header__logo-sub">Texas</span></a>
       <div class="picker">…</div>
       <nav class="site-header__nav" aria-label="Primary">
         <a class="site-header__link" aria-current="page" href>News</a>…</nav>
       <div class="site-header__actions">
         <form class="site-header__search" role="search">
           <input type="search" placeholder="Search"><button class="icon-btn">…</button></form>
         <button class="icon-btn site-header__theme">…</button>
         <a class="btn btn--primary btn--small site-header__cta">Advertise</a>
       </div>
     </div>
   </header>
   Sticky. On phones the nav becomes a second, horizontally scrolling row and
   the search input collapses to its icon (add .is-open to expand it).
   .is-scrolled adds a shadow.
   ========================================================================== */
.site-header {
  position: sticky;
  top: 0;
  z-index: var(--z-header);
  border-bottom: 1px solid var(--line);
  transition: box-shadow var(--dur);
}

/* The frosted background lives on a pseudo-element. Putting backdrop-filter
   on the header itself would make it the containing block for the picker's
   fixed-position bottom sheet and break it on phones. */
.site-header::before {
  content: "";
  position: absolute;
  inset: 0;
  z-index: -1;
  background: color-mix(in srgb, var(--bg) 88%, transparent);
  -webkit-backdrop-filter: saturate(1.4) blur(14px);
  backdrop-filter: saturate(1.4) blur(14px);
}

.site-header.is-scrolled { box-shadow: var(--shadow-md); }

.site-header__inner {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  column-gap: var(--sp-3);
  min-height: var(--header-h);
}

.site-header__logo { display: inline-flex; align-items: baseline; gap: 6px; flex: none; color: var(--ink); }
.site-header__logo:hover { color: var(--ink); }
.site-header__logo-mark { font-family: var(--font-display); font-size: 1.625rem; font-weight: 800; letter-spacing: -0.04em; line-height: 1; }
.site-header__logo-sub { font-family: var(--font-mono); font-size: 0.6875rem; font-weight: 700; letter-spacing: var(--tracking-kicker); text-transform: uppercase; color: var(--accent-text); }

.site-header__nav {
  display: flex;
  order: 10;
  flex: 1 0 100%;
  gap: var(--sp-1);
  overflow-x: auto;
  margin-inline: calc(var(--gutter) * -1);
  padding: 0 var(--gutter) var(--sp-2);
  scrollbar-width: none;
}

.site-header__nav::-webkit-scrollbar { display: none; }

.site-header__link {
  flex: none;
  padding: 6px 10px;
  border-radius: var(--r-pill);
  font-size: var(--fs-sm);
  font-weight: 500;
  color: var(--ink-2);
  white-space: nowrap;
}

.site-header__link:hover { background: var(--surface-2); color: var(--ink); }
.site-header__link[aria-current="page"] { background: var(--ink); color: var(--bg); }

.site-header__actions { display: flex; align-items: center; gap: var(--sp-1); margin-left: auto; }
.site-header__search { display: flex; align-items: center; }

.site-header__search input {
  width: 0;
  min-width: 0;
  height: 40px;
  padding: 0;
  background: var(--surface);
  border: 1px solid transparent;
  border-radius: var(--r-pill);
  font-size: var(--fs-sm);
  opacity: 0;
  transition: width var(--dur) var(--ease), opacity var(--dur), padding var(--dur);
}

.site-header__search.is-open input,
.site-header__search input:focus { width: min(46vw, 220px); padding: 0 var(--sp-3); border-color: var(--line-strong); opacity: 1; }
.site-header__cta { display: none; }

@media (min-width: 600px) {
  .site-header__cta { display: inline-flex; }
}

@media (min-width: 900px) {
  .site-header__inner { flex-wrap: nowrap; }
  .site-header__nav { order: 0; flex: 1 1 auto; margin: 0; padding: 0; }
  .site-header__search input { width: 150px; padding: 0 var(--sp-3); border-color: var(--line); opacity: 1; }
  .site-header__search input:focus { width: 220px; }
}

/* ==========================================================================
   4. CITY / ZONE PICKER
   <div class="picker">
     <button class="picker__btn" aria-haspopup="dialog" aria-expanded="false">
       <svg…pin>
       <span class="picker__label"><span class="picker__city">Dallas</span>
         <span class="picker__zone">Oak Cliff</span></span>
       <svg class="picker__chevron"…></button>
     <div class="picker__panel" role="dialog" aria-label="Choose your city" hidden>
       <div class="picker__head">
         <input class="picker__search" type="search" placeholder="Search city or ZIP">
         <button class="picker__locate"><svg…> Use my location</button></div>
       <div class="picker__body">
         <section class="picker__group">
           <h3 class="picker__group-title">North Texas / DFW</h3>
           <ul class="picker__list">
             <li><button class="picker__item" aria-current="true">Dallas
               <span class="picker__item-meta">7 zones</span></button></li>…</ul>
         </section>
         <section class="picker__group picker__group--zones">…same list with
           .picker__item picker__item--zone…</section>
         <p class="picker__empty">No cities match.</p>
       </div>
     </div>
   </div>
   Toggle by removing [hidden] (or adding .is-open) on .picker__panel. It is a
   bottom sheet on phones and an anchored dropdown from 600px.
   ========================================================================== */
.picker { position: relative; min-width: 0; }

.picker__btn {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  max-width: 100%;
  min-height: 40px;
  padding: 4px 10px;
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: var(--r-pill);
  text-align: left;
  transition: border-color var(--dur-fast);
}

.picker__btn:hover, .picker__btn[aria-expanded="true"] { border-color: var(--ink); }
.picker__btn svg { width: 16px; height: 16px; flex: none; color: var(--accent-text); }
.picker__chevron { color: var(--muted) !important; transition: transform var(--dur); }
.picker__btn[aria-expanded="true"] .picker__chevron { transform: rotate(180deg); }
.picker__label { display: flex; flex-direction: column; min-width: 0; line-height: 1.15; }
.picker__city { font-size: var(--fs-sm); font-weight: 600; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.picker__zone { font-family: var(--font-mono); font-size: 0.625rem; letter-spacing: 0.06em; text-transform: uppercase; color: var(--muted); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.picker__zone:empty { display: none; }

.picker__panel {
  position: fixed;
  inset: auto 0 0 0;
  z-index: var(--z-dropdown);
  display: flex;
  flex-direction: column;
  max-height: 80dvh;
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: var(--r-lg) var(--r-lg) 0 0;
  box-shadow: var(--shadow-lg);
  animation: sheet-up var(--dur) var(--ease);
}

.picker__head { display: grid; gap: var(--sp-2); padding: var(--sp-4); border-bottom: 1px solid var(--line); }

.picker__search {
  width: 100%;
  height: 44px;
  padding: 0 var(--sp-4);
  background: var(--bg);
  border: 1px solid var(--line-strong);
  border-radius: var(--r-pill);
}

.picker__locate {
  display: inline-flex;
  align-items: center;
  gap: var(--sp-2);
  justify-self: start;
  padding: 6px 0;
  font-size: var(--fs-sm);
  font-weight: 600;
  color: var(--accent-text);
}

.picker__locate svg { width: 16px; height: 16px; }
.picker__locate:hover { text-decoration: underline; }
.picker__body { overflow-y: auto; overscroll-behavior: contain; padding: var(--sp-2) var(--sp-4) var(--sp-4); }
.picker__group + .picker__group { margin-top: var(--sp-3); }

.picker__group-title {
  position: sticky;
  top: 0;
  padding: var(--sp-2) 0;
  background: var(--surface);
  font-family: var(--font-mono);
  font-size: 0.6875rem;
  font-weight: 700;
  letter-spacing: var(--tracking-kicker);
  text-transform: uppercase;
  color: var(--muted);
}

.picker__list { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 2px var(--sp-2); }

.picker__item {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--sp-2);
  width: 100%;
  min-height: 40px;
  padding: 6px 10px;
  border-radius: var(--r-sm);
  font-size: var(--fs-sm);
  text-align: left;
}

.picker__item:hover { background: var(--surface-2); color: var(--ink); }
.picker__item[aria-current="true"], .picker__item.is-active { background: var(--accent-soft); font-weight: 600; }
.picker__item-meta { flex: none; font-family: var(--font-mono); font-size: 0.6875rem; color: var(--muted); }
.picker__group--zones { padding: var(--sp-3); background: var(--surface-2); border-radius: var(--r-md); }
.picker__group--zones .picker__group-title { position: static; background: none; padding-top: 0; }
.picker__group--zones .picker__item:hover { background: var(--surface); }
.picker__empty { padding: var(--sp-5) 0; text-align: center; color: var(--muted); font-size: var(--fs-sm); }

@keyframes sheet-up {
  from { transform: translateY(24px); opacity: 0; }
}

@media (min-width: 600px) {
  .picker__panel {
    position: absolute;
    inset: calc(100% + 8px) auto auto 0;
    width: 440px;
    max-height: min(70vh, 560px);
    border-radius: var(--r-lg);
  }
}

/* ==========================================================================
   5. BOTTOM TAB BAR (phones only; rendered by app.js into <nav id="tab-bar">)
   <nav class="tab-bar" id="tab-bar" aria-label="Sections">
     <a class="tab-bar__item" aria-current="page" href="index.html">
       <svg class="tab-bar__icon"…><span class="tab-bar__label">Home</span>
       <span class="tab-bar__dot"></span>   (optional unread marker)
     </a>… Home, Local, Classifieds, Live, Me
   </nav>
   ========================================================================== */
.tab-bar {
  position: fixed;
  inset: auto 0 0 0;
  z-index: var(--z-tabbar);
  display: grid;
  grid-auto-flow: column;
  grid-auto-columns: 1fr;
  height: calc(var(--tabbar-h) + env(safe-area-inset-bottom, 0px));
  padding-bottom: env(safe-area-inset-bottom, 0px);
  background: color-mix(in srgb, var(--surface) 94%, transparent);
  -webkit-backdrop-filter: blur(14px);
  backdrop-filter: blur(14px);
  border-top: 1px solid var(--line);
}

.tab-bar__item {
  position: relative;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 3px;
  color: var(--muted);
  font-size: 0.6875rem;
  font-weight: 600;
}

.tab-bar__item:hover { color: var(--ink); }
.tab-bar__item[aria-current="page"] { color: var(--ink); }
.tab-bar__item[aria-current="page"]::before { content: ""; position: absolute; top: 0; width: 28px; height: 3px; border-radius: 0 0 3px 3px; background: var(--accent); }
.tab-bar__icon { width: 22px; height: 22px; }
.tab-bar__dot { position: absolute; top: 9px; left: calc(50% + 8px); width: 8px; height: 8px; border-radius: 50%; background: var(--accent); border: 2px solid var(--surface); box-sizing: content-box; }

@media (min-width: 900px) {
  .tab-bar { display: none; }
}

/* ==========================================================================
   6. CARDS
   Base anatomy (every variant uses the same elements):
   <article class="card card--story">
     <div class="card__media">            (set ratio with style="--ratio: 4/3")
       <img src alt loading="lazy">
       <div class="card__badges"><span class="badge badge--live">Live</span></div>
       <button class="card__save icon-btn icon-btn--solid" aria-label="Save">…</button>
     </div>
     <div class="card__body">
       <span class="card__kicker kicker">Dallas · Oak Cliff</span>
       <h3 class="card__title"><a class="card__link" href>Headline</a></h3>
       <p class="card__dek">Optional one-liner.</p>
       <div class="card__meta"><span>2h ago</span><span>60-sec read</span></div>
     </div>
   </article>
   .card__link stretches over the whole card, so the card is one tap target;
   other controls inside (save button, chips) stay clickable above it.

   Variants
     .card--story       photo + kicker + headline (default news card)
     .card--hero        photo fills the card, text overlaid at the bottom, big title
     .card--overlay     same overlay treatment at normal title size (bento tiles)
     .card--horizontal  small thumbnail on the left, for "More stories" lists
     .card--native      in-feed sponsored card; add .card__label + .card__sponsor
     .card--event       poster ratio with .card__date (month/day block)
     .card--business    4:3 photo with .card__rating and an open/closed badge
     .card--classified  square photo with .price; --featured adds accent outline
     .card--score       no media; wraps a .scoreboard
     .card--stat        big number: .card__stat-value, __stat-label, __stat-trend
                        tones: .card--accent, .card--ink, .card--ok, .card--alert
   ========================================================================== */
.card {
  position: relative;
  display: flex;
  flex-direction: column;
  min-width: 0;
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: var(--r-md);
  overflow: hidden;
  transition: border-color var(--dur), box-shadow var(--dur), transform var(--dur);
}

.card:has(.card__link):hover { border-color: var(--line-strong); box-shadow: var(--shadow-md); transform: translateY(-2px); }
.card:has(.card__link:focus-visible) { outline: 3px solid var(--focus); outline-offset: 2px; }
.card__link:focus-visible { outline: none; }

.card__media {
  position: relative;
  flex: none;
  aspect-ratio: var(--ratio, 16 / 10);
  background: var(--surface-2);
  overflow: hidden;
}

.card__media img, .card__media video { width: 100%; height: 100%; object-fit: cover; transition: transform var(--dur-slow) var(--ease); }
.card:hover .card__media img { transform: scale(1.04); }

/* Gradient that makes white text legible on a photo. Applied automatically
   by the hero/overlay/event variants, or opt in with .card__media--scrim. */
.card__media--scrim::after,
.card--hero .card__media::after,
.card--overlay .card__media::after { content: ""; position: absolute; inset: 0; background: var(--scrim); pointer-events: none; }

.card__badges { position: absolute; top: var(--sp-3); left: var(--sp-3); z-index: 2; display: flex; flex-wrap: wrap; gap: 6px; }
.card__save { position: absolute; top: var(--sp-2); right: var(--sp-2); z-index: 3; }
.card__save[aria-pressed="true"] { color: var(--accent-text); }
.card__body { display: flex; flex-direction: column; flex: 1; gap: var(--sp-2); padding: var(--sp-3) var(--sp-4) var(--sp-4); }
.card__kicker { font-size: 0.6875rem; }
.card__title { font-size: var(--fs-md); line-height: 1.22; }
.card__link { color: inherit; }
.card__link::after { content: ""; position: absolute; inset: 0; z-index: 1; }
.card__link:hover { color: inherit; }
.card__dek { font-size: var(--fs-sm); color: var(--ink-2); line-height: 1.45; }

.card__meta {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 4px 10px;
  margin-top: auto;
  padding-top: var(--sp-1);
  font-size: var(--fs-xs);
  color: var(--muted);
}

.card__meta > * + *::before { content: "·"; margin-right: 10px; }
.card__footer { position: relative; z-index: 2; display: flex; align-items: center; justify-content: space-between; gap: var(--sp-2); margin-top: auto; }

/* --- Hero and overlay: text on image --- */
.card--hero, .card--overlay { min-height: 220px; border: 0; color: var(--on-media); }
.card--hero .card__media, .card--overlay .card__media { position: absolute; inset: 0; aspect-ratio: auto; }
.card--hero .card__body, .card--overlay .card__body { position: relative; justify-content: flex-end; padding: var(--sp-4); }
.card--hero .card__kicker, .card--overlay .card__kicker { color: var(--on-media); opacity: 0.92; }
.card--hero .card__dek, .card--overlay .card__dek,
.card--hero .card__meta, .card--overlay .card__meta { color: var(--on-media-muted); margin-top: 0; }
.card--hero .card__title, .card--overlay .card__title { text-shadow: 0 1px 12px rgba(0, 0, 0, 0.35); }
.card--hero { min-height: 340px; border-radius: var(--r-lg); }
.card--hero .card__title { font-size: var(--fs-xl); max-width: 22ch; }
.card--hero .card__dek { font-size: var(--fs-base); max-width: 52ch; }

@media (min-width: 900px) {
  .card--hero { min-height: 440px; }
  .card--hero .card__body { padding: var(--sp-6); }
  .card--hero .card__title { font-size: var(--fs-2xl); }
}

/* --- Horizontal list item --- */
.card--horizontal { flex-direction: row; align-items: stretch; border-width: 0 0 1px; border-radius: 0; background: none; }
.card--horizontal:has(.card__link):hover { transform: none; box-shadow: none; }
.card--horizontal .card__media { width: 96px; aspect-ratio: 1; margin-block: var(--sp-3); border-radius: var(--r-sm); }
.card--horizontal .card__body { padding: var(--sp-3) 0 var(--sp-3) var(--sp-3); gap: var(--sp-1); }
.card--horizontal .card__title { font-size: var(--fs-base); }

/* --- Native ad: same shape as a story card, clearly labelled --- */
.card--native { background: var(--surface-2); border-style: dashed; border-color: var(--line-strong); }

.card__label {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--sp-2);
  padding: 6px var(--sp-4);
  border-bottom: 1px solid var(--line);
  font-family: var(--font-mono);
  font-size: 0.6875rem;
  font-weight: 700;
  letter-spacing: var(--tracking-kicker);
  text-transform: uppercase;
  color: var(--ink-2);
}

.card__sponsor { font-size: var(--fs-xs); color: var(--muted); }
.card__sponsor strong { color: var(--ink); font-weight: 600; }
.card__cta { position: relative; z-index: 2; align-self: flex-start; margin-top: auto; font-size: var(--fs-sm); font-weight: 700; color: var(--accent-text); }
.card__cta::after { content: " \2192"; }

/* --- Event poster --- */
.card--event .card__media { --ratio: 4 / 5; }

.card__date {
  position: absolute;
  top: var(--sp-3);
  left: var(--sp-3);
  z-index: 2;
  display: grid;
  place-items: center;
  min-width: 48px;
  padding: 6px 8px;
  background: var(--surface);
  color: var(--ink);
  border-radius: var(--r-sm);
  box-shadow: var(--shadow-sm);
  line-height: 1;
  text-align: center;
}

.card__date-month { font-family: var(--font-mono); font-size: 0.625rem; font-weight: 700; letter-spacing: 0.1em; text-transform: uppercase; color: var(--accent-text); }
.card__date-day { margin-top: 3px; font-family: var(--font-display); font-size: 1.5rem; font-weight: 700; }

/* --- Business tile --- */
.card--business .card__media { --ratio: 4 / 3; }
.card__rating { display: inline-flex; align-items: center; gap: 4px; font-size: var(--fs-sm); font-weight: 600; font-variant-numeric: tabular-nums; }
.card__rating svg { width: 14px; height: 14px; color: var(--accent); }
.card__rating span { font-weight: 400; color: var(--muted); }

/* --- Classified tile --- */
.card--classified .card__media { --ratio: 1; }
.card--classified .card__body { gap: 2px; padding: var(--sp-3); }
.card--classified .card__title { font-family: var(--font-ui); font-size: var(--fs-sm); font-weight: 500; letter-spacing: 0; line-height: 1.35; }
.card--classified .card__meta { padding-top: 2px; }
.card--featured { border-color: var(--accent); box-shadow: 0 0 0 1px var(--accent); }

/* --- Score tile --- */
.card--score .card__body { gap: var(--sp-3); }

/* --- Stat tile --- */
.card--stat { justify-content: space-between; padding: var(--sp-4); gap: var(--sp-3); }
.card__stat-value { font-family: var(--font-display); font-size: clamp(2.25rem, 1.6rem + 3vw, 3.5rem); font-weight: 700; line-height: 0.95; letter-spacing: -0.03em; font-variant-numeric: tabular-nums; }
.card__stat-value small { font-size: 0.45em; font-weight: 600; letter-spacing: 0; }
.card__stat-label { font-size: var(--fs-sm); color: var(--ink-2); line-height: 1.35; }
.card__stat-trend { display: inline-flex; align-items: center; gap: 4px; font-family: var(--font-mono); font-size: var(--fs-xs); font-weight: 700; color: var(--ok-text); }
.card__stat-trend--down { color: var(--alert-text); }

.card--accent { background: var(--accent); border-color: transparent; color: var(--accent-ink); }
.card--ink { background: var(--ink); border-color: transparent; color: var(--bg); }
.card--ok { background: var(--ok-soft); border-color: transparent; }
.card--alert { background: var(--alert-soft); border-color: transparent; }
.card--accent .kicker, .card--accent .card__stat-label, .card--accent .card__stat-trend,
.card--ink .kicker, .card--ink .card__stat-label, .card--ink .card__stat-trend { color: inherit; opacity: 0.85; }

/* ==========================================================================
   7. RAIL AND STORY TILES
   Rail: horizontal snap scroller that bleeds to the screen edge on phones.
   <div class="rail-wrap">
     <button class="rail__btn rail__btn--prev" aria-label="Previous">…</button>
     <div class="rail rail--stories" tabindex="0">…children…</div>
     <button class="rail__btn rail__btn--next" aria-label="Next">…</button>
   </div>
   Modifiers set child width: .rail--stories (story tiles), .rail--cards
   (260px cards), .rail--posters (180px event posters), .rail--tiles (160px).

   Story tile: 9:16 tap-through card that opens stories.html.
   <a class="story-tile" href="stories.html?c=dallas&i=0">
     <img src alt loading="lazy">
     <span class="story-tile__dots"><i class="is-seen"></i><i class="is-active"></i><i></i></span>
     <span class="story-tile__body">
       <span class="story-tile__kicker">Traffic</span>
       <span class="story-tile__title">I-30 closures this weekend</span></span>
   </a>
   .is-seen on the tile dims it. The same .story-tile__dots markup works as
   the progress bar in the full-screen viewer.
   ========================================================================== */
.rail-wrap { position: relative; }

.rail {
  display: flex;
  gap: var(--gap);
  overflow-x: auto;
  overscroll-behavior-x: contain;
  scroll-snap-type: x mandatory;
  scroll-padding-inline: var(--gutter);
  margin-inline: calc(var(--gutter) * -1);
  padding: 4px var(--gutter) var(--sp-3);
  scrollbar-width: none;
}

.rail::-webkit-scrollbar { display: none; }
.rail > * { flex: 0 0 auto; scroll-snap-align: start; }
.rail--stories > * { width: 132px; }
.rail--cards > * { width: min(78vw, 280px); }
.rail--posters > * { width: 180px; }
.rail--tiles > * { width: 160px; }

.rail__btn {
  display: none;
  position: absolute;
  top: 50%;
  z-index: 3;
  width: 44px;
  height: 44px;
  place-items: center;
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: 50%;
  box-shadow: var(--shadow-md);
  transform: translateY(-50%);
}

.rail__btn svg { width: 18px; height: 18px; }
.rail__btn--prev { left: -12px; }
.rail__btn--next { right: -12px; }
.rail__btn[disabled] { opacity: 0; pointer-events: none; }

@media (min-width: 900px) {
  .rail__btn { display: grid; }
  .rail--stories > * { width: 168px; }
}

.story-tile {
  position: relative;
  display: block;
  aspect-ratio: 9 / 16;
  background: var(--surface-2);
  border-radius: var(--r-lg);
  color: var(--on-media);
  overflow: hidden;
  outline: 2px solid var(--accent);
  outline-offset: 2px;
  transition: transform var(--dur) var(--ease);
}

.story-tile:hover { color: var(--on-media); transform: translateY(-3px); }
.story-tile:focus-visible { outline: 3px solid var(--focus); }
.story-tile.is-seen { outline-color: var(--line-strong); }
.story-tile.is-seen img { filter: saturate(0.7); }
.story-tile img { width: 100%; height: 100%; object-fit: cover; }
.story-tile::before { content: ""; position: absolute; inset: 0 0 auto; z-index: 1; height: 30%; background: var(--scrim-top); }
.story-tile::after { content: ""; position: absolute; inset: 0; background: var(--scrim); }

.story-tile__dots { position: absolute; inset: 8px 8px auto; z-index: 2; display: flex; gap: 3px; }
.story-tile__dots > * { flex: 1; height: 3px; border-radius: 2px; background: rgba(255, 255, 255, 0.4); overflow: hidden; }
.story-tile__dots > .is-seen, .story-tile__dots > .is-active { background: #fff; }
.story-tile__body { position: absolute; inset: auto 0 0; z-index: 2; display: grid; gap: 4px; padding: var(--sp-3); }
.story-tile__kicker { font-family: var(--font-mono); font-size: 0.625rem; font-weight: 700; letter-spacing: 0.1em; text-transform: uppercase; opacity: 0.9; }
.story-tile__title { font-family: var(--font-display); font-size: var(--fs-sm); font-weight: 600; line-height: 1.2; }

@media (min-width: 900px) {
  .story-tile__title { font-size: var(--fs-base); }
}

/* ==========================================================================
   8. AD SLOTS (filled by assets/js/ads.js)
   <div class="ad-slot ad-slot--billboard" data-ad-slot="statewide-billboard"
        data-ad-size="970x250"></div>
   The empty box reserves its final size with aspect-ratio, so nothing shifts
   when the ad arrives. ads.js adds .is-filled and renders:
     <span class="ad-slot__label">Advertisement</span>
     <a class="ad-slot__link" href rel="sponsored noopener">
       <img class="ad-slot__media" alt>
       <span class="ad-slot__body">
         <span class="ad-slot__advertiser">H-E-B</span>
         <span class="ad-slot__headline">…</span>
         <span class="ad-slot__text">…</span>
         <span class="ad-slot__cta">Learn more</span></span></a>
     <p class="ad-slot__disclaimer">Pol. adv. paid for by …</p>

   Size variants (class, or matched automatically from data-ad-size)
     .ad-slot--billboard    970x250 desktop, 320x100 phones
     .ad-slot--leaderboard  728x90 desktop, 320x50 phones
     .ad-slot--mrec         300x250
     .ad-slot--native       no fixed ratio; holds a .card.card--native
     .ad-slot--sponsor-badge  inline "Presented by …" pill for heroes/strips
   ========================================================================== */
.ad-slot {
  position: relative;
  display: block;
  width: 100%;
  margin-inline: auto;
  background: var(--surface-2);
  border: 1px solid var(--line);
  border-radius: var(--r-md);
  overflow: hidden;
  container-type: inline-size;
}

.ad-slot__label {
  position: absolute;
  top: 0;
  left: 0;
  z-index: 3;
  padding: 3px 8px;
  background: var(--surface);
  color: var(--ink-2);
  border-bottom-right-radius: var(--r-sm);
  font-family: var(--font-mono);
  font-size: 0.625rem;
  font-weight: 700;
  line-height: 1.4;
  letter-spacing: var(--tracking-kicker);
  text-transform: uppercase;
}

/* Before ads.js runs, show a quiet placeholder label so the reserved box
   does not look like a broken image. */
.ad-slot:empty::before { content: "Advertisement"; position: absolute; inset: 0; display: grid; place-items: center; font-family: var(--font-mono); font-size: 0.625rem; letter-spacing: var(--tracking-kicker); text-transform: uppercase; color: var(--muted); }

.ad-slot__link { position: absolute; inset: 0; display: flex; align-items: stretch; color: var(--on-media); }
.ad-slot__link:hover { color: var(--on-media); }
.ad-slot__media { position: absolute; inset: 0; width: 100%; height: 100%; object-fit: cover; }
.ad-slot__link::after { content: ""; position: absolute; inset: 0; background: linear-gradient(90deg, rgba(9, 14, 28, 0.86) 0%, rgba(9, 14, 28, 0.55) 55%, rgba(9, 14, 28, 0.1) 100%); }

.ad-slot__body {
  position: relative;
  z-index: 2;
  display: flex;
  flex-direction: column;
  justify-content: center;
  gap: 4px;
  max-width: 62%;
  padding: 22px var(--sp-4) var(--sp-3);
}

.ad-slot__advertiser { font-family: var(--font-mono); font-size: 0.625rem; font-weight: 700; letter-spacing: 0.1em; text-transform: uppercase; opacity: 0.9; }
.ad-slot__headline { font-family: var(--font-display); font-size: clamp(0.9375rem, 4.4cqi, 2rem); font-weight: 600; line-height: 1.12; }
.ad-slot__text { display: none; font-size: var(--fs-sm); line-height: 1.4; color: var(--on-media-muted); }

.ad-slot__cta {
  align-self: flex-start;
  margin-top: 4px;
  padding: 5px 12px;
  background: var(--accent);
  color: var(--accent-ink);
  border-radius: var(--r-pill);
  font-size: var(--fs-xs);
  font-weight: 700;
  white-space: nowrap;
}

.ad-slot__disclaimer { position: absolute; inset: auto 0 0 auto; z-index: 3; max-width: 100%; padding: 2px 8px; background: rgba(9, 14, 28, 0.8); color: #fff; font-size: 0.625rem; line-height: 1.4; }

/* Billboard */
.ad-slot--billboard, .ad-slot[data-ad-size="970x250"] { aspect-ratio: 320 / 100; max-width: 970px; }

/* Leaderboard: body turns into a single row. */
.ad-slot--leaderboard, .ad-slot[data-ad-size="728x90"] { aspect-ratio: 320 / 50; max-width: 728px; border-radius: var(--r-sm); }
.ad-slot--leaderboard .ad-slot__body, .ad-slot[data-ad-size="728x90"] .ad-slot__body { flex-direction: row; align-items: center; gap: var(--sp-3); max-width: none; padding: 14px var(--sp-3) 0; }
.ad-slot--leaderboard .ad-slot__headline, .ad-slot[data-ad-size="728x90"] .ad-slot__headline { font-size: clamp(0.8125rem, 3cqi, 1.25rem); }
.ad-slot--leaderboard .ad-slot__advertiser, .ad-slot[data-ad-size="728x90"] .ad-slot__advertiser,
.ad-slot--leaderboard .ad-slot__cta, .ad-slot[data-ad-size="728x90"] .ad-slot__cta { display: none; }

/* Medium rectangle: image on top, text at the bottom. */
.ad-slot--mrec, .ad-slot[data-ad-size="300x250"] { aspect-ratio: 300 / 250; max-width: 300px; }
.ad-slot--mrec .ad-slot__link::after, .ad-slot[data-ad-size="300x250"] .ad-slot__link::after { background: var(--scrim); }
.ad-slot--mrec .ad-slot__body, .ad-slot[data-ad-size="300x250"] .ad-slot__body { justify-content: flex-end; max-width: none; padding: var(--sp-4); }
.ad-slot--mrec .ad-slot__headline, .ad-slot[data-ad-size="300x250"] .ad-slot__headline { font-size: 1.25rem; }

@media (min-width: 600px) {
  .ad-slot__text { display: block; }
  .ad-slot--leaderboard .ad-slot__text, .ad-slot[data-ad-size="728x90"] .ad-slot__text { display: none; }
  .ad-slot--leaderboard, .ad-slot[data-ad-size="728x90"] { aspect-ratio: 728 / 90; }
  .ad-slot--leaderboard .ad-slot__body, .ad-slot[data-ad-size="728x90"] .ad-slot__body { padding: 0 var(--sp-5); }
  .ad-slot--leaderboard .ad-slot__cta, .ad-slot[data-ad-size="728x90"] .ad-slot__cta { display: inline-block; align-self: center; margin: 0 0 0 auto; }
}

@media (min-width: 900px) {
  .ad-slot--billboard, .ad-slot[data-ad-size="970x250"] { aspect-ratio: 970 / 250; }
  .ad-slot--billboard .ad-slot__body, .ad-slot[data-ad-size="970x250"] .ad-slot__body { max-width: 52%; padding: var(--sp-6); gap: var(--sp-2); }
}

/* Native: the slot is only a wrapper; the card inside carries the look. */
.ad-slot--native { background: none; border: 0; border-radius: 0; overflow: visible; min-height: 260px; container-type: normal; }
.ad-slot--native.is-filled { min-height: 0; }
.ad-slot--native > .card { height: 100%; }
.ad-slot--native:empty::before { background: var(--surface-2); border: 1px dashed var(--line-strong); border-radius: var(--r-md); }

/* Sponsor badge: "Presented by <advertiser>" pill. Reserves one line. */
.ad-slot--sponsor-badge {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  width: auto;
  min-width: 150px;
  min-height: 30px;
  margin: 0;
  padding: 4px 12px;
  border-radius: var(--r-pill);
  font-size: var(--fs-xs);
  color: var(--ink-2);
  white-space: nowrap;
  container-type: normal;
}

.ad-slot--sponsor-badge:empty::before { content: ""; }
.ad-slot--sponsor-badge .ad-slot__label { position: static; padding: 0; background: none; color: var(--muted); font-size: 0.625rem; }
.ad-slot--sponsor-badge .ad-slot__link { position: static; display: inline; color: var(--ink); font-weight: 700; }
.ad-slot--sponsor-badge .ad-slot__link::after { content: none; }
.ad-slot--sponsor-badge .ad-slot__link:hover { color: var(--accent-text); }

/* ==========================================================================
   9. FORMS
   <div class="field">
     <label class="field__label" for="x">Title <span class="field__req">*</span></label>
     <input class="input" id="x">      (.select for <select>, .textarea)
     <p class="field__hint">Shown in search.</p>
     <p class="field__error">Required.</p>   (add .is-invalid to .field)
   </div>
   .form-row lays two fields side by side from 600px.
   .input-group puts an icon or prefix inside an input.
   <label class="check"><input type="checkbox"> I agree…</label> for
   checkboxes and radios; .check--card makes a selectable tile.
   <label class="toggle"><input type="checkbox" role="switch">
     <span class="toggle__track"></span><span class="toggle__label">Dark mode</span></label>
   .consent is small legal copy (TCPA etc). .upload is a photo dropzone with
   .upload__grid of .upload__thumb.
   ========================================================================== */
.field { display: grid; gap: 6px; }
.field__label { font-size: var(--fs-sm); font-weight: 600; }
.field__req { color: var(--alert-text); }
.field__hint { font-size: var(--fs-xs); color: var(--muted); }
.field__error { display: none; font-size: var(--fs-xs); font-weight: 600; color: var(--alert-text); }
.field.is-invalid .field__error { display: block; }
.field.is-invalid .input, .field.is-invalid .select, .field.is-invalid .textarea { border-color: var(--alert); }

.input, .select, .textarea {
  width: 100%;
  min-height: 46px;
  padding: 10px 14px;
  background: var(--surface);
  color: var(--ink);
  border: 1px solid var(--line-strong);
  border-radius: var(--r-md);
  transition: border-color var(--dur-fast), box-shadow var(--dur-fast);
}

.input::placeholder, .textarea::placeholder { color: var(--muted); opacity: 1; }
.input:hover, .select:hover, .textarea:hover { border-color: var(--ink-2); }
.input:focus, .select:focus, .textarea:focus { outline: none; border-color: var(--focus); box-shadow: 0 0 0 3px color-mix(in srgb, var(--focus) 30%, transparent); }
.textarea { min-height: 120px; resize: vertical; line-height: 1.5; }

.select {
  appearance: none;
  padding-right: 40px;
  background-image: linear-gradient(45deg, transparent 50%, var(--muted) 50%), linear-gradient(135deg, var(--muted) 50%, transparent 50%);
  background-position: calc(100% - 20px) 50%, calc(100% - 15px) 50%;
  background-size: 5px 5px;
  background-repeat: no-repeat;
}

.form-row { display: grid; gap: var(--sp-4); }

@media (min-width: 600px) {
  .form-row { grid-template-columns: repeat(2, minmax(0, 1fr)); }
}

.input-group { position: relative; display: flex; align-items: center; }
.input-group > svg, .input-group__prefix { position: absolute; left: 14px; width: 18px; color: var(--muted); pointer-events: none; }
.input-group > .input { padding-left: 42px; }

.check { display: flex; align-items: flex-start; gap: 10px; font-size: var(--fs-sm); line-height: 1.45; cursor: pointer; }
.check input { flex: none; width: 20px; height: 20px; margin-top: 1px; accent-color: var(--accent); }
.check--card { align-items: center; padding: var(--sp-3) var(--sp-4); background: var(--surface); border: 1px solid var(--line-strong); border-radius: var(--r-md); }
.check--card:has(input:checked) { border-color: var(--accent); box-shadow: 0 0 0 1px var(--accent); background: var(--accent-soft); }

.toggle { display: inline-flex; align-items: center; gap: 10px; font-size: var(--fs-sm); cursor: pointer; }
.toggle input { position: absolute; opacity: 0; width: 1px; height: 1px; }
.toggle__track { position: relative; flex: none; width: 44px; height: 26px; background: var(--line-strong); border-radius: var(--r-pill); transition: background var(--dur); }
.toggle__track::after { content: ""; position: absolute; top: 3px; left: 3px; width: 20px; height: 20px; background: #fff; border-radius: 50%; box-shadow: var(--shadow-sm); transition: transform var(--dur) var(--ease); }
.toggle input:checked + .toggle__track { background: var(--ok); }
.toggle input:checked + .toggle__track::after { transform: translateX(18px); }
.toggle input:focus-visible + .toggle__track { outline: 3px solid var(--focus); outline-offset: 2px; }

.consent { font-size: var(--fs-xs); line-height: 1.5; color: var(--muted); }
.consent a { text-decoration: underline; }

.upload { display: grid; place-items: center; gap: var(--sp-2); padding: var(--sp-6) var(--sp-4); background: var(--surface); border: 2px dashed var(--line-strong); border-radius: var(--r-md); text-align: center; color: var(--muted); cursor: pointer; }
.upload:hover, .upload.is-dragover { border-color: var(--accent); background: var(--accent-soft); }
.upload__grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(84px, 1fr)); gap: var(--sp-2); }
.upload__thumb { position: relative; aspect-ratio: 1; border-radius: var(--r-sm); overflow: hidden; }
.upload__thumb img { width: 100%; height: 100%; object-fit: cover; }

/* ==========================================================================
   10. TABS AND STEPPER
   <div class="tabs" role="tablist">
     <button class="tabs__tab" role="tab" aria-selected="true">Saved</button>…
   </div>
   <div class="tabs__panel" role="tabpanel">…</div>   (toggle with [hidden])
   .tabs--pill renders tabs as a segmented control.

   <ol class="stepper">
     <li class="stepper__step is-done"><span class="stepper__num">1</span>
       <span class="stepper__label">Category</span></li>
     <li class="stepper__step is-active" aria-current="step">…</li>
   </ol>
   <div class="stepper__panel">…one wizard step…</div>
   <div class="stepper__nav">Back / Next buttons</div>
   ========================================================================== */
.tabs { display: flex; gap: var(--sp-1); overflow-x: auto; border-bottom: 1px solid var(--line); scrollbar-width: none; }
.tabs::-webkit-scrollbar { display: none; }

.tabs__tab {
  position: relative;
  flex: none;
  min-height: 44px;
  padding: 0 var(--sp-3);
  font-size: var(--fs-sm);
  font-weight: 600;
  color: var(--muted);
  white-space: nowrap;
}

.tabs__tab:hover { color: var(--ink); }
.tabs__tab[aria-selected="true"], .tabs__tab.is-active { color: var(--ink); }
.tabs__tab[aria-selected="true"]::after, .tabs__tab.is-active::after { content: ""; position: absolute; inset: auto var(--sp-3) -1px; height: 3px; background: var(--accent); border-radius: 3px 3px 0 0; }
.tabs__count { margin-left: 4px; padding: 1px 6px; background: var(--surface-2); border-radius: var(--r-pill); font-family: var(--font-mono); font-size: 0.6875rem; }
.tabs__panel { padding-top: var(--sp-5); }

.tabs--pill { display: inline-flex; max-width: 100%; padding: 4px; background: var(--surface-2); border: 0; border-radius: var(--r-pill); }
.tabs--pill .tabs__tab { min-height: 36px; border-radius: var(--r-pill); }
.tabs--pill .tabs__tab::after { content: none; }
.tabs--pill .tabs__tab[aria-selected="true"], .tabs--pill .tabs__tab.is-active { background: var(--surface); box-shadow: var(--shadow-sm); }

.stepper { display: flex; align-items: center; gap: var(--sp-2); counter-reset: step; }
.stepper__step { display: flex; align-items: center; gap: var(--sp-2); flex: 1; min-width: 0; color: var(--muted); font-size: var(--fs-sm); font-weight: 600; }
.stepper__step::after { content: ""; flex: 1; height: 2px; min-width: 8px; background: var(--line); border-radius: 2px; }
.stepper__step:last-child { flex: none; }
.stepper__step:last-child::after { content: none; }

.stepper__num {
  display: grid;
  place-items: center;
  flex: none;
  width: 30px;
  height: 30px;
  background: var(--surface);
  border: 2px solid var(--line-strong);
  border-radius: 50%;
  font-family: var(--font-mono);
  font-size: var(--fs-xs);
  font-weight: 700;
}

.stepper__label { display: none; white-space: nowrap; }
.stepper__step.is-active { color: var(--ink); }
.stepper__step.is-active .stepper__num { background: var(--accent); border-color: var(--accent); color: var(--accent-ink); }
.stepper__step.is-active .stepper__label { display: inline; }
.stepper__step.is-done .stepper__num { background: var(--ok); border-color: var(--ok); color: #fff; }
.stepper__step.is-done::after { background: var(--ok); }
.stepper__panel { padding-block: var(--sp-5); }
.stepper__nav { display: flex; justify-content: space-between; gap: var(--sp-3); padding-top: var(--sp-4); border-top: 1px solid var(--line); }

@media (min-width: 900px) {
  .stepper__label { display: inline; }
}

/* ==========================================================================
   11. DRAWER, CHAT, MODAL, TOAST
   Backdrop shared by drawer and modal: <div class="backdrop is-open"></div>

   <aside class="drawer" role="dialog" aria-modal="true" aria-label="Messages">
     <header class="drawer__header"><h2 class="drawer__title">Sam R.</h2>
       <button class="icon-btn drawer__close" aria-label="Close">…</button></header>
     <div class="drawer__body">
       <div class="chat">
         <p class="chat__notice">Keep it in BOLO chat…</p>
         <div class="chat__msg chat__msg--them">Hi! <time class="chat__time">2:14 PM</time></div>
         <div class="chat__msg chat__msg--me">Is this still available?</div>
       </div></div>
     <footer class="drawer__footer">
       <div class="chat__quick chip-row">…chips…</div>
       <form class="chat__composer"><input class="input"><button class="btn btn--primary">Send</button></form>
     </footer>
   </aside>
   Add .is-open to slide in from the right (full width on phones).

   <div class="modal" role="dialog" aria-modal="true">   (add .is-open)
     <div class="modal__dialog">   (.modal__dialog--wide for 720px)
       <header class="modal__header"><h2 class="modal__title">…</h2>
         <button class="icon-btn modal__close">…</button></header>
       <div class="modal__body">…</div>
       <footer class="modal__footer">…buttons…</footer></div></div>

   <div class="toast-region" aria-live="polite">
     <div class="toast toast--ok">Saved to My BOLO</div></div>
   Toast modifiers: --ok, --alert. .is-leaving fades it out.
   ========================================================================== */
.backdrop { position: fixed; inset: 0; z-index: var(--z-backdrop); background: var(--backdrop); opacity: 0; visibility: hidden; transition: opacity var(--dur), visibility var(--dur); }
.backdrop.is-open { opacity: 1; visibility: visible; }

.drawer {
  position: fixed;
  inset: 0 0 0 auto;
  z-index: var(--z-drawer);
  display: flex;
  flex-direction: column;
  width: min(100%, 420px);
  background: var(--surface);
  border-left: 1px solid var(--line);
  box-shadow: var(--shadow-lg);
  transform: translateX(100%);
  visibility: hidden;
  transition: transform var(--dur-slow) var(--ease), visibility var(--dur-slow);
}

.drawer.is-open { transform: none; visibility: visible; }
.drawer__header { display: flex; align-items: center; justify-content: space-between; gap: var(--sp-3); padding: var(--sp-3) var(--sp-4); border-bottom: 1px solid var(--line); }
.drawer__title { font-size: var(--fs-md); }
.drawer__body { flex: 1; overflow-y: auto; overscroll-behavior: contain; padding: var(--sp-4); }
.drawer__footer { display: grid; gap: var(--sp-3); padding: var(--sp-3) var(--sp-4) calc(var(--sp-3) + env(safe-area-inset-bottom, 0px)); border-top: 1px solid var(--line); }
.drawer__footer .chip-row { margin-inline: calc(var(--sp-4) * -1); padding-inline: var(--sp-4); }

.chat { display: flex; flex-direction: column; gap: var(--sp-2); }
.chat__notice { align-self: center; max-width: 34ch; margin-bottom: var(--sp-2); padding: var(--sp-2) var(--sp-3); background: var(--surface-2); border-radius: var(--r-md); font-size: var(--fs-xs); color: var(--muted); text-align: center; }
.chat__msg { max-width: 82%; padding: 9px 13px; border-radius: 18px; font-size: var(--fs-sm); line-height: 1.4; overflow-wrap: anywhere; }
.chat__msg--them { align-self: flex-start; background: var(--surface-2); border-bottom-left-radius: 4px; }
.chat__msg--me { align-self: flex-end; background: var(--ink); color: var(--bg); border-bottom-right-radius: 4px; }
.chat__time { display: block; margin-top: 2px; font-size: 0.625rem; opacity: 0.7; }
.chat__composer { display: flex; gap: var(--sp-2); }
.chat__composer .input { border-radius: var(--r-pill); }

.modal {
  position: fixed;
  inset: 0;
  z-index: var(--z-modal);
  display: grid;
  align-items: end;
  justify-items: center;
  background: var(--backdrop);
  opacity: 0;
  visibility: hidden;
  transition: opacity var(--dur), visibility var(--dur);
}

.modal.is-open { opacity: 1; visibility: visible; }

.modal__dialog {
  display: flex;
  flex-direction: column;
  width: 100%;
  max-width: 520px;
  max-height: 90dvh;
  background: var(--surface);
  border-radius: var(--r-lg) var(--r-lg) 0 0;
  box-shadow: var(--shadow-lg);
  transform: translateY(24px);
  transition: transform var(--dur-slow) var(--ease);
}

.modal.is-open .modal__dialog { transform: none; }
.modal__dialog--wide { max-width: 720px; }
.modal__header { display: flex; align-items: center; justify-content: space-between; gap: var(--sp-3); padding: var(--sp-4) var(--sp-4) var(--sp-3) var(--sp-5); }
.modal__title { font-size: var(--fs-lg); }
.modal__body { overflow-y: auto; padding: 0 var(--sp-5) var(--sp-5); }
.modal__footer { display: flex; flex-wrap: wrap; justify-content: flex-end; gap: var(--sp-2); padding: var(--sp-3) var(--sp-5) calc(var(--sp-4) + env(safe-area-inset-bottom, 0px)); border-top: 1px solid var(--line); }

@media (min-width: 600px) {
  .modal { align-items: center; padding: var(--sp-5); }
  .modal__dialog { border-radius: var(--r-lg); }
}

.toast-region {
  position: fixed;
  inset: auto var(--sp-4) calc(var(--tabbar-h) + var(--sp-4) + env(safe-area-inset-bottom, 0px));
  z-index: var(--z-toast);
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: var(--sp-2);
  pointer-events: none;
}

.toast {
  display: flex;
  align-items: center;
  gap: var(--sp-2);
  max-width: 100%;
  padding: 12px 18px;
  background: var(--ink);
  color: var(--bg);
  border-radius: var(--r-pill);
  box-shadow: var(--shadow-lg);
  font-size: var(--fs-sm);
  font-weight: 500;
  pointer-events: auto;
  animation: sheet-up var(--dur) var(--ease);
  transition: opacity var(--dur), transform var(--dur);
}

.toast::before { content: ""; flex: none; width: 8px; height: 8px; border-radius: 50%; background: var(--accent); }
.toast--ok::before { background: var(--ok); }
.toast--alert::before { background: var(--alert); }
.toast.is-leaving { opacity: 0; transform: translateY(8px); }

@media (min-width: 900px) {
  .toast-region { inset: auto var(--sp-5) var(--sp-5) auto; align-items: flex-end; }
}

/* ==========================================================================
   12. PRICE, SCOREBOARD, MAP
   <span class="price">$240 <span class="price__unit">/mo</span>
     <s class="price__was">$300</s></span>
   Modifiers: --lg (listing page), --free (sage "Free").

   <div class="scoreboard">
     <div class="scoreboard__head"><span class="kicker">6A · Week 11</span>
       <span class="badge badge--live">Q3 4:12</span></div>
     <div class="scoreboard__row is-winner">
       <span class="scoreboard__logo">RH</span>
       <span class="scoreboard__team">Rockwall-Heath</span>
       <span class="scoreboard__score">28</span></div>
     <div class="scoreboard__row">…</div>
     <p class="scoreboard__meta">Wilkerson-Sanders Stadium</p>
   </div>

   <div class="map-wrap">                   (set ratio with style="--ratio: 1")
     <svg class="map-wrap__svg" viewBox…>
       <path class="map-region is-active" …/> <circle class="map-dot" …/></svg>
     <button class="map-wrap__pin map-wrap__pin--alert" style="left:42%;top:30%">3</button>
     <div class="map-wrap__legend">…chips…</div>
     <div class="map-wrap__tooltip" style="left:…;top:…">Dallas</div>
   </div>
   ========================================================================== */
.price { display: inline-flex; align-items: baseline; gap: 6px; font-family: var(--font-display); font-size: var(--fs-md); font-weight: 700; line-height: 1.1; font-variant-numeric: tabular-nums; }
.price--lg { font-size: var(--fs-2xl); }
.price--free { color: var(--ok-text); }
.price__unit { font-family: var(--font-ui); font-size: 0.7em; font-weight: 500; color: var(--muted); }
.price__was { font-family: var(--font-ui); font-size: 0.7em; font-weight: 400; color: var(--muted); }

.scoreboard { display: grid; gap: var(--sp-2); }
.scoreboard__head { display: flex; align-items: center; justify-content: space-between; gap: var(--sp-2); }
.scoreboard__row { display: grid; grid-template-columns: auto minmax(0, 1fr) auto; align-items: center; gap: var(--sp-3); color: var(--ink-2); }
.scoreboard__row + .scoreboard__row { padding-top: var(--sp-2); border-top: 1px solid var(--line); }
.scoreboard__logo { display: grid; place-items: center; width: 34px; height: 34px; background: var(--surface-2); border-radius: 50%; font-family: var(--font-mono); font-size: 0.6875rem; font-weight: 700; overflow: hidden; }
.scoreboard__team { font-size: var(--fs-sm); font-weight: 500; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.scoreboard__score { font-family: var(--font-mono); font-size: 1.5rem; font-weight: 700; line-height: 1; font-variant-numeric: tabular-nums; }
.scoreboard__row.is-winner { color: var(--ink); }
.scoreboard__row.is-winner .scoreboard__team { font-weight: 700; }
.scoreboard__meta { font-size: var(--fs-xs); color: var(--muted); }

.map-wrap { position: relative; aspect-ratio: var(--ratio, 4 / 3); background: var(--surface-2); border: 1px solid var(--line); border-radius: var(--r-lg); overflow: hidden; }
.map-wrap__svg, .map-wrap > svg, .map-wrap > iframe { width: 100%; height: 100%; border: 0; }
.map-region { fill: var(--surface); stroke: var(--line-strong); stroke-width: 1.5; stroke-linejoin: round; cursor: pointer; transition: fill var(--dur); }
.map-region:hover, .map-region:focus-visible { fill: var(--accent-soft); outline: none; }
.map-region.is-active { fill: var(--accent); stroke: var(--accent); }
.map-road { fill: none; stroke: var(--line-strong); stroke-width: 2; stroke-linecap: round; }
.map-road--slow { stroke: var(--accent); }
.map-road--jam { stroke: var(--alert); }
.map-dot { fill: var(--ink); stroke: var(--surface); stroke-width: 2; }
.map-dot.is-active { fill: var(--accent); }
.map-label { font-family: var(--font-mono); font-size: 10px; fill: var(--ink-2); pointer-events: none; }

.map-wrap__pin {
  position: absolute;
  display: grid;
  place-items: center;
  min-width: 26px;
  height: 26px;
  padding: 0 6px;
  background: var(--ink);
  color: var(--bg);
  border: 2px solid var(--surface);
  border-radius: var(--r-pill);
  font-family: var(--font-mono);
  font-size: 0.6875rem;
  font-weight: 700;
  transform: translate(-50%, -50%);
}

.map-wrap__pin--live { background: var(--accent); color: var(--accent-ink); animation: pulse 1.6s ease-out infinite; }
.map-wrap__pin--alert { background: var(--alert); color: #fff; }
.map-wrap__pin--ok { background: var(--ok); color: #fff; }
.map-wrap__legend { position: absolute; inset: auto var(--sp-3) var(--sp-3); display: flex; flex-wrap: wrap; gap: 6px; }
.map-wrap__tooltip { position: absolute; z-index: 2; padding: 6px 10px; background: var(--ink); color: var(--bg); border-radius: var(--r-sm); font-size: var(--fs-xs); font-weight: 600; white-space: nowrap; transform: translate(-50%, calc(-100% - 10px)); pointer-events: none; }

/* ==========================================================================
   13. FOOTER (rendered by app.js into <footer id="site-footer">)
   <footer class="footer" id="site-footer">
     <div class="container">
       <div class="footer__top">
         <div class="footer__brand"><span class="footer__logo">BOLO Texas</span>
           <p class="footer__tagline">…</p></div>
         <form class="footer__signup">…input + button…</form></div>
       <div class="footer__cols">
         <div class="footer__col"><h3 class="footer__title">Sections</h3>
           <ul class="footer__links"><li><a href>News</a></li>…</ul></div>…</div>
       <div class="footer__cloud">
         <div class="footer__region"><h3 class="footer__title"><a href>North Texas / DFW</a></h3>
           <ul class="footer__cities"><li><a href>Dallas</a></li>…</ul></div>…</div>
       <div class="footer__legal"><p>© BOLO Texas</p>
         <ul class="footer__legal-links"><li><a href>Terms</a></li>…</ul></div>
     </div>
   </footer>
   Always navy, in both themes.
   ========================================================================== */
.footer {
  margin-top: var(--sp-8);
  padding-block: var(--sp-7) var(--sp-5);
  background: #0e1626;
  color: #e9e3d6;
  font-size: var(--fs-sm);
}

.footer a { color: inherit; opacity: 0.82; }
.footer a:hover { color: #fff; opacity: 1; text-decoration: underline; text-underline-offset: 3px; }
.footer__top { display: grid; gap: var(--sp-5); padding-bottom: var(--sp-6); border-bottom: 1px solid rgba(255, 255, 255, 0.14); }
.footer__logo { font-family: var(--font-display); font-size: 2rem; font-weight: 800; letter-spacing: -0.03em; line-height: 1; color: #fff; }
.footer__tagline { max-width: 42ch; margin-top: var(--sp-2); opacity: 0.75; }
.footer__signup { display: flex; gap: var(--sp-2); align-self: center; }
.footer__signup .input { background: rgba(255, 255, 255, 0.08); border-color: rgba(255, 255, 255, 0.25); color: #fff; border-radius: var(--r-pill); }
.footer__signup .input::placeholder { color: rgba(255, 255, 255, 0.6); }
.footer__cols { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: var(--sp-5); padding-block: var(--sp-6); }
.footer__title { margin-bottom: var(--sp-3); font-family: var(--font-mono); font-size: 0.6875rem; font-weight: 700; letter-spacing: var(--tracking-kicker); text-transform: uppercase; color: #f79668; }
.footer__title a { opacity: 1; }
.footer__links { display: grid; gap: var(--sp-2); }
.footer__cloud { display: grid; gap: var(--sp-5); padding-block: var(--sp-6); border-top: 1px solid rgba(255, 255, 255, 0.14); }
.footer__cities { display: flex; flex-wrap: wrap; gap: 4px 14px; font-size: var(--fs-xs); line-height: 1.7; }
.footer__legal { display: flex; flex-wrap: wrap; justify-content: space-between; gap: var(--sp-3); padding-top: var(--sp-5); border-top: 1px solid rgba(255, 255, 255, 0.14); font-size: var(--fs-xs); opacity: 0.85; }
.footer__legal-links { display: flex; flex-wrap: wrap; gap: 4px 16px; }

@media (min-width: 600px) {
  .footer__cols { grid-template-columns: repeat(4, minmax(0, 1fr)); }
  .footer__cloud { grid-template-columns: repeat(2, minmax(0, 1fr)); }
}

@media (min-width: 900px) {
  .footer__top { grid-template-columns: minmax(0, 1fr) minmax(0, 420px); }
  .footer__cloud { grid-template-columns: repeat(4, minmax(0, 1fr)); }
}

/* ==========================================================================
   14. SHARED FIXES AND SMALL MODIFIERS
   Rules that page agents had to patch locally. Everything here is additive:
   no existing class changes its name or its markup.
   ========================================================================== */

/* A navy "stage" that stays dark in both themes (the footer colour), for
   full-screen viewers and dark bands. --stage-ink is the text on it. */
:root {
  --stage: #0e1626;
  --stage-ink: #e9e3d6;
}

/* In-page anchors clear the sticky header. :where() keeps the specificity
   at zero, so any page rule that sets its own offset still wins. */
:where(main [id]) { scroll-margin-top: calc(var(--header-h) + var(--sp-4)); }

/* Linked data chips (a.chip--data) get a hover that suits the tinted pill. */
a.chip--data:hover, button.chip--data:hover { background: var(--line); border-color: transparent; color: var(--ink); }

/* Moderate / warning tone: between neutral and alert. */
.badge--warn { background: var(--accent-soft); color: var(--accent-text); }
.chip--warn { background: var(--accent-soft); border-color: transparent; color: var(--accent-text); }

/* Ad slots inside a rail take the rail's card width, not the full row. */
.rail > .ad-slot { flex: 0 0 auto; margin-inline: 0; }
.rail--cards > .ad-slot { width: min(78vw, 280px); }
.rail--posters > .ad-slot { width: 180px; }
.rail--tiles > .ad-slot { width: 160px; }
.rail > .ad-slot--native { aspect-ratio: auto; }

/* Ads and cards nested in .prose keep their own link styling. */
.prose .ad-slot a, .prose .card a { text-decoration: none; }
.prose .ad-slot__link, .prose .ad-slot__link:hover { color: var(--on-media); }
.prose .ad-slot--sponsor-badge .ad-slot__link { color: var(--ink); }
.prose .card__link, .prose .card__link:hover { color: inherit; }

/* A sponsor badge in a .stack keeps its pill width instead of stretching. */
.stack > .ad-slot--sponsor-badge { align-self: flex-start; }

/* Paired fields in a .form-row stay top-aligned when only one has a hint. */
.field { align-content: start; }

/* A bento grid in the main column of .layout-sidebar has less room, so it
   stays at two columns until the page is wide enough for four. */
@media (min-width: 900px) and (max-width: 1199.98px) {
  .layout-sidebar .bento { grid-template-columns: repeat(2, minmax(0, 1fr)); }
  .layout-sidebar .bento > .span-3,
  .layout-sidebar .bento > .span-4 { grid-column: 1 / -1; }
}

/* Sidebar on the left (a sticky table of contents beside long text):
   <div class="layout-sidebar layout-sidebar--left"><aside class="layout-sidebar__aside">…</aside><div>…</div></div> */
@media (min-width: 900px) {
  .layout-sidebar--left { grid-template-columns: 240px minmax(0, 1fr); }
}

/* Button row inside a card (BOLO.card.business with { actions: true }). It
   sits above the card's stretched link so each button is clickable. */
.card__actions { position: relative; z-index: 2; display: flex; flex-wrap: wrap; gap: 6px; padding-top: var(--sp-2); }

/* Pricing card: <article class="card card--plan"> with a .card__price figure
   in .card__body. Add .card--featured to outline the recommended plan. */
.card--plan .card__body { gap: var(--sp-3); padding: var(--sp-5); }
.card__price { font-family: var(--font-display); font-size: var(--fs-2xl); font-weight: 700; line-height: 1; font-variant-numeric: tabular-nums; }
.card__price small { font-family: var(--font-ui); font-size: var(--fs-sm); font-weight: 500; color: var(--muted); }

/* ==========================================================================
   15. PAGE FURNITURE
   Breadcrumbs
     <nav class="breadcrumbs" aria-label="Breadcrumb"><ol>
       <li><a href="index.html">Texas</a></li>
       <li><a href="city.html?c=dallas">Dallas</a></li>
       <li aria-current="page">Oak Cliff</li></ol></nav>
   Add .breadcrumbs--on-media when it sits on a photo.

   Page hero: full-bleed photo with scrim and overlaid title, for city, zone
   and region pages.
     <section class="page-hero">
       <img class="page-hero__media" src alt="">
       <div class="page-hero__body container">
         <nav class="breadcrumbs breadcrumbs--on-media">…</nav>
         <h1 class="page-hero__title">Dallas</h1>
         <p class="page-hero__text">Big D, zone by zone</p>
         <div class="page-hero__row">chips, .ad-slot--sponsor-badge</div>
       </div>
     </section>
   Modifiers: --compact (shorter), --card (rounded, for use inside a container).

   Section nav: sticky strip of in-page anchors under the header.
     <nav class="section-nav" aria-label="On this page"><div class="container">
       <div class="section-nav__list">
         <a class="section-nav__link" href="#news" aria-current="true">News</a>…
       </div></div></nav>

   Section sub-row: sponsor badge or note directly under a .section-head.
     <div class="section-sub">…</div>

   CTA panel: title, text and a button row.
     <div class="cta-panel">
       <div class="cta-panel__body"><h2 class="cta-panel__title">…</h2><p class="cta-panel__text">…</p></div>
       <div class="cta-panel__actions"><a class="btn btn--primary">…</a></div>
     </div>
   Modifier: --ink (navy band in both themes).

   Empty state: dashed box for "nothing here yet".
     <div class="empty-state"><svg…>
       <h3 class="empty-state__title">No listings match</h3>
       <p class="empty-state__text">Try a wider area.</p>
       <div class="empty-state__actions"><button class="btn">Clear filters</button></div></div>
   ========================================================================== */
.breadcrumbs ol { display: flex; flex-wrap: wrap; align-items: center; gap: 4px 0; margin: 0; padding: 0; list-style: none; font-family: var(--font-mono); font-size: 0.6875rem; font-weight: 700; letter-spacing: var(--tracking-kicker); text-transform: uppercase; color: var(--muted); }
.breadcrumbs li { display: inline-flex; align-items: center; }
.breadcrumbs li + li::before { content: ""; width: 6px; height: 6px; margin-inline: 8px 12px; border-top: 1.5px solid currentColor; border-right: 1.5px solid currentColor; transform: rotate(45deg); opacity: 0.6; }
.breadcrumbs a { color: inherit; }
.breadcrumbs a:hover { color: var(--ink); text-decoration: underline; text-underline-offset: 3px; }
.breadcrumbs [aria-current] { color: var(--ink); }
.breadcrumbs--on-media ol { color: var(--on-media-muted); }
.breadcrumbs--on-media a:hover, .breadcrumbs--on-media [aria-current] { color: var(--on-media); }

.page-hero { position: relative; isolation: isolate; display: grid; align-items: end; min-height: 300px; background: var(--stage); color: var(--on-media); overflow: hidden; }
.page-hero__media { position: absolute; inset: 0; z-index: -2; width: 100%; height: 100%; object-fit: cover; }
.page-hero::before { content: ""; position: absolute; inset: 0 0 auto; z-index: -1; height: 40%; background: var(--scrim-top); }
.page-hero::after { content: ""; position: absolute; inset: 0; z-index: -1; background: var(--scrim); }
.page-hero__body { display: grid; gap: var(--sp-3); padding-block: var(--sp-7) var(--sp-5); }
.page-hero__title { font-size: var(--fs-display); line-height: var(--lh-tight); color: var(--on-media); text-shadow: 0 1px 16px rgba(0, 0, 0, 0.35); }
.page-hero__text { max-width: 44ch; font-size: var(--fs-md); color: var(--on-media-muted); }
.page-hero__row { display: flex; flex-wrap: wrap; align-items: center; gap: var(--sp-2); }
.page-hero--compact { min-height: 220px; }
.page-hero--card { border-radius: var(--r-lg); }
.page-hero--card .page-hero__body { padding-inline: var(--sp-4); }

@media (min-width: 900px) {
  .page-hero { min-height: 420px; }
  .page-hero--compact { min-height: 280px; }
  .page-hero__body { padding-block: var(--sp-9) var(--sp-6); }
  .page-hero--card .page-hero__body { padding-inline: var(--sp-6); }
}

.section-nav { background: var(--bg); border-block: 1px solid var(--line); }
.section-nav__list { display: flex; gap: var(--sp-1); overflow-x: auto; margin-inline: calc(var(--gutter) * -1); padding-inline: var(--gutter); scrollbar-width: none; }
.section-nav__list::-webkit-scrollbar { display: none; }
.section-nav__link { flex: none; display: inline-flex; align-items: center; min-height: 44px; padding: 0 var(--sp-3); border-bottom: 2px solid transparent; font-size: var(--fs-sm); font-weight: 600; color: var(--muted); white-space: nowrap; }
.section-nav__link:hover { color: var(--ink); }
.section-nav__link[aria-current="true"], .section-nav__link[aria-current="location"], .section-nav__link.is-active { color: var(--ink); border-bottom-color: var(--accent); }

@media (min-width: 900px) {
  .section-nav { position: sticky; top: var(--header-h); z-index: var(--z-sticky); }
}

.section-sub { display: flex; flex-wrap: wrap; align-items: center; gap: var(--sp-2) var(--sp-3); margin-bottom: var(--sp-4); font-size: var(--fs-sm); color: var(--muted); }

.cta-panel { display: grid; gap: var(--sp-4); padding: var(--sp-5); background: var(--surface-2); border-radius: var(--r-lg); }
.cta-panel__body { display: grid; gap: var(--sp-2); }
.cta-panel__title { font-size: var(--fs-lg); }
.cta-panel__text { max-width: 56ch; color: var(--ink-2); }
.cta-panel__actions { display: flex; flex-wrap: wrap; gap: var(--sp-2); }
.cta-panel--ink { background: var(--stage); color: var(--stage-ink); }
.cta-panel--ink .cta-panel__title { color: #fff; }
.cta-panel--ink .cta-panel__text { color: inherit; opacity: 0.85; }
.cta-panel--ink .btn--ghost { border-color: rgba(255, 255, 255, 0.4); color: #fff; }
.cta-panel--ink .btn--ghost:hover { background: rgba(255, 255, 255, 0.12); border-color: #fff; color: #fff; }

@media (min-width: 900px) {
  .cta-panel { grid-template-columns: minmax(0, 1fr) auto; align-items: center; padding: var(--sp-6); }
}

.empty-state { display: grid; justify-items: center; gap: var(--sp-2); padding: var(--sp-6) var(--sp-4); background: var(--surface); border: 1px dashed var(--line-strong); border-radius: var(--r-md); text-align: center; }
.empty-state > svg { width: 32px; height: 32px; color: var(--muted); }
.empty-state__title { font-size: var(--fs-md); }
.empty-state__text { max-width: 44ch; font-size: var(--fs-sm); color: var(--muted); }
.empty-state__actions { display: flex; flex-wrap: wrap; justify-content: center; gap: var(--sp-2); margin-top: var(--sp-2); }

/* ==========================================================================
   16. CONTENT BLOCKS
   Callout: tinted note for legal, safety and "in short" copy.
     <div class="callout callout--alert"><svg…>
       <div class="callout__body"><strong class="callout__title">Meet in public</strong>
         <p>Use a Safe Exchange Zone.</p></div></div>
   Modifiers: --ok, --alert, --accent. The icon is optional.

   Alert bar: one active alert, by level.
     <div class="alert-bar alert-bar--warning"><svg…>
       <div class="alert-bar__body"><strong class="alert-bar__title">…</strong>
         <p class="alert-bar__text">…</p></div>
       <span class="alert-bar__meta">12m ago</span></div>
   Levels: --warning (red), --advisory (orange), --notice (neutral, default).

   Box: plain bordered panel for text, forms and settings.
     <section class="box"><h2 class="box__title">Alerts</h2>…</section>
   Modifier: --tint (filled, no border).

   Toggle row: label on the left, switch on the right (settings lists).
     <label class="toggle-row"><span class="toggle-row__text">
         <span class="toggle-row__label">Traffic alerts</span>
         <span class="toggle-row__hint">Crashes and closures</span></span>
       <span class="toggle"><input type="checkbox" role="switch"><span class="toggle__track"></span></span></label>

   Accordion: native <details>, one per item.
     <div class="accordion"><details class="accordion__item">
       <summary>How are ads labelled?</summary>
       <div class="accordion__body">…</div></details></div>

   Avatar: initials circle (BOLO.initials(name)); may hold an <img>.
     <span class="avatar" aria-hidden="true">MT</span>   --sm, --lg, --accent

   Key / value list: listing attributes, ad specs.
     <dl class="kv"><div class="kv__row"><dt>Condition</dt><dd>Good</dd></div>…</dl>

   Check list: <ul class="list-check"><li>…</li></ul>

   Table: <div class="table-wrap"><table class="table">…</table></div>
   .table--compact tightens it; td.num / th.num right-align numbers;
   tr.is-active highlights a row.

   Date row: schedule or agenda line with a month / day block.
     <ul class="date-list"><li class="date-row">
       <div class="date-row__date"><span class="date-row__month">Nov</span><span class="date-row__day">14</span></div>
       <div class="date-row__body"><p class="date-row__title">…</p><p class="date-row__meta">…</p></div>
       <span class="badge">7:30 PM</span></li></ul>

   Icon chip: round tinted icon holder for tiles and list rows.
     <span class="icon-chip icon-chip--alert"><svg…></span>
   Tones: --accent, --ok, --alert, --ink. Sizes: --sm, --lg.

   Icon tile: category picker tile (icon, label, optional count).
     <div class="icon-tiles"><button class="icon-tile" aria-pressed="true"><svg…>
       <span class="icon-tile__label">Vehicles</span><span class="icon-tile__count">6</span></button>…</div>
   ========================================================================== */
.callout { display: flex; gap: var(--sp-3); padding: var(--sp-3) var(--sp-4); background: var(--surface-2); border: 1px solid var(--line); border-left: 4px solid var(--line-strong); border-radius: var(--r-md); font-size: var(--fs-sm); line-height: 1.5; }
.callout > svg { flex: none; width: 20px; height: 20px; margin-top: 1px; color: var(--ink-2); }
.callout__body { display: grid; gap: 4px; min-width: 0; }
.callout__title { font-weight: 700; }
.callout a { text-decoration: underline; text-underline-offset: 3px; }
.callout--ok { background: var(--ok-soft); border-color: transparent; border-left-color: var(--ok); }
.callout--ok > svg, .callout--ok .callout__title { color: var(--ok-text); }
.callout--alert { background: var(--alert-soft); border-color: transparent; border-left-color: var(--alert); }
.callout--alert > svg, .callout--alert .callout__title { color: var(--alert-text); }
.callout--accent { background: var(--accent-soft); border-color: transparent; border-left-color: var(--accent); }
.callout--accent > svg, .callout--accent .callout__title { color: var(--accent-text); }

.alert-bar { display: flex; align-items: flex-start; gap: var(--sp-3); padding: var(--sp-3) var(--sp-4); background: var(--surface-2); border-radius: var(--r-md); font-size: var(--fs-sm); line-height: 1.45; }
.alert-bar > svg { flex: none; width: 22px; height: 22px; margin-top: 1px; }
.alert-bar__body { display: grid; gap: 2px; flex: 1; min-width: 0; }
.alert-bar__title { font-weight: 700; }
.alert-bar__text { color: var(--ink-2); }
.alert-bar__meta { flex: none; font-family: var(--font-mono); font-size: var(--fs-xs); color: var(--muted); white-space: nowrap; }
.alert-bar--warning { background: var(--alert-soft); }
.alert-bar--warning > svg, .alert-bar--warning .alert-bar__title { color: var(--alert-text); }
.alert-bar--advisory { background: var(--accent-soft); }
.alert-bar--advisory > svg, .alert-bar--advisory .alert-bar__title { color: var(--accent-text); }

.box { padding: var(--sp-4); background: var(--surface); border: 1px solid var(--line); border-radius: var(--r-md); }
.box--tint { background: var(--surface-2); border-color: transparent; }
.box__title { margin-bottom: var(--sp-3); font-size: var(--fs-md); }

@media (min-width: 600px) {
  .box { padding: var(--sp-5); }
}

.toggle-row { display: flex; align-items: center; justify-content: space-between; gap: var(--sp-4); padding-block: var(--sp-3); cursor: pointer; }
.toggle-row + .toggle-row { border-top: 1px solid var(--line); }
.toggle-row__text { display: grid; gap: 2px; min-width: 0; }
.toggle-row__label { font-size: var(--fs-sm); font-weight: 600; }
.toggle-row__hint { font-size: var(--fs-xs); color: var(--muted); }
.toggle-row .toggle { flex: none; }

.accordion { border-top: 1px solid var(--line); }
.accordion__item { border-bottom: 1px solid var(--line); }
.accordion__item > summary { display: flex; align-items: center; justify-content: space-between; gap: var(--sp-4); padding: var(--sp-4) 0; font-weight: 600; cursor: pointer; list-style: none; }
.accordion__item > summary::-webkit-details-marker { display: none; }
.accordion__item > summary::after { content: ""; flex: none; width: 9px; height: 9px; margin-right: 4px; border-right: 2px solid var(--muted); border-bottom: 2px solid var(--muted); transform: translateY(-2px) rotate(45deg); transition: transform var(--dur) var(--ease); }
.accordion__item[open] > summary::after { transform: translateY(2px) rotate(-135deg); }
.accordion__item > summary:hover { color: var(--accent-text); }
.accordion__body { padding-bottom: var(--sp-4); font-size: var(--fs-sm); line-height: 1.55; color: var(--ink-2); }
.accordion__body > * + * { margin-top: var(--sp-2); }

.avatar { display: inline-grid; place-items: center; flex: none; width: 44px; height: 44px; background: var(--surface-2); color: var(--ink); border-radius: 50%; font-family: var(--font-mono); font-size: 0.8125rem; font-weight: 700; overflow: hidden; }
.avatar img { width: 100%; height: 100%; object-fit: cover; }
.avatar--sm { width: 32px; height: 32px; font-size: 0.6875rem; }
.avatar--lg { width: 64px; height: 64px; font-size: 1.125rem; }
.avatar--accent { background: var(--accent-soft); color: var(--accent-text); }

.kv { display: grid; margin: 0; }
.kv__row { display: flex; align-items: baseline; justify-content: space-between; gap: var(--sp-4); padding-block: var(--sp-2); border-bottom: 1px solid var(--line); font-size: var(--fs-sm); }
.kv__row:last-child { border-bottom: 0; }
.kv dt { color: var(--muted); }
.kv dd { margin: 0; font-weight: 600; text-align: right; overflow-wrap: anywhere; }

.list-check { display: grid; gap: var(--sp-2); margin: 0; padding: 0; list-style: none; font-size: var(--fs-sm); line-height: 1.45; }
.list-check > li { position: relative; padding-left: 26px; }
.list-check > li::before { content: ""; position: absolute; left: 5px; top: 0.2em; width: 6px; height: 11px; border-right: 2px solid var(--ok); border-bottom: 2px solid var(--ok); transform: rotate(45deg); }

.table-wrap { overflow-x: auto; }
.table { width: 100%; border-collapse: collapse; font-size: var(--fs-sm); font-variant-numeric: tabular-nums; }
.table th, .table td { padding: var(--sp-2) var(--sp-3); border-bottom: 1px solid var(--line); text-align: left; vertical-align: middle; }
.table thead th { font-family: var(--font-mono); font-size: 0.6875rem; font-weight: 700; letter-spacing: var(--tracking-kicker); text-transform: uppercase; color: var(--muted); border-bottom-color: var(--line-strong); white-space: nowrap; }
.table tbody th { font-weight: 600; }
.table .num { text-align: right; }
.table tr.is-active > * { background: var(--surface-2); font-weight: 700; }
.table--compact th, .table--compact td { padding: 6px var(--sp-2); }

.date-list { display: grid; margin: 0; padding: 0; list-style: none; }
.date-row { display: grid; grid-template-columns: auto minmax(0, 1fr) auto; align-items: center; gap: var(--sp-3); padding-block: var(--sp-3); }
.date-row + .date-row { border-top: 1px solid var(--line); }
.date-row__date { display: grid; place-items: center; width: 48px; padding: 6px 0; background: var(--surface-2); border-radius: var(--r-sm); line-height: 1; }
.date-row__month { font-family: var(--font-mono); font-size: 0.625rem; font-weight: 700; letter-spacing: 0.1em; text-transform: uppercase; color: var(--accent-text); }
.date-row__day { margin-top: 3px; font-family: var(--font-display); font-size: var(--fs-lg); font-weight: 700; }
.date-row__body { display: grid; gap: 2px; min-width: 0; }
.date-row__title { font-size: var(--fs-sm); font-weight: 600; }
.date-row__meta { font-size: var(--fs-xs); color: var(--muted); }

.icon-chip { display: inline-grid; place-items: center; flex: none; width: 40px; height: 40px; background: var(--surface-2); color: var(--ink); border-radius: 50%; }
.icon-chip svg { width: 20px; height: 20px; }
.icon-chip--sm { width: 32px; height: 32px; }
.icon-chip--sm svg { width: 16px; height: 16px; }
.icon-chip--lg { width: 52px; height: 52px; }
.icon-chip--lg svg { width: 26px; height: 26px; }
.icon-chip--accent { background: var(--accent-soft); color: var(--accent-text); }
.icon-chip--ok { background: var(--ok-soft); color: var(--ok-text); }
.icon-chip--alert { background: var(--alert-soft); color: var(--alert-text); }
.icon-chip--ink { background: var(--ink); color: var(--bg); }

.icon-tiles { display: grid; grid-template-columns: repeat(auto-fill, minmax(96px, 1fr)); gap: var(--sp-2); }
.icon-tile { display: grid; justify-items: center; align-content: center; gap: 6px; min-height: 92px; padding: var(--sp-3) var(--sp-2); background: var(--surface); color: var(--ink); border: 1px solid var(--line); border-radius: var(--r-md); text-align: center; transition: background var(--dur-fast), border-color var(--dur-fast); }
.icon-tile svg { width: 26px; height: 26px; color: var(--accent-text); }
.icon-tile:hover { border-color: var(--ink); color: var(--ink); }
.icon-tile__label { font-size: var(--fs-sm); font-weight: 600; line-height: 1.2; }
.icon-tile__count { font-family: var(--font-mono); font-size: var(--fs-xs); color: var(--muted); }
.icon-tile.is-active, .icon-tile[aria-pressed="true"], .icon-tile[aria-current="true"] { background: var(--accent-soft); border-color: var(--accent); box-shadow: 0 0 0 1px var(--accent); }

/* ==========================================================================
   17. LISTING AND MEDIA PIECES
   Gallery: large photo with arrows, a counter and a thumbnail strip.
     <div class="gallery">
       <div class="gallery__stage"><img src alt>
         <button class="gallery__nav gallery__nav--prev icon-btn icon-btn--solid" aria-label="Previous photo">…</button>
         <button class="gallery__nav gallery__nav--next icon-btn icon-btn--solid" aria-label="Next photo">…</button>
         <span class="gallery__count">2 / 5</span></div>
       <div class="gallery__thumbs">
         <button class="gallery__thumb" aria-current="true"><img src alt></button>…</div>
     </div>
   Set the stage shape with style="--ratio: 4 / 3".

   Sticky actions: button bar fixed above the tab bar on phones (Message
   seller, Make offer). It becomes an ordinary row from 900px. Put
   .has-sticky-actions on <body> so the page keeps room for it.
     <div class="sticky-actions"><a class="btn btn--primary">Message</a>…</div>

   Chat additions: <p class="chat__typing"><i></i><i></i><i></i> Marcus is typing…</p>
   (the dots and the text are each optional) and .chat__msg--offer, added
   beside --me or --them, for an offer bubble.

   Upload additions: .upload__remove (x button on a thumbnail),
   .upload__thumb.is-cover (accent ring on the cover photo; a .badge inside a
   thumbnail is pinned bottom-left) and .upload__file (tile for a file that
   is not an image).

   Range: <input class="range" type="range"> with an optional
   <div class="range__scale"><span>1 week</span><span>12 weeks</span></div>.
   ========================================================================== */
.gallery { display: grid; gap: var(--sp-2); min-width: 0; }
.gallery__stage { position: relative; aspect-ratio: var(--ratio, 4 / 3); background: var(--surface-2); border-radius: var(--r-lg); overflow: hidden; }
.gallery__stage > img { width: 100%; height: 100%; object-fit: cover; }
.gallery__nav { position: absolute; top: 50%; z-index: 2; transform: translateY(-50%); }
.gallery__nav--prev { left: var(--sp-2); }
.gallery__nav--next { right: var(--sp-2); }
.gallery__nav[disabled] { opacity: 0.4; pointer-events: none; }
.gallery__count { position: absolute; right: var(--sp-3); bottom: var(--sp-3); z-index: 2; padding: 3px 8px; background: rgba(9, 14, 28, 0.8); color: #fff; border-radius: var(--r-sm); font-family: var(--font-mono); font-size: 0.6875rem; font-weight: 700; }
.gallery__thumbs { display: flex; gap: var(--sp-2); overflow-x: auto; padding: 2px; scrollbar-width: none; }
.gallery__thumbs::-webkit-scrollbar { display: none; }
.gallery__thumb { flex: none; width: 64px; height: 64px; border: 2px solid transparent; border-radius: var(--r-sm); overflow: hidden; opacity: 0.7; transition: opacity var(--dur-fast), border-color var(--dur-fast); }
.gallery__thumb img { width: 100%; height: 100%; object-fit: cover; }
.gallery__thumb:hover { opacity: 1; }
.gallery__thumb.is-active, .gallery__thumb[aria-current="true"] { border-color: var(--accent); opacity: 1; }

.sticky-actions { position: fixed; inset: auto 0 calc(var(--tabbar-h) + env(safe-area-inset-bottom, 0px)); z-index: var(--z-sticky); display: flex; align-items: center; gap: var(--sp-2); padding: var(--sp-2) var(--gutter); background: var(--surface); border-top: 1px solid var(--line); box-shadow: var(--shadow-md); }
.sticky-actions > .btn { flex: 1; }
.has-sticky-actions main { padding-bottom: 64px; }

@media (min-width: 900px) {
  .sticky-actions { position: static; padding: 0; background: none; border: 0; box-shadow: none; }
  .sticky-actions > .btn { flex: none; }
  .has-sticky-actions main { padding-bottom: 0; }
}

.chat__typing { align-self: flex-start; display: inline-flex; align-items: center; gap: 4px; font-size: var(--fs-xs); color: var(--muted); }
.chat__typing > i { width: 6px; height: 6px; background: var(--muted); border-radius: 50%; animation: chat-typing 1.2s ease-in-out infinite; }
.chat__typing > i:nth-child(2) { animation-delay: 0.15s; }
.chat__typing > i:nth-child(3) { animation-delay: 0.3s; }
.chat__msg--offer { border: 1px solid var(--accent); }
.chat__msg--offer.chat__msg--them { background: var(--accent-soft); }

@keyframes chat-typing {
  0%, 60%, 100% { opacity: 0.35; transform: none; }
  30% { opacity: 1; transform: translateY(-3px); }
}

.upload__remove { position: absolute; top: 4px; right: 4px; z-index: 2; display: grid; place-items: center; width: 24px; height: 24px; background: rgba(9, 14, 28, 0.8); color: #fff; border-radius: 50%; }
.upload__remove svg { width: 12px; height: 12px; }
.upload__thumb.is-cover { box-shadow: 0 0 0 2px var(--accent); }
.upload__thumb > .badge { position: absolute; left: 6px; bottom: 6px; }
.upload__file { display: grid; place-items: center; align-content: center; gap: 4px; padding: var(--sp-2); background: var(--surface-2); font-family: var(--font-mono); font-size: 0.625rem; color: var(--ink-2); text-align: center; overflow-wrap: anywhere; }

.range { width: 100%; height: 28px; accent-color: var(--accent); cursor: pointer; }
.range__scale { display: flex; justify-content: space-between; gap: var(--sp-3); font-family: var(--font-mono); font-size: var(--fs-xs); color: var(--muted); }

/* ==========================================================================
   18. MAP ADDITIONS
   More pin tones: .map-wrap__pin--slow (moderate) and --utility (outlined).
   .map-wrap__pin.is-active marks the selected pin.
   .map-wrap__tooltip--below drops the tooltip under a pin near the top edge.
   .map-wrap__label is an HTML label positioned with left / top in %, which
   stays readable on phones (SVG .map-label text shrinks with the viewBox).
   Variants: --road (pill) and --water (italic).
   ========================================================================== */
.map-wrap__pin--slow { background: var(--accent-soft); color: var(--accent-text); border-color: var(--accent); }
.map-wrap__pin--utility { background: var(--surface); color: var(--ink); border-color: var(--line-strong); }
.map-wrap__pin.is-active, .map-wrap__pin[aria-pressed="true"] { z-index: 2; box-shadow: 0 0 0 3px var(--focus); transform: translate(-50%, -50%) scale(1.15); }
.map-wrap__tooltip--below { transform: translate(-50%, 14px); }
.map-wrap__label { position: absolute; font-family: var(--font-mono); font-size: 0.625rem; font-weight: 700; letter-spacing: 0.06em; text-transform: uppercase; color: var(--ink-2); white-space: nowrap; transform: translate(-50%, -50%); pointer-events: none; }
.map-wrap__label--road { padding: 1px 6px; background: var(--surface); border: 1px solid var(--line); border-radius: var(--r-pill); letter-spacing: 0; }
.map-wrap__label--water { font-style: italic; font-weight: 500; text-transform: none; color: var(--muted); }
