/* DirectoryEngine base stylesheet — the design system (epic-011 task-067).
 * CLEAN & TRUSTWORTHY: content-first, crisp type, restrained color, generous whitespace.
 *
 * Theming model: every color/space/radius/type value is a --de-* token. A directory
 * re-themes by overriding ONE seed, --de-color-brand; the brand scale (-strong/-soft/-tint)
 * and every interactive surface derive from it via color-mix(), so a single hex change
 * re-skins the whole site with no per-call-site colors. The shell emits a tiny per-directory
 * :root override (see PageChrome) — nothing here or at call sites is hardcoded.
 * Epic-010's mobile-first behavior and the min-width:64rem desktop compaction are preserved. */

:root {
  /* ── Brand seed + derived scale ──────────────────────────────────────────
   * Override --de-color-brand per directory; the rest of the scale follows. */
  --de-color-brand: #1d4ed8;
  --de-color-brand-contrast: #ffffff;
  /* A darker brand for hover/active and text-on-light links (keeps AA contrast). */
  --de-color-brand-strong: color-mix(in oklab, var(--de-color-brand), #000 18%);
  /* A 12% tint for soft fills (featured surface, hover backgrounds). */
  --de-color-brand-tint: color-mix(in oklab, var(--de-color-brand) 12%, var(--de-color-surface));
  /* A faint 6% wash for large fields (hero, featured rail). */
  --de-color-brand-wash: color-mix(in oklab, var(--de-color-brand) 6%, var(--de-color-surface));
  /* Brand-tinted border for emphasized cards without shouting. */
  --de-color-brand-border: color-mix(in oklab, var(--de-color-brand) 32%, var(--de-color-border));

  /* ── Neutral ramp ────────────────────────────────────────────────────────
   * A cool, slightly-blue gray ramp reads more credible than pure gray. */
  --de-color-text: #16202e;          /* near-black ink, AA on every surface */
  --de-color-text-muted: #5b6675;    /* secondary text, AA on white (4.7:1) */
  --de-color-text-subtle: #7a8595;   /* tertiary labels, large/decorative only */
  --de-color-surface: #ffffff;
  --de-color-surface-alt: #f6f8fb;   /* section wash, footer */
  --de-color-surface-sunk: #eef2f7;  /* count chips, code, inset wells */
  --de-color-border: #e2e8f0;
  --de-color-border-strong: #cbd5e1; /* card edges that need to read as a real line */

  /* ── Semantic colors ─────────────────────────────────────────────────────*/
  --de-color-danger: #c2261b;
  --de-color-danger-tint: color-mix(in oklab, var(--de-color-danger) 9%, var(--de-color-surface));
  --de-color-success: #1f7a4d;
  --de-color-star: #e0a106;          /* rating stars — gold, not brand-colored */

  /* ── Typography ──────────────────────────────────────────────────────────
   * Refined system stack (task-067): zero network cost, zero CLS, instant paint —
   * the SEO/Lighthouse-safe choice. Character comes from a real type scale, weight
   * contrast, tracking, and tabular numerals, not a downloaded face. */
  --de-font-sans: -apple-system, BlinkMacSystemFont, "Segoe UI Variable Text",
    "Segoe UI", Roboto, "Helvetica Neue", Arial, "Noto Sans", sans-serif;
  --de-font-mono: ui-monospace, SFMono-Regular, "SF Mono", "Cascadia Mono",
    Menlo, Consolas, monospace;
  --de-font-size-base: 1.0625rem;    /* 17px base — comfortable body reading */
  --de-line-height: 1.6;
  /* Modular type scale (~1.22), used for headings across pages. */
  --de-text-xs: 0.75rem;
  --de-text-sm: 0.875rem;
  --de-text-md: 1rem;
  --de-text-lg: 1.1875rem;
  --de-text-xl: 1.5rem;
  --de-text-2xl: 1.9375rem;
  --de-text-3xl: 2.5rem;
  --de-tracking-tight: -0.018em;     /* display headings */
  --de-tracking-label: 0.06em;       /* uppercase eyebrows/labels */
  --de-measure: 38rem;               /* readable line length for long-form */

  /* ── Spacing rhythm (unchanged scale + finer steps) ───────────────────────*/
  --de-space-1: 0.25rem;
  --de-space-2: 0.5rem;
  --de-space-3: 1rem;
  --de-space-4: 1.5rem;
  --de-space-5: 2.5rem;
  --de-space-6: 4rem;                /* major section breaks on the money pages */

  /* ── Layout, radius, elevation ────────────────────────────────────────────*/
  --de-content-max-width: 72rem;
  --de-radius-sm: 0.375rem;          /* chips, inputs */
  --de-radius: 0.625rem;             /* cards, buttons — crisp, not bubbly */
  --de-radius-lg: 1rem;              /* hero / large panels */
  /* Restrained elevation: tight, low-opacity, layered — never a heavy drop shadow. */
  --de-shadow-sm: 0 1px 2px rgb(15 33 56 / 0.06);
  --de-shadow-md: 0 1px 2px rgb(15 33 56 / 0.05), 0 4px 12px rgb(15 33 56 / 0.06);
  --de-shadow-lg: 0 2px 4px rgb(15 33 56 / 0.06), 0 12px 28px rgb(15 33 56 / 0.10);

  /* ── Focus ring (a11y: visible on every interactive control) ──────────────*/
  --de-focus-ring: 0 0 0 3px color-mix(in oklab, var(--de-color-brand) 40%, transparent);

  /* Minimum touch-target size (task-064, Mobile NFR): 44px on every interactive control.
   * Desktop-only compaction lives at the bottom of this file (min-width: 64rem). */
  --de-tap-size: 2.75rem;
}

*,
*::before,
*::after {
  box-sizing: border-box;
}

body {
  margin: 0;
  font-family: var(--de-font-sans);
  font-size: var(--de-font-size-base);
  line-height: var(--de-line-height);
  color: var(--de-color-text);
  background: var(--de-color-surface);
  /* Crisp rendering + sensible defaults across the type system. */
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
  text-rendering: optimizeLegibility;
  font-feature-settings: "kern" 1, "liga" 1;
}

/* Headings: real weight + tracking contrast against body text (the "designed" feel). */
h1, h2, h3, h4 {
  color: var(--de-color-text);
  line-height: 1.18;
  letter-spacing: var(--de-tracking-tight);
  text-wrap: balance;
}

h1 {
  font-size: var(--de-text-2xl);
  font-weight: 760;
}

h2 {
  font-size: var(--de-text-xl);
  font-weight: 700;
}

h3 {
  font-size: var(--de-text-lg);
  font-weight: 680;
  letter-spacing: -0.01em;
}

/* Counts, ratings, prices line up cleanly with tabular numerals. */
.city-row-count,
.listing-rating,
.sponsor-price,
.trust-line {
  font-variant-numeric: tabular-nums;
}

/* Default link treatment: brand, underlined with a calm offset — never raw blue. */
a {
  color: var(--de-color-brand-strong);
  text-underline-offset: 0.18em;
  text-decoration-thickness: from-font;
  text-decoration-color: color-mix(in oklab, currentColor 35%, transparent);
}

a:hover {
  text-decoration-color: currentColor;
}

/* A11y: one consistent, visible focus ring on every focusable control; never removed. */
:focus-visible {
  outline: 2px solid transparent;
  box-shadow: var(--de-focus-ring);
  border-radius: var(--de-radius-sm);
}

/* Respect reduced-motion globally (a11y); transitions collapse to instant. */
@media (prefers-reduced-motion: reduce) {
  *,
  *::before,
  *::after {
    animation-duration: 0.01ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 0.01ms !important;
    scroll-behavior: auto !important;
  }
}

/* Accessibility: skip link is visually hidden until focused */
.skip-link {
  position: absolute;
  left: var(--de-space-2);
  top: var(--de-space-2);
  display: inline-flex;
  align-items: center;
  /* Touch target floor (task-064, Mobile NFR): >= 44px. */
  min-height: var(--de-tap-size);
  padding: var(--de-space-2) var(--de-space-3);
  background: var(--de-color-brand);
  color: var(--de-color-brand-contrast);
  border-radius: var(--de-radius);
  transform: translateY(-200%);
  transition: transform 0.15s ease-in-out;
  z-index: 100;
}

.skip-link:focus {
  transform: translateY(0);
}

.site-header {
  border-bottom: 1px solid var(--de-color-border);
  background: color-mix(in oklab, var(--de-color-surface) 88%, transparent);
  /* Calm sticky header so the brand + cities stay reachable while scrolling the list. */
  position: sticky;
  top: 0;
  z-index: 40;
  backdrop-filter: saturate(1.4) blur(8px);
}

.site-nav {
  max-width: var(--de-content-max-width);
  margin: 0 auto;
  padding: var(--de-space-3);
  display: flex;
  align-items: center;
  gap: var(--de-space-4);
}

.site-brand {
  font-weight: 740;
  font-size: 1.1875rem;
  letter-spacing: -0.02em;
  color: var(--de-color-text);
  text-decoration: none;
  /* A small brand mark anchors the wordmark: a brand-colored bar before the name. */
  display: inline-flex;
  align-items: center;
  gap: var(--de-space-2);
}

.site-brand::before {
  content: "";
  width: 0.5rem;
  height: 1.15rem;
  border-radius: 999px;
  background: var(--de-color-brand);
  flex: none;
}

.site-brand:hover {
  color: var(--de-color-brand-strong);
}

.site-main {
  max-width: var(--de-content-max-width);
  margin: 0 auto;
  padding: var(--de-space-5) var(--de-space-3) var(--de-space-6);
  min-height: 60vh;
}

.site-footer {
  border-top: 1px solid var(--de-color-border);
  background: var(--de-color-surface-alt);
  color: var(--de-color-text-muted);
  text-align: center;
  padding: var(--de-space-5) var(--de-space-3);
  font-size: var(--de-text-sm);
}

.site-footer > span {
  display: block;
  margin-top: var(--de-space-2);
  color: var(--de-color-text-subtle);
}

/* Error pages (404/410/500) rendered through the shell */
.error-page {
  text-align: center;
  padding: var(--de-space-5) var(--de-space-3);
}

.error-page h1 {
  color: var(--de-color-danger);
}

.error-page code {
  background: var(--de-color-surface-alt);
  padding: var(--de-space-1) var(--de-space-2);
  border-radius: var(--de-radius);
}

/* ── epic-003: public pages ───────────────────────────────────────────────── */

/* Map component (task-016). Fixed height per the mocks; full width on mobile. Framed as a
 * crisp panel that matches the card system (border-strong + radius + soft elevation). */
.listing-map {
  height: 340px;
  border: 1px solid var(--de-color-border-strong);
  border-radius: var(--de-radius-lg);
  box-shadow: var(--de-shadow-sm);
  overflow: hidden;
  /* Leaflet panes use z-index up to ~700; keep the map below sticky page chrome. */
  isolation: isolate;
  z-index: 0;
}

/* In the side-by-side city map row, the map fills its column height next to the card. */
@media (min-width: 48rem) {
  .city-map-row:has(.featured-card) .listing-map {
    height: 100%;
    min-height: 340px;
  }
}

/* Card highlighted from a map pin (click scrolls to it, hover just highlights). */
.map-highlight {
  outline: 2px solid var(--de-color-brand);
  outline-offset: 2px;
}

/* Shared public-page chrome (task-017/021): nav dropdown + footer links. */
.site-nav {
  flex-wrap: wrap;
}

.site-nav > a:not(.site-brand) {
  color: var(--de-color-text);
  text-decoration: none;
}

.site-nav > a:not(.site-brand):hover {
  color: var(--de-color-brand);
}

.nav-dropdown {
  position: relative;
  margin-left: auto;
}

.nav-dropdown > summary {
  cursor: pointer;
  list-style: none;
  user-select: none;
}

.nav-dropdown > summary::after {
  content: " ▾";
  color: var(--de-color-text-muted);
}

/* Mobile-first (task-064): the open panel flows inline below the summary — a plain
 * details disclosure that grows the header instead of overflowing a 360px viewport.
 * From 48rem up it becomes the anchored popover the desktop mock shows. */
.nav-dropdown-panel {
  z-index: 50;
  margin: var(--de-space-1) 0 0;
  padding: var(--de-space-2);
  list-style: none;
  max-height: 60vh;
  overflow-y: auto;
  background: var(--de-color-surface);
  border: 1px solid var(--de-color-border);
  border-radius: var(--de-radius);
  box-shadow: 0 4px 16px rgb(0 0 0 / 0.1);
}

/* While open on phones, the disclosure takes the full nav row so city names get the
 * whole viewport width instead of the summary's narrow flex slot. */
.nav-dropdown[open] {
  flex: 1 1 100%;
}

@media (min-width: 48rem) {
  .nav-dropdown[open] {
    flex: none;
  }

  .nav-dropdown-panel {
    position: absolute;
    right: 0;
    min-width: 14rem;
  }
}

.nav-dropdown-panel a {
  display: flex;
  align-items: center;
  min-height: var(--de-tap-size);
  padding: var(--de-space-1) var(--de-space-2);
  color: var(--de-color-text);
  text-decoration: none;
  border-radius: var(--de-radius);
}

.nav-dropdown-panel a:hover {
  background: var(--de-color-surface-alt);
}

.footer-links {
  display: flex;
  flex-wrap: wrap;
  gap: var(--de-space-2) var(--de-space-3);
  justify-content: center;
  margin-bottom: var(--de-space-2);
}

.footer-links a {
  color: var(--de-color-text-muted);
}

/* Breadcrumb */
.breadcrumb {
  font-size: var(--de-text-sm);
  color: var(--de-color-text-muted);
  margin-bottom: var(--de-space-3);
}

.breadcrumb a {
  color: var(--de-color-text-muted);
  text-decoration-color: transparent;
}

.breadcrumb a:hover {
  color: var(--de-color-brand-strong);
  text-decoration-color: currentColor;
}

.breadcrumb-sep {
  margin: 0 var(--de-space-2);
  color: var(--de-color-text-subtle);
}

/* Buttons used by listing/page CTAs (real <a>/<button> — SEO-critical links stay links).
 * Mobile-first: comfortable 44px touch height; desktop compacts at the file bottom.
 * Three intents: secondary (default outline), primary (brand fill), ghost (quiet text). */
.button {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: var(--de-space-2);
  min-height: var(--de-tap-size);
  padding: var(--de-space-2) var(--de-space-4);
  border: 1px solid var(--de-color-border-strong);
  border-radius: var(--de-radius);
  color: var(--de-color-text);
  font: inherit;
  font-weight: 580;
  font-size: var(--de-text-sm);
  letter-spacing: 0.005em;
  text-decoration: none;
  white-space: nowrap;
  background: var(--de-color-surface);
  box-shadow: var(--de-shadow-sm);
  cursor: pointer;
  transition: border-color 0.14s ease, color 0.14s ease, background 0.14s ease,
    box-shadow 0.14s ease, transform 0.14s ease;
}

.button:hover {
  border-color: var(--de-color-brand);
  color: var(--de-color-brand-strong);
  text-decoration: none;
}

.button:active {
  transform: translateY(0.5px);
  box-shadow: none;
}

.button-primary {
  background: var(--de-color-brand);
  border-color: var(--de-color-brand);
  color: var(--de-color-brand-contrast);
  box-shadow: var(--de-shadow-sm),
    inset 0 1px 0 color-mix(in oklab, #fff 22%, transparent);
}

.button-primary:hover {
  background: var(--de-color-brand-strong);
  border-color: var(--de-color-brand-strong);
  color: var(--de-color-brand-contrast);
}

/* Ghost: a quiet inline action (e.g. "All articles") that reads as a link-button. */
.button-ghost {
  border-color: transparent;
  background: transparent;
  box-shadow: none;
  color: var(--de-color-brand-strong);
  padding-inline: var(--de-space-2);
}

.button-ghost:hover {
  background: var(--de-color-brand-tint);
  border-color: transparent;
}

/* Icon inside a button or inline affordance: inherits text color, sized to the cap. */
.de-icon {
  width: 1.1em;
  height: 1.1em;
  flex: none;
  fill: currentColor;
  vertical-align: -0.16em;
}

/* ── Shared primitives (task-067): badges, form controls, callouts, WA theming ──────── */

/* Eyebrow / pill badge primitive used by FEATURED, claimed, and small status chips. */
.de-badge {
  display: inline-flex;
  align-items: center;
  gap: var(--de-space-1);
  font-size: var(--de-text-xs);
  font-weight: 640;
  line-height: 1;
  letter-spacing: var(--de-tracking-label);
  text-transform: uppercase;
  padding: 0.35em 0.6em;
  border-radius: 999px;
}

/* Native form controls (claim form fields and any future inputs) — token-driven, AA focus.
 * The WA components below pick up the same brand via CSS variables so both render alike. */
.de-field {
  display: block;
  margin-bottom: var(--de-space-3);
}

.de-field > label,
.de-label {
  display: block;
  font-size: var(--de-text-sm);
  font-weight: 600;
  color: var(--de-color-text);
  margin-bottom: var(--de-space-1);
}

.de-field input,
.de-field select,
.de-field textarea {
  width: 100%;
  min-height: var(--de-tap-size);
  padding: var(--de-space-2) var(--de-space-3);
  font: inherit;
  font-size: 1rem; /* >=16px keeps iOS from auto-zooming the field */
  color: var(--de-color-text);
  background: var(--de-color-surface);
  border: 1px solid var(--de-color-border-strong);
  border-radius: var(--de-radius-sm);
  box-shadow: var(--de-shadow-sm);
}

.de-field textarea {
  min-height: 6rem;
  resize: vertical;
}

.de-field input:hover,
.de-field select:hover,
.de-field textarea:hover {
  border-color: var(--de-color-brand-border);
}

/* WebAwesome controls on merchant surfaces inherit the brand through CSS variables so the
 * claim form matches the public design system (task-070) without per-call-site color. */
:root {
  --wa-color-brand-fill-loud: var(--de-color-brand);
  --wa-color-brand-fill-loud-active: var(--de-color-brand-strong);
  --wa-color-brand-border-loud: var(--de-color-brand);
  --wa-border-radius-m: var(--de-radius);
  --wa-color-success-fill-quiet: var(--de-color-brand-wash);
}

/* City page (task-017 / task-068) */
.trust-line {
  color: var(--de-color-text-muted);
  margin-top: calc(-1 * var(--de-space-2));
}

/* The H1 + trust line read as a tight title block above the intro prose. */
.site-main > h1 + .trust-line {
  font-size: var(--de-text-md);
  font-weight: 540;
}

.city-intro {
  max-width: var(--de-measure);
  color: var(--de-color-text-muted);
  margin-bottom: var(--de-space-5);
}

.city-intro p {
  margin: var(--de-space-3) 0 0;
}

.city-map-row {
  display: grid;
  grid-template-columns: 1fr;
  gap: var(--de-space-4);
  margin: var(--de-space-5) 0;
}

@media (min-width: 48rem) {
  .city-map-row:has(.featured-card) {
    grid-template-columns: 3fr 2fr;
    align-items: stretch;
  }
}

/* The featured (paid) slot must read unmistakably as PREMIUM: a brand-tinted, elevated card
 * with a brand top-rail and a prominent FEATURED label (FTC/Google disclosure, mandatory). */
.featured-card {
  display: flex;
  flex-direction: column;
  gap: var(--de-space-2);
  position: relative;
  overflow: hidden;
  padding: var(--de-space-4);
  background:
    linear-gradient(var(--de-color-brand-wash), var(--de-color-surface) 70%);
  border: 1px solid var(--de-color-brand-border);
  border-radius: var(--de-radius-lg);
  box-shadow: var(--de-shadow-md);
}

/* The brand top-rail — a clear, calm "this is the promoted spot" signal. */
.featured-card::before {
  content: "";
  position: absolute;
  inset: 0 0 auto 0;
  height: 3px;
  background: var(--de-color-brand);
}

.featured-card .listing-card-head h3 {
  font-size: var(--de-text-lg);
}

.featured-card .listing-card-head h3 a {
  color: var(--de-color-text);
}

.featured-label {
  align-self: flex-start;
  font-size: var(--de-text-xs);
  font-weight: 720;
  letter-spacing: var(--de-tracking-label);
  text-transform: uppercase;
  color: var(--de-color-brand-contrast);
  background: var(--de-color-brand);
  border-radius: 999px;
  padding: 0.35em 0.7em;
  box-shadow: var(--de-shadow-sm);
}

/* The featured card's website CTA fills its column — it's the slot's whole purpose. */
.featured-card .listing-card-actions {
  margin-top: auto;
  padding-top: var(--de-space-2);
}

.featured-card .listing-card-actions .button {
  flex: 1 1 100%;
}

.listings-header {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: space-between;
  gap: var(--de-space-2) var(--de-space-3);
  /* Sticky under the sticky site header so the sort stays reachable down a long list. */
  position: sticky;
  top: 3.6rem;
  background: color-mix(in oklab, var(--de-color-surface) 92%, transparent);
  backdrop-filter: blur(6px);
  padding: var(--de-space-3) 0;
  margin-bottom: var(--de-space-2);
  border-bottom: 1px solid var(--de-color-border);
  z-index: 10;
}

.listings-header h2 {
  font-size: var(--de-text-xl);
}

/* The sort control reads as a segmented pill group, not three loose links. */
.sort-control {
  display: flex;
  align-items: center;
  gap: var(--de-space-1);
  font-size: var(--de-text-sm);
  padding: 0.2rem;
  background: var(--de-color-surface-sunk);
  border-radius: 999px;
}

.sort-label {
  color: var(--de-color-text-muted);
  padding-left: var(--de-space-2);
}

/* Mobile-first: 44px touch height (task-064); desktop compacts at the file bottom. */
.sort-option {
  display: inline-flex;
  align-items: center;
  min-height: var(--de-tap-size);
  font-weight: 580;
  color: var(--de-color-text-muted);
  text-decoration: none;
  padding: var(--de-space-1) var(--de-space-3);
  border-radius: 999px;
}

.sort-option:hover {
  color: var(--de-color-brand-strong);
}

.sort-option[aria-current="true"] {
  background: var(--de-color-surface);
  color: var(--de-color-text);
  box-shadow: var(--de-shadow-sm);
}

.listing-cards {
  display: grid;
  gap: var(--de-space-3);
}

.listing-card {
  background: var(--de-color-surface);
  border: 1px solid var(--de-color-border);
  border-radius: var(--de-radius);
  padding: var(--de-space-4);
  box-shadow: var(--de-shadow-sm);
  transition: border-color 0.14s ease, box-shadow 0.14s ease;
}

.listing-card:hover {
  border-color: var(--de-color-border-strong);
  box-shadow: var(--de-shadow-md);
}

.listing-card-head {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  gap: var(--de-space-2) var(--de-space-3);
}

.listing-card-head h3 {
  margin: 0;
  flex: 1;
  min-width: 12rem;
}

.listing-card-head h3 a {
  color: var(--de-color-text);
  text-decoration: none;
}

.listing-card-head h3 a:hover {
  color: var(--de-color-brand-strong);
}

/* The compact "★4.8 (52)" badge never breaks mid-figure — but only as the inline span
 * the cards use. The listing-detail rating line is a <p> with a long sentence
 * ("… reviews on Google · as of Jun 2026") that MUST wrap: nowrap there forced a
 * 364px minimum page width at 360px viewports (task-064 root-cause fix). The leading star
 * glyph in the copy is gold-colored via the ::first-letter on the inline span. */
.listing-rating {
  font-weight: 660;
  color: var(--de-color-text);
}

span.listing-rating {
  white-space: nowrap;
}

/* The "★" is always the first character of the rating string — paint just it gold. */
span.listing-rating::first-letter {
  color: var(--de-color-star);
  font-size: 1.05em;
}

/* "✓ Claimed" — a quiet, trustworthy verified chip (not loud, but unmistakable). */
.claimed-badge {
  display: inline-flex;
  align-items: center;
  font-size: var(--de-text-xs);
  font-weight: 660;
  letter-spacing: 0.01em;
  color: var(--de-color-success);
  background: color-mix(in oklab, var(--de-color-success) 10%, var(--de-color-surface));
  border: 1px solid color-mix(in oklab, var(--de-color-success) 30%, var(--de-color-border));
  border-radius: 999px;
  padding: 0.2em 0.6em;
}

.listing-card-meta {
  color: var(--de-color-text-muted);
  font-size: var(--de-text-sm);
  margin: var(--de-space-3) 0;
}

.listing-card-meta a {
  color: var(--de-color-text-muted);
}

.listing-card-meta a:hover {
  color: var(--de-color-brand-strong);
}

.listing-card-actions {
  display: flex;
  flex-wrap: wrap;
  gap: var(--de-space-2);
}

/* Mobile-first (task-064): card CTAs share the row on phones, shrink to content from
 * 30rem up. The listing-detail action row (a direct child of main, not inside a card)
 * stacks its buttons full width per mocks/listing-detail.md "Responsive". */
.listing-card-actions .button {
  flex: 1;
}

.site-main > .listing-card-actions .button {
  flex: 1 1 100%;
}

@media (min-width: 30rem) {
  .listing-card-actions .button {
    flex: 0 1 auto;
  }
}

@media (min-width: 48rem) {
  .site-main > .listing-card-actions .button {
    flex: 0 1 auto;
  }
}

/* FAQ (details/summary disclosure, keyboard-operable). Custom +/- marker, no emoji. */
.city-faq {
  max-width: var(--de-measure);
}

.city-faq > h2 {
  margin-bottom: var(--de-space-3);
}

.city-faq details {
  border-bottom: 1px solid var(--de-color-border);
}

.city-faq summary {
  display: flex;
  align-items: center;
  gap: var(--de-space-3);
  cursor: pointer;
  font-weight: 620;
  color: var(--de-color-text);
  padding: var(--de-space-3) 0;
  list-style: none;
}

/* Remove the native disclosure triangle (both engines) — we draw our own marker. */
.city-faq summary::-webkit-details-marker {
  display: none;
}

.city-faq summary::marker {
  content: "";
}

.city-faq summary:hover {
  color: var(--de-color-brand-strong);
}

/* A brand "+" that rotates to "×"-feel on open; transform-only, respects reduced-motion. */
.city-faq summary::after {
  content: "";
  margin-left: auto;
  width: 1.1rem;
  height: 1.1rem;
  flex: none;
  background: var(--de-color-brand);
  transition: transform 0.18s ease;
  -webkit-mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 256 256'%3E%3Cpath d='M224 128a8 8 0 0 1-8 8h-80v80a8 8 0 0 1-16 0v-80H40a8 8 0 0 1 0-16h80V40a8 8 0 0 1 16 0v80h80a8 8 0 0 1 8 8Z'/%3E%3C/svg%3E") center / contain no-repeat;
  mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 256 256'%3E%3Cpath d='M224 128a8 8 0 0 1-8 8h-80v80a8 8 0 0 1-16 0v-80H40a8 8 0 0 1 0-16h80V40a8 8 0 0 1 16 0v80h80a8 8 0 0 1 8 8Z'/%3E%3C/svg%3E") center / contain no-repeat;
}

.city-faq details[open] summary::after {
  transform: rotate(45deg);
}

.city-faq details > :not(summary) {
  margin: 0 0 var(--de-space-3);
  color: var(--de-color-text-muted);
  max-width: 60ch;
}

/* Related guides: same clean stacked list device as the home guides. */
.related-guides {
  margin-top: var(--de-space-6);
}

.related-guides > h2 {
  margin-bottom: var(--de-space-3);
}

.related-guides ul {
  list-style: none;
  margin: 0;
  padding: 0;
}

.related-guides li a {
  display: flex;
  align-items: center;
  gap: var(--de-space-3);
  padding: var(--de-space-3) 0;
  color: var(--de-color-text);
  text-decoration: none;
  border-bottom: 1px solid var(--de-color-border);
  font-weight: 540;
}

.related-guides li a:hover {
  color: var(--de-color-brand-strong);
}

/* Directory home (task-018 / task-068) — a calm, credible hero on a soft brand wash. */
.home-hero {
  text-align: center;
  padding: var(--de-space-6) var(--de-space-4) var(--de-space-5);
  margin: calc(-1 * var(--de-space-5)) calc(-1 * var(--de-space-3)) var(--de-space-4);
  background:
    radial-gradient(120% 120% at 50% -10%, var(--de-color-brand-wash), transparent 60%),
    var(--de-color-surface);
  border-bottom: 1px solid var(--de-color-border);
}

.home-hero h1 {
  max-width: 22ch;
  margin: 0 auto var(--de-space-4);
  font-size: var(--de-text-3xl);
  font-weight: 780;
}

/* Mobile-first (task-064, mocks/directory-home.md): search input full width on phones,
 * input + button on one row from 30rem up. A leading search-glyph affordance (CSS-only,
 * via a background icon) signals "search" without extra markup. */
.city-search {
  display: flex;
  flex-direction: column;
  gap: var(--de-space-2);
  justify-content: center;
  max-width: 30rem;
  margin: 0 auto;
}

@media (min-width: 30rem) {
  .city-search {
    flex-direction: row;
  }
}

.city-search input {
  flex: 1;
  /* Touch floor (task-064); the 1rem font also keeps iOS from auto-zooming the field. */
  min-height: var(--de-tap-size);
  box-sizing: border-box;
  padding: var(--de-space-2) var(--de-space-3) var(--de-space-2) 2.6rem;
  font-size: 1rem;
  color: var(--de-color-text);
  background-color: var(--de-color-surface);
  /* Phosphor magnifying-glass, inline data-uri (no network, no JS) — decorative. */
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 256 256' fill='%235b6675'%3E%3Cpath d='M229.66 218.34l-50.07-50.06a88.11 88.11 0 1 0-11.31 11.31l50.06 50.07a8 8 0 0 0 11.32-11.32ZM40 112a72 72 0 1 1 72 72 72.08 72.08 0 0 1-72-72Z'/%3E%3C/svg%3E");
  background-repeat: no-repeat;
  background-position: 0.85rem center;
  background-size: 1.1rem;
  border: 1px solid var(--de-color-border-strong);
  border-radius: var(--de-radius);
  box-shadow: var(--de-shadow-sm);
}

.city-search input::placeholder {
  color: var(--de-color-text-subtle);
}

.city-search input:focus-visible {
  border-color: var(--de-color-brand);
}

.city-search .button {
  font-size: var(--de-text-md);
  cursor: pointer;
}

/* Mobile-first (task-064, mocks/directory-home.md): one column on phones, two from 30rem. */
.city-grid {
  list-style: none;
  padding: 0;
  display: grid;
  grid-template-columns: 1fr;
  gap: var(--de-space-2) var(--de-space-3);
}

@media (min-width: 30rem) {
  .city-grid {
    grid-template-columns: repeat(2, 1fr);
  }
}

.city-grid a {
  color: var(--de-color-text);
}

.city-grid a:hover {
  color: var(--de-color-brand);
}

.no-match {
  color: var(--de-color-text-muted);
}

/* Listing detail (task-019 / task-069) */
.listing-title-row {
  display: flex;
  align-items: center;
  gap: var(--de-space-3);
  flex-wrap: wrap;
}

.listing-title-row h1 {
  margin-bottom: 0;
}

/* The detail rating line: gold star, then the honest source + as-of stamp in muted tone. */
p.listing-rating {
  margin-top: var(--de-space-2);
  color: var(--de-color-text);
}

p.listing-rating::first-letter {
  color: var(--de-color-star);
}

.rating-asof {
  color: var(--de-color-text-muted);
  font-weight: 400;
}

.listing-detail-row {
  display: grid;
  grid-template-columns: 1fr;
  gap: var(--de-space-4);
  margin: var(--de-space-5) 0;
}

@media (min-width: 48rem) {
  .listing-detail-row:has(.listing-map) {
    grid-template-columns: 5fr 4fr;
    align-items: start;
  }
}

/* Contact block reads as a clean definition panel beside the map. */
.contact-block {
  display: flex;
  flex-direction: column;
  gap: var(--de-space-2);
  padding: var(--de-space-4);
  background: var(--de-color-surface-alt);
  border: 1px solid var(--de-color-border);
  border-radius: var(--de-radius);
}

.contact-block p {
  margin: 0;
}

/* Address/phone/website each get a leading icon so the panel scans at a glance. */
.contact-address,
.contact-block p:has(> a[href^="tel:"]),
.contact-block p:has(> a[target="_blank"]) {
  display: flex;
  align-items: baseline;
  gap: var(--de-space-2);
}

.contact-address::before,
.contact-block p:has(> a[href^="tel:"])::before,
.contact-block p:has(> a[target="_blank"])::before {
  content: "";
  width: 1rem;
  height: 1rem;
  flex: none;
  align-self: center;
  background: var(--de-color-text-subtle);
}

.contact-address::before {
  -webkit-mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 256 256'%3E%3Cpath d='M128 16a88.1 88.1 0 0 0-88 88c0 75.3 80 132.17 83.41 134.55a8 8 0 0 0 9.18 0C136 236.17 216 179.3 216 104a88.1 88.1 0 0 0-88-88Zm0 56a32 32 0 1 1-32 32 32 32 0 0 1 32-32Z'/%3E%3C/svg%3E") center / contain no-repeat;
  mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 256 256'%3E%3Cpath d='M128 16a88.1 88.1 0 0 0-88 88c0 75.3 80 132.17 83.41 134.55a8 8 0 0 0 9.18 0C136 236.17 216 179.3 216 104a88.1 88.1 0 0 0-88-88Zm0 56a32 32 0 1 1-32 32 32 32 0 0 1 32-32Z'/%3E%3C/svg%3E") center / contain no-repeat;
}

.contact-block p:has(> a[href^="tel:"])::before {
  -webkit-mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 256 256'%3E%3Cpath d='M222.37 158.46l-47.11-21.11-.13-.06a16 16 0 0 0-15.17 1.4 8.12 8.12 0 0 0-.75.56L134.87 160c-15.42-7.49-31.34-23.29-38.83-38.51l20.78-24.71c.2-.25.39-.5.57-.77a16 16 0 0 0 1.32-15.06v-.12L97.54 33.64a16 16 0 0 0-16.62-9.52A56.26 56.26 0 0 0 32 80c0 79.4 64.6 144 144 144a56.26 56.26 0 0 0 55.88-48.92 16 16 0 0 0-9.51-16.62Z'/%3E%3C/svg%3E") center / contain no-repeat;
  mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 256 256'%3E%3Cpath d='M222.37 158.46l-47.11-21.11-.13-.06a16 16 0 0 0-15.17 1.4 8.12 8.12 0 0 0-.75.56L134.87 160c-15.42-7.49-31.34-23.29-38.83-38.51l20.78-24.71c.2-.25.39-.5.57-.77a16 16 0 0 0 1.32-15.06v-.12L97.54 33.64a16 16 0 0 0-16.62-9.52A56.26 56.26 0 0 0 32 80c0 79.4 64.6 144 144 144a56.26 56.26 0 0 0 55.88-48.92 16 16 0 0 0-9.51-16.62Z'/%3E%3C/svg%3E") center / contain no-repeat;
}

.contact-block p:has(> a[target="_blank"])::before {
  -webkit-mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 256 256'%3E%3Cpath d='M128 24a104 104 0 1 0 104 104A104.12 104.12 0 0 0 128 24Zm87.63 96h-39.4c-1.41-31-12-58.49-29.49-77.46A88.19 88.19 0 0 1 215.63 120ZM96.34 136h63.32c-2.16 30.39-13.61 57.6-31.66 75.92-18-18.32-29.47-45.53-31.66-75.92Zm0-16C98.5 89.61 110 62.4 128 44.08c18.05 18.32 29.5 45.53 31.66 75.92ZM109.26 42.54C91.74 61.51 81.13 89 79.77 120h-39.4a88.19 88.19 0 0 1 68.89-77.46ZM40.37 136h39.4c1.36 31 12 58.49 29.49 77.46A88.19 88.19 0 0 1 40.37 136Zm106.37 77.46c17.52-19 28.13-46.44 29.49-77.46h39.4a88.19 88.19 0 0 1-68.89 77.46Z'/%3E%3C/svg%3E") center / contain no-repeat;
  mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 256 256'%3E%3Cpath d='M128 24a104 104 0 1 0 104 104A104.12 104.12 0 0 0 128 24Zm87.63 96h-39.4c-1.41-31-12-58.49-29.49-77.46A88.19 88.19 0 0 1 215.63 120ZM96.34 136h63.32c-2.16 30.39-13.61 57.6-31.66 75.92-18-18.32-29.47-45.53-31.66-75.92Zm0-16C98.5 89.61 110 62.4 128 44.08c18.05 18.32 29.5 45.53 31.66 75.92ZM109.26 42.54C91.74 61.51 81.13 89 79.77 120h-39.4a88.19 88.19 0 0 1 68.89-77.46ZM40.37 136h39.4c1.36 31 12 58.49 29.49 77.46A88.19 88.19 0 0 1 40.37 136Zm106.37 77.46c17.52-19 28.13-46.44 29.49-77.46h39.4a88.19 88.19 0 0 1-68.89 77.46Z'/%3E%3C/svg%3E") center / contain no-repeat;
}

.hours {
  margin: var(--de-space-1) 0 0;
  padding-top: var(--de-space-2);
  border-top: 1px dashed var(--de-color-border);
  display: grid;
  grid-template-columns: max-content 1fr;
  gap: var(--de-space-1) var(--de-space-4);
  font-size: var(--de-text-sm);
}

.hours dt {
  color: var(--de-color-text-muted);
  font-weight: 560;
}

.hours dd {
  margin: 0;
}

.listing-description {
  max-width: var(--de-measure);
  color: var(--de-color-text);
}

/* Claim CTA — a confident, brand-tinted invitation, clearly the page's owner action. */
.claim-callout {
  margin-top: var(--de-space-6);
  padding: var(--de-space-4) var(--de-space-5);
  background:
    linear-gradient(var(--de-color-brand-wash), var(--de-color-surface) 80%);
  border: 1px solid var(--de-color-brand-border);
  border-radius: var(--de-radius-lg);
}

.claim-callout h2 {
  margin-top: 0;
  margin-bottom: var(--de-space-2);
}

.claim-callout p {
  margin: 0 0 var(--de-space-4);
  color: var(--de-color-text-muted);
  max-width: 48ch;
}

/* Related listings (city-funnel): a wrapped row of name + gold-rating chips. */
.related-list {
  list-style: none;
  padding: 0;
  margin: var(--de-space-3) 0 0;
  display: flex;
  flex-wrap: wrap;
  gap: var(--de-space-2) var(--de-space-3);
}

.related-listings {
  margin-top: var(--de-space-6);
  padding-top: var(--de-space-4);
  border-top: 1px solid var(--de-color-border);
}

.related-list li {
  display: inline-flex;
  align-items: baseline;
  gap: var(--de-space-1);
}

/* Articles (task-020 / task-069) — real long-form reading typography. */
.article-body {
  max-width: var(--de-measure);
  font-size: 1.125rem;
  line-height: 1.7;
}

.article-body > h1 {
  font-size: var(--de-text-2xl);
  margin-bottom: var(--de-space-2);
}

/* The "Updated {month}" stamp sits just under the title as a quiet dateline. */
.article-body > h1 + .trust-line {
  margin: 0 0 var(--de-space-5);
  padding-bottom: var(--de-space-4);
  border-bottom: 1px solid var(--de-color-border);
  font-size: var(--de-text-sm);
  color: var(--de-color-text-muted);
}

/* Body heading scale: clear step-down, generous top space, tight to following text. */
.article-body h2 {
  font-size: var(--de-text-xl);
  margin: var(--de-space-5) 0 var(--de-space-2);
}

.article-body h3 {
  font-size: var(--de-text-lg);
  margin: var(--de-space-4) 0 var(--de-space-2);
}

.article-body p {
  margin: 0 0 var(--de-space-3);
}

.article-body ul,
.article-body ol {
  margin: 0 0 var(--de-space-3);
  padding-left: var(--de-space-4);
}

.article-body li {
  margin-bottom: var(--de-space-2);
}

.article-body li::marker {
  color: var(--de-color-brand);
}

/* Inline links inside prose read as links (underlined brand), distinct from buttons. */
.article-body a {
  color: var(--de-color-brand-strong);
  font-weight: 540;
}

.article-body blockquote {
  margin: var(--de-space-4) 0;
  padding: var(--de-space-2) var(--de-space-4);
  border-left: 3px solid var(--de-color-brand);
  background: var(--de-color-brand-wash);
  border-radius: 0 var(--de-radius) var(--de-radius) 0;
  color: var(--de-color-text-muted);
  font-style: italic;
}

.article-body blockquote p:last-child {
  margin-bottom: 0;
}

.article-body code {
  font-family: var(--de-font-mono);
  font-size: 0.9em;
  background: var(--de-color-surface-sunk);
  padding: 0.1em 0.35em;
  border-radius: var(--de-radius-sm);
}

.article-body pre {
  background: var(--de-color-surface-sunk);
  padding: var(--de-space-3);
  border-radius: var(--de-radius);
  margin: 0 0 var(--de-space-3);
}

.article-body pre code {
  background: none;
  padding: 0;
}

.article-body hr {
  border: none;
  border-top: 1px solid var(--de-color-border);
  margin: var(--de-space-5) 0;
}

.article-body img {
  max-width: 100%;
  height: auto;
  border-radius: var(--de-radius);
  box-shadow: var(--de-shadow-md);
  margin: var(--de-space-3) 0;
}

.article-meta-line {
  margin: var(--de-space-1) 0 0;
  color: var(--de-color-text-muted);
  font-size: var(--de-text-sm);
}

/* Article index: each entry is a titled row + dek, in the clean stacked-list device. */
.article-group {
  margin-bottom: var(--de-space-5);
}

.article-group > h2 {
  margin-bottom: var(--de-space-3);
  padding-bottom: var(--de-space-2);
  border-bottom: 1px solid var(--de-color-border);
}

.article-group ul {
  list-style: none;
  margin: 0;
  padding: 0;
}

.article-group li {
  padding: var(--de-space-3) 0;
  border-bottom: 1px solid var(--de-color-border);
}

.article-group li > a {
  font-size: var(--de-text-lg);
  font-weight: 620;
  color: var(--de-color-text);
  text-decoration: none;
}

.article-group li > a:hover {
  color: var(--de-color-brand-strong);
}

/* City-funnel footer block under each article. */
.article-city-links {
  margin-top: var(--de-space-6);
  border-top: 1px solid var(--de-color-border);
  padding-top: var(--de-space-4);
}

.article-city-links > .button {
  margin-top: var(--de-space-3);
}

/* Ad slot (task-024) — the AdSense responsive unit; absent entirely when ads are off.
 * When present it sits in its own breathing room, visually separated from content so a
 * paid unit never reads as part of the directory's editorial listings. */
.ad-slot {
  display: block;
  margin: var(--de-space-5) 0;
  min-height: 0;
}

/* ── epic-003 task-063: home & nav visual polish (Wave 2 review findings) ──── */

/* Nav items share one visual treatment (plain links, mocks/directory-home.md).
 * WebAwesome's native.css paints every details/summary as a bordered panel with a
 * masked chevron-right icon and content-flow margins — reset all of it for the nav
 * dropdown so 'Cities' matches the 'Articles' link. */
.site-nav > .nav-dropdown {
  margin: 0;
  margin-left: auto;
  padding: 0;
  background: none;
  border: none;
  border-radius: 0;
}

.site-nav > .nav-dropdown > summary {
  display: inline-flex;
  align-items: center;
  gap: var(--de-space-1);
  margin: 0;
  padding: 0;
  font: inherit;
  color: var(--de-color-text);
  background: none;
  border-radius: 0;
}

.site-nav > .nav-dropdown > summary:hover {
  color: var(--de-color-brand);
}

/* The mock's down chevron as plain text — undo WebAwesome's SVG mask (which clips any
 * content to an arrow shape) and its 90° open rotation. */
.site-nav > .nav-dropdown > summary::after {
  content: "▾";
  width: auto;
  height: auto;
  mask: none;
  -webkit-mask: none;
  background: none;
  color: var(--de-color-text-muted);
  rotate: none;
}

.site-nav > .nav-dropdown[open] > summary::after {
  rotate: none;
}

/* Consistent home vertical rhythm: hero → cities → guides breathe equally, and the
 * section headings carry the mock's ruled-line weight. */
.home-cities,
.home-guides {
  margin-top: var(--de-space-6);
}

/* Ruled section heading: the title sits left, a hairline rule extends to the right edge —
 * the "── All N cities ───" device from the mocks, done with a flex pseudo-rule. */
.home-cities > h2,
.home-guides > h2 {
  display: flex;
  align-items: center;
  gap: var(--de-space-3);
  margin: 0 0 var(--de-space-4);
  font-size: var(--de-text-xl);
}

.home-cities > h2::after,
.home-guides > h2::after {
  content: "";
  flex: 1;
  height: 1px;
  background: var(--de-color-border);
}

/* Guides: a clean stacked list with a leading guide glyph instead of a raw bullet. */
.home-guides ul {
  list-style: none;
  margin: 0;
  padding: 0;
  display: grid;
  gap: var(--de-space-1);
}

.home-guides li a {
  display: flex;
  align-items: center;
  gap: var(--de-space-3);
  padding: var(--de-space-3) var(--de-space-3) var(--de-space-3) var(--de-space-2);
  color: var(--de-color-text);
  text-decoration: none;
  border-bottom: 1px solid var(--de-color-border);
  font-weight: 540;
}

.home-guides li:last-child a {
  border-bottom: none;
}

.home-guides li a::before {
  content: "";
  width: 1.05rem;
  height: 1.05rem;
  flex: none;
  background: var(--de-color-brand);
  /* Phosphor article/book glyph, masked to the brand color. */
  -webkit-mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 256 256'%3E%3Cpath d='M208 24H72A32 32 0 0 0 40 56v168a8 8 0 0 0 8 8h144a8 8 0 0 0 0-16H56a16 16 0 0 1 16-16h136a8 8 0 0 0 8-8V32a8 8 0 0 0-8-8Zm-8 152H72a31.8 31.8 0 0 0-16 4.31V56a16 16 0 0 1 16-16h128Z'/%3E%3C/svg%3E") center / contain no-repeat;
  mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 256 256'%3E%3Cpath d='M208 24H72A32 32 0 0 0 40 56v168a8 8 0 0 0 8 8h144a8 8 0 0 0 0-16H56a16 16 0 0 1 16-16h136a8 8 0 0 0 8-8V32a8 8 0 0 0-8-8Zm-8 152H72a31.8 31.8 0 0 0-16 4.31V56a16 16 0 0 1 16-16h128Z'/%3E%3C/svg%3E") center / contain no-repeat;
}

.home-guides li a:hover {
  color: var(--de-color-brand-strong);
}

.home-guides > .button {
  margin-top: var(--de-space-3);
}

/* The trust line gets visibly separated from the content above it (home only — the
 * city page's trust line hugs its h1 on purpose). Rendered as a quiet centered stamp. */
.home-trust {
  margin-top: var(--de-space-6);
  padding-top: var(--de-space-4);
  border-top: 1px solid var(--de-color-border);
  text-align: center;
  color: var(--de-color-text-muted);
  font-size: var(--de-text-sm);
}

/* City list: defined rows per the mock's visual weight — city name left, ACTIVE
 * listing count right as a visually distinct badge. */
.city-grid {
  margin: 0;
}

.city-row {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--de-space-2);
  /* Touch target floor (task-064); a ~2px bump over the padded text height on desktop. */
  min-height: var(--de-tap-size);
  padding: var(--de-space-3) var(--de-space-4);
  font-weight: 580;
  color: var(--de-color-text);
  text-decoration: none;
  background: var(--de-color-surface);
  border: 1px solid var(--de-color-border);
  border-radius: var(--de-radius);
  box-shadow: var(--de-shadow-sm);
  transition: border-color 0.14s ease, box-shadow 0.14s ease, transform 0.14s ease;
}

.city-row:hover {
  color: var(--de-color-brand-strong);
  border-color: var(--de-color-brand-border);
  box-shadow: var(--de-shadow-md);
}

/* The city name carries a small location pin so the row reads as a place, not a label. */
.city-row-name {
  display: inline-flex;
  align-items: center;
  gap: var(--de-space-2);
}

.city-row-name::before {
  content: "";
  width: 0.85rem;
  height: 0.85rem;
  flex: none;
  background: currentColor;
  opacity: 0.55;
  /* Phosphor map-pin, masked so it inherits the row's (brand-on-hover) color. */
  -webkit-mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 256 256'%3E%3Cpath d='M128 16a88.1 88.1 0 0 0-88 88c0 75.3 80 132.17 83.41 134.55a8 8 0 0 0 9.18 0C136 236.17 216 179.3 216 104a88.1 88.1 0 0 0-88-88Zm0 56a32 32 0 1 1-32 32 32 32 0 0 1 32-32Z'/%3E%3C/svg%3E") center / contain no-repeat;
  mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 256 256'%3E%3Cpath d='M128 16a88.1 88.1 0 0 0-88 88c0 75.3 80 132.17 83.41 134.55a8 8 0 0 0 9.18 0C136 236.17 216 179.3 216 104a88.1 88.1 0 0 0-88-88Zm0 56a32 32 0 1 1-32 32 32 32 0 0 1 32-32Z'/%3E%3C/svg%3E") center / contain no-repeat;
}

.city-row-count {
  flex: none;
  min-width: 1.9rem;
  text-align: center;
  font-size: var(--de-text-sm);
  font-weight: 680;
  color: var(--de-color-brand-strong);
  background: var(--de-color-brand-tint);
  border-radius: 999px;
  padding: 0.15rem var(--de-space-2);
}

/* ── Legal / About long-form (task-069) — readable measure, clear section rhythm ─────── */
/* Scoped to .legal-page (set on the <main> content wrapper) so the city/listing pages,
 * which also use <section>/<p>, are untouched — no hidden coupling. */
.legal-page,
.legal-page > p {
  max-width: var(--de-measure);
}

.legal-page > p {
  color: var(--de-color-text-muted);
  margin: 0 0 var(--de-space-3);
}

.legal-operator {
  color: var(--de-color-text-subtle);
  font-size: var(--de-text-sm);
}

.legal-page > section {
  margin-top: var(--de-space-5);
}

.legal-page > section > h2 {
  font-size: var(--de-text-lg);
  margin-bottom: var(--de-space-2);
  padding-bottom: var(--de-space-2);
  border-bottom: 1px solid var(--de-color-border);
}

.legal-page > section > p {
  color: var(--de-color-text-muted);
  margin: 0 0 var(--de-space-3);
}

/* ── epic-005/011 — sponsor page (task-035 / task-070): a confident conversion surface ── */
.sponsor-page {
  max-width: 48rem;
}

/* Value-prop hero: the pitch framed on the brand wash as the offer's headline. */
.sponsor-hero {
  padding: var(--de-space-5);
  margin-bottom: var(--de-space-5);
  background:
    radial-gradient(130% 130% at 0% 0%, var(--de-color-brand-wash), transparent 55%),
    var(--de-color-surface);
  border: 1px solid var(--de-color-brand-border);
  border-radius: var(--de-radius-lg);
}

.sponsor-hero h1 {
  font-size: var(--de-text-2xl);
}

.sponsor-pitch {
  margin: var(--de-space-3) 0 0;
  font-size: var(--de-text-lg);
  color: var(--de-color-text-muted);
  max-width: 46ch;
}

/* Benefits as check-marked value points (brand check glyph, no emoji). */
.sponsor-benefits {
  list-style: none;
  padding: 0;
  margin: 0 0 var(--de-space-5);
  display: grid;
  gap: var(--de-space-3);
}

.sponsor-benefits li {
  display: flex;
  align-items: flex-start;
  gap: var(--de-space-3);
  font-weight: 540;
  margin-bottom: 0;
}

.sponsor-benefits li::before {
  content: "";
  width: 1.35rem;
  height: 1.35rem;
  flex: none;
  margin-top: 0.05rem;
  background: var(--de-color-brand);
  /* Phosphor check-circle, masked to brand. */
  -webkit-mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 256 256'%3E%3Cpath d='M128 24a104 104 0 1 0 104 104A104.11 104.11 0 0 0 128 24Zm45.66 85.66l-56 56a8 8 0 0 1-11.32 0l-24-24a8 8 0 0 1 11.32-11.32L112 148.69l50.34-50.35a8 8 0 0 1 11.32 11.32Z'/%3E%3C/svg%3E") center / contain no-repeat;
  mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 256 256'%3E%3Cpath d='M128 24a104 104 0 1 0 104 104A104.11 104.11 0 0 0 128 24Zm45.66 85.66l-56 56a8 8 0 0 1-11.32 0l-24-24a8 8 0 0 1 11.32-11.32L112 148.69l50.34-50.35a8 8 0 0 1 11.32 11.32Z'/%3E%3C/svg%3E") center / contain no-repeat;
}

/* Price line: the figure reads big and confident; the honest cancel terms read calmly.
 * Kept as one localized string — emphasis via line-height + a leading currency weight. */
.sponsor-price {
  margin: 0 0 var(--de-space-5);
  padding: var(--de-space-4);
  font-size: var(--de-text-xl);
  font-weight: 720;
  line-height: 1.4;
  color: var(--de-color-text);
  background: var(--de-color-surface-alt);
  border: 1px solid var(--de-color-border);
  border-left: 3px solid var(--de-color-brand);
  border-radius: var(--de-radius);
  text-wrap: balance;
}

/* Two CTA paths sit side by side on wider viewports — equal, confident choices. */
.sponsor-cta {
  display: grid;
  gap: var(--de-space-3);
}

@media (min-width: 36rem) {
  .sponsor-cta {
    grid-template-columns: 1fr 1fr;
  }
}

.sponsor-cta-path {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: var(--de-space-3);
  padding: var(--de-space-4);
  background: var(--de-color-surface);
  border: 1px solid var(--de-color-border);
  border-radius: var(--de-radius);
  box-shadow: var(--de-shadow-sm);
}

.sponsor-cta-path h2 {
  margin: 0;
  font-size: var(--de-text-lg);
}

.sponsor-cta-path .button {
  width: 100%;
}

/* ── Claim form (task-070): a calm, labelled conversion form with helpful states ──────── */
.claim-form {
  max-width: 34rem;
}

.claim-back {
  margin: 0 0 var(--de-space-3);
  font-size: var(--de-text-sm);
}

.claim-back a {
  color: var(--de-color-text-muted);
  text-decoration: none;
  font-weight: 540;
}

.claim-back a:hover {
  color: var(--de-color-brand-strong);
}

/* The intro under the H1 sets honest, low-friction expectations. */
.claim-form > h1 {
  margin-bottom: var(--de-space-2);
}

.claim-form > p {
  color: var(--de-color-text-muted);
  max-width: 46ch;
}

/* The form box itself: a single clean panel grouping the fields. */
#claim-box {
  margin-top: var(--de-space-4);
}

#claim-box form {
  display: grid;
  gap: var(--de-space-1);
  padding: var(--de-space-5);
  background: var(--de-color-surface);
  border: 1px solid var(--de-color-border);
  border-radius: var(--de-radius-lg);
  box-shadow: var(--de-shadow-sm);
}

/* WebAwesome form controls inherit brand via the :root --wa-* vars set in task-067; give
 * them consistent vertical rhythm and label weight to match the design system. */
#claim-box wa-input,
#claim-box wa-select,
#claim-box wa-textarea {
  margin-bottom: var(--de-space-3);
  --wa-form-control-label-font-weight: 600;
}

/* The submit button sits apart as the clear primary action. */
#claim-box wa-button[type="submit"] {
  margin-top: var(--de-space-2);
}

/* Inline validation errors read as helpful, not alarming — danger tint, clear, scannable.
 * aria-describedby ties each to its field (wired in ClaimPages.kt); the styling never
 * removes that semantic link. */
.field-error {
  display: flex;
  align-items: flex-start;
  gap: var(--de-space-2);
  margin: calc(-1 * var(--de-space-2)) 0 var(--de-space-3);
  padding: var(--de-space-2) var(--de-space-3);
  font-size: var(--de-text-sm);
  font-weight: 540;
  color: var(--de-color-danger);
  background: var(--de-color-danger-tint);
  border: 1px solid color-mix(in oklab, var(--de-color-danger) 28%, var(--de-color-border));
  border-radius: var(--de-radius-sm);
}

.field-error::before {
  content: "";
  width: 1.05rem;
  height: 1.05rem;
  flex: none;
  margin-top: 0.05rem;
  background: var(--de-color-danger);
  /* Phosphor warning-circle, masked to danger. */
  -webkit-mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 256 256'%3E%3Cpath d='M128 24a104 104 0 1 0 104 104A104.11 104.11 0 0 0 128 24Zm-8 56a8 8 0 0 1 16 0v56a8 8 0 0 1-16 0Zm8 104a12 12 0 1 1 12-12 12 12 0 0 1-12 12Z'/%3E%3C/svg%3E") center / contain no-repeat;
  mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 256 256'%3E%3Cpath d='M128 24a104 104 0 1 0 104 104A104.11 104.11 0 0 0 128 24Zm-8 56a8 8 0 0 1 16 0v56a8 8 0 0 1-16 0Zm8 104a12 12 0 1 1 12-12 12 12 0 0 1-12 12Z'/%3E%3C/svg%3E") center / contain no-repeat;
}

/* The honest review-timeline microcopy below the submit. */
.form-hint {
  margin: var(--de-space-3) 0 0;
  font-size: var(--de-text-sm);
  color: var(--de-color-text-muted);
}

/* WA callouts (claim confirmation/duplicate/already-claimed, and the 429 fragment) read as
 * helpful notices, themed through the design tokens — no loud, no emoji. */
wa-callout {
  --wa-color-brand-fill-quiet: var(--de-color-brand-wash);
  border-radius: var(--de-radius);
}

/* ── epic-010 task-064: responsive mobile pass (Mobile NFR, 360px+) ──────────────────────
 * Mobile-first: the base rules below size every interactive control to the 44px touch
 * floor and keep all content reflowing inside its own container. The single
 * `min-width: 64rem` block at the end restores today's compact desktop metrics, so
 * 1280px rendering stays unchanged. No content is hidden per viewport (SEO parity). */

/* Header nav: brand, Articles link, and the Cities summary all reach the touch floor.
 * The nav already wraps (flex-wrap) — collapse is plain wrapping, no JS. */
.site-nav > a,
.site-nav > .nav-dropdown > summary {
  min-height: var(--de-tap-size);
  display: inline-flex;
  align-items: center;
}

/* Footer links reach the touch floor; the row already wraps. */
.footer-links a {
  display: inline-flex;
  align-items: center;
  min-height: var(--de-tap-size);
}

/* FAQ disclosures: explicit touch floor — never rely on WebAwesome's own summary
 * padding to make the target big enough (no hidden coupling). */
.city-faq summary {
  min-height: var(--de-tap-size);
  align-content: center;
}

/* Portal picker (J-002 multi-listing): row-sized touch targets. */
.portal-picker {
  list-style: none;
  padding: 0;
}

.portal-picker a {
  display: flex;
  align-items: center;
  min-height: var(--de-tap-size);
}

/* List-based navigation links (article index, home guides, related guides/listings,
 * the article's city links): touch-sized rows on phones; desktop compaction reverts. */
.article-group li a,
.home-guides li a,
.related-guides li a,
.related-list li a {
  display: inline-flex;
  align-items: center;
  min-height: var(--de-tap-size);
}

/* Inline action links (tel:, breadcrumbs, the portal's "View public page", claim-page
 * links, card title links): the hit area grows to the touch floor via block padding,
 * and the matching negative margin cancels any layout shift — the page renders
 * identically while the tappable box reaches ~44px. */
.breadcrumb a,
.contact-block p a,
.portal-status a,
.claim-form a,
.listing-card-head h3 a {
  display: inline-block;
  padding-block: calc((var(--de-tap-size) - 1lh) / 2);
  margin-block: calc((1lh - var(--de-tap-size)) / 2);
}

/* WebAwesome buttons (claim form, portal, sponsor CTAs): the medium size renders a hair
 * under 44px — pin the touch floor on the rendered base part. */
wa-button::part(base) {
  min-height: var(--de-tap-size);
}

/* Leaflet zoom controls ship 30x30 — too small for touch. Sized up wherever the pointer
 * is coarse, and on narrow viewports as a fallback for browsers that misreport pointer
 * capabilities. Third-party values, so this stays an override rather than a base style.
 * Selector depth beats leaflet.css's `.leaflet-touch .leaflet-bar a` (loaded after
 * app.css), which would otherwise win the tie. */
@media (pointer: coarse), (max-width: 48rem) {
  .site-main .listing-map .leaflet-control-zoom a {
    width: var(--de-tap-size);
    height: var(--de-tap-size);
    line-height: var(--de-tap-size);
    font-size: 1.4rem;
  }
}

/* Content reflow guards: long URLs and unbroken tokens (merchant-typed websites in the
 * pending diff, markdown article bodies, listing descriptions) wrap inside their own
 * container instead of widening the page. */
.article-body,
.city-intro,
.listing-description,
.contact-block,
.portal-pending {
  overflow-wrap: break-word;
}

/* Markdown code blocks scroll within themselves, never the page. */
.site-main pre {
  overflow-x: auto;
  max-width: 100%;
}

/* Merchant portal chrome (mocks/merchant-portal.md): the "Your listing · Sign out"
 * header row, and the mobile-sticky submit on the edit form. */
.portal-toolbar {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: space-between;
  gap: var(--de-space-2);
}

/* Mock: "Single column; sticky submit button on mobile". Mobile-only — desktop keeps
 * the in-flow button. */
@media (max-width: 48rem) {
  #edit-form-box wa-button[type="submit"] {
    position: sticky;
    bottom: var(--de-space-2);
    z-index: 5;
  }
}

/* ── task-064: desktop compaction ─────────────────────────────────────────────────────────
 * From 64rem up (mouse-driven layouts) the touch floor relaxes back to the exact metrics
 * the desktop had before this pass — 1280px renders unchanged. */
@media (min-width: 64rem) {
  .site-nav > a,
  .site-nav > .nav-dropdown > summary {
    min-height: 0;
  }

  .nav-dropdown-panel a {
    display: block;
    min-height: 0;
  }

  .footer-links a {
    display: inline;
    min-height: 0;
  }

  .sort-option {
    display: inline;
    min-height: 0;
  }

  .article-group li a,
  .home-guides li a,
  .related-guides li a,
  .related-list li a {
    display: inline;
    min-height: 0;
  }

  .breadcrumb a,
  .contact-block p a,
  .portal-status a,
  .claim-form a,
  .listing-card-head h3 a {
    display: inline;
    padding-block: 0;
    margin-block: 0;
  }

  .button {
    display: inline-block;
    min-height: 0;
  }

  /* The portal toolbar's "small" sign-out button stays small on desktop. */
  wa-button[size="small"]::part(base) {
    min-height: 0;
  }
}
