/* ==========================================================================
   Components — buttons, cards, hero, map, forms, charts, tables

   The organising idea: newsprint separates things with rules and whitespace,
   not with floating cards on a grey field. Almost nothing here has a shadow.
   ========================================================================== */

/* --------------------------------------------------------------------------
   Buttons
   -------------------------------------------------------------------------- */

.btn {
  transition: background var(--duration-fast) var(--ease-out),
              color var(--duration-fast) var(--ease-out),
              border-color var(--duration-fast) var(--ease-out),
              transform var(--duration-fast) var(--ease-out),
              box-shadow var(--duration-fast) var(--ease-out);
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: var(--space-2);
  min-height: 44px;
  padding: var(--space-2) var(--space-5);
  border: var(--border-width) solid transparent;
  border-radius: var(--radius-md);
  font-family: var(--font-sans);
  font-size: var(--text-sm);
  font-weight: var(--weight-semibold);
  line-height: 1.2;
  letter-spacing: 0.01em;
  text-decoration: none;
  cursor: pointer;
}

.btn:disabled,
.btn[aria-disabled="true"] {
  opacity: 0.5;
  cursor: not-allowed;
}

/* The primary action is the signal colour — this is a site whose whole purpose
   is getting people to file a report, so the loudest thing on the page should
   be the button that does it. */
.btn--primary {
  background: var(--signal-deep);
  color: #fff;
  box-shadow: 0 1px 0 rgba(255, 255, 255, 0.18) inset,
              0 6px 18px rgba(194, 52, 10, 0.28);
}

.btn--primary:hover:not(:disabled) {
  background: #a82c08;
  color: #fff;
  transform: translateY(-1px);
  box-shadow: 0 1px 0 rgba(255, 255, 255, 0.2) inset,
              0 10px 26px rgba(194, 52, 10, 0.34);
}

.btn--primary:active:not(:disabled) {
  transform: translateY(0);
}

/* For use on the dark panel, where a bordered button needs light edges. */
.btn--ghost-invert {
  background: transparent;
  border-color: var(--border-on-ink);
  color: var(--text-on-ink);
}

.btn--ghost-invert:hover:not(:disabled) {
  background: rgba(247, 248, 251, 0.08);
  border-color: var(--teal-bright);
  color: var(--teal-bright);
}

.btn--secondary {
  background: transparent;
  border-color: var(--text);
  color: var(--text);
}

.btn--secondary:hover:not(:disabled) {
  background: var(--text);
  color: var(--text-on-ink);
}

.btn--ghost {
  background: none;
  color: var(--text-muted);
}

.btn--ghost:hover:not(:disabled) {
  color: var(--text);
  background: var(--bg-sunken);
}

.btn--danger {
  background: var(--danger);
  color: #fff;
}

.btn--success {
  background: var(--success);
  color: #fff;
}

.btn--sm {
  min-height: 36px;
  padding: var(--space-1) var(--space-3);
  font-size: var(--text-xs);
}

.btn--lg {
  min-height: 54px;
  padding: var(--space-3) var(--space-6);
  font-size: var(--text-base);
}

.btn--block {
  width: 100%;
}

.btn svg {
  width: 16px;
  height: 16px;
  flex-shrink: 0;
}

/* --------------------------------------------------------------------------
   Cards
   -------------------------------------------------------------------------- */

.card {
  background: var(--bg);
  border: var(--border-width) solid var(--border);
  padding: var(--space-5);
}

.card--flush {
  padding: 0;
  overflow: hidden;
}

.card--link {
  display: block;
  color: inherit;
  text-decoration: none;
}

.card--link:hover {
  border-color: var(--text);
  color: inherit;
  background: var(--bg-tint);
}

.card__title {
  font-family: var(--font-serif);
  font-size: var(--text-xl);
  font-weight: var(--weight-bold);
  letter-spacing: var(--tracking-tight);
  margin-bottom: var(--space-2);
}

.card__icon {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 38px;
  height: 38px;
  border: var(--border-width) solid var(--text);
  color: var(--text);
  margin-bottom: var(--space-3);
}

.card__icon svg {
  width: 20px;
  height: 20px;
}

/* Concern cards — one per kind of issue a resident can log.

   Each carries a colour from the thermal ramp, chosen by what the issue IS
   rather than at random: heat and fumes are hot, water and land are cool,
   power and light sit in the amber middle. Someone who has scrolled the map
   has already learned that ramp from the legend, so the cards read as part of
   the same system rather than as decoration. */
.concern-card {
  position: relative;
  display: block;
  padding: var(--space-5) var(--space-4) var(--space-4);
  border: var(--border-width) solid var(--border);
  border-radius: var(--radius-lg);
  background: var(--bg-raised);
  color: inherit;
  text-decoration: none;
  overflow: hidden;
  transition: transform var(--duration-base) var(--ease-out),
              box-shadow var(--duration-base) var(--ease-out),
              border-color var(--duration-base) var(--ease-out);
}

/* A bar of the card's own colour along the top, which grows on hover. */
.concern-card::before {
  content: "";
  position: absolute;
  inset: 0 0 auto 0;
  height: 3px;
  background: var(--card-accent, var(--teal));
  transform-origin: left;
  transition: height var(--duration-base) var(--ease-out);
}

.concern-card:hover {
  transform: translateY(-3px);
  border-color: var(--card-accent, var(--teal));
  box-shadow: var(--shadow-md);
  color: inherit;
}

.concern-card:hover::before { height: 6px; }

.concern-card:hover .concern-card__title { color: var(--card-accent-text, var(--teal-deep)); }

.concern-card:hover .concern-card__go {
  opacity: 1;
  transform: translateX(0);
}

.concern-card__chip {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 42px;
  height: 42px;
  border-radius: var(--radius-md);
  background: var(--card-accent-soft, var(--teal-soft));
  color: var(--card-accent-text, var(--teal-deep));
  margin-bottom: var(--space-3);
}

.concern-card__icon {
  width: 21px;
  height: 21px;
}

.concern-card__title {
  font-family: var(--font-display);
  font-size: var(--text-lg);
  font-weight: var(--weight-bold);
  font-stretch: var(--width-normal);
  font-variation-settings: "wdth" 100;
  letter-spacing: -0.01em;
  margin: 0 0 var(--space-1);
  line-height: var(--leading-snug);
  transition: color var(--duration-fast) var(--ease-out);
}

.concern-card__go {
  display: block;
  margin-top: var(--space-3);
  font-family: var(--font-sans);
  font-size: var(--text-xs);
  font-weight: var(--weight-semibold);
  letter-spacing: var(--tracking-kicker);
  text-transform: uppercase;
  color: var(--card-accent-text, var(--teal-deep));
  opacity: 0;
  transform: translateX(-6px);
  transition: opacity var(--duration-base) var(--ease-out),
              transform var(--duration-base) var(--ease-out);
}

/* Hot: things you smell, hear or feel in your body. */
.concern-card--noise,
.concern-card--air_quality,
.concern-card--health {
  --card-accent: var(--signal);
  --card-accent-text: var(--signal-deep);
  --card-accent-soft: var(--signal-soft);
}

/* Amber: the grid and what it does at night. */
.concern-card--power,
.concern-card--light,
.concern-card--traffic {
  --card-accent: var(--amber);
  --card-accent-text: var(--amber-deep);
  --card-accent-soft: var(--amber-soft);
}

/* Cool: land, water, and what happens to it. */
.concern-card--water,
.concern-card--wildlife {
  --card-accent: var(--teal);
  --card-accent-text: var(--teal-deep);
  --card-accent-soft: var(--teal-soft);
}

.concern-card--property {
  --card-accent: var(--violet);
  --card-accent-text: var(--violet-deep);
  --card-accent-soft: var(--violet-soft);
}

@media (prefers-reduced-motion: reduce) {
  .concern-card,
  .concern-card::before,
  .concern-card__go { transition: none; }
  .concern-card:hover { transform: none; }
  .concern-card__go { opacity: 1; transform: none; }
}

/* --------------------------------------------------------------------------
   Hero — the instrument panel

   The identity lives in the switch: a dark, lit panel carrying the animated
   county, then light editorial paper for everything below it. Every colour
   here is a token, and the panel paints its own background explicitly so it
   never borrows the host page's ground.
   -------------------------------------------------------------------------- */

.hero {
  position: relative;
  isolation: isolate;
  background: var(--bg-ink);
  color: var(--text-on-ink);
  padding-block: var(--space-7) var(--space-8);
  overflow: hidden;
}

/* A wash of heat behind the eastern corridor, echoing where the parcels
   actually are. Sits under the content, above the ground. */
.hero::before {
  content: "";
  position: absolute;
  inset: -20% -10% auto auto;
  width: min(70vw, 900px);
  aspect-ratio: 1;
  background: radial-gradient(
    circle at 60% 45%,
    rgba(255, 107, 61, 0.22),
    rgba(232, 148, 12, 0.12) 38%,
    rgba(0, 184, 189, 0.06) 62%,
    transparent 72%
  );
  z-index: -1;
  pointer-events: none;
}

/* A faint grid, like a plan drawing under the map. */
.hero__grain {
  position: absolute;
  inset: 0;
  z-index: -1;
  pointer-events: none;
  opacity: 0.5;
  background-image:
    linear-gradient(to right, rgba(152, 163, 188, 0.07) 1px, transparent 1px),
    linear-gradient(to bottom, rgba(152, 163, 188, 0.07) 1px, transparent 1px);
  background-size: 56px 56px;
  mask-image: radial-gradient(ellipse at 50% 30%, #000 20%, transparent 78%);
}

.hero__grid {
  display: grid;
  gap: var(--space-6);
  grid-template-columns: 1fr;
  align-items: center;
}

@media (min-width: 1000px) {
  .hero__grid {
    grid-template-columns: 1.02fr 0.98fr;
    gap: var(--space-8);
  }
}

.hero__eyebrow {
  display: inline-flex;
  align-items: center;
  gap: var(--space-2);
  margin-bottom: var(--space-4);
  font-family: var(--font-mono);
  font-size: var(--text-xs);
  font-weight: var(--weight-medium);
  letter-spacing: var(--tracking-kicker);
  text-transform: uppercase;
  color: var(--teal-bright);
}

/* A slow blink — the "live" light on a piece of equipment. */
.hero__pulse {
  width: 8px;
  height: 8px;
  border-radius: 50%;
  background: var(--teal-bright);
  box-shadow: 0 0 0 0 rgba(0, 184, 189, 0.6);
  animation: hero-pulse 2.4s var(--ease-in-out) infinite;
}

@keyframes hero-pulse {
  0%, 100% { box-shadow: 0 0 0 0 rgba(0, 184, 189, 0.55); }
  60%      { box-shadow: 0 0 0 9px rgba(0, 184, 189, 0); }
}

.hero__title {
  font-size: var(--text-4xl);
  font-weight: 800;
  font-stretch: 78%;
  font-variation-settings: "wdth" 78;
  letter-spacing: var(--tracking-tighter);
  line-height: 0.94;
  margin-bottom: var(--space-4);
  color: var(--text-on-ink);
  /* 18ch suited the old two-line headline. The current one has to name the
     site and the action, and at 18ch that ran to five lines on a 1280x800
     laptop — which pushed "Open the map", the one button the site is built
     around, below the fold. */
  max-width: 21ch;
}

.hero__lead {
  font-family: var(--font-serif);
  font-size: var(--text-lg);
  line-height: var(--leading-snug);
  color: var(--text-on-ink-muted);
  max-width: 52ch;
  margin-bottom: var(--space-5);
}

.hero__actions {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-3);
  margin-bottom: var(--space-6);
}

/* Four figures under the copy, in mono, so the panel reads as a readout. */
.hero__figures {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: var(--space-4);
  margin: 0;
  padding-top: var(--space-5);
  border-top: var(--border-width) solid var(--border-on-ink);
}

@media (min-width: 560px) {
  .hero__figures { grid-template-columns: repeat(4, minmax(0, 1fr)); }
}

.hero__figures dt {
  font-family: var(--font-sans);
  font-size: var(--text-xs);
  letter-spacing: var(--tracking-kicker);
  text-transform: uppercase;
  color: var(--text-on-ink-muted);
  margin-bottom: var(--space-1);
}

.hero__figures dd {
  margin: 0;
  font-family: var(--font-mono);
  font-size: var(--text-xl);
  font-weight: 600;
  color: var(--text-on-ink);
  font-variant-numeric: tabular-nums;
}

/* --------------------------------------------------------------------------
   Hero figure — the timeline
   -------------------------------------------------------------------------- */

.hero-fig {
  margin: 0;
  position: relative;
}

.hero-fig__stage {
  position: relative;
}

.hero-fig__svg {
  width: 100%;
  height: auto;
  overflow: visible;
}

.hero-fig__district {
  fill: rgba(152, 163, 188, 0.06);
  stroke: rgba(152, 163, 188, 0.22);
  stroke-width: 1;
}

.hero-fig__outline {
  fill: none;
  stroke: var(--text-on-ink);
  stroke-width: 2;
  stroke-linejoin: round;
  opacity: 0.85;
}

/* Dots are hidden until their year arrives. */
.hero-fig__dot {
  opacity: 0;
  transform-box: fill-box;
  transform-origin: center;
  transform: scale(0.2);
  transition: opacity 420ms var(--ease-out), transform 420ms var(--ease-spring);
}

.hero-fig__dot.is-on {
  opacity: 0.92;
  transform: scale(1);
}

.hero-fig__dot--operational { fill: var(--teal-bright); }
.hero-fig__dot--under_construction { fill: var(--amber-bright); }
.hero-fig__dot--proposed { fill: var(--violet-bright); }
.hero-fig__dot--unknown { fill: var(--text-on-ink-muted); }

/* The year a parcel lands, it flares once. */
.hero-fig__dot.is-new {
  animation: dot-land 900ms var(--ease-out);
}

@keyframes dot-land {
  0%   { filter: drop-shadow(0 0 0 currentColor); }
  25%  { filter: drop-shadow(0 0 7px var(--signal-bright)); }
  100% { filter: drop-shadow(0 0 0 transparent); }
}

/* ---- Readout ------------------------------------------------------------ */

/* The readout sits over the empty north-west of the county, which is exactly
   the part of the map that stays empty for the whole animation. */
.hero-fig__readout {
  position: absolute;
  top: 0;
  left: 0;
  display: flex;
  flex-direction: column;
  gap: var(--space-1);
  pointer-events: none;
  text-shadow: 0 2px 12px rgba(10, 15, 28, 0.85);
}

.hero-fig__year {
  font-family: var(--font-mono);
  font-size: clamp(2.5rem, 1.6rem + 4vw, 4.5rem);
  font-weight: 600;
  line-height: 1;
  color: var(--text-on-ink);
  font-variant-numeric: tabular-nums;
  letter-spacing: -0.03em;
}

.hero-fig__tally {
  display: flex;
  align-items: baseline;
  gap: var(--space-2);
  max-width: 22ch;
}

.hero-fig__count {
  font-family: var(--font-mono);
  font-size: var(--text-xl);
  font-weight: 600;
  color: var(--signal-bright);
  font-variant-numeric: tabular-nums;
}

.hero-fig__unit {
  font-family: var(--font-sans);
  font-size: var(--text-xs);
  line-height: 1.25;
  color: var(--text-on-ink-muted);
}

/* ---- Controls ----------------------------------------------------------- */

.hero-fig__controls {
  display: grid;
  grid-template-columns: auto 1fr;
  grid-template-areas: "play scrub" ". range";
  align-items: center;
  gap: var(--space-2) var(--space-3);
  margin-top: var(--space-3);
}

.hero-fig__play {
  grid-area: play;
  appearance: none;
  width: 40px;
  height: 40px;
  border-radius: 50%;
  border: var(--border-width) solid var(--border-on-ink);
  background: var(--bg-ink-raised);
  color: var(--text-on-ink);
  font-size: 0.7rem;
  line-height: 1;
  cursor: pointer;
  display: flex;
  align-items: center;
  justify-content: center;
}

.hero-fig__play:hover {
  border-color: var(--teal-bright);
  color: var(--teal-bright);
}

.hero-fig__scrub {
  grid-area: scrub;
  width: 100%;
  accent-color: var(--signal-bright);
  cursor: pointer;
  min-width: 0;
}

.hero-fig__range {
  grid-area: range;
  display: flex;
  justify-content: space-between;
  font-family: var(--font-mono);
  font-size: var(--text-xs);
  color: var(--text-on-ink-muted);
}

.hero-fig__caption {
  margin-top: var(--space-4);
  font-family: var(--font-sans);
  font-size: var(--text-xs);
  line-height: var(--leading-normal);
  color: var(--text-on-ink-muted);
  border-top: var(--border-width) solid var(--border-on-ink);
  padding-top: var(--space-3);
}

.hero-fig__caption strong { color: var(--text-on-ink); }

@media (prefers-reduced-motion: reduce) {
  .hero__pulse,
  .hero-fig__dot,
  .hero-fig__dot.is-new {
    animation: none;
    transition: none;
  }
  /* The finished map is the point; a reader who can't take motion gets it
     immediately rather than getting an empty county. */
  .hero-fig__dot.is-on { opacity: 0.92; transform: none; }
  .hero-fig__controls { display: none; }
}

/* --------------------------------------------------------------------------
   Stat tiles — big serif numerals separated by rules
   -------------------------------------------------------------------------- */

.stat-grid {
  display: grid;
  gap: 0;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  border-top: var(--rule-heavy) solid var(--text);
}

/* Tiles carry the status colour of what they count, so the headline numbers
   and the map legend teach each other. */
.stat--operational { --tile: var(--teal); --tile-text: var(--teal-deep); }
.stat--construction { --tile: var(--amber); --tile-text: var(--amber-deep); }
.stat--proposed { --tile: var(--violet); --tile-text: var(--violet-deep); }
.stat--reported { --tile: var(--signal); --tile-text: var(--signal-deep); }

.stat[class*="stat--"]::before {
  content: "";
  display: block;
  width: 28px;
  height: 3px;
  background: var(--tile);
  margin-bottom: var(--space-3);
  border-radius: var(--radius-pill);
}

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

.stat {
  padding: var(--space-4) var(--space-4) var(--space-5) 0;
  border-bottom: var(--rule-width) solid var(--rule);
}

.stat + .stat {
  padding-left: var(--space-4);
  border-left: var(--rule-width) solid var(--rule);
}

@media (min-width: 900px) {
  .stat-grid--4 .stat,
  .stat-grid--3 .stat {
    border-bottom: 0;
  }
}

.stat__value {
  display: block;
  font-family: var(--font-mono);
  font-size: clamp(2.25rem, 1.6rem + 2.4vw, 3.25rem);
  font-weight: 600;
  line-height: 1;
  letter-spacing: -0.04em;
  font-variant-numeric: tabular-nums;
}

.stat__label {
  display: block;
  margin-top: var(--space-2);
  font-family: var(--font-sans);
  font-size: var(--text-sm);
  font-weight: var(--weight-semibold);
  color: var(--text);
}

.stat__note {
  display: block;
  margin-top: var(--space-1);
  font-family: var(--font-sans);
  font-size: var(--text-xs);
  color: var(--text-subtle);
}

/* These use the MIDDLE tier rather than the deep text tier, and that is
   deliberate: the numbers are set around 3rem, which is well past the 24px
   large-text threshold, so 3:1 is the bar and the middle tier clears 4:1 on
   paper. It is the more vivid of the two and still legible. The labels beneath
   them stay in the normal text colour, where the 4.5:1 bar does apply. */
.stat--operational .stat__value {
  color: var(--status-operational);
}
.stat--construction .stat__value {
  color: var(--status-construction);
}
.stat--proposed .stat__value {
  color: var(--status-proposed);
}
.stat--reported .stat__value {
  color: var(--status-reported);
}

/* --------------------------------------------------------------------------
   Badges
   -------------------------------------------------------------------------- */

.badge {
  display: inline-flex;
  align-items: center;
  gap: var(--space-1);
  padding: 0.12em 0.5em;
  font-family: var(--font-sans);
  font-size: 0.6875rem;
  font-weight: var(--weight-semibold);
  letter-spacing: 0.03em;
  line-height: 1.55;
  white-space: nowrap;
  border: var(--border-width) solid transparent;
}

.badge--operational {
  background: var(--status-operational-soft);
  color: var(--status-operational);
}
.badge--under_construction {
  background: var(--status-construction-soft);
  color: var(--status-construction);
}
.badge--proposed {
  background: var(--status-proposed-soft);
  color: var(--status-proposed);
}
.badge--reported,
.badge--community {
  background: var(--status-reported-soft);
  color: var(--status-reported);
}
.badge--unknown,
.badge--neutral {
  background: transparent;
  color: var(--text-muted);
  border-color: var(--border-strong);
}
.badge--pending {
  background: var(--warning-soft);
  color: var(--warning);
}
.badge--approved {
  background: var(--success-soft);
  color: var(--success);
}
.badge--rejected {
  background: var(--danger-soft);
  color: var(--danger);
}

.badge-list {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-1);
}

.severity {
  display: inline-flex;
  align-items: center;
  gap: var(--space-1);
  font-family: var(--font-sans);
  font-size: var(--text-xs);
  font-weight: var(--weight-semibold);
  color: var(--text-muted);
}

.severity__dot {
  width: 9px;
  height: 9px;
  border-radius: 50%;
  flex-shrink: 0;
}

.severity--1 .severity__dot {
  background: var(--severity-1);
}
.severity--2 .severity__dot {
  background: var(--severity-2);
}
.severity--3 .severity__dot {
  background: var(--severity-3);
}
.severity--4 .severity__dot {
  background: var(--severity-4);
}
.severity--5 .severity__dot {
  background: var(--severity-5);
}

/* --------------------------------------------------------------------------
   Banners
   -------------------------------------------------------------------------- */

.banner {
  display: flex;
  align-items: flex-start;
  gap: var(--space-3);
  padding: var(--space-4);
  border: var(--border-width) solid var(--border);
  border-left: var(--rule-heavy) solid var(--text);
  background: var(--bg);
  font-size: var(--text-sm);
  line-height: var(--leading-normal);
}

.banner svg {
  width: 18px;
  height: 18px;
  flex-shrink: 0;
  margin-top: 3px;
}

.banner--info {
  border-left-color: var(--accent);
  background: var(--accent-soft);
  border-color: transparent;
  border-left-color: var(--accent);
}
.banner--warning {
  background: var(--warning-soft);
  border-color: transparent;
  border-left-color: var(--warning);
}
.banner--danger {
  background: var(--danger-soft);
  border-color: transparent;
  border-left-color: var(--danger);
}
.banner--success {
  background: var(--success-soft);
  border-color: transparent;
  border-left-color: var(--success);
}

/* Sits between the dark masthead and whatever follows, so it is styled to
   belong to the panel rather than interrupting it with a cream stripe. */
.demo-banner {
  background: var(--bg-ink-raised);
  color: var(--text-on-ink-muted);
  border-bottom: var(--border-width) solid var(--border-on-ink);
  padding-block: var(--space-2);
  font-family: var(--font-sans);
  font-size: var(--text-xs);
  text-align: center;
}

.demo-banner strong { color: var(--amber-bright); }

.demo-banner a { color: var(--teal-bright); }

.demo-banner a:hover { color: var(--text-on-ink); }

.demo-banner[hidden] {
  display: none;
}

/* --------------------------------------------------------------------------
   Map
   -------------------------------------------------------------------------- */

.map-shell {
  position: relative;
  border: var(--border-width) solid var(--text);
  overflow: hidden;
  background: var(--bg-sunken);
}

.map {
  width: 100%;
  height: 62vh;
  min-height: 400px;
  max-height: 760px;
  background: var(--bg-sunken);
}

.map--compact {
  height: 330px;
  min-height: 0;
}

.leaflet-container {
  font-family: var(--font-sans);
  background: var(--bg-sunken);
}

.leaflet-control-attribution {
  background: rgba(255, 255, 255, 0.9) !important;
  color: #444 !important;
  font-size: 10px !important;
}

.leaflet-control-attribution a {
  color: var(--accent) !important;
}

.leaflet-bar a {
  color: var(--text) !important;
}

/* Markers: distinct colour AND distinct shape, so the map reads without
   colour vision. The legend states both. */
.pin {
  display: block;
  width: 100%;
  height: 100%;
  border: 2px solid #fff;
  box-shadow: 0 1px 3px rgba(0, 0, 0, 0.4);
}

.pin--operational {
  background: var(--status-operational);
  border-radius: 50%;
}

.pin--under_construction {
  background: var(--status-construction);
}

.pin--proposed {
  background: var(--status-proposed);
  transform: rotate(45deg);
}

.pin--report {
  background: var(--status-reported);
  border-radius: 50% 50% 50% 2px;
  transform: rotate(45deg);
  width: 18px;
  height: 18px;
}

.pin--unknown {
  background: var(--text-subtle);
  border-radius: 50%;
}

.marker-cluster-custom {
  display: flex;
  align-items: center;
  justify-content: center;
}

.marker-cluster-custom span {
  background: var(--bg-ink);
  color: #fff;
  border-radius: 50%;
  width: 32px;
  height: 32px;
  display: flex;
  align-items: center;
  justify-content: center;
  font-family: var(--font-sans);
  font-size: var(--text-xs);
  font-weight: var(--weight-bold);
  border: 2px solid #fff;
  box-shadow: 0 1px 4px rgba(0, 0, 0, 0.35);
}

/* A cluster carries the colour of what is inside it. Each status has its own
   cluster group, so "17" over Ashburn reads as seventeen operational sites
   rather than seventeen unspecified things. These are the both-grounds tier of
   the ramp, because a cluster can land on paper or on satellite imagery. */
.marker-cluster-custom--operational span {
  background: var(--status-operational);
}

.marker-cluster-custom--under_construction span {
  background: var(--status-construction);
}

.marker-cluster-custom--proposed span {
  background: var(--status-proposed);
}

.marker-cluster-custom--reports span {
  background: var(--status-reported);
}

/* Popups */
.leaflet-popup-content-wrapper {
  background: var(--bg);
  color: var(--text);
  border-radius: var(--radius-sm);
  border: var(--border-width) solid var(--text);
  box-shadow: var(--shadow-lg);
}

.leaflet-popup-tip {
  background: var(--bg);
  border: var(--border-width) solid var(--text);
}

.leaflet-popup-content {
  margin: var(--space-4);
  font-size: var(--text-sm);
  line-height: var(--leading-snug);
  min-width: 210px;
}

.leaflet-popup-close-button {
  color: var(--text-muted) !important;
  padding: 6px 6px 0 0 !important;
}

.popup__title {
  font-family: var(--font-serif);
  font-size: var(--text-lg);
  font-weight: var(--weight-bold);
  letter-spacing: var(--tracking-tight);
  margin: 0 0 var(--space-2);
  padding-right: var(--space-4);
  line-height: var(--leading-snug);
}

.popup__meta {
  margin: var(--space-3) 0 0;
  display: grid;
  grid-template-columns: auto 1fr;
  gap: var(--space-1) var(--space-3);
  font-family: var(--font-sans);
  font-size: var(--text-xs);
  padding-top: var(--space-2);
  border-top: var(--rule-width) solid var(--rule);
}

.popup__meta dt {
  color: var(--text-subtle);
}

.popup__meta dd {
  margin: 0;
  font-variant-numeric: tabular-nums;
}

.popup__body {
  margin: var(--space-2) 0 0;
  color: var(--text-muted);
}

.popup__source {
  margin-top: var(--space-3);
  padding-top: var(--space-2);
  border-top: var(--rule-width) solid var(--rule);
  font-family: var(--font-sans);
  font-size: var(--text-xs);
  color: var(--text-subtle);
}

/* Legend */
.legend {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-2) var(--space-5);
  padding: var(--space-3) var(--space-4);
  border-top: var(--border-width) solid var(--border);
  background: var(--bg);
  font-family: var(--font-sans);
  font-size: var(--text-xs);
}

.legend__item {
  display: inline-flex;
  align-items: center;
  gap: var(--space-2);
  color: var(--text-muted);
}

.legend__swatch {
  width: 13px;
  height: 13px;
  flex-shrink: 0;
  border: 2px solid var(--bg);
  box-shadow: 0 0 0 1px var(--border-strong);
}

.legend__swatch--operational {
  background: var(--status-operational);
  border-radius: 50%;
}
.legend__swatch--under_construction {
  background: var(--status-construction);
}
.legend__swatch--proposed {
  background: var(--status-proposed);
  transform: rotate(45deg);
}
.legend__swatch--report {
  background: var(--status-reported);
  border-radius: 50% 50% 50% 2px;
  transform: rotate(45deg);
}

/* --------------------------------------------------------------------------
   Filters and layer toggles
   -------------------------------------------------------------------------- */

.filter-bar {
  display: grid;
  gap: var(--space-3);
  padding: var(--space-4) 0;
  border-block: var(--rule-width) solid var(--rule);
  margin-bottom: var(--space-5);
}

@media (min-width: 720px) {
  .filter-bar {
    grid-template-columns: repeat(3, minmax(0, 1fr)) auto;
    align-items: end;
    gap: var(--space-4);
  }
}

.filter-bar__field {
  display: flex;
  flex-direction: column;
  gap: var(--space-1);
  min-width: 0;
}

.filter-bar__field label {
  font-family: var(--font-sans);
  font-size: var(--text-xs);
  font-weight: var(--weight-bold);
  color: var(--text);
  text-transform: uppercase;
  letter-spacing: var(--tracking-kicker);
}

.filter-bar__result {
  font-family: var(--font-sans);
  font-size: var(--text-sm);
  color: var(--text-muted);
  margin-bottom: var(--space-3);
}

.layer-toggles {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-2);
  padding: var(--space-3) var(--space-4);
  background: var(--bg);
  border-bottom: var(--border-width) solid var(--border);
}

.layer-toggle {
  display: inline-flex;
  align-items: center;
  gap: var(--space-2);
  padding: var(--space-1) var(--space-3);
  border: var(--border-width) solid var(--border-strong);
  font-family: var(--font-sans);
  font-size: var(--text-xs);
  font-weight: var(--weight-medium);
  color: var(--text-muted);
  cursor: pointer;
  min-height: 34px;
  user-select: none;
  transition: border-color var(--duration-fast) var(--ease-out),
    color var(--duration-fast) var(--ease-out);
}

.layer-toggle input {
  position: absolute;
  opacity: 0;
  width: 1px;
  height: 1px;
}

.layer-toggle:has(input:checked) {
  color: var(--text);
  border-color: var(--text);
}

.layer-toggle:has(input:focus-visible) {
  outline: 2px solid var(--focus-ring);
  outline-offset: 2px;
}

.layer-toggle:has(input:not(:checked)) .legend__swatch {
  opacity: 0.25;
}

.layer-toggle__count {
  color: var(--text-subtle);
  font-variant-numeric: tabular-nums;
}

/* --------------------------------------------------------------------------
   Forms
   -------------------------------------------------------------------------- */

.field {
  display: flex;
  flex-direction: column;
  gap: var(--space-2);
  margin-bottom: var(--space-5);
}

.field__label {
  font-family: var(--font-sans);
  font-size: var(--text-sm);
  font-weight: var(--weight-semibold);
  color: var(--text);
}

.field__required {
  color: var(--danger);
  margin-left: 2px;
}

.field__optional {
  color: var(--text-subtle);
  font-weight: var(--weight-normal);
  margin-left: var(--space-2);
  font-size: var(--text-xs);
}

.field__help {
  font-family: var(--font-sans);
  font-size: var(--text-xs);
  color: var(--text-muted);
  margin: 0;
  line-height: var(--leading-normal);
}

.field__error {
  font-family: var(--font-sans);
  font-size: var(--text-xs);
  color: var(--danger);
  font-weight: var(--weight-semibold);
  margin: 0;
}

.field__error:empty {
  display: none;
}

.input,
.select,
.textarea {
  width: 100%;
  min-height: 46px;
  padding: var(--space-2) var(--space-3);
  border: var(--border-width) solid var(--border-strong);
  border-radius: var(--radius-sm);
  background: var(--bg);
  color: var(--text);
  font-family: var(--font-sans);
  font-size: var(--text-base);
  transition: border-color var(--duration-fast) var(--ease-out);
}

.input::placeholder,
.textarea::placeholder {
  color: var(--text-subtle);
}

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

.textarea {
  min-height: 140px;
  resize: vertical;
  line-height: var(--leading-normal);
  font-family: var(--font-serif);
}

.select {
  appearance: none;
  background-image: url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' fill='%235a5a55'%3E%3Cpath d='M4.5 6.5 8 10l3.5-3.5z'/%3E%3C/svg%3E");
  background-repeat: no-repeat;
  background-position: right var(--space-3) center;
  background-size: 16px;
  padding-right: var(--space-6);
}

.field--invalid .input,
.field--invalid .select,
.field--invalid .textarea,
.field--invalid .checkbox-grid,
.field--invalid .location-picker {
  border-color: var(--danger);
}

.checkbox-grid {
  display: grid;
  gap: var(--space-1);
  grid-template-columns: 1fr;
  padding: var(--space-3);
  border: var(--border-width) solid var(--border);
  background: var(--bg);
}

@media (min-width: 560px) {
  .checkbox-grid {
    grid-template-columns: repeat(2, minmax(0, 1fr));
    gap: var(--space-2);
  }
}

.choice {
  display: flex;
  align-items: flex-start;
  gap: var(--space-3);
  padding: var(--space-2);
  cursor: pointer;
  font-family: var(--font-sans);
  font-size: var(--text-sm);
  min-height: 40px;
  transition: background var(--duration-fast) var(--ease-out);
}

.choice:hover {
  background: var(--bg-sunken);
}

.choice input {
  width: 19px;
  height: 19px;
  margin: 2px 0 0;
  flex-shrink: 0;
  accent-color: var(--bg-ink);
  cursor: pointer;
}

.choice__text {
  display: flex;
  flex-direction: column;
  gap: 2px;
}

.choice__title {
  font-weight: var(--weight-medium);
}

.choice__note {
  font-size: var(--text-xs);
  color: var(--text-muted);
}

.radio-stack {
  display: flex;
  flex-direction: column;
  border: var(--border-width) solid var(--border);
  padding: var(--space-3);
  background: var(--bg);
}

fieldset {
  border: 0;
  margin: 0;
  padding: 0;
}

legend {
  padding: 0;
}

.severity-scale {
  display: flex;
  gap: var(--space-2);
}

.severity-scale__option {
  flex: 1;
  position: relative;
}

.severity-scale__option input {
  position: absolute;
  opacity: 0;
  width: 1px;
  height: 1px;
}

.severity-scale__option span {
  display: flex;
  align-items: center;
  justify-content: center;
  min-height: 46px;
  border: var(--border-width) solid var(--border-strong);
  font-family: var(--font-serif);
  font-weight: var(--weight-bold);
  font-size: var(--text-lg);
  cursor: pointer;
  background: var(--bg);
  color: var(--text-muted);
  transition: background var(--duration-fast) var(--ease-out),
    color var(--duration-fast) var(--ease-out),
    border-color var(--duration-fast) var(--ease-out);
}

.severity-scale__option input:checked + span {
  background: var(--bg-ink);
  border-color: var(--bg-ink);
  color: var(--text-on-ink);
}

.severity-scale__option input:focus-visible + span {
  outline: 2px solid var(--focus-ring);
  outline-offset: 2px;
}

.severity-scale__ends {
  display: flex;
  justify-content: space-between;
  font-family: var(--font-sans);
  font-size: var(--text-xs);
  color: var(--text-subtle);
  margin-top: var(--space-1);
}

/* Honeypot — off-screen, not display:none, so bots still see it. */
.hp-field {
  position: absolute !important;
  left: -9999px;
  top: -9999px;
  width: 1px;
  height: 1px;
  overflow: hidden;
}

.location-picker {
  border: var(--border-width) solid var(--border);
  overflow: hidden;
  background: var(--bg);
}

.location-picker__actions {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-2);
  padding: var(--space-3);
  border-bottom: var(--border-width) solid var(--border);
}

.location-picker__status {
  padding: var(--space-2) var(--space-3);
  font-family: var(--font-sans);
  font-size: var(--text-xs);
  color: var(--text-muted);
  background: var(--bg-sunken);
  border-top: var(--border-width) solid var(--border);
  min-height: 32px;
}

.address-search {
  display: flex;
  gap: var(--space-2);
  padding: 0 var(--space-3) var(--space-3);
}

.address-search .input {
  flex: 1;
  min-width: 0;
}

.photo-drop {
  border: 2px dashed var(--border-strong);
  padding: var(--space-6);
  text-align: center;
  background: var(--bg);
  cursor: pointer;
  transition: border-color var(--duration-fast) var(--ease-out),
    background var(--duration-fast) var(--ease-out);
}

.photo-drop:hover,
.photo-drop.is-dragover {
  border-color: var(--text);
  background: var(--bg-sunken);
}

.photo-drop__icon {
  color: var(--text-subtle);
  margin: 0 auto var(--space-2);
  width: 28px;
  height: 28px;
}

.photo-previews {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(110px, 1fr));
  gap: var(--space-2);
  margin-top: var(--space-3);
}

.photo-previews:empty {
  display: none;
}

.photo-preview {
  position: relative;
  overflow: hidden;
  border: var(--border-width) solid var(--border);
  background: var(--bg-sunken);
  aspect-ratio: 4 / 3;
}

.photo-preview img {
  width: 100%;
  height: 100%;
  object-fit: cover;
}

.photo-preview__remove {
  position: absolute;
  top: var(--space-1);
  right: var(--space-1);
  width: 28px;
  height: 28px;
  border: 0;
  border-radius: 50%;
  background: rgba(0, 0, 0, 0.75);
  color: #fff;
  cursor: pointer;
  display: flex;
  align-items: center;
  justify-content: center;
  font-size: var(--text-base);
  line-height: 1;
}

.photo-preview__remove:hover {
  background: var(--danger);
}

.photo-preview__name {
  position: absolute;
  bottom: 0;
  left: 0;
  right: 0;
  padding: var(--space-1);
  background: rgba(0, 0, 0, 0.7);
  color: #fff;
  font-family: var(--font-sans);
  font-size: 10px;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

/* Form sections are separated by rules, numbered like a printed form. */
.form-section {
  border-top: var(--rule-heavy) solid var(--text);
  padding-top: var(--space-4);
  margin-bottom: var(--space-7);
}

.form-section__header {
  margin-bottom: var(--space-5);
}

.form-section__step {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 26px;
  height: 26px;
  background: var(--bg-ink);
  color: var(--text-on-ink);
  font-family: var(--font-sans);
  font-size: var(--text-xs);
  font-weight: var(--weight-bold);
  margin-right: var(--space-3);
  flex-shrink: 0;
}

.form-section h2 {
  font-size: var(--text-2xl);
  margin-bottom: var(--space-2);
  display: flex;
  align-items: center;
}

.form-actions {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-3);
  align-items: center;
  margin-top: var(--space-5);
}

.form-status {
  font-family: var(--font-sans);
  font-size: var(--text-sm);
  font-weight: var(--weight-medium);
}

.form-status--error {
  color: var(--danger);
}

.form-status--success {
  color: var(--success);
}

/* --------------------------------------------------------------------------
   Report list
   -------------------------------------------------------------------------- */

.report-list {
  display: grid;
  gap: 0;
  list-style: none;
  margin: 0;
  padding: 0;
}

.report-list li {
  margin: 0;
}

.report-card {
  padding: var(--space-5) 0;
  border-top: var(--rule-width) solid var(--rule);
}

.report-card__head {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-2);
  align-items: baseline;
  justify-content: space-between;
  margin-bottom: var(--space-2);
}

.report-card__where {
  font-family: var(--font-serif);
  font-weight: var(--weight-bold);
  font-size: var(--text-xl);
  letter-spacing: var(--tracking-tight);
  margin: 0;
}

.report-card__when {
  font-family: var(--font-sans);
  font-size: var(--text-xs);
  color: var(--text-subtle);
  font-variant-numeric: tabular-nums;
}

.report-card__body {
  color: var(--text);
  font-size: var(--text-base);
  line-height: var(--leading-normal);
  margin-bottom: var(--space-3);
  max-width: 68ch;
}

.report-card__footer {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-2) var(--space-4);
  align-items: center;
  justify-content: space-between;
}

.report-card__photos {
  display: flex;
  gap: var(--space-2);
  margin-bottom: var(--space-3);
  flex-wrap: wrap;
}

.report-card__photos img {
  width: 90px;
  height: 68px;
  object-fit: cover;
  border: var(--border-width) solid var(--border);
}

/* --------------------------------------------------------------------------
   Charts — CSS bars inside real table markup
   -------------------------------------------------------------------------- */

.chart {
  width: 100%;
  border-collapse: collapse;
  font-family: var(--font-sans);
  font-size: var(--text-sm);
}

.chart caption {
  text-align: left;
  font-family: var(--font-serif);
  font-weight: var(--weight-bold);
  font-size: var(--text-xl);
  letter-spacing: var(--tracking-tight);
  margin-bottom: var(--space-3);
  padding-bottom: var(--space-2);
  border-bottom: var(--rule-heavy) solid var(--text);
  color: var(--text);
}

.chart th,
.chart td {
  padding: var(--space-2) 0;
  text-align: left;
  vertical-align: middle;
  border-bottom: var(--rule-width) solid var(--rule);
}

.chart tr:last-child th,
.chart tr:last-child td {
  border-bottom: 0;
}

/* These three are scoped under .chart on purpose. `.chart th, .chart td` above
   is (0,1,1) and sets `padding: var(--space-2) 0`; an unscoped `.chart__label`
   is only (0,1,0), so its horizontal padding would lose and the longest bar
   would sit flush against its value. */
.chart .chart__label {
  width: 36%;
  font-weight: var(--weight-normal);
  color: var(--text);
  padding-right: var(--space-3);
}

.chart .chart__bar-cell {
  width: 42%;
  padding-right: var(--space-4);
}

.chart .chart__value {
  width: 22%;
  text-align: right;
  font-variant-numeric: tabular-nums;
  color: var(--text-muted);
  white-space: nowrap;
  padding-left: var(--space-2);
}

.chart__bar {
  height: 10px;
  background: var(--bg-ink);
  min-width: 3px;
}

.chart__bar--reported {
  background: var(--status-reported);
}
.chart__bar--operational {
  background: var(--status-operational);
}
.chart__bar--construction {
  background: var(--status-construction);
}
.chart__bar--proposed {
  background: var(--status-proposed);
}

.chart__track {
  background: var(--bg-sunken);
  overflow: hidden;
  /* The track takes its height from the bar inside it. A zero row has no bar,
     so without this the rail would collapse and the row would look broken
     rather than empty. Matches .chart__bar's height. */
  min-height: 10px;
}

.timeline-chart {
  display: flex;
  align-items: flex-end;
  gap: var(--space-1);
  height: 150px;
  padding-top: var(--space-4);
  border-bottom: var(--rule-width) solid var(--rule);
}

.timeline-chart__col {
  flex: 1;
  display: flex;
  flex-direction: column;
  justify-content: flex-end;
  align-items: center;
  gap: var(--space-1);
  height: 100%;
  min-width: 0;
}

.timeline-chart__bar {
  width: 100%;
  max-width: 36px;
  background: var(--bg-ink);
  min-height: 2px;
}

.timeline-chart__label {
  font-family: var(--font-sans);
  font-size: 10px;
  color: var(--text-subtle);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  max-width: 100%;
}

/* --------------------------------------------------------------------------
   Tables
   -------------------------------------------------------------------------- */

.table-scroll {
  overflow-x: auto;
  -webkit-overflow-scrolling: touch;
}

.data-table {
  width: 100%;
  border-collapse: collapse;
  font-family: var(--font-sans);
  font-size: var(--text-sm);
  min-width: 580px;
}

.data-table th,
.data-table td {
  padding: var(--space-3) var(--space-3) var(--space-3) 0;
  text-align: left;
  border-bottom: var(--rule-width) solid var(--rule);
  vertical-align: top;
}

.data-table thead th {
  font-weight: var(--weight-bold);
  color: var(--text);
  font-size: var(--text-xs);
  text-transform: uppercase;
  letter-spacing: var(--tracking-kicker);
  white-space: nowrap;
  border-bottom: var(--rule-heavy) solid var(--text);
}

.data-table tbody tr {
  transition: background var(--duration-fast) var(--ease-out);
}

.data-table tbody tr:hover {
  background: var(--bg-sunken);
}

/* --------------------------------------------------------------------------
   States
   -------------------------------------------------------------------------- */

.empty-state {
  text-align: center;
  padding: var(--space-7) var(--space-4);
  color: var(--text-muted);
  border: var(--border-width) dashed var(--border-strong);
}

.empty-state__title {
  font-family: var(--font-serif);
  font-size: var(--text-xl);
  font-weight: var(--weight-bold);
  color: var(--text);
  margin-bottom: var(--space-2);
}

.loading {
  display: flex;
  align-items: center;
  gap: var(--space-3);
  padding: var(--space-7);
  justify-content: center;
  color: var(--text-muted);
  font-family: var(--font-sans);
  font-size: var(--text-sm);
}

.spinner {
  width: 18px;
  height: 18px;
  border: 2px solid var(--border-strong);
  border-top-color: var(--text);
  border-radius: 50%;
  flex-shrink: 0;
}

/* --------------------------------------------------------------------------
   FAQ
   -------------------------------------------------------------------------- */

.faq-item {
  border-top: var(--rule-width) solid var(--rule);
}

.faq-item summary {
  padding: var(--space-4) 0;
  cursor: pointer;
  font-family: var(--font-serif);
  font-weight: var(--weight-bold);
  font-size: var(--text-lg);
  letter-spacing: var(--tracking-tight);
  list-style: none;
  display: flex;
  justify-content: space-between;
  gap: var(--space-3);
  align-items: center;
}

.faq-item summary::-webkit-details-marker {
  display: none;
}

.faq-item summary::after {
  content: "+";
  font-family: var(--font-sans);
  font-size: var(--text-xl);
  font-weight: var(--weight-normal);
  color: var(--text-subtle);
  line-height: 1;
  flex-shrink: 0;
  transition: transform var(--duration-base) var(--ease-spring);
}

.faq-item[open] summary::after {
  content: "\2212";
  transform: rotate(180deg);
}

.faq-item summary:hover {
  color: var(--accent);
}

.faq-item__body {
  padding: 0 0 var(--space-5);
  color: var(--text-muted);
  font-size: var(--text-base);
  max-width: 68ch;
  animation: fade-up var(--duration-base) var(--ease-out) both;
}

.faq-item__body > *:last-child {
  margin-bottom: 0;
}

/* --------------------------------------------------------------------------
   Misc
   -------------------------------------------------------------------------- */

.pagination {
  display: flex;
  gap: var(--space-2);
  justify-content: center;
  align-items: center;
  margin-top: var(--space-5);
}

.link-list {
  list-style: none;
  padding: 0;
  margin: 0;
}

.link-list li {
  margin: 0;
  border-bottom: var(--rule-width) solid var(--rule);
}

.link-list li:last-child {
  border-bottom: 0;
}

/* Label and note stack on narrow screens. Side by side they overflow: the note
   is flex-shrink: 0 and its text is long enough to push past 320px. */
.link-list a {
  display: flex;
  flex-direction: column;
  gap: var(--space-1);
  padding: var(--space-3) 0;
  text-decoration: none;
  color: var(--text);
  font-family: var(--font-sans);
  transition: padding-left var(--duration-fast) var(--ease-out);
}

@media (min-width: 560px) {
  .link-list a {
    flex-direction: row;
    justify-content: space-between;
    align-items: baseline;
    gap: var(--space-4);
  }
}

.link-list a:hover {
  color: var(--accent);
  padding-left: var(--space-2);
}

.link-list__note {
  color: var(--text-subtle);
  font-size: var(--text-xs);
  min-width: 0;
}

@media (min-width: 560px) {
  .link-list__note {
    text-align: right;
    flex-shrink: 0;
  }
}

/* ============================================================================
   Map controls — basemap, overlays, tools
   ==========================================================================*/

.map-controls {
  background: var(--bg);
  border-bottom: var(--border-width) solid var(--border);
}

.map-controls__sheet > summary {
  padding: var(--space-3) var(--space-4);
  font-family: var(--font-sans);
  font-size: var(--text-sm);
  font-weight: var(--weight-semibold);
  letter-spacing: var(--tracking-kicker);
  text-transform: uppercase;
  cursor: pointer;
  list-style: none;
}

.map-controls__sheet > summary::-webkit-details-marker {
  display: none;
}

.map-controls__sheet > summary::after {
  content: " +";
  color: var(--text-subtle);
}

.map-controls__sheet[open] > summary::after {
  content: " −";
}

.map-controls__sheet > summary:focus-visible {
  outline: 2px solid var(--focus-ring);
  outline-offset: -2px;
}

.map-controls__body {
  display: grid;
  gap: var(--space-4);
  padding: 0 var(--space-4) var(--space-4);
}

@media (min-width: 900px) {
  .map-controls__body {
    grid-template-columns: auto 1fr auto;
    align-items: start;
    gap: var(--space-6);
  }
}

.map-controls__title {
  font-family: var(--font-sans);
  font-size: var(--text-xs);
  font-weight: var(--weight-semibold);
  letter-spacing: var(--tracking-kicker);
  text-transform: uppercase;
  color: var(--text-subtle);
  margin: 0 0 var(--space-2);
}

/* The readout is a live region, so it must keep its box even when empty —
   a container that collapses to zero height shifts the map every time the
   measure tool says anything. */
.map-controls__readout {
  min-height: 1.35em;
  margin: 0;
  padding: 0 var(--space-4) var(--space-3);
  font-family: var(--font-sans);
  font-size: var(--text-sm);
  color: var(--text-muted);
}

.map-controls__readout.is-error {
  color: var(--danger);
}

/* ---- Segmented basemap switcher ---------------------------------------- */

.segmented {
  display: inline-flex;
  flex-wrap: wrap;
  border: var(--border-width) solid var(--border-strong);
}

.segmented__option {
  appearance: none;
  border: 0;
  border-right: var(--border-width) solid var(--border-strong);
  background: var(--bg);
  color: var(--text-muted);
  font-family: var(--font-sans);
  font-size: var(--text-xs);
  font-weight: var(--weight-medium);
  padding: var(--space-2) var(--space-3);
  cursor: pointer;
}

.segmented__option:last-child {
  border-right: 0;
}

.segmented__option:hover {
  background: var(--bg-sunken);
  color: var(--text);
}

.segmented__option[aria-checked="true"] {
  background: var(--bg-ink);
  color: var(--text-on-ink);
}

.segmented__option:focus-visible {
  outline: 2px solid var(--focus-ring);
  outline-offset: -2px;
  z-index: 1;
}

/* ---- Overlay toggles ---------------------------------------------------- */

.overlay-toggles {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(180px, 1fr));
  gap: var(--space-2);
}

.overlay-toggle {
  display: grid;
  grid-template-columns: auto 1fr;
  gap: 0 var(--space-2);
  align-items: baseline;
  padding: var(--space-2);
  border: var(--border-width) solid var(--border);
  cursor: pointer;
  font-family: var(--font-sans);
}

.overlay-toggle:hover {
  border-color: var(--border-strong);
  background: var(--bg-tint);
}

.overlay-toggle:has(input:checked) {
  border-color: var(--text);
  background: var(--bg-sunken);
}

.overlay-toggle input {
  grid-row: span 2;
  align-self: center;
  margin: 0;
}

.overlay-toggle__label {
  font-size: var(--text-sm);
  font-weight: var(--weight-medium);
}

.overlay-toggle__hint {
  grid-column: 2;
  font-size: var(--text-xs);
  color: var(--text-subtle);
  line-height: var(--leading-snug);
}

/* ---- Tools -------------------------------------------------------------- */

.map-tools {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-2);
  margin-bottom: var(--space-3);
}

.map-tools .btn[aria-pressed="true"] {
  background: var(--bg-ink);
  color: var(--text-on-ink);
  border-color: var(--bg-ink);
}

.map-search {
  display: flex;
  gap: var(--space-2);
}

.map-search .input {
  min-width: 0;
  flex: 1 1 auto;
}

.map-search__results {
  margin-top: var(--space-2);
  border: var(--border-width) solid var(--border-strong);
  max-height: 220px;
  overflow-y: auto;
  background: var(--bg);
}

.map-search__result {
  display: block;
  width: 100%;
  text-align: left;
  border: 0;
  border-bottom: var(--border-width) solid var(--border);
  background: none;
  padding: var(--space-2) var(--space-3);
  font-family: var(--font-sans);
  font-size: var(--text-xs);
  line-height: var(--leading-snug);
  color: var(--text-muted);
  cursor: pointer;
}

.map-search__result:last-child {
  border-bottom: 0;
}

.map-search__result:hover,
.map-search__result:focus-visible {
  background: var(--accent-soft);
  color: var(--text);
}

/* ---- Map surface states ------------------------------------------------- */

.map--measuring {
  cursor: crosshair;
}

.map--measuring .leaflet-grab {
  cursor: crosshair;
}

/* While a tool is armed the map is a canvas to click on, so markers must stop
   swallowing the click. Without this, clicking anywhere near a pin or a cluster
   — which is most of eastern Loudoun — does nothing at all: the marker eats the
   event, the map never hears it, and the reader is left pressing a dead map. */
.map--measuring .leaflet-marker-pane,
.map--measuring .leaflet-overlay-pane,
.map--measuring .leaflet-marker-icon {
  pointer-events: none;
}

/* Markers are drawn for a light background. Over aerial imagery they vanish
   into tree cover and asphalt without a halo, so the base layer switch adds
   this class and the pins grow an outline. */
.map--dark-base .pin {
  box-shadow: 0 0 0 1.5px rgba(255, 255, 255, 0.95), 0 1px 3px rgba(0, 0, 0, 0.5);
}

/* The halo goes on the span, not the wrapper. `.marker-cluster-custom` is a
   square flex box; the circle is its child, so a shadow on the parent draws a
   white square around a round cluster. */
.map--dark-base .marker-cluster-custom span {
  box-shadow: 0 0 0 1px rgba(0, 0, 0, 0.45), 0 2px 6px rgba(0, 0, 0, 0.55);
}

.map--dark-base .district-label span,
.map--dark-base .ring-label span {
  background: rgba(255, 255, 255, 0.92);
}

/* Fallback for browsers without the Fullscreen API — notably Safari on iOS,
   where Element.requestFullscreen doesn't exist at all. */
.map-shell.is-pseudo-fullscreen {
  position: fixed;
  inset: 0;
  z-index: 2000;
  border: 0;
}

.map-shell.is-pseudo-fullscreen .map {
  height: 100vh;
  max-height: none;
}

/* ---- Overlay labels ----------------------------------------------------- */

.district-label span,
.ring-label span {
  display: inline-block;
  transform: translate(-50%, -50%);
  white-space: nowrap;
  font-family: var(--font-sans);
  font-size: var(--text-xs);
  font-weight: var(--weight-semibold);
  letter-spacing: var(--tracking-kicker);
  text-transform: uppercase;
  color: var(--text);
  background: rgba(255, 255, 255, 0.82);
  padding: 1px 5px;
}

.ring-label span {
  text-transform: none;
  letter-spacing: 0;
  color: var(--accent);
}

.leaflet-heat-layer {
  pointer-events: none;
}

/* ============================================================================
   Report detail — sheet, record view, photo lightbox
   ==========================================================================*/

.report-card__more {
  margin: var(--space-3) 0 0;
  font-family: var(--font-sans);
  font-size: var(--text-sm);
  font-weight: var(--weight-medium);
}

/* ---- The sheet ---------------------------------------------------------- */

.report-sheet {
  position: fixed;
  z-index: 1200;
  background: var(--bg);
  border: var(--border-width) solid var(--text);
  box-shadow: var(--shadow-lg);
  display: flex;
  flex-direction: column;
  overflow: hidden;
}

/* Bottom sheet on a phone, side sheet from about tablet width up. */
.report-sheet {
  left: 0;
  right: 0;
  bottom: 0;
  max-height: 88vh;
  transform: translateY(100%);
  transition: transform 260ms cubic-bezier(0.2, 0.7, 0.3, 1);
}

.report-sheet.is-open {
  transform: translateY(0);
}

@media (min-width: 860px) {
  .report-sheet {
    left: auto;
    top: 0;
    bottom: 0;
    width: min(520px, 44vw);
    max-height: none;
    transform: translateX(100%);
  }

  .report-sheet.is-open {
    transform: translateX(0);
  }
}

@media (prefers-reduced-motion: reduce) {
  .report-sheet {
    transition: none;
  }
}

.report-sheet__bar {
  display: flex;
  justify-content: flex-end;
  padding: var(--space-2) var(--space-3);
  border-bottom: var(--border-width) solid var(--border);
  flex: 0 0 auto;
}

.report-sheet__close {
  appearance: none;
  border: var(--border-width) solid var(--border-strong);
  background: var(--bg);
  font-family: var(--font-sans);
  font-size: var(--text-xs);
  font-weight: var(--weight-semibold);
  letter-spacing: var(--tracking-kicker);
  text-transform: uppercase;
  padding: var(--space-2) var(--space-3);
  cursor: pointer;
}

.report-sheet__close:hover {
  background: var(--bg-ink);
  color: var(--text-on-ink);
  border-color: var(--bg-ink);
}

.report-sheet__body {
  overflow-y: auto;
  padding: var(--space-4) var(--space-5) var(--space-6);
  flex: 1 1 auto;
  -webkit-overflow-scrolling: touch;
}

/* ---- The record --------------------------------------------------------- */

.detail__head {
  border-bottom: var(--rule-heavy) solid var(--text);
  padding-bottom: var(--space-3);
  margin-bottom: var(--space-4);
}

.detail__head h2 {
  font-size: var(--text-2xl);
  line-height: var(--leading-tight);
  letter-spacing: var(--tracking-tight);
  margin: var(--space-1) 0 var(--space-2);
}

.detail__when {
  font-family: var(--font-sans);
  font-size: var(--text-sm);
  color: var(--text-subtle);
  margin: 0;
}

.detail__badges {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-3);
  align-items: center;
  justify-content: space-between;
  margin-bottom: var(--space-5);
}

.detail__subhead {
  font-family: var(--font-sans);
  font-size: var(--text-xs);
  font-weight: var(--weight-semibold);
  letter-spacing: var(--tracking-kicker);
  text-transform: uppercase;
  color: var(--text-subtle);
  margin: var(--space-5) 0 var(--space-2);
  padding-bottom: var(--space-1);
  border-bottom: var(--border-width) solid var(--border);
}

.detail__subsubhead {
  font-family: var(--font-sans);
  font-size: var(--text-sm);
  font-weight: var(--weight-semibold);
  margin: var(--space-4) 0 var(--space-2);
}

.detail__body {
  font-size: var(--text-base);
  line-height: var(--leading-loose);
  /* Reports are typed into a textarea and people press Enter. Preserving the
     line breaks they made is the difference between a legible account and one
     run-on paragraph. */
  white-space: pre-wrap;
  margin: 0 0 var(--space-3);
}

.detail__meta {
  display: grid;
  grid-template-columns: minmax(9rem, auto) 1fr;
  gap: var(--space-2) var(--space-4);
  margin: 0 0 var(--space-4);
  font-family: var(--font-sans);
  font-size: var(--text-sm);
}

.detail__meta dt {
  color: var(--text-subtle);
}

.detail__meta dd {
  margin: 0;
  font-weight: var(--weight-medium);
}

.detail__list {
  list-style: none;
  padding: 0;
  margin: 0 0 var(--space-3);
  font-family: var(--font-sans);
  font-size: var(--text-sm);
}

.detail__list li {
  padding: var(--space-2) 0;
  border-bottom: var(--border-width) solid var(--border);
}

.detail__list li:last-child {
  border-bottom: 0;
}

.detail__actions {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-2);
  margin: var(--space-5) 0 var(--space-3);
}

/* ---- Photos ------------------------------------------------------------- */

.photo-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(140px, 1fr));
  gap: var(--space-2);
  margin-bottom: var(--space-2);
}

.photo-grid__item {
  appearance: none;
  border: var(--border-width) solid var(--border-strong);
  background: var(--bg-sunken);
  padding: 0;
  cursor: zoom-in;
  overflow: hidden;
  aspect-ratio: 4 / 3;
}

.photo-grid__item img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  display: block;
}

.photo-grid__item:hover img {
  opacity: 0.88;
}

.photo-grid__item:focus-visible {
  outline: 2px solid var(--focus-ring);
  outline-offset: 2px;
}

/* ---- Lightbox ----------------------------------------------------------- */

.lightbox {
  position: fixed;
  inset: 0;
  z-index: 1400;
  background: rgba(18, 18, 18, 0.92);
  display: none;
  align-items: center;
  justify-content: center;
  padding: var(--space-5);
}

.lightbox.is-open {
  display: flex;
}

.lightbox img {
  max-width: 100%;
  max-height: 88vh;
  object-fit: contain;
  background: #000;
}

.lightbox__close,
.lightbox__nav {
  position: absolute;
  appearance: none;
  border: var(--border-width) solid rgba(255, 255, 255, 0.55);
  background: rgba(18, 18, 18, 0.65);
  color: #fff;
  cursor: pointer;
  font-size: 1.75rem;
  line-height: 1;
  width: 48px;
  height: 48px;
  display: flex;
  align-items: center;
  justify-content: center;
}

.lightbox__close {
  top: var(--space-4);
  right: var(--space-4);
  font-size: 1.5rem;
}

.lightbox__nav--prev {
  left: var(--space-3);
}

.lightbox__nav--next {
  right: var(--space-3);
}

.lightbox__close:hover,
.lightbox__nav:hover {
  background: #fff;
  color: var(--text);
}

.lightbox__close:focus-visible,
.lightbox__nav:focus-visible {
  outline: 2px solid #fff;
  outline-offset: 2px;
}

.lightbox__count {
  position: absolute;
  bottom: var(--space-4);
  left: 50%;
  transform: translateX(-50%);
  color: rgba(255, 255, 255, 0.85);
  font-family: var(--font-sans);
  font-size: var(--text-sm);
  margin: 0;
}

/* Lock the page behind an open overlay. Without this, scrolling the sheet past
   its end scrolls the page underneath — the "scroll chaining" that makes a
   modal feel broken on touch. */
body.has-lightbox,
body.has-sheet {
  overflow: hidden;
}

@media (min-width: 860px) {
  /* The side sheet leaves the page visible and usable beside it, so only the
     lightbox — which covers everything — locks scrolling at this width. */
  body.has-sheet {
    overflow: auto;
  }
}

.breadcrumb {
  font-family: var(--font-sans);
  font-size: var(--text-sm);
  margin: var(--space-4) 0 var(--space-3);
}

.callout--muted {
  border-left: var(--rule-heavy) solid var(--border-strong);
  background: var(--bg-sunken);
  padding: var(--space-3) var(--space-4);
  font-size: var(--text-sm);
  margin: var(--space-4) 0;
}

/* ============================================================================
   Watchlist ticker

   Not a <marquee>. That element is deprecated, cannot be paused, and screen
   readers handle it badly. This is a CSS transform on a duplicated track:
   two identical runs sit side by side, the pair translates left by exactly
   half its width, and the restart is invisible because run two is now where
   run one was.

   It stops on hover, on keyboard focus, on the pause button, and entirely
   under prefers-reduced-motion — where it degrades to a strip you scroll
   yourself rather than one that moves on its own.
   ==========================================================================*/

.ticker {
  display: flex;
  align-items: stretch;
  background: var(--bg-ink);
  color: var(--text-on-ink);
  border-bottom: var(--border-width) solid var(--bg-ink);
  font-family: var(--font-sans);
  font-size: var(--text-sm);
  overflow: hidden;
  /* The ticker is a flex item of <body>, and a flex item's default
     `min-width: auto` refuses to shrink below its content — which here is a
     1,800px scrolling track. Without this the element simply grows to fit,
     its own `overflow: hidden` never has anything to clip, and the whole page
     scrolls sideways on a phone. */
  min-width: 0;
}

.ticker__lead {
  flex: 0 0 auto;
  display: flex;
  align-items: center;
  padding: var(--space-2) var(--space-3);
  background: var(--status-reported);
  font-size: var(--text-xs);
  font-weight: var(--weight-bold);
  letter-spacing: var(--tracking-kicker);
  text-transform: uppercase;
}

.ticker__lead a {
  color: #fff;
  text-decoration: none;
}

.ticker__lead a:hover,
.ticker__lead a:focus-visible {
  text-decoration: underline;
}

.ticker__viewport {
  flex: 1 1 auto;
  overflow: hidden;
  min-width: 0;
}

.ticker__track {
  display: flex;
  width: max-content;
  animation: ticker-scroll var(--ticker-duration, 40s) linear infinite;
}

.ticker__run {
  display: flex;
  align-items: center;
  flex: 0 0 auto;
}

@keyframes ticker-scroll {
  from {
    transform: translateX(0);
  }
  to {
    /* Exactly one run width. The second run has arrived where the first was,
       so the reset is invisible. */
    transform: translateX(-50%);
  }
}

/* Pause on hover, on the button, and when keyboard focus lands on one of the
   scrolling links. Note the focus rule is scoped to the viewport, not the whole
   ticker: clicking Play leaves focus on the Play button, and a `.ticker:focus-
   within` rule would then hold it paused until the reader clicked elsewhere —
   pressing Play and watching nothing happen. The reason to pause on focus is
   that someone is tabbing through the links and needs them to hold still, and
   that is exactly what the viewport contains. */
.ticker:hover .ticker__track,
.ticker__viewport:focus-within .ticker__track,
.ticker.is-paused .ticker__track {
  animation-play-state: paused;
}

.ticker__item {
  display: inline-flex;
  align-items: center;
  gap: var(--space-2);
  padding: var(--space-2) var(--space-5) var(--space-2) 0;
  margin-right: var(--space-5);
  color: var(--text-on-ink);
  text-decoration: none;
  white-space: nowrap;
  border-right: var(--border-width) solid rgba(255, 255, 255, 0.22);
}

.ticker__item:hover,
.ticker__item:focus-visible {
  color: #fff;
}

.ticker__item:hover .ticker__place,
.ticker__item:focus-visible .ticker__place {
  text-decoration: underline;
  text-decoration-thickness: 2px;
}

.ticker__item:focus-visible {
  outline: 2px solid #fff;
  outline-offset: -2px;
}

.ticker__flag {
  width: 8px;
  height: 8px;
  flex: 0 0 auto;
  background: var(--status-reported);
  transform: rotate(45deg);
}

.ticker__place {
  font-weight: var(--weight-semibold);
}

.ticker__count {
  color: rgba(255, 255, 255, 0.82);
}

.ticker__cats {
  color: rgba(255, 255, 255, 0.6);
  font-size: var(--text-xs);
}

.ticker__controls {
  flex: 0 0 auto;
  display: flex;
  align-items: stretch;
  border-left: var(--border-width) solid rgba(255, 255, 255, 0.22);
}

.ticker__btn {
  appearance: none;
  border: 0;
  background: none;
  color: rgba(255, 255, 255, 0.82);
  font-family: var(--font-sans);
  font-size: var(--text-xs);
  font-weight: var(--weight-medium);
  padding: 0 var(--space-3);
  cursor: pointer;
  min-width: 44px;
}

.ticker__btn:hover {
  background: rgba(255, 255, 255, 0.12);
  color: #fff;
}

.ticker__btn:focus-visible {
  outline: 2px solid #fff;
  outline-offset: -2px;
}

@media (prefers-reduced-motion: reduce) {
  /* No movement at all. `animation: none` rather than a near-zero duration:
     an animation with a fill mode otherwise flashes its start state. The strip
     becomes something the reader scrolls, which is why the viewport gets its
     own overflow here. */
  .ticker__track {
    animation: none;
  }

  .ticker__viewport {
    overflow-x: auto;
  }

  /* The duplicate run is decorative and hidden from assistive technology.
     With nothing moving it is just the same ten links a second time. */
  .ticker__run[aria-hidden="true"] {
    display: none;
  }
}

@media print {
  .ticker {
    display: none;
  }
}

/* ============================================================================
   Watchlist page
   ==========================================================================*/

.watch-list {
  border-top: var(--rule-heavy) solid var(--text);
}

.watch-card {
  display: grid;
  grid-template-columns: 88px 1fr;
  gap: var(--space-4);
  padding: var(--space-4) 0;
  border-bottom: var(--rule-width) solid var(--rule);
  align-items: start;
}

.watch-card__rank {
  text-align: right;
  border-right: var(--rule-width) solid var(--rule);
  padding-right: var(--space-4);
}

.watch-card__count {
  display: block;
  font-family: var(--font-serif);
  font-size: var(--text-2xl);
  font-weight: var(--weight-black);
  letter-spacing: var(--tracking-tighter);
  line-height: 1;
}

.watch-card__unit {
  display: block;
  font-family: var(--font-sans);
  font-size: var(--text-xs);
  color: var(--text-subtle);
  text-transform: uppercase;
  letter-spacing: var(--tracking-kicker);
}

.watch-card__body h3 {
  font-family: var(--font-serif);
  font-size: var(--text-xl);
  font-weight: var(--weight-bold);
  letter-spacing: var(--tracking-tight);
  margin: 0 0 var(--space-1);
  line-height: var(--leading-snug);
}

.watch-card__body h3 a {
  color: inherit;
  text-decoration: none;
}

.watch-card__body h3 a:hover {
  text-decoration: underline;
  text-decoration-thickness: 2px;
}

.watch-card__meta {
  font-family: var(--font-sans);
  font-size: var(--text-sm);
  color: var(--text-subtle);
  margin: 0 0 var(--space-2);
}

@media (max-width: 520px) {
  .watch-card {
    grid-template-columns: 1fr;
    gap: var(--space-2);
  }

  .watch-card__rank {
    text-align: left;
    border-right: 0;
    padding-right: 0;
    display: flex;
    align-items: baseline;
    gap: var(--space-2);
  }
}

/* ============================================================================
   News — headline lists
   ==========================================================================*/

.chips {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-2);
  margin-bottom: var(--space-4);
}

.chip {
  appearance: none;
  border: var(--border-width) solid var(--border-strong);
  background: var(--bg);
  color: var(--text-muted);
  font-family: var(--font-sans);
  font-size: var(--text-xs);
  font-weight: var(--weight-medium);
  padding: var(--space-1) var(--space-3);
  min-height: 32px;
  cursor: pointer;
  border-radius: var(--radius-pill);
}

.chip:hover {
  border-color: var(--text);
  color: var(--text);
}

.chip.is-active {
  background: var(--bg-ink);
  border-color: var(--bg-ink);
  color: var(--text-on-ink);
}

.chip:focus-visible {
  outline: 2px solid var(--focus-ring);
  outline-offset: 2px;
}

.news-list {
  list-style: none;
  padding: 0;
  margin: 0;
  border-top: var(--rule-heavy) solid var(--text);
}

.news-item {
  padding: var(--space-4) 0;
  border-bottom: var(--rule-width) solid var(--rule);
}

.news-item__link {
  /* Headlines come from third-party feeds and occasionally arrive as one
     unbroken token — a form-encoded title with + instead of spaces, or a very
     long URL-like string. Without this, a single such headline is a 600px word
     that pushes the whole page sideways on a phone. */
  overflow-wrap: anywhere;
  font-family: var(--font-serif);
  font-size: var(--text-lg);
  font-weight: var(--weight-bold);
  letter-spacing: var(--tracking-tight);
  line-height: var(--leading-snug);
  color: var(--text);
  text-decoration: none;
  display: inline-block;
}

.news-item__link:hover,
.news-item__link:focus-visible {
  color: var(--accent);
  text-decoration: underline;
  text-decoration-thickness: 2px;
}

/* An external link opening in a new tab should say so — otherwise the back
   button appears broken to anyone not watching the tab bar. */
.news-item__link::after {
  content: " ↗";
  font-size: 0.8em;
  color: var(--text-subtle);
  text-decoration: none;
}

.news-item__meta {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-1) var(--space-3);
  align-items: baseline;
  margin: var(--space-2) 0 0;
  font-family: var(--font-sans);
  font-size: var(--text-xs);
  color: var(--text-subtle);
}

.news-item__source {
  font-weight: var(--weight-semibold);
  color: var(--text-muted);
  text-transform: uppercase;
  letter-spacing: var(--tracking-kicker);
}

.news-item__topic {
  border: var(--border-width) solid var(--border);
  padding: 0 var(--space-2);
}

.news-list--compact .news-item {
  padding: var(--space-3) 0;
}

.news-list--compact .news-item__link {
  font-size: var(--text-base);
}


/* ============================================================================
   Reading progress

   A hairline across the top of the viewport showing how far through the page
   you are. On a long single-column page this is genuinely useful orientation,
   and it costs one element and a transform — no layout, no repaint of content.
   ==========================================================================*/

.progress-rail {
  position: fixed;
  top: 0;
  left: 0;
  right: 0;
  height: 3px;
  z-index: 950;
  pointer-events: none;
  background: transparent;
}

.progress-rail__fill {
  height: 100%;
  width: 100%;
  transform: scaleX(0);
  transform-origin: left;
  background: linear-gradient(
    to right,
    var(--teal-bright),
    var(--violet-bright) 38%,
    var(--amber-bright) 68%,
    var(--signal-bright)
  );
  will-change: transform;
}

@media (prefers-reduced-motion: reduce) {
  /* The bar still tracks position — it just doesn't ease between values. */
  .progress-rail__fill { transition: none; }
}

@media print {
  .progress-rail { display: none; }
}

/* ---- Kicker gets the ramp ------------------------------------------------ */

.kicker::after {
  background: linear-gradient(to right, var(--rule), transparent);
}

.kicker--accent { color: var(--signal-deep); }

.kicker--accent::after {
  background: linear-gradient(to right, var(--signal), transparent);
}

/* ==========================================================================
   Facility picker

   A multi-select combobox. It replaced a free-text box on the report form,
   so it has to feel at least as easy as typing: the whole control is one
   click target, the escape hatch ("I'm not sure") is the first thing in the
   list, and nothing is required.
   ========================================================================== */

.fpicker {
  position: relative;
}

.fpicker__control {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--space-2);
  min-height: 46px;
  padding: var(--space-2);
  background: var(--bg-raised);
  border: var(--border-width) solid var(--border-strong);
  border-radius: var(--radius-md);
  cursor: text;
}

.fpicker__control:focus-within {
  border-color: var(--focus-ring);
  box-shadow: 0 0 0 3px rgba(0, 105, 108, 0.16);
}

.fpicker.is-filled .fpicker__control {
  border-color: var(--accent);
}

.fpicker__chips {
  display: contents;
  list-style: none;
  margin: 0;
  padding: 0;
}

.fpicker__chip {
  display: inline-flex;
  align-items: center;
  gap: var(--space-1);
  max-width: 100%;
  padding: 3px 3px 3px var(--space-3);
  border-radius: var(--radius-pill);
  background: var(--accent-soft);
  border: var(--border-width) solid var(--teal);
  color: var(--teal-deep);
  font-family: var(--font-sans);
  font-size: var(--text-sm);
  font-weight: var(--weight-semibold);
}

/* The three kinds read differently on purpose: a named campus is a fact, a
   district is a neighbourhood, "not sure" is an admission, and typed text is
   unverified. Colour alone doesn't carry it — the labels differ too. */
.fpicker__chip--district {
  background: var(--violet-soft);
  border-color: var(--violet);
  color: var(--violet-deep);
}

.fpicker__chip--unsure {
  background: var(--bg-sunken);
  border-color: var(--border-strong);
  color: var(--text-muted);
}

.fpicker__chip--custom {
  background: var(--amber-soft);
  border-color: var(--amber);
  color: var(--amber-deep);
}

.fpicker__chip > span {
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.fpicker__remove {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  flex-shrink: 0;
  width: 22px;
  height: 22px;
  padding: 0;
  border: 0;
  border-radius: 50%;
  background: transparent;
  color: inherit;
  cursor: pointer;
}

.fpicker__remove svg {
  width: 12px;
  height: 12px;
}

.fpicker__remove:hover {
  background: rgba(15, 21, 36, 0.12);
}

.fpicker__remove:focus-visible {
  outline: 2px solid var(--focus-ring);
  outline-offset: 1px;
}

.fpicker__input {
  flex: 1 1 12ch;
  /* Flex items refuse to shrink below their content by default, which with a
     long placeholder pushes the control wider than its column. */
  min-width: 0;
  border: 0;
  background: none;
  padding: var(--space-1) 0;
  font: inherit;
  font-family: var(--font-sans);
  font-size: var(--text-base);
  color: var(--text);
}

.fpicker__input:focus {
  outline: none;
}

.fpicker__toggle {
  display: flex;
  align-items: center;
  justify-content: center;
  flex-shrink: 0;
  width: 28px;
  height: 28px;
  margin-left: auto;
  padding: 0;
  border: 0;
  border-radius: var(--radius-sm);
  background: none;
  color: var(--text-subtle);
  cursor: pointer;
}

.fpicker__toggle svg {
  width: 16px;
  height: 16px;
  transition: transform var(--duration-fast) var(--ease-out);
}

.fpicker__input[aria-expanded="true"] ~ .fpicker__toggle svg {
  transform: rotate(180deg);
}

.fpicker__list {
  position: absolute;
  z-index: 40;
  top: calc(100% + 4px);
  left: 0;
  right: 0;
  max-height: 320px;
  overflow-y: auto;
  overscroll-behavior: contain;
  margin: 0;
  padding: var(--space-1);
  list-style: none;
  background: var(--bg-raised);
  border: var(--border-width) solid var(--border-strong);
  border-radius: var(--radius-md);
  box-shadow: var(--shadow-lg);
}

.fpicker__option {
  display: flex;
  flex-direction: column;
  gap: 1px;
  padding: var(--space-2) var(--space-3);
  border-radius: var(--radius-sm);
  cursor: pointer;
}

.fpicker__option.is-active,
.fpicker__option:hover {
  background: var(--accent-soft);
}

.fpicker__option[aria-selected="true"] {
  background: var(--accent);
  color: var(--text-on-accent);
}

.fpicker__option[aria-selected="true"] .fpicker__option-hint {
  color: rgba(255, 255, 255, 0.82);
}

/* "I'm not sure" is pinned at the top and set apart, because it is the answer
   most people need and the one they are least likely to believe is allowed. */
.fpicker__option--unsure {
  border-bottom: var(--border-width) solid var(--border);
  border-radius: var(--radius-sm) var(--radius-sm) 0 0;
  margin-bottom: var(--space-1);
}

.fpicker__option-label {
  font-family: var(--font-sans);
  font-size: var(--text-sm);
  font-weight: var(--weight-semibold);
}

.fpicker__option-hint {
  font-size: var(--text-xs);
  color: var(--text-subtle);
  line-height: 1.35;
}

.fpicker__more {
  padding: var(--space-2) var(--space-3);
  font-size: var(--text-xs);
  color: var(--text-subtle);
  text-align: center;
}

.fpicker__status {
  margin: var(--space-2) 0 0;
  font-family: var(--font-mono);
  font-size: var(--text-xs);
  color: var(--text-subtle);
}

.fpicker__status:empty {
  display: none;
}

/* --------------------------------------------------------------------------
   The Alley — the project's Substack

   Deliberately the accent teal rather than Substack's own orange. The signal
   orange on this site belongs to one action, "Report an issue", and a second
   orange button beside it in the header would leave a reader with two equally
   loud things to do and no idea which matters. Teal reads as prominent
   without competing.
   -------------------------------------------------------------------------- */

.btn--substack {
  background: var(--accent);
  color: #fff;
  border-color: var(--accent);
}

.btn--substack:hover:not(:disabled) {
  background: var(--accent-hover);
  border-color: var(--accent-hover);
  color: #fff;
  transform: translateY(-1px);
}

.btn--substack:active:not(:disabled) {
  transform: translateY(0);
}

.btn--substack strong {
  font-weight: var(--weight-bold);
  letter-spacing: 0.04em;
}

.btn__substack-mark {
  flex: none;
  opacity: 0.85;
}

/* Two buttons in the header need more room than one. Below this the footer
   band and the card under the home page headlines carry it instead, so
   nothing is lost on a phone. */
.header-actions .header-substack {
  display: none;
}

@media (min-width: 900px) {
  .header-actions .header-substack {
    display: inline-flex;
  }
}

.footer-actions {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-3);
}

/* The card under the headlines. A rule and a colour field rather than a
   floating panel, to sit with the rest of the page. */
.substack-card {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-4);
  margin-top: var(--space-5);
  padding: var(--space-5);
  border: var(--border-width) solid var(--border);
  border-left: 3px solid var(--accent);
  border-radius: var(--radius-md);
  background: var(--accent-soft);
}

.substack-card__body {
  flex: 1 1 20rem;
  min-width: 0;
}

.substack-card__body .kicker {
  margin: 0 0 var(--space-1);
}

.substack-card__body h3 {
  margin: 0 0 var(--space-2);
}

.substack-card__body p {
  margin: 0;
}

.substack-card__action {
  flex: none;
}

@media (max-width: 560px) {
  .substack-card {
    flex-direction: column;
    align-items: stretch;
  }

  .substack-card__action .btn {
    width: 100%;
  }
}

/* --------------------------------------------------------------------------
   County activity — filings and changes to the parcel record
   -------------------------------------------------------------------------- */

.activity-sources {
  list-style: none;
  margin: 0 0 var(--space-6);
  padding: var(--space-4) var(--space-5);
  border: var(--border-width) solid var(--border);
  border-left: 3px solid var(--accent);
  border-radius: var(--radius-md);
  font-size: var(--text-sm);
}

.activity-sources li + li {
  margin-top: var(--space-2);
}

.activity-list {
  list-style: none;
  margin: 0;
  padding: 0;
  border-top: var(--border-width) solid var(--border);
}

.activity-item {
  padding: var(--space-5) 0;
  border-bottom: var(--border-width) solid var(--border);
}

.activity-item__head {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--space-3);
  margin-bottom: var(--space-2);
}

.activity-item__kind {
  font-family: var(--font-sans);
  font-size: var(--text-xs);
  font-weight: var(--weight-semibold);
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--text-muted);
}

.activity-item__name {
  margin: 0 0 var(--space-2);
  font-size: var(--text-lg);
}

.activity-item__desc {
  margin: 0 0 var(--space-2);
  font-size: var(--text-sm);
}

.activity-item__meta {
  margin: 0;
}

.activity-list--compact .activity-item {
  padding: var(--space-4) 0;
}

.activity-list--compact .activity-item__desc {
  display: none;
}

.activity-more {
  margin-top: var(--space-5);
}

/* Status. The county's own wording is kept as the label; only the colour is
   ours, so nothing is asserted that the filing does not say. */
.pill {
  display: inline-flex;
  align-items: center;
  padding: 2px var(--space-3);
  border-radius: var(--radius-pill);
  font-family: var(--font-sans);
  font-size: var(--text-xs);
  font-weight: var(--weight-semibold);
  line-height: 1.6;
  white-space: nowrap;
}

.pill--approved {
  background: rgba(0, 95, 94, 0.12);
  color: var(--accent-hover);
}

.pill--pending {
  background: rgba(194, 52, 10, 0.1);
  color: var(--signal-deep);
}

.pill--closed,
.pill--unknown {
  background: var(--border);
  color: var(--text-muted);
}

.activity-changes {
  list-style: none;
  margin: 0;
  padding: 0;
}

.activity-change {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  gap: var(--space-2) var(--space-3);
  padding: var(--space-3) 0 var(--space-3) var(--space-4);
  border-bottom: var(--border-width) solid var(--border);
  border-left: 3px solid var(--border-strong);
}

.activity-change--appeared { border-left-color: var(--signal-deep); }
.activity-change--status   { border-left-color: var(--accent); }
.activity-change--withdrawn { border-left-color: var(--border-strong); }

.activity-change__what {
  font-family: var(--font-sans);
  font-size: var(--text-xs);
  font-weight: var(--weight-semibold);
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--text-muted);
}

.activity-change__name {
  font-weight: var(--weight-semibold);
}

.activity-change__detail {
  font-variant-numeric: tabular-nums;
}

.activity-change__when {
  margin-left: auto;
}

@media (max-width: 560px) {
  .activity-change__when {
    margin-left: 0;
    width: 100%;
  }
}

/* Two buttons where there was one, so they wrap rather than overflow on a
   phone. The hero already has its own flex rule; this is for the closing
   call to action. */
.cta-actions {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-3);
}

/* --------------------------------------------------------------------------
   The map button

   The map is what this site is for — the reason somebody arrives and the
   thing every other page refers back to. It was a transparent outline the
   same size as everything beside it, which made the one indispensable route
   through the site the quietest thing in the hero.

   Bright teal filled, with near-black text: 7.82:1, better contrast than the
   orange report button manages at 5.54:1. A different hue entirely, so it can
   be the loudest thing on the page without fighting the report button — the
   two read as "look at this" and "do this", not as two rival primaries.
   -------------------------------------------------------------------------- */

.btn--map {
  background: var(--teal-bright);
  border-color: var(--teal-bright);
  color: var(--bg-ink);
  font-weight: var(--weight-bold);
  letter-spacing: 0.01em;
  box-shadow: 0 6px 22px rgba(0, 184, 189, 0.28);
}

.btn--map:hover:not(:disabled) {
  background: #3fd3d7;
  border-color: #3fd3d7;
  color: var(--bg-ink);
  transform: translateY(-1px);
  box-shadow: 0 10px 30px rgba(0, 184, 189, 0.38);
}

.btn--map:active:not(:disabled) {
  transform: translateY(0);
}

.btn--map .btn__icon {
  flex: none;
  opacity: 0.9;
}

/* Bigger than btn--lg, and wider than its label needs, so it is unmistakably
   the largest target in the row rather than merely a coloured-in twin. */
.btn--map.btn--lg {
  min-height: 62px;
  padding-inline: var(--space-7);
  font-size: var(--text-lg);
}

/* On a phone it takes the full width and leads the stack — the map is the
   thing worth giving a whole row to. */
@media (max-width: 560px) {
  .btn--map.btn--lg {
    width: 100%;
  }
}
