/* ======================================================================
   LeezenPuls.MS · stylesheet
   ----------------------------------------------------------------------
   Built on the ParkPuls stylesheet of October 2026 so the suite chrome
   (top bar, KPI ribbon, status pill, footer, phone tabs) is the same code,
   not a look-alike. Park-only components (pins, price planner, destination
   search, Wochenmuster) were cut; LeezenPuls' own parts (flow canvas,
   station nodes, transport bar, day curve) live in the LEEZENPULS block at
   the end, which also carries the brand-colour overrides.
   Fonts: Barlow Condensed (wordmark), IBM Plex Sans Condensed (display),
   DM Sans (UI), Space Grotesk (figures).
   ====================================================================== */

:root {
  /* Type stack — --serif token is now condensed grotesque, not a serif.
     Name kept for backward-compat with existing var(--serif) references. */
  --serif: 'IBM Plex Sans Condensed', 'Inter', system-ui, -apple-system, "Segoe UI", sans-serif;
  --sans:  'DM Sans', system-ui, -apple-system, "Segoe UI", Roboto, "Helvetica Neue", sans-serif;
  /* Figures / metrics font. Name kept as --mono for back-compat; it is now a
     proportional display face (Space Grotesk) used with tabular figures. */
  --mono:  'Space Grotesk', 'DM Sans', system-ui, sans-serif;

  /* Paper / ink — soft blue-tinted off-white canvas */
  --paper:       #eef4fa;             /* light-blue tinted off-white */
  --paper-deep:  #dde8f2;             /* slightly deeper, used behind tiles */
  --surface:     #ffffff;
  --surface-2:   #f4f8fc;
  --ink:         #14202c;             /* deep blue-tinged primary text */
  --ink-2:       #3a4a5c;             /* secondary text */
  --muted:       #5e6c7d;             /* aligned with BusPuls/Home for WCAG AA (≥4.5:1 on paper & white) */
  --dim:         #b3c0cd;
  --hairline:    rgba(20, 40, 64, 0.10);
  --hairline-2:  rgba(20, 40, 64, 0.06);
  --hl:          rgba(20, 40, 64, 0.045);
  --hl-hover:    rgba(20, 40, 64, 0.07);

  /* Brand — Stadtwerke cyan, shared with the ParkPuls.MS / BusPuls.MS mark */
  --brand:       #1CA75C;          /* Leezen green, the sign in the logo */
  --brand-2:     #0f7a3f;          /* text and focus green: 5.42:1 on white, 4.89:1 on paper, 4.78:1 on --brand-soft */
  /* Filled-button cyan. Deliberately NOT theme-flipped: it is a background that
     carries white text (5.00:1) and must stay dark in the dark theme too. */
  --brand-cta:   #0f7a3f;
  --brand-soft:  #e3f5ea;
  --brand-line:  rgba(28, 167, 92, 0.34);

  /* Accent — coral, used by the heart/pulse in the mark and by .MS in the wordmark */
  --accent:      #FF4757;
  --accent-soft: #ffe9eb;
  --accent-line: rgba(255, 71, 87, 0.32);

  /* Signal palette (legible on white) */
  --good:        #15803d;
  --good-soft:   #dcfce7;
  --warn:        #b45309;
  --warn-soft:   #fef0c7;
  --bad:         #c2410c;
  --bad-soft:    #fee4cf;
  --crit:        #b91c1c;
  --crit-soft:   #fee2e2;
  --info:        #1d4ed8;
  --info-soft:   #dbeafe;
  --leicht:      #ca8a04;          /* golden yellow — Leichte Verzögerungen */
  --leicht-soft: #fef9c3;
  --early:       #1d4ed8;

  --radius-s:  8px;
  --radius:    12px;
  --radius-l:  18px;

  /* ===== Liquid glass (ported from BusPuls/ZugPuls, 2026-09-16) ============
     ParkPuls was the odd one out: five ad-hoc backdrop-filter declarations,
     two of them on rules a later block overrode to `none`. This is the same
     four-tier token system its siblings use, so the three sites finally share
     one visual language and one set of escape hatches.

     Two lessons carried over from the August work, both load-bearing:

     1. Glass belongs on FLOATING panels, not on chrome. The top bar, the
        footer, the rail and the list cards sit on an opaque background, so a
        backdrop-filter there costs contrast and buys nothing. They stay solid.
     2. It is the blur RADIUS, not the alpha, that stops map labels ghosting
        through. The old .mapcard used blur(10px) and the map read through it;
        these values are 30 bis 40 px like the siblings'.

     Contrast rule that comes with the system: at alpha 0.72 over the darkest
     plausible backdrop, --muted falls below AA while --ink-2 holds. So NO
     --muted text on a glass surface; use --ink-2. The exception is
     --glass-panel (alpha 0.94, built for text-dense dropdowns) where --muted
     is fine. */
  --glass-chrome: rgba(251, 253, 255, 0.80);   /* mobile sheets                */
  --glass-raised: rgba(255, 255, 255, 0.74);   /* map cards, reasons card, FAB */
  --glass-panel:  rgba(255, 255, 255, 0.94);   /* search dropdown, peek popup  */
  --glass-inset:  rgba(213, 226, 238, 0.58);   /* segmented grooves, fields    */

  --glass-fx-chrome: saturate(1.4) blur(36px);
  --glass-fx-raised: saturate(1.35) blur(40px);
  --glass-fx-panel:  saturate(1.2) blur(30px);
  --glass-fx-inset:  saturate(1.2) blur(12px);

  /* Specular: bright inner line on top, blue-grey hairline below. The edge is
     what reads as "glass" even where the tint alone would disappear. */
  --glass-edge: inset 0 1px 0 rgba(255,255,255,0.9),
                inset 0 -1px 0 rgba(20,40,64,0.06);
  --glass-edge-hi: inset 0 1.5px 0 #ffffff,
                   inset 0 0 0 1px rgba(255,255,255,0.35),
                   inset 0 -1px 0 rgba(20,40,64,0.08);

  /* Opaque fallbacks; the two escape hatches below write these verbatim into
     the same variables so the system tips over as a whole, never half-way. */
  --glass-chrome-solid: #fcfeff;
  --glass-raised-solid: #ffffff;
  --glass-inset-solid:  #dde8f2;

  --shadow-sm: 0 1px 2px rgba(20,40,64,0.06), 0 1px 1px rgba(20,40,64,0.04);
  --shadow-md: 0 4px 18px rgba(20,40,64,0.08), 0 2px 4px rgba(20,40,64,0.05);
  --shadow-lg: 0 12px 36px rgba(20,40,64,0.12), 0 4px 12px rgba(20,40,64,0.07);

  /* --drawer-bottom / --footer-h are gone (2026-09-16): they existed to lift the
     map and its controls clear of the old FIXED top bar and footer. The grid
     shell makes both part of the normal flow, so nothing has to be lifted. The
     comment claiming they were "updated in JS via ResizeObserver" was stale:
     no such code existed. */

  /* Chart.js grid lines (read from JS via getComputedStyle) */
  --chart-grid: rgba(20, 40, 64, 0.06);
}

html { color-scheme: light; }

/* Dark theme removed 2026-09-16. ParkPuls is light-only: one palette to keep
   at AA, one set of map tiles, one legend. The toggle, js/theme-init.js, the
   dark token block and the dark CARTO basemap all went with it. */

/* ---- The two glass escape hatches (P6-3, 2026-09-16) ----------------------
   ParkPuls had neither, which is why its ad-hoc backdrop-filters degraded to
   near-transparent panels over the map on browsers without the property.

   Both blocks write the OPAQUE fallbacks into the same variables, so the system
   tips over as a whole rather than half-way. */
@supports not ((backdrop-filter: blur(1px)) or (-webkit-backdrop-filter: blur(1px))) {
  :root {
    --glass-chrome: var(--glass-chrome-solid);
    --glass-raised: var(--glass-raised-solid);
    --glass-panel:  var(--glass-raised-solid);
    --glass-inset:  var(--glass-inset-solid);
  }
}
/* "Reduce transparency" (iOS/macOS, Windows). An accessibility setting, not a
   taste: whoever sets it gets no glass at all, not a weaker version. */
@media (prefers-reduced-transparency: reduce) {
  :root {
    --glass-chrome: var(--glass-chrome-solid);
    --glass-raised: var(--glass-raised-solid);
    --glass-panel:  var(--glass-raised-solid);
    --glass-inset:  var(--glass-inset-solid);
    --glass-fx-chrome: none;
    --glass-fx-raised: none;
    --glass-fx-panel:  none;
    --glass-fx-inset:  none;
    --glass-edge:    none;
    --glass-edge-hi: none;
  }
}

* { box-sizing: border-box; }
html, body {
  margin: 0; height: 100%;
  background: var(--paper);
  color: var(--ink);
  font-family: var(--sans);
  font-size: 14px;
  line-height: 1.45;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
}
button { font-family: inherit; }
button, [role="button"] { -webkit-tap-highlight-color: transparent; }
:focus-visible { outline: 2px solid var(--brand-2); outline-offset: 2px; border-radius: 4px; }

a { color: var(--brand-2); text-decoration: none; }
a:hover { text-decoration: underline; }

/* Metric figures: Space Grotesk is proportional, so request tabular (fixed-width)
   numerals everywhere numbers are displayed, to keep columns and live-updating
   values from shifting. */
.rk-v, .gc-free .gc-n, .dt-num, .gp-free strong,
.pp-pill .pp-n, .dt-sub, .hchart-val {
  font-variant-numeric: tabular-nums;
  font-feature-settings: "tnum" 1;
}

/* ===== Leaflet integration ===== */
.leaflet-container { font-family: var(--sans); background: var(--paper-deep); }
.leaflet-control-zoom a, .leaflet-control-layers-toggle {
  background: var(--surface) !important;
  color: var(--ink-2) !important;
  border: 1px solid var(--hairline) !important;
  box-shadow: var(--shadow-sm) !important;
}
.leaflet-control-zoom a { font-weight: 600; }
.leaflet-control-zoom a:hover { background: var(--brand-soft) !important; color: var(--brand-2) !important; }
.leaflet-control-layers {
  border-radius: var(--radius) !important;
  border: 1px solid var(--hairline) !important;
  box-shadow: var(--shadow-md) !important;
  background: var(--surface) !important;
  color: var(--ink) !important;
  padding: 6px 8px !important;
}
.leaflet-control-layers-expanded { padding: 10px 12px !important; }
.leaflet-control-attribution {
  background: color-mix(in srgb, var(--surface) 80%, transparent) !important;
  color: var(--ink-2) !important;
  font-size: 10.5px;
  border-radius: 6px 0 0 0;
  backdrop-filter: blur(6px);
}
.leaflet-control-attribution a { color: var(--brand-2) !important; }
.leaflet-bottom.leaflet-right { right: 0 !important; }
.leaflet-bottom .leaflet-control { margin-bottom: 0 !important; }
/* The .leaflet-top/.leaflet-bottom OFFSETS live with the shell layout further
   down; they used to be lifted by --drawer-bottom/--footer-h for the old fixed
   chrome. [merged 2026-09-16] */

/* ===== Topbar (header bar) ===== */
/* The authoritative #topbar rule lives with the shell layout below; only the
   stacking context is set here. [merged 2026-09-16] */
#topbar { z-index: 800; }

#brand { display: flex; align-items: center; gap: 12px; flex: 0 0 auto; min-width: 0; }
#brand h1 {
  font-family: var(--serif);
  font-size: 23px;
  font-weight: 600;          /* Plex Condensed 600 ≈ visual weight of old DM Serif 400 */
  margin: 0;
  letter-spacing: 0.1px;
  color: var(--brand);       /* cyan, matches the wordmark in the locked lockup */
}

@keyframes pulse-good {
  0%   { box-shadow: 0 0 0 0   rgba(21,128,61,0.55); }
  60%  { box-shadow: 0 0 0 7px rgba(21,128,61,0); }
  100% { box-shadow: 0 0 0 0   rgba(21,128,61,0); }
}

/* Trend arrow — coloured badge centred vertically on the right edge.
   Hidden when stable; green pill = improving, red pill = worsening. */
.kpi-trend {
  position: absolute;
  right: 2px;
  top: 50%;
  transform: translateY(-50%);
  display: flex; align-items: center; justify-content: center;
  width: 19px; height: 19px;
  border-radius: 5px;
  opacity: 0;
  transition: opacity .35s;
  pointer-events: none;
}
.kpi-trend.trend-good,
.kpi-trend.trend-bad  { opacity: 1; }
.kpi-trend.trend-good { color: var(--good); background: var(--good-soft); }
.kpi-trend.trend-bad  { color: var(--crit); background: var(--crit-soft); }
.kpi-trend svg { width: 12px; height: 12px; display: block; }






/* Merged status pill: dot + system-health label + hover-revealed timestamp.
   The pill takes a tone class (.tone-good / warn / bad / info) that paints
   both the dot and the soft background. Network-state classes (.err on dot
   for fetch failure, .load on dot during the very first poll) override
   the dot color but keep the pill quietly neutral. */
#status-pill {
  display: inline-flex; align-items: center; gap: 8px;
  padding: 6px 14px 6px 11px;
  border-radius: 999px;
  border: 1px solid var(--hairline);
  background: var(--surface);
  font-family: var(--sans);
  font-size: 13px;
  font-weight: 600;
  color: var(--ink-2);
  cursor: default;
  box-shadow: var(--shadow-sm);
  transition: background .25s, border-color .25s, color .25s;
  /* div replaces former button – suppress default outline in favour of
     the :focus-visible rule below, which already shows a brand outline */
  outline: none;
}
#status-pill:focus-visible { outline: 2px solid var(--brand-2); outline-offset: 2px; border-radius: 999px; }
#status-pill.tone-good { background: var(--good-soft); border-color: rgba(21,128,61,0.25);  color: var(--good); }
#status-pill.tone-warn { background: var(--warn-soft); border-color: rgba(180,83,9,0.25);   color: var(--warn); }
#status-pill.tone-bad  { background: var(--crit-soft); border-color: rgba(185,28,28,0.25);  color: var(--crit); }
#status-pill.tone-info  { background: var(--info-soft);   border-color: rgba(29,78,216,0.25);  color: var(--info); }
#status-pill.tone-leicht{ background: var(--leicht-soft); border-color: rgba(202,138,4,0.25); color: var(--leicht); }
#status-pill.tone-off  { background: var(--surface-2); border-color: var(--hairline);       color: var(--muted); }

#status-pill .dot {
  width: 9px; height: 9px; border-radius: 50%;
  background: var(--good);
  box-shadow: 0 0 0 0 rgba(21,128,61,0.45);
  animation: pulse-good 2s infinite;
}
#status-pill.tone-warn .dot { background: var(--warn); animation: pulse-warn 2.4s ease-out infinite; }
#status-pill.tone-bad  .dot { background: var(--crit); animation: none; }
#status-pill.tone-info   .dot { background: var(--info); }
#status-pill.tone-leicht .dot { background: var(--leicht); }
#status-pill.tone-off  .dot { background: var(--muted); animation: none; box-shadow: none; }
#status-pill .dot.err  { background: var(--crit); animation: none; }
#status-pill .dot.load { background: var(--warn); animation: pulse-warn 1.2s infinite; }
@keyframes pulse-warn {
  0%   { box-shadow: 0 0 0 0   rgba(180,83,9,0.45); }
  70%  { box-shadow: 0 0 0 7px rgba(180,83,9,0); }
  100% { box-shadow: 0 0 0 0   rgba(180,83,9,0); }
}

/* Status pill: inline expansion reveals the last-update timestamp on hover/
   focus (no JS layout math, pure CSS max-width transition). The reasons for
   the current verdict live in the separate #reasonsCard below the chrome. */
#status-pill .last-update-wrap {
  display: inline-flex; align-items: center; gap: 6px;
  max-width: 0;
  opacity: 0;
  overflow: hidden;
  white-space: nowrap;
  transition: max-width .25s ease, opacity .2s ease;
  font-weight: 500;
  color: currentColor;
}
#status-pill .last-update-wrap .sep { opacity: 0.5; }
#status-pill:hover .last-update-wrap,
#status-pill:focus-within .last-update-wrap { max-width: 240px; opacity: 0.85; margin-left: 2px; }

/* Floating reasons card – snug against the chrome's bottom edge at the
   top-right corner. The Leaflet zoom moved to top-left so this can have
   the corner. Visible only when the verdict is non-Reibungsloser Betrieb (and
   only when the user hasn't dismissed it for the current verdict). */
#reasonsCard {
  position: fixed;
  z-index: 700;
  top: 68px;                      /* clear of the 56 px top bar */
  right: 14px;
  width: 280px;
  max-width: calc(100vw - 28px);
  background: var(--glass-raised);
  -webkit-backdrop-filter: var(--glass-fx-raised);
          backdrop-filter: var(--glass-fx-raised);
  border: 1px solid var(--hairline);
  border-left: 3px solid var(--muted);
  border-radius: var(--radius);
  box-shadow: var(--shadow-md);
  padding: 11px 14px 13px 13px;
  transition: top .35s cubic-bezier(.2,.7,.2,1), border-color .25s ease;
  font-family: var(--sans);
}
#reasonsCard[hidden] { display: none; }
#reasonsCard.tone-info   { border-left-color: var(--info); }
#reasonsCard.tone-leicht { border-left-color: var(--leicht); }
#reasonsCard.tone-warn { border-left-color: var(--warn); }
#reasonsCard.tone-bad  { border-left-color: var(--crit); }
#reasonsCard.tone-off  { border-left-color: var(--muted); }
.reasons-card-title {
  font-size: 10px;
  font-weight: 700;
  letter-spacing: 1.2px;
  text-transform: uppercase;
  color: var(--muted);
  margin: 0 0 8px 0;
  padding-right: 22px;       /* clear the X button */
}
.reasons-list {
  list-style: none;
  padding: 0;
  margin: 0;
  display: flex;
  flex-direction: column;
  gap: 6px;
}
.reasons-list li {
  padding-left: 14px;
  position: relative;
  font-size: 12.5px;
  line-height: 1.4;
  color: var(--ink);
}
.reasons-list li::before {
  content: "•";
  position: absolute;
  left: 2px;
  top: 0;
  color: var(--muted);
}
#reasonsClose {
  position: absolute;
  top: 7px;
  right: 7px;
  width: 22px;
  height: 22px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  background: transparent;
  border: 0;
  border-radius: 6px;
  color: var(--muted);
  cursor: pointer;
  padding: 0;
  transition: background .12s ease, color .12s ease;
}
#reasonsClose:hover, #reasonsClose:focus-visible {
  background: var(--hl);
  color: var(--ink);
  outline: none;
}
#reasonsClose svg { width: 12px; height: 12px; }

@media (max-width: 560px){
  #reasonsCard {
    width: auto;
    left: 14px;
    right: 14px;
    top: 68px;
  }
}



@media (max-width: 700px){
  #drawerTabs { grid-column: 1 / -1; justify-self: start; }
}



/* ---- Statistics drawer tabs --------------------------------------------- */
.drawer-tabs {
  display: flex;
  gap: 6px;
  justify-self: center;
}
.dtab {
  padding: 5px 15px;
  border-radius: 20px;
  border: 1.5px solid var(--brand);
  background: transparent;
  color: var(--brand);
  font: 600 12px/1.2 var(--sans);
  cursor: pointer;
  transition: background .15s, color .15s;
  white-space: nowrap;
}
.dtab.is-active {
  background: var(--brand);
  color: #fff;
}
.dtab:hover:not(.is-active) { background: var(--brand-soft); }
.dtab:focus-visible { outline: 2px solid var(--brand-2); outline-offset: 2px; }

/* ---- Drawer panes -------------------------------------------------------- */
.drawer-pane         { display: none; }
.drawer-pane.is-active { display: block; }

/* ---- History chart grid -------------------------------------------------- */
.hist-charts {
  display: grid;
  grid-template-columns: repeat(4, minmax(0, 1fr));
  gap: 14px;
  align-items: stretch;
}
.hchart-card {
  background: var(--surface);
  border: 1px solid var(--hairline);
  border-radius: var(--radius);
  box-shadow: var(--shadow-sm);
  padding: 14px 16px 12px;
  min-width: 0;
  display: flex;
  flex-direction: column;
  gap: 8px;
}
.hchart-card h3 {
  font-family: var(--sans);
  font-size: 10px;
  font-weight: 700;
  letter-spacing: 1.2px;
  text-transform: uppercase;
  margin: 0;
  color: var(--muted);
  display: flex;
  align-items: center;
  gap: 8px;
}
.hchart-card h3 .hc-dot {
  width: 8px; height: 8px;
  border-radius: 50%;
  flex-shrink: 0;
}
/* Inline legend: this window vs the 24 h before it [2026-10-05]. */
.hchart-card h3 .hc-key { margin-left: auto; display: inline-flex; gap: 10px; font-size: 10.5px; font-weight: 500;
  letter-spacing: 0; text-transform: none; white-space: nowrap; }
.hchart-card h3 .hc-key > span { display: inline-flex; align-items: center; gap: 5px; }
.hchart-card h3 .hc-key i { display: inline-block; width: 12px; height: 3px; border-radius: 2px; }
.hchart-card h3 .hc-key i.prev { background: rgba(148,163,184,0.7); }
.hchart-wrap {
  position: relative;
  height: 150px;
  flex-shrink: 0;
}
.hchart-foot {
  display: flex;
  align-items: baseline;
  gap: 3px;
}
.hchart-val {
  font: 700 22px/1 var(--sans);
  color: var(--ink);
}
.hchart-suffix {
  font: 500 13px/1 var(--sans);
  color: var(--muted);
  margin-left: 3px;
}
.hchart-foot { flex-wrap: wrap; }
.hchart-note { margin-left: auto; font-size: 12px; }   /* "Ø am 8. Okt." on the 30-day cards [R3-03] */
.hchart-since {
  grid-column: 1 / -1;
  font: 400 11px var(--sans);
  color: var(--muted);
  font-style: italic;
  text-align: center;
  padding-top: 2px;
}@media (max-width: 900px){
  .hist-charts { grid-template-columns: repeat(2, minmax(0, 1fr)); }
}@media (max-width: 540px){
  .hist-charts { grid-template-columns: 1fr; }
}


















.segmented {
  display: flex;
  background: var(--glass-inset);
  -webkit-backdrop-filter: var(--glass-fx-inset);
          backdrop-filter: var(--glass-fx-inset);
  border: 1px solid var(--hairline);
  border-radius: 10px;
  padding: 3px;
  gap: 2px;
}
.segmented [role="tab"] {
  /* Was `.segmented label`; the items became <button> for aria-allowed-role,
     so the UA button chrome has to be reset here. [2026-09-16] */
  appearance: none; background: transparent; border: 0; font-family: inherit;
  flex: 1;
  text-align: center;
  padding: 7px 3px;
  border-radius: 7px;
  font-size: 11.5px;
  font-weight: 600;
  color: var(--ink-2);
  cursor: pointer;
  transition: background .15s ease, color .15s ease;
  user-select: none;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  min-width: 0;
}
.segmented input { display: none; }
.segmented [role="tab"]:hover { color: var(--brand-2); }
.segmented [role="tab"].active {
  background: var(--surface);
  color: var(--brand-2);
  box-shadow: 0 1px 2px rgba(0,0,0,0.06), inset 0 0 0 1px var(--brand-line);
}

.btn {
  flex: 1;
  padding: 7px 10px;
  background: var(--paper-deep);
  border: 1px solid var(--hairline);
  border-radius: 8px;
  color: var(--ink-2);
  cursor: pointer;
  font-size: 12px;
  font-weight: 600;
  transition: all .15s ease;
}
.btn:hover {
  border-color: var(--brand-line);
  color: var(--brand-2);
  background: var(--brand-soft);
}




/* Legend rows now live inside the controls panel's Legende section */
#legend-body-inner {
  display: flex; flex-direction: column; gap: 6px;
}
.legend-row {
  display: flex; align-items: center; gap: 8px;
  font-size: 11.5px;
  color: var(--ink-2);
}
.legend-row .sw {
  display: inline-block; width: 10px; height: 10px; border-radius: 3px;
  flex-shrink: 0;
}



.leaflet-marker-icon { background: transparent; border: none; }







/* Popups */
.leaflet-popup-content-wrapper {
  border-radius: var(--radius) !important;
  box-shadow: var(--shadow-lg) !important;
  background: var(--surface) !important;
  color: var(--ink) !important;
  border: 1px solid var(--hairline) !important;
}
.leaflet-popup-tip { background: var(--surface) !important; box-shadow: 0 2px 4px rgba(0,0,0,0.08) !important; }
.leaflet-popup-content { margin: 14px 16px !important; line-height: 1.4; font-family: var(--sans); }
.leaflet-popup-close-button {
  color: var(--muted) !important;
  font-size: 18px !important;
  padding: 6px 8px 0 0 !important;
}


@keyframes bdb-pulse {
  0%, 100% { background: var(--paper); }
  50%       { background: var(--crit-soft); }
}





@keyframes dep-pulse-orange {
  0%, 100% { box-shadow: 0 0 0 0   rgba(245, 158, 11, 0.35); }
  50%      { box-shadow: 0 0 0 4px rgba(245, 158, 11, 0); }
}
@keyframes dep-pulse-red {
  0%, 100% { box-shadow: 0 0 0 0   rgba(220, 38, 38, 0.40); }
  50%      { box-shadow: 0 0 0 5px rgba(220, 38, 38, 0); }
}

@keyframes dep-board-switch {
  0%, 100% { background: rgba(22, 163, 74, 1); }   /* filled  */
  50%      { background: rgba(22, 163, 74, 0); }   /* hollow  */
}


/* ===== Loading veil (first paint) ===== */
#loading-veil {
  position: fixed;
  inset: 0;
  z-index: 2000;
  display: flex; align-items: center; justify-content: center;
  background: var(--paper);
  transition: opacity .4s ease;
}
#loading-veil .veil-inner {
  display: flex; flex-direction: column; align-items: center; gap: 14px;
  text-align: center;
}
#loading-veil h2, #loading-veil .veil-title {
  font-family: var(--serif);
  font-weight: 600;
  font-size: 28px;
  letter-spacing: 0.2px;
  color: var(--ink);
  margin: 0;
}
#loading-veil p {
  font-size: 12px;
  color: var(--muted);
  letter-spacing: 0.6px;
  text-transform: uppercase;
  margin: 0;
}
#loading-veil .veil-bar {
  width: 200px; height: 3px;
  background: var(--paper-deep);
  border-radius: 999px;
  overflow: hidden;
  position: relative;
}
#loading-veil .veil-bar::before {
  content: ""; position: absolute;
  top: 0; left: -40%;
  width: 40%; height: 100%;
  background: linear-gradient(90deg, transparent, var(--brand), transparent);
  animation: veil-slide 1.4s infinite;
}
@keyframes veil-slide { to { left: 100%; } }
#loading-veil.is-hidden { opacity: 0; pointer-events: none; visibility: hidden; transition: opacity .3s ease, visibility 0s linear .3s; }@media (max-width: 1100px){
  .hchart-wrap { height: 120px; }
  #brand h1 { font-size: 19px; }
}

/* Compress topbar at medium-narrow widths: tighten padding/gap and collapse
   the status pill to dot + background only. This frees ~140 px that the full
   "Reibungsloser Betrieb" label would otherwise consume, preventing KPI tiles
   from being squeezed into each other in the 660–820 px range. The ParkPuls
   chrome has no KPI strip, so the status label now stays visible — only the
   hover-reveal timestamp is dropped on touch widths. */@media (max-width: 820px){
  #topbar { padding: 0 16px; gap: 18px; }
  #status-pill .last-update-wrap { display: none; }
  #status-pill { padding: 6px 10px; }
}

/* Narrowest desktop – stack the topbar (brand on its own row, KPIs below) */@media (max-width: 560px){
  #topbar {
    flex-wrap: wrap;
    padding: 10px 12px;
    gap: 10px 12px;
  }
  #brand { width: 100%; }
  .hchart-wrap { height: 100px; }
  #map { top: 0; }
}

/* ===== Site footer (legal strip) =====
   A slim single-line strip pinned to the bottom of the viewport. Sources /
   API attributions live on the standalone /quellen.html page linked from
   here, so this strip carries only the © line, the legal links, and the
   short disclaimer. It is a normal grid row of #app, so nothing has to be
   lifted clear of it. [comment corrected 2026-09-16: it previously described
   the fixed-footer layout that the grid shell replaced.] */
#site-footer {
  /* position/background/z-index are set by the shell rule further down; this
     block keeps the typography and spacing. [merged 2026-09-16] */
  border-top: 1px solid var(--hairline);
  padding: 5px 18px 6px;
  font-family: var(--sans);
  font-size: 11px;
  color: var(--ink-2);
  line-height: 1.5;
  text-align: center;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
#site-footer a { color: var(--brand-2); text-decoration: none; }
#site-footer a:hover { text-decoration: underline; }
#site-footer .footer-row { display: inline; }
#site-footer .sep { color: var(--dim); padding: 0 6px; }
#site-footer .heart { color: #e11d48; }   /* signage rose, deliberately a touch warmer than --crit */
#site-footer .muted-note { color: var(--muted); }@media (max-width: 980px){
  /* Below ~980 px the single line can no longer fit – allow it to wrap and
     drop the right-hand disclaimer onto its own row to keep the line height
     small instead of stretching the strip. */
  #site-footer { white-space: normal; padding: 5px 12px 6px; font-size: 10.5px; }
  #site-footer .muted-note { display: block; margin-top: 1px; }
  #site-footer .sep:last-of-type { display: none; }
}@media (max-width: 540px){
  #site-footer { font-size: 10px; }
  #site-footer .sep { padding: 0 4px; }
}
/* Visually-hidden helper for screen-reader-only text (e.g. "Liebe" next to ♥) */
.sr-only { position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px; overflow: hidden; clip: rect(0,0,0,0); white-space: nowrap; border: 0; }

/* Reduced motion */@media (prefers-reduced-motion: reduce){
  *, *::before, *::after {
    animation-duration: 0.001ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 0.001ms !important;
  }
}







/* (Legacy BusPuls @media 720px mobile block removed 2026-06-05 – it hid the map
   with no FAB to summon it and fought the redesign shell. Mobile is now defined
   in one coherent block at the end of this file.) */

/* =====================================================================
   ParkPuls.MS — parking-specific additions
   (garage popups, full-marker pulse, name labels, legend note, filter list)
   ===================================================================== */

/* Legend note under the colour swatches */
.legend-note {
  margin-top: 8px; padding-top: 8px;
  border-top: 1px solid var(--hairline-2);
  font-size: 10.5px; color: var(--muted);
}


/* Feed-unreachable banner (shown only when the live fetch fails) */
#feed-error {
  position: fixed; left: 50%; bottom: 18px; transform: translateX(-50%);
  z-index: 2000; box-sizing: border-box;
  /* max-content + an explicit cap: with left:50% the shrink-to-fit width was
     capped at half the viewport (about 195 px on a phone). [R3-08] */
  width: max-content; max-width: min(560px, calc(100vw - 32px));
  background: var(--surface); color: var(--ink-2);
  border: 1px solid var(--hairline); border-left: 3px solid var(--warn);
  border-radius: 12px; box-shadow: var(--shadow-md);
  padding: 12px 40px 12px 16px; font-size: 13px; line-height: 1.5;
}
#feed-error strong { color: var(--ink); }
#feed-error code { font-family: ui-monospace, SFMono-Regular, Menlo, monospace; font-size: 12px;
  background: var(--hl); padding: 1px 5px; border-radius: 5px; }
#feed-error button {
  position: absolute; top: 8px; right: 10px;
  background: none; border: none; cursor: pointer;
  font-size: 14px; color: var(--muted); line-height: 1;
}
#feed-error button:hover { color: var(--ink); }


/* =============================================================================
   Redesign v2 layout — merged from the former redesign.css (2026-06-04).
   Loaded as part of style.css; these rules sit AFTER the base rules above so
   they override the legacy fixed-topbar / full-bleed-map layout.
   ============================================================================= */
/* =============================================================================
   ParkPuls.MS — redesign v2 (desktop-first)
   Loaded AFTER style.css. Defines the two-pane app shell (top bar · Parkfinder
   rail · map / Statistik) and overrides the legacy fixed-topbar / full-bleed-map
   layout. Reuses style.css for popup, charts, loading veil and status tones.
   ============================================================================= */

[hidden] { display: none !important; }

/* ---- App shell ------------------------------------------------------------ */
#app {
  display: grid;
  grid-template-rows: 56px minmax(0, 1fr) auto;
  height: 100vh;
  overflow: hidden;
}

/* ---- Top bar (authoritative; the legacy fixed/glass rule was merged away
        2026-09-16, only its z-index remains up top) --------------------------- */
#topbar {
  position: static;
  height: 56px;
  display: flex;
  align-items: center;
  gap: 18px;
  padding: 0 18px;
  background: var(--surface);
  border-bottom: 1px solid var(--hairline);
}
.rd-brand { display: flex; align-items: center; gap: 8px; }
a.rd-brand { color: inherit; text-decoration: none; border-radius: 8px; }   /* link to the hub (D4); look unchanged */
.rd-mark { display: flex; align-items: center; }
.rd-mark img { height: 36px; width: auto; display: block; }
.rd-word { font-family: var(--serif); font-weight: 600; font-size: 22px; margin: 0; letter-spacing: .2px; color: var(--ink); }
.rd-sfx { color: var(--accent); }   /* coral .MS — matches Home/Bus/Zug and the ParkPuls card on the hub */
/* .rd-made rules removed 2026-09-16 with the duplicate "Made with ♥" in the top bar. */
.rd-topbar-spacer { flex: 1; }
/* The "Made with ♥" tagline also lives in the footer; drop it from the top bar
   once the chrome gets tight so it never collides with the view switch / pill. */

/* Live city KPIs in the chrome (moved out of the rail 2026-09-16). Two figures,
   no tiles: a tinted card for each would re-import the visual weight the rail
   just shed. A hairline divider is enough to separate them. */
.rd-kpis { display: flex; align-items: center; }
.rd-kpi { position: relative; display: flex; flex-direction: column; justify-content: center; padding-right: 26px; }
.rd-kpi + .rd-kpi { padding-left: 15px; border-left: 1px solid var(--hairline); }
.rk-v { font-family: var(--mono); font-weight: 700; font-size: 19px; line-height: 1.1; color: var(--ink); font-variant-numeric: tabular-nums; }
.rk-k { font-size: 10.5px; line-height: 1.2; color: var(--muted); white-space: nowrap;
  text-transform: uppercase; letter-spacing: .4px; font-weight: 600; }   /* KPI labels as on home and BusPuls (S16, light step) */
.rd-kpi-hero .rk-v { font-size: 21px; color: var(--brand-2); }
.rk-short { display: none; }   /* ≤720px swaps the long label for this one */

/* Lage status pill (reuses #status-pill + tone-* from paintStatusPill) */
#topbar #status-pill {
  position: static; display: inline-flex; align-items: center; gap: 7px;
  padding: 6px 13px; border-radius: 999px; font-size: 13px; font-weight: 500;
  background: var(--hl); color: var(--ink-2); border: 1px solid var(--hairline); cursor: default;
}
#topbar #status-pill .dot { width: 8px; height: 8px; border-radius: 50%; background: var(--muted); }
#topbar #status-pill .last-update-wrap { color: var(--muted); font-weight: 400; }
/* Tone rules carry #topbar so they beat the neutral #topbar #status-pill base
   above (S3, 2026-10-09: the pill stayed grey). Four-step scale shared with
   home, BusPuls and ZugPuls (D2): good green, leicht yellow, orange, bad red. */
#topbar #status-pill.tone-good { background: var(--good-soft); color: var(--good); border-color: transparent; }
#topbar #status-pill.tone-good .dot { background: var(--good); }
#topbar #status-pill.tone-leicht { background: var(--leicht-soft); color: #7a5f00; border-color: transparent; }
#topbar #status-pill.tone-leicht .dot { background: #eab308; }
#topbar #status-pill.tone-orange { background: var(--bad-soft); color: #9a3412; border-color: transparent; }
#topbar #status-pill.tone-orange .dot { background: #ea580c; }
#topbar #status-pill.tone-warn { background: var(--warn-soft); color: #92400e; border-color: transparent; }   /* --warn is 4.42:1 on this tint */
#topbar #status-pill.tone-warn .dot { background: var(--warn); }
#topbar #status-pill.tone-bad { background: var(--crit-soft); color: var(--crit); border-color: transparent; }
#topbar #status-pill.tone-bad .dot { background: var(--crit); }
#topbar #status-pill.tone-off { background: var(--hl); color: var(--muted); }
#topbar #status-pill .dot.load { background: var(--brand); animation: pp-pulse 1.1s ease-in-out infinite; }
#topbar #status-pill .dot.err  { background: var(--crit); }
@keyframes pp-pulse { 0%,100% { opacity: 1; } 50% { opacity: .35; } }

/* ---- Two-pane shell ------------------------------------------------------- */
#shell { display: grid; grid-template-columns: 384px minmax(0, 1fr); min-height: 0; }
/* Statistik view: the rail is hidden, so collapse its track and let the
   Statistik panel use the full width instead of being trapped in 384px. */
#shell.stat-mode { grid-template-columns: minmax(0, 1fr); }

#rail {
  background: var(--surface); border-right: 1px solid var(--hairline);
  display: flex; flex-direction: column; min-height: 0; overflow: hidden;
}
#rail-list { display: flex; flex-direction: column; min-height: 0; flex: 1; }

/* The city-pulse tiles and the occupancy-distribution bar were removed here
   2026-09-16 (markup and renderPulse with them). The two figures worth keeping
   moved to the top bar as .rd-kpis; the distribution bar's four colours were
   the single biggest source of hue in the rail and described data quality, not
   where to park. */

/* Mobile-only filter-sheet trigger and chrome; the ≤720px block below turns
   these on and defines the bottom-sheet behaviour. */
.filter-summary, .find-veil, .find-sheet-head { display: none; }

/* Find controls */
.find { padding: 12px 16px 11px; border-bottom: 1px solid var(--hairline); display: flex; flex-direction: column; gap: 9px; }
.rd-search { display: flex; align-items: center; gap: 8px; background: var(--hl); border-radius: 9px; padding: 0 11px; height: 38px; }
.rd-search svg { flex: 0 0 auto; width: 16px; height: 16px; color: var(--muted); }
.rd-search input { border: 0; background: transparent; outline: none; width: 100%; font-family: var(--sans); font-size: 14px; color: var(--ink); }
.find-row { display: flex; gap: 8px; align-items: center; flex-wrap: wrap; }
#sortMode { flex: 1 1 150px; font-family: var(--sans); font-size: 13px; border: 1px solid var(--hairline); border-radius: 9px; padding: 8px 9px; background: var(--surface); color: var(--ink); cursor: pointer; }
.rd-chips { display: flex; gap: 6px; }
.rd-chip { font-family: var(--sans); font-size: 12px; font-weight: 500; border: 1px solid var(--hairline); background: var(--surface); color: var(--muted); border-radius: 999px; padding: 6px 11px; cursor: pointer; }
.rd-chip.is-active { background: var(--brand-cta); border-color: var(--brand-cta); color: #fff; }   /* white on --brand is 2.39:1 [R3-04] */

/* List */
.list-meta { padding: 8px 16px; font-size: 12px; color: var(--muted); }
.list-wrap { flex: 1; overflow: auto; min-height: 0; }
/* Each list entry is a row: the card button (opens detail) + an optional
   mobile-only "locate on map" button beside it. The row carries the divider. */
.gc-row { display: flex; align-items: stretch; border-bottom: 1px solid var(--hairline-2); }
.gcard {
  flex: 1 1 auto; min-width: 0;
  display: flex; gap: 11px; align-items: center; text-align: left;
  padding: 9px 16px; border: 0;
  background: transparent; cursor: pointer; font-family: var(--sans);
}
.gcard:hover { background: var(--hl); }
.gcard.is-sel { background: var(--brand-soft); box-shadow: inset 3px 0 0 var(--brand); }
/* Tap-to-locate button — hidden on desktop (list-hover highlights instead). */
.gc-locate {
  display: none; flex: 0 0 auto; width: 50px; border: 0; border-left: 1px solid var(--hairline-2);
  background: transparent; color: var(--muted); cursor: pointer;
  align-items: center; justify-content: center; transition: color .15s ease, background .15s ease;
}
.gc-locate svg { width: 20px; height: 20px; }
.gc-locate:active { color: var(--brand-2); background: var(--hl); }
.gc-row.is-sel .gc-locate { color: var(--brand-2); }
.gc-dot { width: 11px; height: 11px; border-radius: 50%; flex: 0 0 auto; }
.gc-main { flex: 1; min-width: 0; }
.gc-name { display: block; font-weight: 500; font-size: 14px; color: var(--ink); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
/* Distance rides on the name line [2026-10-05]: as the last chip it wrapped
   onto a third line on 3 of 8 phone rows. */
.gc-head { display: flex; align-items: baseline; gap: 8px; min-width: 0; }
.gc-head .gc-name { flex: 0 1 auto; min-width: 0; }
.gc-km { flex: 0 0 auto; font-size: 11.5px; color: var(--muted); font-variant-numeric: tabular-nums; white-space: nowrap; }
.gc-bar { display: block; height: 5px; border-radius: 3px; background: var(--hl); overflow: hidden; margin: 5px 0 4px; }
/* Single blue, not the band colour — see the note in renderParkList(). Only a
   genuinely full house breaks out of it, because "0 frei" is the one state the
   bar can report that the number beside it cannot make more obvious. */
.gc-bar-f { display: block; height: 100%; border-radius: 3px; background: var(--brand); }
.gc-bar-f.is-full { background: var(--crit); }
.gc-meta { display: flex; gap: 8px; flex-wrap: wrap; font-size: 11px; color: var(--muted); }
/* Badges [reworked 2026-09-16]. Every badge used to carry its own tint — cyan
   price, blue distance, green charging, outlined type — so a single row could
   show four hues that encoded nothing: the words already say which is which.
   They are one quiet grey chip now, and the only badge allowed a colour is the
   one that contradicts what the row otherwise implies: "voll". Emphasis where
   it is still wanted (price) comes from weight, not hue. */
.gc-meta .b { background: var(--hl); border-radius: 5px; padding: 1px 6px; color: var(--muted); }
/* Price estimate for the chosen duration — the one badge with extra weight. */
.gc-meta .b.b-price { color: var(--ink-2); font-weight: 600; }
.gc-meta .b.b-price.b-closed { color: var(--muted); font-weight: 500; }
/* Closed houses: when they reopen (live schedule). */
.gc-meta .b.b-reopen { color: var(--ink-2); font-weight: 500; }
/* Open house with 0 free spaces — the single exception that keeps a colour. */
.gc-meta .b.b-full { background: var(--crit-soft); color: var(--crit); font-weight: 600; }
/* Facility type: caption text, not a chip — it is on every row. */
.gc-meta .b.b-type { background: transparent; padding-left: 0; padding-right: 0; color: var(--muted); }
/* Untrusted sensor — honest "no live data" marker; the dashed edge does the
   distinguishing that a tint used to. */
.gc-meta .b.b-nodata { color: var(--muted); font-weight: 500; border: 1px dashed var(--hairline); }
.gc-meta .b.b-typical,
.gc-meta .b.b-dist,
.gc-meta .b.b-ev { color: var(--muted); font-weight: 500; }
.gc-free { flex: 0 0 auto; text-align: right; min-width: 54px; }
.gc-free .gc-n { display: block; font-family: var(--mono); font-weight: 700; font-size: 22px; line-height: 1; }
.gc-free .gc-l { display: block; font-size: 10px; color: var(--muted); }
.gcard.is-closed .gc-name { color: var(--muted); }

/* Trends link under the result list — the demoted view switch [2026-09-16]. */
.rail-foot { flex: 0 0 auto; border-top: 1px solid var(--hairline); background: var(--surface); }
.rail-foot-link {
  display: flex; align-items: center; gap: 8px; width: 100%;
  padding: 11px 16px; border: 0; background: transparent; cursor: pointer;
  font-family: var(--sans); font-size: 12.5px; font-weight: 500; color: var(--brand-2); text-align: left;
}
.rail-foot-link:hover { background: var(--hl); }
.rail-foot-link svg { width: 15px; height: 15px; flex: 0 0 auto; }

/* Detail */
#rail-detail { display: flex; flex-direction: column; min-height: 0; overflow: auto; }
.dt-top { padding: 13px 16px; border-bottom: 1px solid var(--hairline); }
.dt-back { display: inline-flex; align-items: center; gap: 6px; border: 0; background: transparent; color: var(--brand-2); font-family: var(--sans); font-size: 13px; font-weight: 500; cursor: pointer; padding: 0; margin-bottom: 10px; }
.dt-type { display: block; font-size: 10px; font-weight: 700; letter-spacing: 1.2px; text-transform: uppercase; color: var(--muted); margin-bottom: 3px; }
.dt-name:focus { outline: none; }   /* programmatic focus target (tabindex -1), not a control */
.dt-name { font-family: var(--serif); font-weight: 600; font-size: 22px; margin: 0; color: var(--ink); }
.dt-badge { display: inline-block; font-size: 11px; font-weight: 600; color: #fff; border-radius: 999px; padding: 2px 10px; margin-top: 7px; }
.dt-badge[hidden] { display: none; }
.dt-hero { display: flex; align-items: baseline; gap: 8px; padding: 14px 16px 8px; }
.dt-hero .dt-num { font-family: var(--mono); font-weight: 700; font-size: 30px; }
.dt-hero .dt-lbl { font-size: 13px; color: var(--muted); }
/* flex: none is load-bearing [2026-09-16]: #rail-detail is a column flex
   container, so this empty 8 px div was a shrinkable flex item and collapsed to
   height 0 whenever the panel's content overflowed — which is always. The bar
   has never actually been visible in the detail view. */
/* overflow visible + position relative [2026-10-05]: the typical-level tick
   (.dt-bar-typ) overhangs the bar by 4 px top and bottom. */
.dt-bar { flex: 0 0 auto; position: relative; height: 10px; border-radius: 5px; background: var(--hl); margin: 2px 16px 9px; }
.dt-bar-fill { display: block; height: 100%; border-radius: 5px; background: var(--brand); }
.dt-sub { padding: 0 16px 10px; font-size: 12.5px; color: var(--muted); font-variant-numeric: tabular-nums; }
.dt-rows { padding: 0 16px; }
.dt-sec { display: flex; align-items: center; gap: 6px; font-size: 10px; font-weight: 700; letter-spacing: 1px; text-transform: uppercase; color: var(--muted); margin: 18px 0 2px; }
.dt-sec .dt-ic { width: 13px; height: 13px; color: var(--brand-2); }
.dt-rows .dt-sec:first-child { margin-top: 20px; }   /* was 2px: the trend hint now sits right above [2026-10-06] */
.dt-row { display: flex; justify-content: space-between; gap: 14px; padding: 8px 0; border-top: 1px solid var(--hairline-2); font-size: 13px; }
.dt-sec + .dt-row { border-top: 0; }
.dt-row .k { color: var(--muted); flex: 0 0 auto; }
.dt-row .v { text-align: right; color: var(--ink); }
.dt-row.full { display: block; }
.dt-row.full .v { display: block; text-align: left; }
/* Live opening status (rendered inside a row value) */
.dt-open { display: block; font-weight: 600; color: var(--ink); }
.dt-open i { display: inline-block; width: 8px; height: 8px; border-radius: 50%; margin-right: 6px; background: var(--muted); vertical-align: 1px; }
.dt-open.good { color: var(--good); } .dt-open.good i { background: var(--good); }
.dt-open.warn { color: var(--warn); } .dt-open.warn i { background: var(--warn); }
.dt-open.off  { color: var(--muted); } .dt-open.off i { background: var(--dim); }
.dt-open-sub { display: block; font-size: 11.5px; font-weight: 400; color: var(--muted); margin-top: 2px; }
.dt-actions { padding: 14px 16px 18px; }
.dt-route { display: flex; gap: 8px; }
.dt-actions .dt-sec-route { margin: 4px 0 8px; }
.dt-actions { border-top: 1px solid var(--hairline-2); margin-top: 14px; }
.dt-btn { flex: 1; text-align: center; border: 1px solid var(--hairline); border-radius: 9px; padding: 10px; font-family: var(--sans); font-weight: 500; font-size: 13px; color: var(--ink); background: var(--surface); text-decoration: none; }
.dt-btn:hover { background: var(--hl); text-decoration: none; }
.dt-btn.primary { background: var(--brand-cta); border-color: var(--brand-cta); color: #fff; }   /* R3-04, R6-12 */
.dt-link { display: block; text-align: center; margin-top: 10px; color: var(--brand-2); font-size: 12.5px; }

/* ---- Map area ------------------------------------------------------------- */
#mapwrap { position: relative; min-height: 0; }
#map { position: absolute; inset: 0; background: var(--paper-deep); transition: none; }
.leaflet-top.leaflet-left { top: 12px; left: 12px; }
.leaflet-bottom { bottom: 0 !important; }

/* Slim legend strip. Everything that used to float over this map — the
   "Einfärben nach / Ebenen" card, the collapsible legend card, and on mobile
   the sheet that held both — was removed 2026-09-16. What is left is the one
   thing a first-time visitor actually needs from that corner: what the pin
   colours mean. Built once by renderLegendBar(). */
.legend-bar {
  position: absolute; z-index: 600; left: 12px; bottom: 12px;
  display: flex; flex-wrap: wrap; align-items: center; gap: 5px 14px;
  max-width: calc(100% - 24px);
  padding: 7px 13px; border-radius: 999px;
  background: var(--glass-raised);
  -webkit-backdrop-filter: var(--glass-fx-raised);
          backdrop-filter: var(--glass-fx-raised);
  border: 1px solid var(--hairline);
  box-shadow: var(--shadow-sm), var(--glass-edge);
  font-size: 11.5px; line-height: 1; color: var(--ink-2);   /* not --muted: sits on glass */
}
.legend-bar[hidden] { display: none; }
.legend-bar .lb-i { display: inline-flex; align-items: center; gap: 5px; white-space: nowrap; }
.legend-bar .lb-i i { width: 9px; height: 9px; border-radius: 50%; flex: 0 0 auto; box-shadow: 0 0 0 1px var(--hairline); }

/* ---- Statistik view ------------------------------------------------------- */
#statview { position: absolute; inset: 0; z-index: 500; background: var(--paper); overflow: auto; padding: 18px 22px 26px; }
.sv-head { display: flex; align-items: center; justify-content: space-between; gap: 16px; margin-bottom: 16px; flex-wrap: wrap; }
.sv-title { display: flex; flex-direction: column; align-items: flex-start; gap: 2px; }
.sv-back { border: 0; background: transparent; padding: 0; cursor: pointer; font-family: var(--sans); font-size: 12.5px; font-weight: 500; color: var(--brand-2); }
.sv-back:hover { text-decoration: underline; }
.sv-main { font-family: var(--serif); font-size: 18px; font-weight: 600; color: var(--ink); }
#statview .drawer-tabs { display: flex; gap: 4px; background: var(--hl); border-radius: 9px; padding: 3px; }
#statview .dtab { border: 0; background: transparent; cursor: pointer; font-family: var(--sans); font-size: 13px; font-weight: 500; color: var(--ink-2); padding: 7px 16px; border-radius: 7px; }
#statview .dtab:hover:not(.is-active) { color: var(--ink); }   /* inactive tab was --muted, 4.46:1 on the track [R3-20] */
#statview .dtab.is-active { background: var(--surface); color: var(--ink); box-shadow: var(--shadow-sm); }
#statview .drawer-pane { display: none; }
#statview .drawer-pane.is-active { display: block; }
#statview .hist-charts { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 16px; }
@media (max-width: 1100px){ #statview .hist-charts { grid-template-columns: 1fr; } }

/* Statistik empty / error state (shown until history data accrues) */
.sv-empty {
  grid-column: 1 / -1;
  max-width: 460px;
  margin: 40px auto;
  text-align: center;
  color: var(--muted);
}
.sv-empty svg { width: 40px; height: 40px; color: var(--brand); opacity: .8; margin-bottom: 10px; }
.sv-empty h4 { font-family: var(--serif); font-size: 17px; font-weight: 600; color: var(--ink); margin: 0 0 6px; }
.sv-empty p { font-size: 13px; line-height: 1.5; margin: 0; }

/* ---- Footer (override legacy position:fixed) ------------------------------ */
/* position: relative, not static [2026-10-05]: the drop-up directory panel
   (#standorte-verzeichnis > nav, position:absolute; bottom:100%) needs a
   positioned ancestor. With a static footer it anchored to the viewport and
   opened ABOVE the top edge, off-screen: on phones that hid the only
   Impressum/Datenschutz links on the page. z-index lifts it over the map. */
#site-footer { position: relative; background: var(--surface); z-index: 1100; }

/* Reasons card keeps the top-right corner the map-control card used to share
   with it (see the base rule up top); the legend now owns bottom-left. */

/* Floating action button (map ⇄ list) — only used on the mobile layout below. */
.map-fab { display: none; }

/* =============================================================================
   Mobile / narrow-viewport layout (≤ 720 px) — list-first
   -----------------------------------------------------------------------------
   The Parkfinder rail becomes the full-screen primary surface. The map is a
   full-screen overlay summoned by a floating button; the Statistik view reuses
   that overlay (it renders inside #mapwrap). Desktop layout above is untouched.
   ============================================================================= */
@media (max-width: 720px) {

  /* Top bar: one compact row that fits a ~360 px phone. */
  #topbar { gap: 8px; padding: 0 11px; position: relative; z-index: 1300; flex-wrap: nowrap; }
  .rd-mark img { height: 26px; }
  .rd-word { font-size: 16px; }
  .rd-kpi { padding-right: 22px; }
  .rd-kpi + .rd-kpi { padding-left: 10px; }
  .rk-v { font-size: 16px; }
  .rd-kpi-hero .rk-v { font-size: 17px; }
  .rk-k { font-size: 9.5px; }
  .rk-long { display: none; }
  .rk-short { display: inline; }
  #topbar #status-pill { padding: 5px 9px; font-size: 12px; gap: 6px; min-width: 0; max-width: 42vw; }
  #topbar #status-pill .txt { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
  #topbar #status-pill .last-update-wrap { display: none; }   /* reclaim width */

  /* Shell collapses to a single full-height cell holding the rail. */
  #shell { grid-template-columns: 1fr; grid-template-rows: minmax(0, 1fr); }
  #rail { border-right: 0; }
  .list-wrap { padding-bottom: 56px; }    /* clears the fixed footer */
  .gc-locate { display: inline-flex; }    /* touch: tap a row to locate its pin */

  /* The mobile one-line city-pulse summary went with the pulse block itself
     (2026-09-16); the two live figures are in the top bar on every width. */

  /* ---- Filter summary bar: collapsed trigger for the find/planner sheet --- */
  .filter-summary {
    display: flex; align-items: center; gap: 8px; width: 100%;
    padding: 10px 16px; border: 0; border-bottom: 1px solid var(--hairline);
    background: var(--surface-2); cursor: pointer; text-align: left;
    font-family: var(--sans);
  }
  .filter-summary .fs-ico { width: 15px; height: 15px; color: var(--brand-2); flex: 0 0 auto; }
  .filter-summary .fs-txt { flex: 1 1 auto; min-width: 0; font-size: 12px; font-weight: 500; color: var(--ink); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
  .filter-summary .fs-chev { width: 13px; height: 13px; color: var(--muted); flex: 0 0 auto; transition: transform .2s ease; }
  .filter-summary[aria-expanded="true"] .fs-chev { transform: rotate(180deg); }

  /* ---- Find section becomes a bottom sheet, opened by .filter-summary ---- */
  .find-veil {
    display: block; position: fixed; top: 56px; left: 0; right: 0; bottom: 0;
    z-index: 1350; background: rgba(13,20,29,.32); opacity: 0; pointer-events: none;
    transition: opacity .25s ease;
  }
  .find-veil.is-open { opacity: 1; pointer-events: auto; }
  #find {
    position: fixed; left: 0; right: 0; bottom: 0; z-index: 1360;
    max-height: 82vh; overflow-y: auto;
    background: var(--glass-chrome);
    -webkit-backdrop-filter: var(--glass-fx-chrome);
            backdrop-filter: var(--glass-fx-chrome);
    border-radius: 16px 16px 0 0; box-shadow: var(--shadow-lg); border-bottom: 0;
    transform: translateY(101%);
    transition: transform .3s cubic-bezier(.2,.7,.2,1);
    padding-bottom: max(11px, env(safe-area-inset-bottom, 0px));
  }
  #find.is-open { transform: translateY(0); }
  .find-sheet-head { display: block; }
  .fsh-handle { display: block; width: 36px; height: 4px; border-radius: 99px; background: var(--dim); margin: 9px auto 2px; }
  .fsh-row { display: flex; align-items: center; justify-content: space-between; padding: 5px 2px 11px; border-bottom: 1px solid var(--hairline); margin-bottom: 2px; }
  .fsh-row h2 { font-family: var(--serif); font-size: 15px; font-weight: 600; margin: 0; color: var(--ink); }
  .fsh-close { border: 0; background: transparent; color: var(--brand-2); font-family: var(--sans); font-size: 12.5px; font-weight: 600; cursor: pointer; padding: 4px 2px; }

  /* Map + Statistik live in a full-screen overlay, slid up when summoned. */
  #mapwrap {
    position: fixed;
    top: 56px; left: 0; right: 0; bottom: 0;
    z-index: 1200;
    transform: translateY(101%);
    transition: transform .32s cubic-bezier(.2,.7,.2,1);
    box-shadow: 0 -10px 30px rgba(20,40,64,.20);
  }
  body.map-open #mapwrap,
  body.stat-view #mapwrap { transform: translateY(0); }

  /* Legend: a docked strip along the bottom of the map overlay rather than a
     floating pill — at 360 px a pill either wraps to three lines or covers the
     map. Horizontally scrollable; nothing floats over the bottom right any
     more, so it sits where a legend belongs. */
  .legend-bar {
    left: 10px; right: 10px; bottom: 16px; max-width: none;
    gap: 5px 12px; padding: 8px 12px; border-radius: 12px; font-size: 10.5px;
  }
  body.stat-view .legend-bar { display: none; }

  /* Floating action button: toggles the map overlay (Karte ⇄ Liste). */
  .map-fab {
    display: inline-flex; align-items: center; gap: 8px;
    position: fixed; right: 16px; bottom: 18px; z-index: 1300;
    height: 48px; padding: 0 18px 0 15px; margin: 0;
    border: 0; border-radius: 999px; cursor: pointer;
    /* --brand-cta, not --brand: white on #00B4F0 measures 2.38:1, and --brand-2
       lightens in the dark theme so it cannot back white text. [2026-09-16] */
    background: var(--brand-cta); color: #fff;
    font-family: var(--sans); font-size: 14px; font-weight: 600;
    box-shadow: 0 6px 18px rgba(28,167,92,.42), 0 2px 6px rgba(20,40,64,.18);
    transition: filter .15s ease;
  }
  .map-fab:hover, .map-fab:focus-visible { filter: brightness(.92); outline: none; }
  .map-fab svg { width: 20px; height: 20px; }
  body.stat-view .map-fab { display: none; }   /* no map FAB while viewing trends */

  /* Status-reasons hint docks above the FAB, full width. */
  /* Below the filter bar rather than over it: the card is a dismissible note
     about the current situation, and it must not cover the primary control. */
  #reasonsCard {
    left: 10px; right: 10px; top: 102px; bottom: auto;
    width: auto; max-width: none; max-height: 38vh; overflow: auto;
    background: var(--surface);
    -webkit-backdrop-filter: none; backdrop-filter: none;
  }

  /* Statistik view: edge-to-edge with a little breathing room. */
  #statview { padding: 16px 14px 24px; }
  .sv-head { gap: 10px 14px; }
}

/* Extra-narrow phones (e.g. 360 px): shave a few more pixels so nothing overflows. */
@media (max-width: 380px) {
  #topbar { gap: 6px; padding: 0 10px; }
  .rd-mark img { height: 24px; }
  .rd-word { font-size: 15px; }
  #topbar #status-pill { padding: 5px 8px; font-size: 11.5px; max-width: 34vw; }
}

/* =============================================================================
   UI polish layer (2026-06) — theme toggle, skeleton loading, micro-
   interactions, panel transitions, themed scrollbars, glass map cards.
   All motion is killed globally by the prefers-reduced-motion rule above.
   ============================================================================= */

/* ---- Skeleton loading (shown until the first poll lands) ---- */
@keyframes sk-shimmer { from { background-position: 200% 0; } to { background-position: -200% 0; } }
.sk {
  border-radius: 6px;
  background: linear-gradient(90deg, var(--hl) 25%, var(--hl-hover) 50%, var(--hl) 75%);
  background-size: 200% 100%;
  animation: sk-shimmer 1.4s linear infinite;
}
.sk-card { display: flex; gap: 11px; align-items: center; padding: 14px 16px; border-bottom: 1px solid var(--hairline-2); }
.sk-dot  { width: 11px; height: 11px; border-radius: 50%; flex: 0 0 auto; }
.sk-main { flex: 1; display: flex; flex-direction: column; gap: 8px; }
.sk-line { height: 10px; }
.sk-line.w60 { width: 60%; }
.sk-line.w85 { width: 85%; }
.sk-num  { width: 44px; height: 26px; border-radius: 7px; flex: 0 0 auto; }

/* ---- Micro-interactions: list cards ---- */
.gcard { transition: background .15s ease; }
.gcard:active { background: var(--hl-hover); }
.gcard:hover .gc-name { color: var(--brand-2); }
.gcard .gc-bar-f { transition: width .45s cubic-bezier(.2,.7,.2,1), background-color .3s ease; }

/* ---- Chips, tabs, buttons: smooth + tactile ---- */
.rd-chip { transition: background .15s ease, color .15s ease, border-color .15s ease, transform .12s ease; }
.rd-chip:hover:not(.is-active) { border-color: var(--brand-line); color: var(--brand-2); }
.rd-chip:active { transform: scale(.95); }
.rail-foot-link, .sv-back { transition: background .18s ease, color .18s ease; }
.dt-btn { transition: background .15s ease, border-color .15s ease, transform .12s ease; }
.dt-btn:active { transform: scale(.97); }
.dt-btn.primary:hover { background: #0b6a36; border-color: #0b6a36; }
.gp-more { transition: background .15s ease, color .15s ease; }
.dt-back { transition: color .15s ease; }
.dt-back:hover { color: var(--brand); }
#sortMode { transition: border-color .15s ease; }
#sortMode:hover { border-color: var(--brand-line); }
/* Inert while a destination is set (list is always sorted by distance then) */
#sortMode:disabled { opacity: .55; cursor: not-allowed; }
#sortMode:disabled:hover { border-color: var(--hairline); }
.rd-search { transition: box-shadow .15s ease; }
.rd-search:focus-within { box-shadow: inset 0 0 0 2px var(--brand-2); }   /* was brand-line (~1.4:1), too faint for a focus ring [audit 2026-10-05] */

/* ---- Panel / view transitions ---- */
@keyframes pp-fade-up  { from { opacity: 0; transform: translateY(8px); }  to { opacity: 1; transform: none; } }
@keyframes pp-slide-in { from { opacity: 0; transform: translateX(14px); } to { opacity: 1; transform: none; } }
#statview:not([hidden])    { animation: pp-fade-up  .28s cubic-bezier(.2,.7,.2,1); }
#rail-detail:not([hidden]) { animation: pp-slide-in .26s cubic-bezier(.2,.7,.2,1); }
#rail-list:not([hidden])   { animation: pp-fade-up  .22s cubic-bezier(.2,.7,.2,1); }

/* ---- Map pins: gentle lift on hover ---- */
.pp-pill { transition: transform .15s ease; }
.pp-pill:hover { transform: translate(calc(-50% + var(--ox, 0px)), calc(-100% - 6px + var(--oy, 0px))) scale(1.07); }


/* ---- Narrow viewports: keep the chrome from widening the page ----
   #app's implicit grid column sizes to min-content, so an overflowing top
   bar silently widens the whole layout past the viewport. Clamp the track
   and shave the top-bar pieces so everything fits a 360–390 px phone. */
#app { grid-template-columns: minmax(0, 1fr); }

@media (max-width: 720px) {
  #topbar { gap: 6px; padding: 0 9px; }
  .rd-mark img { height: 24px; }
  .rd-word { font-size: 15px; }
  #topbar #status-pill { padding: 5px 8px; font-size: 11.5px; max-width: 30vw; flex-shrink: 1; }
}
@media (max-width: 380px) {
  #topbar { gap: 5px; }
  .rd-word { font-size: 14px; }
  .rd-kpi { padding-right: 18px; }
  .rd-kpi + .rd-kpi { padding-left: 8px; }
  .rk-v { font-size: 15px; }
  .rd-kpi-hero .rk-v { font-size: 16px; }
  /* The dot already carries the verdict; on a 360 px bar the word does not fit
     beside two KPIs. Kept in the a11y tree (clip, not display:none) so the
     aria-live status still announces "Sehr voll" rather than nothing. */
  #topbar #status-pill .txt { position: absolute; width: 1px; height: 1px; overflow: hidden; clip: rect(0 0 0 0); clip-path: inset(50%); white-space: nowrap; }
  #topbar #status-pill { padding: 6px; }
}

/* =============================================================================
   2026-06-11 additions — review follow-ups
   ============================================================================= */

/* ---- Empty search state in the Parkfinder list (#6) ---- */
.list-empty {
  padding: 36px 22px;
  text-align: center;
  color: var(--muted);
}
.list-empty svg { width: 34px; height: 34px; color: var(--dim); margin-bottom: 8px; }
.list-empty h4 { font-family: var(--serif); font-size: 16px; font-weight: 600; color: var(--ink); margin: 0 0 5px; }
.list-empty p { font-size: 12.5px; line-height: 1.5; margin: 0 0 14px; }
.list-empty p strong { color: var(--ink-2); }
.list-empty-reset {
  border: 1px solid var(--hairline); border-radius: 999px; cursor: pointer;
  background: var(--surface); color: var(--brand-2);
  font-family: var(--sans); font-size: 12.5px; font-weight: 500; padding: 7px 14px;
  transition: background .15s ease, border-color .15s ease;
}
.list-empty-reset:hover { border-color: var(--brand-line); background: var(--brand-soft); }

/* ---- Detail sparkline: free spaces over the last 24 h (#14) ---- */
.dt-spark { padding: 12px 16px 4px; }
.dt-spark-head {
  display: flex; align-items: baseline; justify-content: space-between; gap: 10px;
  font-size: 10.5px; font-weight: 600; letter-spacing: .5px; text-transform: uppercase;
  color: var(--muted); margin-bottom: 6px;
}
/* Inline legend on the right of a chart head [2026-10-05]: swatches drawn the
   way the series are drawn (solid blue, dashed grey, dashed mean). */
.dt-key { display: inline-flex; gap: 10px; font-size: 11px; font-weight: 500; text-transform: none; letter-spacing: 0; white-space: nowrap; }
.dt-key > span { display: inline-flex; align-items: center; gap: 5px; }
.dt-key i { display: inline-block; width: 14px; }
.dt-key .k-now i { height: 3px; border-radius: 2px; background: var(--brand); }
.dt-key .k-typ i { border-top: 2px dashed var(--muted); }
.dt-key .k-avg { color: var(--ink-2); font-weight: 600; font-variant-numeric: tabular-nums; }
.dt-key .k-avg::before { content: ""; display: inline-block; width: 14px; border-top: 2px dashed var(--ink-2); opacity: .7; }
.dt-key .k-avg:empty { display: none; }
.dt-spark-wrap { position: relative; height: 104px; }
/* Best-arrival hint + 30-day history strip (detail drawer, 2026-07-16) */
.dt-best { display: flex; align-items: center; gap: 10px; margin: 8px 16px 0; padding: 9px 11px; border-radius: 10px;
  background: var(--surface-2); font-size: 12.5px; color: var(--ink-2); }
.dt-best[hidden] { display: none; }
.dt-best b { font-variant-numeric: tabular-nums; color: var(--ink); }
.dt-best-ic { flex: 0 0 auto; width: 30px; height: 30px; border-radius: 50%; display: grid; place-items: center;
  background: var(--hl-hover); color: var(--ink-2); }
.dt-best-ic .dt-ic { width: 16px; height: 16px; }
.dt-best-ic.good { background: var(--good-soft); color: var(--good); }
.dt-best-ic.warn { background: var(--warn-soft); color: var(--warn); }
.dt-best-txt { display: flex; flex-direction: column; gap: 1px; line-height: 1.4; min-width: 0; }
.dt-best-txt .h { color: var(--ink); font-weight: 500; }
.dt-best-txt small { font-size: 11px; color: var(--muted); }
.dt-hist30 { padding: 14px 16px 10px; }
.dt-hist30-plot { position: relative; height: 56px; border-bottom: 1px solid var(--hairline); }
.dt-hist30-bars { display: flex; align-items: flex-end; gap: 2px; height: 56px; }
.dt-hist30-bars span { flex: 1; min-height: 2px; border-radius: 2px 2px 0 0; background: var(--brand); }
.dt-hist30-bars span.sun { opacity: .45; }
.dt-hist30-mean { position: absolute; left: 0; right: 0; height: 0; border-top: 1.5px dashed var(--ink-2); opacity: .7; pointer-events: none; }
/* Days without a usable average keep their slot so the strip stays a time axis
   (2026-09-16): a hairline sitting on the baseline, clearly not a bar. */
.dt-hist30-bars span.h30-gap {
  height: 2px; border-radius: 1px;
  background: repeating-linear-gradient(90deg,
    var(--hairline-2, rgba(20,40,64,0.14)) 0 2px, transparent 2px 4px);
}
.dt-hist30-note { font-size: 10.5px; line-height: 1.45; color: var(--muted, #7a8a9b); margin-top: 4px; }
.dt-hist30-axis { display: flex; justify-content: space-between; gap: 8px;
  font-size: 10.5px; color: var(--muted, #7a8a9b); margin-top: 3px; }

/* Typical-occupancy line for a planned arrival (P4-1, 2026-09-16): a statement
   about a DIFFERENT moment than the live hero above it, so it is visually set
   apart rather than blending into the live block. */
.dt-slot {
  margin: 0 16px 10px; padding: 9px 11px;
  background: var(--info-soft); border-left: 3px solid var(--info);
  border-radius: 8px; font-size: 12.5px; line-height: 1.45; color: var(--ink-2);
}
.dt-slot b { color: var(--ink); }

/* ---- Detail at-a-glance layer [2026-10-05] ----------------------------- */
.dt-ic { width: 14px; height: 14px; flex: 0 0 auto; }
/* Typical-level tick on the hero bar: ink, with a white halo so it reads on
   both the blue fill and the empty track. */
.dt-bar-typ { position: absolute; top: -4px; bottom: -4px; width: 3px; margin-left: -1.5px;
  border-radius: 2px; background: var(--ink); box-shadow: 0 0 0 1.5px var(--surface); }
.dt-bar-typ[hidden] { display: none; }
.dt-vs { flex: 0 0 auto; align-self: flex-start; display: inline-flex; align-items: center; gap: 8px;
  margin: -2px 16px 14px; padding: 5px 11px 5px 9px; border-radius: 999px;
  font-size: 12px; line-height: 1.35; color: var(--ink-2); background: var(--hl-hover); }
.dt-vs[hidden] { display: none; }
.dt-vs b { font-weight: 600; color: var(--ink); }
.dt-vs-tick { flex: 0 0 auto; width: 3px; height: 13px; border-radius: 2px; background: var(--ink); }
.dt-vs.is-fuller  { background: var(--warn-soft); }
.dt-vs.is-fuller b { color: var(--warn); }
.dt-vs.is-emptier { background: var(--good-soft); }
.dt-vs.is-emptier b { color: var(--good); }
.dt-tiles { flex: 0 0 auto; display: grid; grid-auto-flow: column; grid-auto-columns: minmax(0, 1fr);
  gap: 8px; margin: 0 16px 4px; }
.dt-tiles[hidden] { display: none; }
.dt-tile { display: flex; flex-direction: column; gap: 2px; min-width: 0; padding: 9px 10px 10px;
  border: 1px solid var(--hairline-2); border-radius: 10px; background: var(--surface-2); }
.dt-tile-lbl { display: flex; align-items: center; gap: 5px; font-size: 11px; font-weight: 600; color: var(--muted); }
.dt-tile-lbl > span { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.dt-tile-val { font-family: var(--mono); font-weight: 600; font-size: 17px; line-height: 1.2; color: var(--ink);
  font-variant-numeric: tabular-nums; overflow-wrap: anywhere; }
.dt-tile-sub { font-size: 11px; line-height: 1.3; color: var(--muted); }
.dt-tile.tone-good .dt-tile-lbl { color: var(--good); }
.dt-tile.tone-warn .dt-tile-lbl { color: var(--warn); }
.dt-tile.tone-off  .dt-tile-val { color: var(--ink-2); font-size: 14px; }
.dt-spark-empty { height: 104px; display: flex; align-items: center; justify-content: center;
  font-size: 12.5px; color: var(--muted); text-align: center; padding: 0 12px; }

/* ---- Prominent partial-data notice above the Statistik charts (#16) ---- */
.hchart-notice {
  grid-column: 1 / -1;
  padding: 11px 16px;
  background: var(--info-soft);
  border: 1px solid var(--hairline-2);
  border-left: 3px solid var(--info);
  border-radius: 10px;
  font-size: 13px; line-height: 1.5; color: var(--ink-2);
}
.hchart-notice strong { color: var(--ink); }

/* ---- Mobile refinements (#10) ---- */
@media (max-width: 720px) {
  /* These two used to keep the footer and the Trends row clear of the floating
     "Karte" button. That button is gone [2026-09-20], replaced by the segmented
     switch in the top bar, so the 112 px and 130 px right insets were reserving
     space for something that no longer exists. Both now run the full width. */
  .rail-foot-link { padding: 9px 16px; font-size: 12px; }

  /* P3-6 [2026-09-16]: measured at 390x844 the footer occupied 741–844, about
     12 % of the viewport, permanently, for legal links and a disclaimer, while
     the result list got 572 px. Keep the copyright line and the drop-up trigger
     (the crawlable directory must stay in the DOM and stays reachable); move the
     legal/sibling links and the disclaimer into that panel's territory by
     hiding them from the bar itself. No SEO cost: every link is still rendered
     in #standorte-verzeichnis below. */
  #site-footer .footer-row a,
  #site-footer .footer-row .sep,
  #site-footer .footer-row .muted-note { display: none; }
  #site-footer .footer-row a.f-keep,
  #site-footer .footer-row .sep.f-keep { display: inline; }
  #site-footer { padding-top: 3px; padding-bottom: 4px; }
  /* The panel opened from the bar carries what the bar no longer shows. */
  #standorte-verzeichnis > nav .f-legal { display: block; margin-top: 8px; }
  /* Its label says so: "Standorte & Rechtliches" instead of the directory name. [R3-22] */
  #standorte-verzeichnis > summary .sum-long { display: none; }
  #standorte-verzeichnis > summary .sum-short { display: inline; white-space: nowrap; }
}

/* ---- Themed scrollbars (rail list, detail, Statistik, mobile find sheet) ---- */
.list-wrap, #rail-detail, #statview, #find { scrollbar-width: thin; scrollbar-color: var(--dim) transparent; }
.list-wrap::-webkit-scrollbar, #rail-detail::-webkit-scrollbar, #statview::-webkit-scrollbar, #find::-webkit-scrollbar { width: 8px; }
.list-wrap::-webkit-scrollbar-thumb, #rail-detail::-webkit-scrollbar-thumb, #statview::-webkit-scrollbar-thumb, #find::-webkit-scrollbar-thumb {
  background: var(--dim); border-radius: 99px; border: 2px solid transparent; background-clip: content-box;
}

/* ==========================================================================
   Footer-Standortverzeichnis als Drop-up-Panel [2026-07-07]
   Der Footer ist eine fixierte Ein-Zeilen-Leiste (nowrap/hidden). Das SEO-
   <details> expandierte IN der Leiste und wuchs über die Karte. Jetzt: die
   Summary bleibt ein Inline-Element in der Zeile, das geöffnete <nav> legt
   sich als eigenes Panel ÜBER die Leiste (absolute zum fixed Footer).
   Farben über Vars → folgt automatisch dem Dark-Theme.
   ========================================================================== */
#site-footer:has(#standorte-verzeichnis[open]) { overflow: visible; }
#standorte-verzeichnis { display: inline; }
#standorte-verzeichnis > summary {
  display: inline; cursor: pointer; list-style: none;
  color: var(--brand-2);
}
@media (min-width: 721px) { #standorte-verzeichnis > summary .sum-short { display: none; } }   /* scoped: an unscoped rule here would beat the earlier ≤720px swap */
#standorte-verzeichnis > summary::-webkit-details-marker { display: none; }
#standorte-verzeichnis > summary::before { content: " · "; color: var(--dim); }
#standorte-verzeichnis > summary::after  { content: " ▴"; font-size: 8px; }
#standorte-verzeichnis[open] > summary::after { content: " ▾"; font-size: 8px; }
/* Panel-only legal block: above 720px the bar itself shows these links, so the
   panel copy is hidden. Scoped by min-width rather than left unconditional,
   because the matching `display:block` sits in an EARLIER @media block and an
   unqualified rule here would override it at every width. [P3-6] */
@media (min-width: 721px) {
  #standorte-verzeichnis > nav .f-legal { display: none; }
}
#standorte-verzeichnis > nav {
  position: absolute; left: 0; right: 0; bottom: 100%;
  background: var(--surface);
  border-top: 1px solid var(--hairline);
  box-shadow: 0 -10px 26px rgba(20, 40, 64, 0.14);
  padding: 12px 18px; line-height: 2.1;
  white-space: normal; text-align: center;
  max-height: 45vh; overflow-y: auto;
}

/* Nachschärfung [2026-07-07]: Durch die zusätzlichen Footer-Elemente ist die
   Ein-Zeilen-Leiste ~1150 px breit – unterhalb davon würde die nowrap/ellipsis-
   Regel (>980 px) die Verzeichnis-Summary unsichtbar abschneiden. Früher
   umbrechen statt clippen. */
@media (max-width: 1220px) {
  #site-footer { white-space: normal; }
}

/* ======================================================================
   MOBILE REFINEMENT  [2026-09-20]
   ----------------------------------------------------------------------
   Placed at the very END of this file on purpose: park's earlier mobile
   rules live inside @media blocks, and an equal-specificity rule in a
   LATER block wins over them regardless of the media query's narrowness
   (the source-order trap noted in the 2026-09-16 review). Everything that
   has to override an earlier @media rule therefore belongs here.

   Measured at 390 x 844 before the change: 57 of 99 interactive elements
   were under the 44 px Apple/WCAG 2.5.5 touch minimum, the FAB covered the
   right 97 px of the full-width "Trends" button, and nothing stopped the
   result list scrolling underneath the open filter sheet.
   ====================================================================== */

/* ---- List-only mode (Leaflet did not load) --------------------------- */
#map-unavailable {
  margin: 10px 12px; padding: 12px 14px;
  border: 1px solid var(--hairline); border-left: 3px solid var(--brand-cta);
  border-radius: 10px; background: var(--surface-2);
  font-family: var(--sans); font-size: 13px; line-height: 1.55; color: var(--ink);
}
#map-unavailable strong { color: var(--ink); }
#map-unavailable #map-retry {
  display: inline-block; margin-top: 8px; min-height: 44px; padding: 0 16px;
  border: 0; border-radius: 999px; cursor: pointer;
  background: var(--brand-cta); color: #fff;
  font-family: var(--sans); font-size: 14px; font-weight: 600;
}
body.no-map .map-fab { display: none; }
/* Same reason as the FAB: with no map there is nothing to switch to, and a Map
   tab that opens an empty overlay is worse than no tab at all. [2026-09-20] */
body.no-map .view-tabs { display: none; }
body.no-map #legendBar { display: none; }
body.no-map #map { display: none; }
/* Desktop keeps its second column; label it rather than leaving grey. */
body.no-map #mapwrap::after {
  content: "Karte nicht verfügbar. Die Liste links ist vollständig.";
  position: absolute; inset: 0; display: flex;
  align-items: center; justify-content: center; text-align: center;
  padding: 0 32px; color: var(--muted);
  font-family: var(--sans); font-size: 14px;
  background: var(--surface-2);
}

@media (max-width: 720px) {

  /* ---- Touch targets: 44 px minimum on everything that is an action --- */
  /* Exempt by design: #status-pill (a live region, not a control) and the
     Leaflet attribution links (legally required, never a primary action). */
  .filter-summary { min-height: 48px; padding: 12px 16px; }
  .filter-summary .fs-txt { font-size: 13px; }
  .fsh-close {
    color: #0b6a36;   /* --brand-2 is 4.30:1 on the sheet's glass (axe, 2026-10-09) */
    min-height: 44px; min-width: 72px; padding: 0 12px;
    display: inline-flex; align-items: center; justify-content: center;
    font-size: 14px;
  }
  .fsh-row { padding: 2px 2px 6px; }

  /* 16 px on every text input and select, not for weight but because iOS
     Safari zooms the whole page in when a focused field measures less. That
     zoom then has to be pinched back out by hand, and it fires on the very
     first tap most visitors make (the destination search). */
  #destSearch, #sortMode, #planDay, #planTime { font-size: 16px; }
  .rd-search { min-height: 44px; }
  #destSearch { min-height: 44px; }
  .rd-geo { width: 44px; height: 44px; flex: 0 0 44px; }
  #sortMode { min-height: 44px; }
  #planDay, #planTime { min-height: 44px; }

  .pd-sum { min-height: 48px; }
  .pl-step { width: 44px; height: 44px; font-size: 20px; }
  /* 5 presets between two 44 px steppers in a 358 px sheet: the 7 px stepper
     gap and the 5 px grid gap together left 42.4 px per preset. Tighten both
     and the row clears 44 with room over. */
  .pl-stepper { gap: 5px; }
  .pl-presets { gap: 3px; }
  .pl-preset { min-height: 44px; padding: 0 4px; }
  .pl-when button { min-height: 44px; padding: 0 14px; }
  .rd-chip { min-height: 44px; padding: 0 14px; }
  /* nowrap is belt and braces now that the row has its full width back; it was
     load-bearing while the FAB lane squeezed the label onto two lines. */
  .rail-foot-link { min-height: 48px; white-space: nowrap; }
  .gp-more { min-height: 44px; }
  .dt-back { min-height: 44px; }
  .dt-btn { min-height: 44px; display: inline-flex; align-items: center; justify-content: center; }

  /* The ✕ stays a 12 px glyph; only its hit area grows. */
  #reasonsClose { width: 40px; height: 40px; }

  /* Leaflet ships 30 x 30 zoom buttons, which is a miss on a phone even
     though pinch-zoom also works. */
  .leaflet-control-zoom a,
  .leaflet-touch .leaflet-control-zoom a {
    width: 44px; height: 44px; line-height: 44px; font-size: 20px;
  }

  /* The footer directory is a crawlable list of 21 links at 13 px line boxes.
     line-height carries the rhythm; the padding carries the tap. */
  #standorte-verzeichnis > nav a { display: inline-block; padding: 12px 5px; line-height: 20px; }

  /* ---- Mobile chrome: tabs in, KPIs and status pill out --------------- */
  /* The two city KPIs and the status pill are context, not the task. At 390 px
     they cost 238 px of a 390 px bar; at 360 px the pill was already squeezed
     to 22 px, i.e. showing nothing. Dropping them is what makes room for the
     view switch. The elements stay in the DOM — paintStatusPill() and
     setNumAnimated() keep writing to them harmlessly, and desktop is
     unaffected. Problems still reach the user: the reasons card, the feed
     error banner and the loading veil are all independent of the pill. */
  .rd-kpis, #topbar #status-pill { display: none; }
  .rd-topbar-spacer { flex: 1 1 auto; }

  /* Segmented view switch. Replaces the FAB, which could only toggle and never
     say which view you were in. */
  .view-tabs {
    display: inline-flex; flex: 0 0 auto; gap: 2px;
    background: var(--surface-2); border: 1px solid var(--hairline);
    border-radius: 999px; padding: 3px;
  }
  .view-tabs .vt {
    min-height: 38px; min-width: 76px; padding: 0 16px;
    border: 0; border-radius: 999px; background: transparent;
    color: var(--muted); cursor: pointer;
    font-family: var(--sans); font-size: 13.5px; font-weight: 600;
    transition: background .16s ease, color .16s ease;
  }
  /* 38 px keeps the control inside a 56 px bar; the tap area is 44 px. */
  .view-tabs .vt::after {
    content: ""; position: absolute; left: 0; right: 0; top: 50%;
    height: 44px; transform: translateY(-50%);
  }
  .view-tabs .vt { position: relative; }
  .view-tabs .vt.is-active { background: var(--brand-cta); color: #fff; }
  .view-tabs .vt:focus-visible { outline: 2px solid var(--brand-2); outline-offset: 2px; }

  .map-fab { display: none; }
  /* The directory summary keeps its natural 28 px. Deliberate exception: it is
     a crawlable-nav disclosure, not a task control, and forcing 44 px on a
     permanently visible footer row costs 29 px of result list on every screen.
     The 21 links INSIDE the panel do get 44 px, since the panel only exists
     while it is open. */

  /* The FAB lane that used to sit here is gone with the FAB itself
     [2026-09-20]; see the .rail-foot-link rule above for the rest of it. */

  /* ---- Safe area: keep the floating chrome off the home indicator ----- */
  /* Only the safe-area inset is layered on here; the base offsets live with
     their own rules above, so each property has exactly one source of truth. */
  .legend-bar { bottom: calc(16px + env(safe-area-inset-bottom, 0px)); }
  #rail-detail { padding-bottom: 56px; }
  /* The Parkleitsystem deep link was an 18 px line in a 44 px world. */
  .dt-link { display: inline-flex; align-items: center; min-height: 44px; }

  /* ---- Filter sheet: lock the page behind it -------------------------- */
  body.sheet-open { overflow: hidden; }
  body.sheet-open #rail { overflow: hidden; }
  #find { overscroll-behavior: contain; }

  /* ---- Reasons card: two notes, and never taller than a third --------- */
  #reasonsCard { max-height: 30vh; }
}

@media (max-width: 380px) {
  /* 360 px: the type chips are the one row that cannot hold 44 px tall AND
     three labels side by side without wrapping. Keep the height, shrink the
     inside. */
  .rd-chip { padding: 0 10px; font-size: 12px; }
  .view-tabs .vt { min-width: 64px; padding: 0 10px; font-size: 13px; }
  .lang-toggle { font-size: 10px; padding: 0 7px; margin-right: 6px; }
}

/* ======================================================================
   i18n  [2026-09-20]
   ====================================================================== */

/* DE/EN switch. Lives in the TOP BAR since 2026-09-20; it was in the footer,
   which on a phone is below the fold of a scrolling list, so it was findable
   only by someone who already knew to look. Auto-detection still covers most
   visitors, but an escape hatch has to be visible to be one.
   Quiet by design: a small neutral pill, so it does not compete with the view
   tabs beside it. 28 px tall to sit comfortably in a 56 px bar. */
.lang-toggle {
  flex: 0 0 auto;
  border: 1px solid var(--hairline); background: var(--surface);
  color: var(--muted); font-family: var(--sans); font-size: 11px;
  font-weight: 700; letter-spacing: .04em;
  border-radius: 999px; padding: 0 9px; cursor: pointer; line-height: 26px;
  position: relative;
  transition: color .15s ease, border-color .15s ease, background .15s ease;
}
.lang-toggle:hover, .lang-toggle:focus-visible {
  border-color: var(--brand-line); color: var(--brand-2); background: var(--hl);
}
.lang-toggle:focus-visible { outline: 2px solid var(--brand-2); outline-offset: 2px; }

/* The crawlable directory links into /parken/, /statistik/ and the 21
   /parkhaus/ pages, all of which are server-rendered German: in English that is
   a trail out of a translated app into untranslated prose. So those links go —
   but the PANEL stays, because at ≤720px it is the only place the Impressum and
   Datenschutz links live (the bar hides its own to save 50 px of an 844 px
   screen). Losing the panel would take the legally required links with it.
   Hidden with CSS, never removed: the server always ships the German document,
   so crawlers still see every link. */
body.lang-en #standorte-verzeichnis > nav > a { display: none; }
/* The " · " separators between the hidden links are bare text nodes; hide them
   with the links and give the legal block its size back. [2026-10-05] */
body.lang-en #standorte-verzeichnis > nav { font-size: 0; }
body.lang-en #standorte-verzeichnis > nav .f-legal { font-size: 10.5px; margin-top: 0; }
body.lang-en #mapwrap::after { content: "Map unavailable – the list on the left is complete."; }

@media (min-width: 721px) {
  /* Desktop shows every link in the bar itself, so the panel has nothing left
     to carry once the German page links are gone. */
  body.lang-en #standorte-verzeichnis { display: none; }
}

@media (max-width: 720px) {
  /* The footer bar is ~38 px tall, so a 44 px button would grow it on every
     screen. Keep the pill small and extend only the HIT area, which costs no
     layout height. */
  /* The footer bar is two 10 px lines in ~38 px. The toggle must fit INSIDE
     The top bar is 56 px, so the pill keeps a 28 px visual height and only the
     HIT area grows to 44 px, which costs no layout. */
  .lang-toggle { margin-right: 8px; }
  .lang-toggle::after {
    content: ""; position: absolute; left: 50%; top: 50%;
    width: 44px; height: 44px; transform: translate(-50%, -50%);
  }

  /* The bar keeps hiding its own links in English exactly as in German: the
     drop-up panel above carries them, at 44 px each. Moving them into the bar
     instead cost 107 px of result list, which is not a trade a legal link is
     worth when the panel already solves it. */
}


/* ======================================================================
   View tabs are a mobile-only control [2026-09-20]
   ----------------------------------------------------------------------
   Above 720px the rail and the map are side by side, so there is nothing to
   switch between and the segmented control would be a button that does
   nothing visible.
   ====================================================================== */
@media (min-width: 721px) {
  .view-tabs { display: none; }
}

/* ======================================================================
   Park-Puls: pill score, "Warum?" button + panel, phone strip (2026-10-12)
   Ported from BusPuls (bus/web/css/style.css, "Bus-Puls" block), adapted to
   the ParkPuls tokens. Text on the glass panel uses --ink-2, never --muted.
   ====================================================================== */
:root { --pulse-yellow: #eab308; --pulse-orange: #ea580c; --leicht-text: #854d0e; }
.pill-wrap { display: inline-flex; align-items: center; gap: 6px; min-width: 0; }
#topbar #status-pill .score-mini { font-family: var(--mono); font-weight: 700; font-variant-numeric: tabular-nums; margin-left: 2px; }
#topbar #status-pill .score-mini:empty { display: none; }
.why-btn {
  display: inline-flex; align-items: center; gap: 4px; flex: none;
  border: 1px solid var(--hairline); border-radius: 999px;
  background: var(--surface); color: var(--ink-2);
  padding: 5px 9px 5px 10px; min-height: 30px;
  font: inherit; font-size: 12px; font-weight: 600; cursor: pointer;
}
.why-btn[hidden] { display: none; }
.why-btn:hover { background: var(--hl-hover); }
.why-btn:focus-visible { outline: 2px solid var(--brand-2); outline-offset: 2px; }
.why-btn .why-chev { width: 9px; height: 6px; transition: transform .16s ease; }
.why-btn[aria-expanded="true"] .why-chev { transform: rotate(180deg); }
.why-btn[aria-expanded="true"] { background: var(--hl-hover); }
@media (prefers-reduced-motion: reduce) { .why-btn .why-chev { transition: none; } }

.pulse-why {
  position: fixed; top: 64px; right: 14px; z-index: 1500;
  width: min(calc(100vw - 28px), 384px);
  max-height: calc(100vh - 84px); max-height: calc(100dvh - 84px); overflow-y: auto;
  background: var(--glass-panel); border: 1px solid var(--hairline);
  backdrop-filter: var(--glass-fx-panel); -webkit-backdrop-filter: var(--glass-fx-panel);
  border-radius: var(--radius); box-shadow: var(--glass-edge-hi), var(--shadow-lg);
  padding: 12px 14px 12px; font-family: var(--sans); color: var(--ink); text-align: left;
}
.pulse-why[hidden] { display: none; }
.pulse-why:focus { outline: none; }
.pw-close {
  position: absolute; top: 8px; right: 8px; width: 32px; height: 32px;
  display: grid; place-items: center; border: 0; border-radius: 999px;
  background: transparent; color: var(--ink-2); cursor: pointer;
}
.pw-close svg { width: 14px; height: 14px; }
.pw-close:hover { background: var(--hl-hover); }
.pw-close:focus-visible { outline: 2px solid var(--brand-2); outline-offset: 1px; }
.pw-head { display: flex; align-items: baseline; gap: 8px; flex-wrap: wrap; padding-right: 34px; }
.pw-score { font-family: var(--mono); font-weight: 700; font-size: 19px; color: var(--ink); }
.pw-meta { font-size: 11.5px; color: var(--ink-2); }
.pw-band { flex-basis: 100%; font-size: 13px; font-weight: 600; margin-top: -2px; }
.pw-band[data-tone="good"]   { color: var(--good); }
.pw-band[data-tone="leicht"] { color: var(--leicht-text); }
.pw-band[data-tone="orange"] { color: var(--bad); }
.pw-band[data-tone="bad"]    { color: var(--crit); }
.pw-scale { display: flex; gap: 3px; margin: 9px 0 8px; }
.pw-seg {
  position: relative; flex: 1; text-align: center; border-radius: 5px;
  font-family: var(--mono); font-size: 10px; line-height: 18px; color: var(--ink-2); background: var(--hairline-2);
}
.pw-seg[data-tone="good"]   { background: var(--good-soft);   color: var(--good); }
.pw-seg[data-tone="leicht"] { background: var(--leicht-soft); color: var(--leicht-text); }
.pw-seg[data-tone="orange"] { background: var(--bad-soft);    color: var(--bad); }
.pw-seg[data-tone="bad"]    { background: var(--crit-soft);   color: var(--crit); }
.pw-seg[data-current="1"] { font-weight: 700; color: #fff; }
.pw-seg[data-current="1"][data-tone="good"]   { background: var(--good); }
.pw-seg[data-current="1"][data-tone="leicht"] { background: var(--pulse-yellow); color: #1c1917; }
.pw-seg[data-current="1"][data-tone="orange"] { background: var(--pulse-orange); }
.pw-seg[data-current="1"][data-tone="bad"]    { background: var(--crit); }
.pw-tick {
  position: absolute; bottom: -5px; width: 0; height: 0; transform: translateX(-50%);
  border-left: 5px solid transparent; border-right: 5px solid transparent; border-bottom: 5px solid var(--ink);
}
.pw-title {
  font-size: 11px; text-transform: uppercase; letter-spacing: .06em; color: var(--ink-2); font-weight: 600;
  margin: 12px 0 2px; display: flex; justify-content: space-between; gap: 8px;
}
.pw-share { text-transform: none; letter-spacing: 0; font-weight: 500; }
.pw-note { font-size: 11.5px; color: var(--ink-2); margin: 8px 0 0; line-height: 1.4; }
.pw-note[hidden] { display: none; }
.pw-how { border-top: 1px solid var(--hairline-2); padding-top: 8px; margin-top: 10px; }
.pw-link {
  margin-top: 8px; padding: 6px 0; border: 0; background: none; cursor: pointer;
  font: inherit; font-size: 12.5px; font-weight: 600; color: var(--brand-2); min-height: 32px;
}
.pw-link:hover { text-decoration: underline; }
.pw-link:focus-visible { outline: 2px solid var(--brand-2); outline-offset: 2px; border-radius: 4px; }
.pulse-why .rowlist { display: flex; flex-direction: column; }
.pulse-why .rowlist .row {
  display: flex; align-items: center; gap: 10px; width: 100%;
  padding: 6.5px 2px; border: 0; border-bottom: 1px solid var(--hairline-2);
  background: none; color: inherit; font: inherit; font-size: 13px; text-align: left; cursor: pointer; min-height: 44px;
}
.pulse-why .rowlist .row:last-child { border-bottom: 0; }
.pulse-why .rowlist .row:hover { background: var(--hl-hover); }
.pulse-why .rowlist .row:focus-visible { outline: 2px solid var(--brand-2); outline-offset: -2px; }
.pulse-why .rowlist .grow { flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; color: var(--ink); font-weight: 500; }
.pulse-why .rowlist .num { font-family: var(--mono); font-weight: 600; white-space: nowrap; font-variant-numeric: tabular-nums; }
.pulse-why .rowlist .num[data-tone="leicht"] { color: var(--leicht-text); }
.pulse-why .rowlist .num[data-tone="orange"] { color: var(--bad); }
.pulse-why .rowlist .num[data-tone="bad"]    { color: var(--crit); }
.pulse-why .rowlist .empty { color: var(--ink-2); padding: 8px 2px; font-size: 12.5px; }
.pulse-why .row-chev { color: var(--ink-2); font-size: 15px; line-height: 1; }

.pw-trail-svg { display: block; width: 100%; max-width: 356px; height: auto; margin-top: 4px; overflow: visible; }
.pt-band { opacity: .10; }
.pt-band.pt-good   { fill: var(--good); }
.pt-band.pt-leicht { fill: var(--pulse-yellow); }
.pt-band.pt-orange { fill: var(--pulse-orange); }
.pt-band.pt-bad    { fill: var(--crit); }
.pt-grid { stroke: var(--hairline); stroke-width: 1; stroke-dasharray: 2 3; }
.pt-ylab, .pt-xlab { fill: var(--ink-2); font-family: var(--mono); font-size: 11px; }
.pt-line { fill: none; stroke: var(--ink-2); stroke-width: 1.6; stroke-linejoin: round; stroke-linecap: round; }
.pt-dot { stroke: var(--surface); stroke-width: 1.2; }
.pt-dot.pt-good   { fill: var(--good); }
.pt-dot.pt-leicht { fill: var(--pulse-yellow); }
.pt-dot.pt-orange { fill: var(--pulse-orange); }
.pt-dot.pt-bad    { fill: var(--crit); }
.pt-dot.pt-partial { fill: var(--surface); stroke-width: 1.8; }
.pt-dot.pt-partial.pt-good   { stroke: var(--good); }
.pt-dot.pt-partial.pt-leicht { stroke: var(--pulse-yellow); }
.pt-dot.pt-partial.pt-orange { stroke: var(--pulse-orange); }
.pt-dot.pt-partial.pt-bad    { stroke: var(--crit); }

/* Phone strip: hidden on desktop, where the top-bar pill carries the Puls. */
.pulse-strip { display: none; }
@media (max-width: 720px) {
  .pill-wrap { display: none; }
  .pulse-strip {
    display: flex; align-items: center; gap: 9px; width: calc(100% - 24px);
    margin: 10px 12px 0; padding: 9px 12px; min-height: 44px;
    border: 1px solid transparent; border-radius: var(--radius);
    background: var(--hl); color: var(--ink-2); cursor: pointer;
    font: inherit; font-size: 13.5px; text-align: left;
  }
  .pulse-strip:focus-visible { outline: 2px solid var(--brand-2); outline-offset: 2px; }
  .pulse-strip .ps-dot { width: 10px; height: 10px; border-radius: 50%; background: var(--muted); flex: none; }
  .pulse-strip .ps-txt { flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; font-weight: 600; }
  .pulse-strip .ps-txt b { font-family: var(--mono); font-weight: 700; font-variant-numeric: tabular-nums; }
  .pulse-strip .ps-sep { margin: 0 6px; opacity: .5; }
  .pulse-strip .ps-why { flex: none; font-size: 12.5px; font-weight: 600; }
  .pulse-strip[data-tone="good"]   { background: var(--good-soft);   color: var(--good); }
  .pulse-strip[data-tone="good"]   .ps-dot { background: var(--good); }
  .pulse-strip[data-tone="leicht"] { background: var(--leicht-soft); color: var(--leicht-text); }
  .pulse-strip[data-tone="leicht"] .ps-dot { background: var(--pulse-yellow); }
  .pulse-strip[data-tone="orange"] { background: var(--bad-soft);    color: #9a3412; }
  .pulse-strip[data-tone="orange"] .ps-dot { background: var(--pulse-orange); }
  .pulse-strip[data-tone="bad"]    { background: var(--crit-soft);   color: var(--crit); }
  .pulse-strip[data-tone="bad"]    .ps-dot { background: var(--crit); }
  .pulse-strip[data-tone="warn"]   { background: var(--warn-soft);   color: #92400e; }
  .pulse-strip[data-tone="warn"]   .ps-dot { background: var(--warn); }
  .pulse-why { top: 60px; left: 10px; right: 10px; width: auto; max-height: calc(100dvh - 76px); }
}

/* ======================================================================
   Puls card in the 24 Stunden / 30 Tage statistics (2026-10-12)
   Same block in BusPuls, ZugPuls and ParkPuls (tools/pulse-stats/puls-card.css);
   the card itself comes from js/puls-stats.js.
   2026-10-13: one of the pane's charts, not a banner. It spans two columns of
   a three-column pane grid (Puls + four charts fill two rows), has the chart
   height of its neighbours, and its explanation sits behind the "i".
   ====================================================================== */
.puls-holder { display: contents; }
.puls-card { grid-column: span 2; min-width: 0; order: -1; }
/* Pane-wide notes never take a chart's grid cell: notices and the grey-line
   caption run as one full row on top, "Daten ab …" as one row at the end. */
.hist-charts > .hchart-notice, .hist-charts > .hchart-compare-note { grid-column: 1 / -1; order: -2; }
.hist-charts > .hchart-since { grid-column: 1 / -1; order: 99; }
.puls-card h3 .ptip { margin-left: 8px; }
.puls-card h3 .hc-key { margin-left: auto; }
.puls-legend { display: flex; flex-wrap: wrap; gap: 3px 10px; margin: 4px 0 0; font-size: 10.5px; color: var(--ink-2, #3a4a5c); }
.puls-legend span { display: inline-flex; align-items: center; gap: 4px; }
.puls-legend i { display: inline-block; width: 9px; height: 9px; border-radius: 2px; }
@media (max-width: 540px) { .puls-card { grid-column: auto; } }

/* ======================================================================
   Info tips in the Puls panels (2026-10-12). Explanations live behind a
   small "i": hover or keyboard focus on desktop, tap on phones (js toggles
   .is-open). Same block on all four sites (tools/pulse-stats/ptip.css).
   ====================================================================== */
.ptip { position: relative; display: inline-flex; vertical-align: middle; margin-left: auto; }
.ptip-btn {
  position: relative; width: 22px; height: 22px; padding: 0; border-radius: 50%;
  display: inline-grid; place-items: center; cursor: help;
  border: 1px solid var(--hairline, rgba(20,40,64,.14)); background: transparent;
  color: var(--ink-2, #3a4a5c); font: 700 12px/1 Georgia, "Times New Roman", serif; font-style: italic;
}
.ptip-btn::after { content: ""; position: absolute; inset: -11px; }   /* 44 px touch target */
.ptip-btn:hover, .ptip.is-open .ptip-btn { background: var(--hl-hover, rgba(20,40,64,.07)); }
.ptip-btn:focus-visible { outline: 2px solid var(--brand-2, var(--brand-deep, #0078a1)); outline-offset: 2px; }
.ptip-pop {
  display: none; position: absolute; top: calc(100% + 8px); right: -6px; z-index: 30;
  width: min(290px, calc(100vw - 48px)); padding: 10px 12px; border-radius: 10px;
  background: var(--surface, #fff); border: 1px solid var(--hairline, rgba(20,40,64,.14));
  box-shadow: 0 10px 28px rgba(0,0,0,.18);
  font: 400 12px/1.45 var(--sans, system-ui, sans-serif); color: var(--ink-2, #3a4a5c);
  text-align: left; text-transform: none; letter-spacing: 0; white-space: normal;
}
.ptip-pop::before { content: ""; position: absolute; left: 0; right: 0; top: -10px; height: 10px; }   /* hover bridge */
.ptip:hover .ptip-pop, .ptip:focus-within .ptip-pop, .ptip.is-open .ptip-pop { display: block; }
.ptip-pop p { margin: 0 0 6px; }
.ptip-pop p:last-child { margin-bottom: 0; }
.ptip-p { display: block; margin: 0 0 6px; }
.ptip-pop a { display: inline-block; margin-top: 2px; }
.ptip-pop a { color: var(--brand-2, var(--brand, #0078a1)); font-weight: 600; text-decoration: none; }
.ptip-pop a:hover { text-decoration: underline; }

/* ======================================================================
   SUITE CHROME  [2026-10-12]
   ----------------------------------------------------------------------
   Top bar, status pill and footer follow ZugPuls, the reference build of
   the suite. Same anatomy, same type scale, same order in all three apps;
   each app keeps its own theme (Zug night, Bus and Park daylight).
   Spec and measurements: SUITE-CHROME.md at the repo root.
   One block at the end of the file: it wins the cascade and can be
   reverted in one cut. Phone layout decisions of 2026-09-20 (tabs instead
   of KPIs and pill, Puls strip in the list) are unchanged.
   ====================================================================== */
:root { --wordmark: 'Barlow Condensed', 'IBM Plex Sans Condensed', system-ui, sans-serif; }

/* Wordmark at every width: BARLOW 700 uppercase, ink + coral */
.rd-word {
  font-family: var(--wordmark); font-weight: 700; font-size: 27px;
  letter-spacing: 1px; text-transform: uppercase; line-height: 1.5;
  color: var(--ink); white-space: nowrap;
}
.rd-sfx { color: var(--accent); }
#site-footer a { color: var(--brand-2); }
#site-footer .heart { color: var(--accent); }

@media (min-width: 721px) {
  /* ---- Bar: 84 px like ZugPuls and BusPuls (was 56) -------------------- */
  #app { grid-template-rows: 84px minmax(0, 1fr) auto; }
  #topbar { height: 84px; padding: 0 20px; gap: 20px; }
  .rd-brand { gap: 12px; }
  .rd-mark img { height: 34px; }

  /* ---- KPI ribbon: label on top, figure below, hairline dividers ------ */
  .rd-kpis { align-self: center; }
  .rd-kpi,
  .rd-kpi + .rd-kpi {
    min-height: 62px; padding: 6px 16px;
    display: flex; flex-direction: column; justify-content: space-between; gap: 2px;
    border-left: 1px solid var(--hairline);
  }
  .rd-kpi:last-child { border-right: 1px solid var(--hairline); }
  .rk-k {
    font-size: 10px; font-weight: 400; line-height: 1.3;
    letter-spacing: 1.1px; text-transform: uppercase; color: var(--muted);
  }
  .rk-row { display: flex; align-items: flex-end; gap: 4px; }
  /* Figures keep the ParkPuls voice (Space Grotesk, blue hero figure);
     only size and placement follow the ZugPuls ribbon. */
  .rk-v { font-size: 20px; line-height: 1.1; }
  .rd-kpi-hero .rk-v { font-size: 21px; }
  .rd-kpi .kpi-trend {
    position: static; transform: none; width: auto; height: auto;
    margin-bottom: 4px; background: none; border-radius: 0;
  }

  /* 36 px round control, the suite's topbar control size */
  .lang-toggle { height: 36px; min-width: 36px; line-height: 34px; padding: 0 10px; }

  /* ---- Status pill: neutral surface, dot carries the colour ------------ */
  #topbar #status-pill,
  #topbar #status-pill[class] {
    display: flex; align-items: center; gap: 8px;
    background: var(--surface); border: 1px solid var(--hairline);
    color: var(--ink); box-shadow: var(--glass-edge), var(--shadow-sm);
    padding: 8px 15px; font-size: 13px; font-weight: 400;
    border-radius: 999px; white-space: nowrap; max-width: none;
  }
  #topbar #status-pill .dot { width: 9px; height: 9px; }
  #topbar #status-pill.tone-good   .dot { background: var(--good);         box-shadow: 0 0 7px rgba(21,128,61,0.55); }
  #topbar #status-pill.tone-leicht .dot { background: var(--pulse-yellow); box-shadow: 0 0 7px rgba(234,179,8,0.6); }
  #topbar #status-pill.tone-orange .dot { background: var(--pulse-orange); box-shadow: 0 0 7px rgba(234,88,12,0.55); }
  #topbar #status-pill.tone-warn   .dot { background: var(--warn);         box-shadow: 0 0 7px rgba(180,83,9,0.5); }
  #topbar #status-pill.tone-bad    .dot { background: var(--crit);         box-shadow: 0 0 7px rgba(185,28,28,0.5); }
  #topbar #status-pill.tone-info   .dot { background: var(--info);         box-shadow: none; }
  #topbar #status-pill.tone-off    .dot { background: var(--dim);          box-shadow: none; }
  #topbar #status-pill[class*="tone-"] .dot { animation: none; }
  #topbar #status-pill .txt { font-weight: 600; font-size: 13px; color: var(--ink); }
  #topbar #status-pill .score-mini {
    display: inline; font-family: var(--mono); font-weight: 600; font-size: 13px;
    color: var(--ink); min-width: 2ch; text-align: right;
  }
  #topbar #status-pill .score-mini:empty { display: none; }
  #topbar #status-pill .last-update-wrap {
    display: inline-flex; align-items: center; max-width: none; opacity: 1; margin: 0;
    color: var(--muted); font-size: 12px; font-weight: 400; font-variant-numeric: tabular-nums;
  }
  #topbar #status-pill .last-update-wrap .sep { margin: 0 6px 0 0; padding: 0; opacity: 1; color: var(--dim); }
  #topbar .why-btn {
    display: inline-flex; align-items: center; gap: 4px;
    border: 1px solid var(--hairline); border-radius: 999px;
    background: transparent; color: var(--muted); box-shadow: none;
    padding: 1px 6px 1px 7px; margin-left: 2px; height: auto; min-height: 0; cursor: pointer;
    font: inherit; font-size: 11px; line-height: 1.6; font-weight: 400;
  }
  #topbar .why-btn[hidden] { display: none; }
  #topbar .why-btn:hover,
  #topbar .why-btn[aria-expanded="true"] { background: var(--hl-hover); color: var(--ink); }
  /* The panel opens 8 px below the taller bar */
  .pulse-why { top: 92px; max-height: calc(100vh - 112px); max-height: calc(100dvh - 112px); }

  /* ---- Footer: ZugPuls one-line bar ------------------------------------- */
  #site-footer {
    display: flex; align-items: center; height: 28px;
    padding: 0 14px; text-align: left;
    font-size: 11.5px; line-height: 1.5; color: var(--muted);
    white-space: nowrap; overflow: visible;
  }
  #site-footer .footer-row {
    display: flex; align-items: center; gap: 7px;
    flex-wrap: nowrap; overflow: hidden; white-space: nowrap; min-width: 0;
  }
  #site-footer .footer-row .sep { display: inline; padding: 0; color: var(--dim); }
  #site-footer .muted-note { display: block; margin: 0; color: var(--muted); overflow: hidden; text-overflow: ellipsis; }
  #site-footer .muted-note .mn-short { display: none; }
  #standorte-verzeichnis { display: block; margin-left: 10px; flex-shrink: 0; }
  #standorte-verzeichnis > summary { display: inline; white-space: nowrap; }
  #standorte-verzeichnis > summary::before { content: none; }
}
@media (min-width: 721px) and (max-width: 1399px) {
  #site-footer .muted-note .mn-long  { display: none; }
  #site-footer .muted-note .mn-short { display: inline; }
}
@media (min-width: 721px) and (max-width: 1200px) {
  #site-footer .f-item,
  #site-footer .f-item + .sep { display: none; }
}

@media (max-width: 720px) {
  .rd-word { font-size: 20px; }
  .rd-mark img { height: 26px; }
  /* Phone bar keeps only the way home and the directory, as on ZugPuls the
     decoration goes first: "StadtPuls · Standorte & Rechtliches". */
  #site-footer .f-item, #site-footer .f-item + .sep { display: none; }
  #site-footer { font-size: 12px; color: var(--muted); }
  #site-footer .muted-note .mn-short { display: none; }
}
@media (max-width: 400px) { .rd-word { font-size: 18px; } }
@media (max-width: 360px) { .rd-word { font-size: 16px; letter-spacing: .6px; } .rd-mark img { height: 24px; } }


/* ======================================================================
   LEEZENPULS  [October 2026]
   ----------------------------------------------------------------------
   Everything that is LeezenPuls' own: the flow canvas, the station nodes,
   the transport bar, the day curve, the list's direction bar. Last in the
   file so it wins the cascade over the shared chrome where it has to.
   Flow colours validated with the dataviz palette check (green/blue/pink,
   all pairs: CVD ΔE >= 9.8, normal-vision ΔE >= 28, contrast >= 3:1).
   ====================================================================== */
:root {
  --flow-in:   #15803d;   /* stadteinwärts */
  --flow-out:  #2563eb;   /* stadtauswärts */
  --flow-ring: #db2777;   /* along the Promenade ring */
  --flow-ring-soft: rgba(219, 39, 119, 0.45);
}

/* ---- KPI ribbon: unit after the 15-minute figure ---- */
.rk-u { font-size: 11px; color: var(--muted); margin-left: 3px; align-self: flex-end; margin-bottom: 2px; white-space: nowrap; }
#kNowClock { font-variant-numeric: tabular-nums; }

/* ---- Flow canvas: above the tiles, below the station markers ---- */
#flowCanvas { position: absolute; left: 0; top: 0; pointer-events: none; }   /* lives in Leaflet's flowPane, see buildMap() */
body.no-map #flowCanvas, body.no-map #flowbar { display: none; }

/* ---- Station nodes ---- */
.cp-marker { background: transparent !important; border: 0 !important; }
.cp-node {
  position: absolute; left: 0; top: 0; transform: translate(-50%, -50%);
  width: 14px; height: 14px; border-radius: 50%;
  display: grid; place-items: center;
  background: var(--brand-2);   /* white figures need 4.5:1; --brand is 3.1:1 */
  border: 2px solid #fff; box-shadow: 0 1px 4px rgba(20,40,64,.35);
  color: #fff; cursor: pointer;
  transition: width .18s ease, height .18s ease, box-shadow .15s ease;
}
.cp-node.is-ring { background: var(--flow-ring); }
.cp-node.is-missing { background: var(--surface); border: 2px dashed var(--dim); box-shadow: none; }
.cp-node.is-sel { box-shadow: 0 0 0 3px var(--ink), 0 0 0 6px rgba(255,255,255,.85); }
.cp-node.is-hover { box-shadow: 0 0 0 4px var(--brand-line), 0 4px 12px rgba(20,40,64,.4); }
.cp-v { font-family: var(--mono); font-weight: 700; line-height: 1; font-variant-numeric: tabular-nums; pointer-events: none; text-shadow: 0 1px 1px rgba(0,0,0,.25); }
.leaflet-marker-icon:focus-visible .cp-node, .leaflet-marker-icon:focus .cp-node { outline: 2px solid var(--brand-2); outline-offset: 3px; }
.cp-label.leaflet-tooltip {
  background: var(--glass-panel); -webkit-backdrop-filter: var(--glass-fx-panel); backdrop-filter: var(--glass-fx-panel);
  border: 1px solid var(--hairline); border-radius: 8px; box-shadow: var(--shadow-md), var(--glass-edge);
  font-family: var(--sans); font-size: 12px; color: var(--ink-2); padding: 6px 9px; line-height: 1.4;
}
.cp-label strong { color: var(--ink); font-weight: 600; }
.cp-label .cp-tip-sub { color: var(--ink-2); }
.cp-label.leaflet-tooltip-top::before { border-top-color: var(--hairline); }

/* ---- Legend strip: sits above the transport bar ---- */
#legendBar { bottom: 84px; }
.dt-curve .dt-spark-head { flex-wrap: wrap; }
.dt-curve .dt-key { flex-wrap: wrap; white-space: normal; }
.legend-bar .lb-ring { width: 12px !important; height: 12px !important; background: transparent !important; box-shadow: none !important; border: 1.5px solid var(--ink-2); }

/* ---- Transport bar ---- */
#flowbar {
  position: absolute; z-index: 650; left: 12px; right: 12px; bottom: 12px;
  display: flex; align-items: center; gap: 12px;
  padding: 9px 12px; border-radius: var(--radius);
  background: var(--glass-raised); -webkit-backdrop-filter: var(--glass-fx-raised); backdrop-filter: var(--glass-fx-raised);
  border: 1px solid var(--hairline); box-shadow: var(--shadow-md), var(--glass-edge);
}
.fb-play {
  flex: 0 0 auto; width: 44px; height: 44px; border-radius: 50%; border: 0; cursor: pointer;
  display: grid; place-items: center; background: var(--brand-cta); color: #fff;
  box-shadow: 0 4px 12px rgba(28,167,92,.35); transition: filter .15s ease, transform .12s ease;
}
.fb-play:hover { filter: brightness(.92); }
.fb-play:active { transform: scale(.95); }
.fb-play svg { width: 22px; height: 22px; }
.fb-play svg[hidden] { display: none; }
.fb-clock { flex: 0 0 auto; min-width: 3.4em; font-family: var(--mono); font-weight: 700; font-size: 22px; color: var(--ink); font-variant-numeric: tabular-nums; }
.fb-track { position: relative; flex: 1 1 auto; min-width: 120px; height: 44px; }
.fb-spark { position: absolute; left: 8px; right: 8px; top: 2px; height: 26px; width: calc(100% - 16px); pointer-events: none; }
.fb-range { position: absolute; left: 0; right: 0; top: 16px; width: 100%; margin: 0; height: 22px; background: transparent; -webkit-appearance: none; appearance: none; cursor: pointer; }
.fb-range::-webkit-slider-runnable-track { height: 4px; border-radius: 2px; background: var(--hairline); }
.fb-range::-moz-range-track { height: 4px; border-radius: 2px; background: var(--hairline); }
.fb-range::-webkit-slider-thumb { -webkit-appearance: none; width: 18px; height: 18px; margin-top: -7px; border-radius: 50%; background: var(--surface); border: 2px solid var(--brand-cta); box-shadow: 0 1px 4px rgba(20,40,64,.3); }
.fb-range::-moz-range-thumb { width: 14px; height: 14px; border-radius: 50%; background: var(--surface); border: 2px solid var(--brand-cta); box-shadow: 0 1px 4px rgba(20,40,64,.3); }
.fb-range:focus-visible { outline: none; }
.fb-range:focus-visible::-webkit-slider-thumb { box-shadow: 0 0 0 3px var(--brand-2); }
.fb-range:focus-visible::-moz-range-thumb { box-shadow: 0 0 0 3px var(--brand-2); }
.fb-ticks { position: absolute; left: 8px; right: 8px; bottom: -1px; display: flex; justify-content: space-between; font-size: 10px; color: var(--ink-2); pointer-events: none; font-variant-numeric: tabular-nums; }
.fb-speed { flex: 0 0 auto; display: inline-flex; gap: 2px; padding: 3px; border-radius: 9px; background: var(--glass-inset); border: 1px solid var(--hairline); }
.fb-speed button { border: 0; background: transparent; color: var(--ink-2); font: 600 12px/1 var(--sans); padding: 7px 9px; border-radius: 7px; cursor: pointer; min-width: 34px; }
.fb-speed button.is-active { background: var(--surface); color: var(--brand-2); box-shadow: 0 1px 2px rgba(0,0,0,.08), inset 0 0 0 1px var(--brand-line); }
.fb-day { flex: 0 0 auto; display: inline-flex; align-items: center; gap: 2px; }
.fb-step { width: 34px; height: 38px; border: 1px solid var(--hairline); background: var(--surface); color: var(--ink); border-radius: 9px; font: 600 18px/1 var(--sans); cursor: pointer; }
.fb-step:hover:not(:disabled) { background: var(--brand-soft); color: var(--brand-2); border-color: var(--brand-line); }
.fb-step:disabled { opacity: .4; cursor: default; }
.fb-date { position: relative; display: inline-flex; align-items: center; justify-content: center; height: 38px; min-width: 92px; padding: 0 10px; border: 1px solid var(--hairline); border-radius: 9px; background: var(--surface); cursor: pointer; }
.fb-date-txt { font-family: var(--mono); font-weight: 600; font-size: 14px; color: var(--ink); font-variant-numeric: tabular-nums; white-space: nowrap; pointer-events: none; }
.fb-date input { position: absolute; inset: 0; opacity: 0; width: 100%; height: 100%; cursor: pointer; border: 0; padding: 0; }
.fb-date input::-webkit-calendar-picker-indicator { position: absolute; inset: 0; width: 100%; height: 100%; margin: 0; cursor: pointer; }
.fb-date:focus-within { box-shadow: 0 0 0 2px var(--brand-2); }
body.is-loading-day .fb-date-txt { opacity: .45; }
body.is-loading-day #cp-list { opacity: .55; transition: opacity .2s; }

/* ---- List: direction bar + chips ---- */
.gc-io { display: flex; gap: 2px; height: 5px; margin: 5px 0 4px; border-radius: 3px; overflow: hidden; background: var(--hl); }
.gc-io .seg-in { background: var(--flow-in); }
.gc-io .seg-out { background: var(--flow-out); }
.gc-io.is-ring .seg-in { background: var(--flow-ring); }
.gc-io.is-ring .seg-out { background: var(--flow-ring-soft); }
.gc-meta .b.b-dir { background: transparent; padding-left: 0; padding-right: 0; color: var(--muted); font-variant-numeric: tabular-nums; }
.gc-meta .b.b-vs { font-weight: 600; font-variant-numeric: tabular-nums; }
.gc-meta .b.b-vs[data-tone="good"]   { background: var(--good-soft); color: var(--good); }
.gc-meta .b.b-vs[data-tone="leicht"] { background: var(--leicht-soft); color: var(--leicht-text, #854d0e); }
.gc-meta .b.b-vs[data-tone="orange"] { background: var(--bad-soft); color: #9a3412; }
.gc-meta .b.b-vs[data-tone="bad"]    { background: var(--crit-soft); color: var(--crit); }
.gc-meta .b.b-fault { border: 1px dashed var(--dim); color: var(--ink-2); }
.gc-meta .b.b-part { border: 1px dashed var(--hairline); color: var(--muted); }
.gc-free .gc-n { color: var(--ink); }
.gc-free .gc-now { font-variant-numeric: tabular-nums; white-space: nowrap; }
.gc-row .gcard { align-items: center; }

/* ---- Detail ---- */
.dt-vs[data-tone="good"]   { background: var(--good-soft); }
.dt-vs[data-tone="good"] b { color: var(--good); }
.dt-vs[data-tone="leicht"] { background: var(--leicht-soft); }
.dt-vs[data-tone="leicht"] b { color: var(--leicht-text, #854d0e); }
.dt-vs[data-tone="orange"] { background: var(--bad-soft); }
.dt-vs[data-tone="orange"] b { color: #9a3412; }
.dt-vs[data-tone="bad"] { background: var(--crit-soft); }
.dt-vs[data-tone="bad"] b { color: var(--crit); }
.dt-note { flex: 0 0 auto; margin: 0 16px 10px; padding: 9px 11px; border-radius: 8px; background: var(--surface-2); border-left: 3px solid var(--warn); font-size: 12.5px; line-height: 1.45; color: var(--ink-2); }
.dt-curve { flex: 0 0 auto; padding: 4px 16px 8px; }
.dt-curve-wrap { position: relative; height: 132px; }
.dt-curve-wrap canvas { width: 100%; height: 100%; display: block; }
.dt-key .k-in i  { height: 8px; border-radius: 2px; background: var(--flow-in); width: 10px; }
.dt-key .k-out i { height: 8px; border-radius: 2px; background: var(--flow-out); width: 10px; }
.dt-key .k-typ i { border-top: 2px dashed var(--ink); opacity: .7; }
#rail-detail.is-ring .dt-key .k-in i { background: var(--flow-ring); }
#rail-detail.is-ring .dt-key .k-out i { background: var(--flow-ring-soft); }
.dt-tiles { margin: 6px 16px 4px; }
.dt-tile.t-in  { border-top: 3px solid var(--flow-in); }
.dt-tile.t-out { border-top: 3px solid var(--flow-out); }
#rail-detail.is-ring .dt-tile.t-in { border-top-color: var(--flow-ring); }
#rail-detail.is-ring .dt-tile.t-out { border-top-color: var(--flow-ring-soft); }
.dt-tile-lbl > span { text-transform: none; }
.dt-hist30-bars .h30 { flex: 1; min-height: 2px; border: 0; padding: 0; border-radius: 2px 2px 0 0; background: var(--brand); cursor: pointer; }
.dt-hist30-bars .h30.we { background: color-mix(in srgb, var(--brand) 45%, transparent); }
.dt-hist30-bars .h30.is-cur { background: var(--ink); }
.dt-hist30-bars .h30:hover { filter: brightness(.85); }
.dt-hist30-bars .h30:focus-visible { outline: 2px solid var(--brand-2); outline-offset: 1px; }
.dt-hist30-bars .h30.h30-gap { height: 2px; border-radius: 1px; background: repeating-linear-gradient(90deg, rgba(20,40,64,.18) 0 2px, transparent 2px 4px); }
.dt-link { text-align: left; }

/* ---- Warum panel: day vs typical curve ---- */
.pt-day { fill: none; stroke: var(--brand-2); stroke-width: 2; stroke-linejoin: round; stroke-linecap: round; }
.pt-typ { fill: none; stroke: var(--ink-2); stroke-width: 1.5; stroke-dasharray: 4 3; }
.pw-share i { display: inline-block; width: 14px; margin: 0 4px 0 8px; vertical-align: middle; }
.pw-share i.pw-k-day { height: 2px; background: var(--brand-2); }
.pw-share i.pw-k-typ { border-top: 1.5px dashed var(--ink-2); }
.pw-band[data-tone="off"] { color: var(--ink-2); }

/* ---- Statistik ---- */
#statview .hist-charts { grid-template-columns: repeat(2, minmax(0, 1fr)); }
#statview .hc-wide { grid-column: 1 / -1; }
.hchart-sep { display: inline-block; width: 18px; }
.hchart-card h3 .hc-key { margin-left: auto; flex-wrap: wrap; justify-content: flex-end; }
.hchart-card h3 .hc-key i { width: 12px; height: 8px; border-radius: 2px; }
.sv-loading { grid-column: 1 / -1; padding: 40px 0; text-align: center; color: var(--muted); font-size: 13px; }
@media (max-width: 1100px) { #statview .hist-charts { grid-template-columns: minmax(0, 1fr); } }

/* ---- Feed error banner sits above the transport bar ---- */
#feed-error { bottom: 96px; }

/* ---- Phones ---- */
@media (max-width: 720px) {
  #flowbar { left: 8px; right: 8px; bottom: calc(8px + env(safe-area-inset-bottom, 0px)); flex-wrap: wrap; gap: 8px 10px; padding: 8px 10px; }
  .fb-clock { font-size: 19px; min-width: 3.1em; }
  .fb-track { flex: 1 1 calc(100% - 120px); }
  .fb-speed { order: 3; }
  .fb-day { order: 4; margin-left: auto; }
  .fb-speed button { min-height: 38px; min-width: 40px; }
  .fb-step { width: 44px; height: 44px; }
  .fb-date { height: 44px; }
  #legendBar { left: 8px; right: auto; max-width: calc(100% - 16px); padding: 6px 10px; }
  .legend-bar .lb-size { display: none; }
  #feed-error { bottom: 150px; }
  .rd-kpis { display: none; }
  .list-wrap { padding-bottom: 12px; }
  .leaflet-top.leaflet-left { top: 8px; left: 8px; }
}
@media (max-width: 380px) {
  .fb-speed button { min-width: 34px; padding: 7px 6px; }
  .fb-date { min-width: 82px; padding: 0 6px; }
}
@media (prefers-reduced-motion: reduce) {
  .cp-node { transition: none; }
}
