/* ============================================================
   czyjezdzimetro.pl — drenched single-composition status board
   See DESIGN.md for the system, PRODUCT.md for the strategy.
   ============================================================ */

/* --- Tokens --------------------------------------------------- */
:root {
  /* Status colors (Drenched Rule). Hues tuned for deuteranopia safety:
     green at hue 152 (slightly cool), disrupted at hue 35 (warm/orange-red),
     so brightness + hue both differ between running and disrupted. */
  --color-running: oklch(50% 0.14 152);   /* ~ #1f7a4f */
  --color-disrupted: oklch(50% 0.17 35);  /* ~ #a54628 */
  /* Reported (user signal contradicts WTP). Hue 85 = amber, but lightness 76%
     vs 50% for green/red — brightness contrast alone distinguishes it for
     deuteranopes. Communicates "unverified, in motion". */
  --color-reported: oklch(76% 0.15 85);   /* ~ #c69a14 */
  --color-unknown: oklch(48% 0.012 250);  /* ~ #6b6c74 */

  /* Brand accent. ZTM red, used for favicon, focus rings, brand marks.
     NOT for "disrupted" status — that breaks the rule. */
  --color-ztm: oklch(58% 0.22 25);        /* ~ #c52446 */

  /* WKD (Warszawska Kolej Dojazdowa). Distinct hue from all status colors
     (152/35/85/250) and from ZTM (25) — WKD is a separate carrier, not part
     of ZTM, so its chip reads as a different operator. Hue 250 + higher chroma
     separates it from --color-unknown (also 250 but near-neutral). */
  --color-wkd: oklch(54% 0.19 275);       /* ~ #6d3a9e violet */

  /* KM (Koleje Mazowieckie). Orange — suburban rail brand color. Hue 65 sits
     opposite WKD (275) and clear of status/brand hues (25/35/85/152/250). */
  --color-km: oklch(70% 0.17 65);         /* ~ #d4862a orange */

  /* On-drenched surfaces */
  --color-on-color: oklch(98% 0.005 145);
  --color-on-color-dim: oklch(98% 0.005 145 / 0.72);
  --color-on-color-faint: oklch(98% 0.005 145 / 0.45);

  /* Off-white for loading/unknown surface */
  --color-loading: oklch(96% 0.008 145);
  --color-on-loading: oklch(28% 0.02 145);

  /* Type */
  --font-sans: "Archivo", system-ui, -apple-system, "Segoe UI", sans-serif;

  --text-display: clamp(2.5rem, 9vw, 4.5rem);
  --text-headline: 1.25rem;
  --text-body: 1.0625rem;
  --text-label: 0.875rem;
  --text-meta: 0.8125rem;

  /* Spacing (4pt base) */
  --space-xs: 4px;
  --space-sm: 8px;
  --space-md: 16px;
  --space-lg: 24px;
  --space-xl: 48px;
  --space-2xl: 96px;

  /* Motion */
  --ease-out-quart: cubic-bezier(0.25, 1, 0.5, 1);
  --duration-state: 300ms;
  --duration-micro: 150ms;

  /* Layout */
  --maxw: 720px;
  --dot-size: clamp(72px, 22vw, 108px);
  --dot-stroke: clamp(3px, 0.9vw, 4px);
}

/* --- Reset ---------------------------------------------------- */
*, *::before, *::after { box-sizing: border-box; }
html, body { margin: 0; padding: 0; }
html { overscroll-behavior: none; }
body { min-height: 100vh; min-height: 100dvh; }
button { font: inherit; color: inherit; background: none; border: none; cursor: pointer; }
ul { margin: 0; padding: 0; list-style: none; }
a { color: inherit; }
svg { display: block; }
:focus { outline: none; }
[hidden] { display: none !important; }
:focus-visible {
  outline: 3px solid var(--color-ztm);
  outline-offset: 3px;
  border-radius: 2px;
}

/* --- Body / drenched background ------------------------------ */
body {
  font-family: var(--font-sans);
  font-weight: 400;
  font-size: var(--text-body);
  line-height: 1.5;
  color: var(--color-on-color);
  background-color: var(--color-unknown);
  transition: background-color var(--duration-state) var(--ease-out-quart),
              color var(--duration-state) var(--ease-out-quart);
  -webkit-font-smoothing: antialiased;
  text-rendering: optimizeLegibility;
  font-kerning: normal;
  padding:
    max(var(--space-md), env(safe-area-inset-top))
    max(var(--space-md), env(safe-area-inset-right))
    max(var(--space-md), env(safe-area-inset-bottom))
    max(var(--space-md), env(safe-area-inset-left));
}

body[data-state="running"]    { background-color: var(--color-running); }
body[data-state="m1-down"]    { background-color: var(--color-disrupted); }
body[data-state="m2-down"]    { background-color: var(--color-disrupted); }
body[data-state="both-down"]  { background-color: var(--color-disrupted); }
body[data-state="m1-reported"],
body[data-state="m2-reported"],
body[data-state="both-reported"] { background-color: var(--color-reported); }
body[data-state="unknown"]    { background-color: var(--color-unknown); }
body[data-state="loading"] {
  background-color: var(--color-loading);
  color: var(--color-on-loading);
}

/* --- Skip link ------------------------------------------------ */
.skip-link {
  position: absolute;
  left: -9999px;
  top: var(--space-sm);
  padding: var(--space-sm) var(--space-md);
  background: var(--color-on-color);
  color: var(--color-running);
  text-decoration: none;
  font-weight: 600;
  border-radius: 2px;
  z-index: 10;
}
.skip-link:focus {
  left: var(--space-sm);
}

/* --- Main layout ---------------------------------------------- */
main {
  min-height: calc(100dvh - 2 * var(--space-md));
  display: flex;
  flex-direction: column;
  justify-content: space-between;
  gap: var(--space-lg);
  max-width: var(--maxw);
  margin: 0 auto;
  padding: var(--space-lg) var(--space-md);
}

/* --- Status zone ---------------------------------------------- */
.status {
  flex: 1 1 auto;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  text-align: center;
  gap: var(--space-lg);
  padding: var(--space-md) 0;
}

/* --- Live indicator (liveness pulse) ------------------------- */
/* Always-on pulse signaling the page is monitoring. Hidden during
   loading/unknown — only shown when real status data is present. */
.live {
  display: inline-flex;
  align-items: center;
  gap: var(--space-sm);
}
.live-dot {
  width: 8px;
  height: 8px;
  border-radius: 50%;
  background-color: var(--color-on-color);
  animation: live-pulse 2.2s ease-in-out infinite;
}
.live-label {
  font-size: var(--text-meta);
  font-weight: 500;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--color-on-color-dim);
  line-height: 1;
}
@keyframes live-pulse {
  0%, 100% { opacity: 0.3; }
  50%      { opacity: 1; }
}
body[data-state="loading"] .live,
body[data-state="unknown"] .live { display: none; }

.dots {
  display: flex;
  gap: clamp(var(--space-md), 6vw, var(--space-xl));
  align-items: flex-start;
}

/* Dot is a white plate carrying the M1/M2 label in the line's status color.
   The plate is on-color so it reads as a graphical element against the
   drenched body (which carries the page-level status signal). Inside the
   plate: letter color = status, plate shape = operational state (circle =
   running, rounded square = disrupted), strikethrough reinforces disrupted.
   Color + shape + text redundancy per DESIGN.md. */
.dot {
  width: var(--dot-size);
  height: var(--dot-size);
  border-radius: 50%;
  display: grid;
  place-items: center;
  padding: 0;
  font-family: var(--font-sans);
  font-weight: 700;
  font-size: calc(var(--dot-size) * 0.42);
  letter-spacing: -0.02em;
  line-height: 1;
  color: var(--color-running);
  background-color: var(--color-on-color);
  border: 2px solid transparent;
  transition:
    background-color var(--duration-state) var(--ease-out-quart),
    color var(--duration-state) var(--ease-out-quart),
    border-color var(--duration-state) var(--ease-out-quart),
    border-radius var(--duration-state) var(--ease-out-quart);
  cursor: pointer;
}
.dot:disabled {
  cursor: default;
}
.dot:focus-visible {
  outline: 3px solid var(--color-ztm);
  outline-offset: 3px;
}
.dot-letter {
  display: inline-block;
}

/* Per-dot state. Plate stays on-color; letter color carries the status. */
.dot[data-state="running"] {
  background-color: var(--color-on-color);
  color: var(--color-running);
  animation: pulse-soft 3.2s var(--ease-out-quart) infinite;
}
.dot[data-state="disrupted"] {
  background-color: var(--color-on-color);
  color: var(--color-disrupted);
  border-radius: 28%;
  animation: pulse 2.4s var(--ease-out-quart) infinite;
}
.dot[data-state="disrupted"] .dot-letter {
  text-decoration: line-through;
  text-decoration-thickness: clamp(2px, 0.6vw, 3px);
  text-decoration-color: var(--color-disrupted);
}

/* Reported-* states: amber letter on white plate, faster pulse (live, awaiting
   WTP confirmation). Darker amber than --color-reported so it clears AA on the
   white plate; the body drenched reported-amber is the lighter hue. */
.dot[data-state="reported-down"],
.dot[data-state="reported-running"] {
  background-color: var(--color-on-color);
  color: oklch(42% 0.13 85);
  animation: pulse 1.6s var(--ease-out-quart) infinite;
}

.dot[data-state="loading"] {
  background-color: transparent;
  color: var(--color-on-loading);
  border-color: currentColor;
}
body[data-state="loading"] .dot { color: var(--color-on-loading); }

@keyframes pulse {
  0%, 100% { opacity: 1; transform: scale(1); }
  50%      { opacity: 0.78; transform: scale(0.97); }
}
@keyframes pulse-soft {
  0%, 100% { opacity: 1; transform: scale(1); }
  50%      { opacity: 0.92; transform: scale(0.985); }
}

/* --- Headline ------------------------------------------------- */
.headline {
  font-size: var(--text-display);
  font-weight: 600;
  line-height: 1.02;
  letter-spacing: -0.025em;
  margin: 0;
  text-wrap: balance;
}

.headline-meta {
  margin: 0;
  font-size: var(--text-body);
  font-weight: 400;
  color: var(--color-on-color-dim);
  text-wrap: balance;
}
body[data-state="loading"] .headline-meta { color: var(--color-on-loading); }

/* --- Line strip (per-station metro status) -------------------- */
/* Accordion per disrupted line. Collapsed by default — the big dot and
   scope summary already answer "is it bad?"; the chip grid is drill-down
   for "which stations exactly?". Each chip = one closed station; cause
   station is inverted (solid fill) so it pops among its neighbors.
   Wrapping flex (not horizontal scroll) so every chip stays visible on
   every viewport — "horizontal feel" without hiding content off-edge.
   Plate surface matches .other-disruptions-details; height transition
   uses grid-template-rows per impeccable/brand.md. */
.line-strip {
  width: 100%;
  display: flex;
  flex-direction: column;
  gap: var(--space-md);
  padding: var(--space-sm) 0;
}
.line-strip-rows {
  display: flex;
  flex-direction: column;
  gap: var(--space-md);
}
.line-strip-card {
  background-color: var(--color-on-color);
  color: var(--color-on-loading);
  border-radius: 4px;
  width: 100%;
  max-width: 480px;
  margin: 0 auto;
  text-align: left;
  overflow: hidden;
  animation: line-strip-card-in var(--duration-state) var(--ease-out-quart);
}
@keyframes line-strip-card-in {
  from { opacity: 0; transform: translateY(6px); }
  to   { opacity: 1; transform: translateY(0); }
}
.line-strip-card-trigger {
  display: flex;
  align-items: center;
  gap: var(--space-md);
  padding: var(--space-md);
  width: 100%;
  font-family: var(--font-sans);
  text-align: left;
  border: none;
  background: transparent;
  cursor: pointer;
  color: inherit;
  transition: background-color var(--duration-micro) var(--ease-out-quart);
}
.line-strip-card-trigger:hover {
  background-color: oklch(28% 0.02 145 / 0.03);
}
.line-strip-card-trigger:focus-visible {
  outline: 2px solid var(--color-ztm);
  outline-offset: -2px;
}
.line-strip-card-line {
  font-size: 1.5rem;
  font-weight: 700;
  letter-spacing: -0.01em;
  line-height: 1;
  color: var(--color-disrupted);
  flex-shrink: 0;
}
.line-strip-card-scope {
  flex: 1 1 0;
  min-width: 0;
  font-size: var(--text-meta);
  font-weight: 600;
  line-height: 1.35;
  color: oklch(35% 0.02 145);
  text-wrap: pretty;
}
.line-strip-card-chevron {
  flex-shrink: 0;
  width: 20px;
  height: 20px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  color: oklch(45% 0.02 145);
  transition: transform var(--duration-state) var(--ease-out-quart);
}
.line-strip-card-chevron svg {
  width: 14px;
  height: 14px;
}
.line-strip-card[data-expanded="true"] .line-strip-card-chevron {
  transform: rotate(180deg);
}
.line-strip-card-body {
  display: grid;
  grid-template-rows: 0fr;
  transition: grid-template-rows var(--duration-state) var(--ease-out-quart);
}
.line-strip-card[data-expanded="true"] .line-strip-card-body {
  grid-template-rows: 1fr;
}
.line-strip-card-body-inner {
  overflow: hidden;
  min-height: 0;
}
.line-strip-card[data-expanded="true"] .line-strip-card-body-inner {
  padding: 0 var(--space-md) var(--space-md);
}
.line-strip-card-stations {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-xs);
  padding-top: var(--space-sm);
  border-top: 1px solid oklch(28% 0.02 145 / 0.08);
}
.station-chip {
  display: inline-flex;
  align-items: center;
  gap: 7px;
  padding: 7px 12px 7px 10px;
  background-color: oklch(50% 0.17 35 / 0.09);
  border: 1px solid oklch(50% 0.17 35 / 0.2);
  border-radius: 3px;
  font-family: var(--font-sans);
  font-size: var(--text-label);
  font-weight: 600;
  letter-spacing: 0.01em;
  color: oklch(35% 0.13 35);
  cursor: pointer;
  transition:
    background-color var(--duration-micro) var(--ease-out-quart),
    border-color var(--duration-micro) var(--ease-out-quart),
    transform var(--duration-micro) var(--ease-out-quart);
}
.station-chip:hover {
  background-color: oklch(50% 0.17 35 / 0.18);
  border-color: oklch(50% 0.17 35 / 0.4);
}
.station-chip:active {
  transform: translateY(1px);
}
.station-chip:focus-visible {
  outline: 2px solid var(--color-ztm);
  outline-offset: 2px;
}
.station-chip-tick {
  width: 7px;
  height: 7px;
  border-radius: 50%;
  background: var(--color-disrupted);
  flex-shrink: 0;
}
.station-chip[data-cause="true"] {
  background-color: var(--color-disrupted);
  border-color: var(--color-disrupted);
  color: var(--color-on-color);
}
.station-chip[data-cause="true"] .station-chip-tick {
  background: var(--color-on-color);
  box-shadow: 0 0 0 1.5px var(--color-disrupted);
}
.station-chip[data-state="unknown"] {
  background-color: oklch(50% 0.01 145 / 0.06);
  border-style: dashed;
  border-color: oklch(50% 0.01 145 / 0.3);
  color: oklch(45% 0.02 145);
}
.station-chip[data-state="unknown"] .station-chip-tick {
  background: transparent;
  border: 1.5px dashed oklch(50% 0.01 145 / 0.5);
}
@media (max-width: 380px) {
  .line-strip-card-trigger {
    padding: var(--space-sm) var(--space-md);
    gap: 0 var(--space-sm);
  }
  .line-strip-card-line { font-size: 1.25rem; }
  .station-chip {
    font-size: 0.8125rem;
    padding: 6px 10px 6px 8px;
  }
}

/* --- Other disruptions (tram + bus + SKM, in status zone) ----- */
/* Flat row of square plates, one per disrupted line. Plates are buttons:
   click to expand an inline detail panel below the row showing the WTP
   message body. Plate bg is ZTM red (hue 25) — distinct from the body
   drenched color (green=152, disrupted=35, reported=85), so it stays
   visible in every body state. */
.other-disruptions {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: var(--space-sm);
  width: 100%;
}
.other-disruptions-body {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-xs);
  justify-content: center;
}

.line-chip {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-width: 36px;
  height: 36px;
  padding: 0 var(--space-xs);
  font-family: var(--font-sans);
  font-size: 0.9375rem;
  font-weight: 700;
  letter-spacing: 0.02em;
  font-variant-numeric: tabular-nums;
  background-color: var(--color-ztm);
  color: var(--color-on-color);
  border: 1px solid transparent;
  border-radius: 3px;
  cursor: pointer;
  transition:
    background-color var(--duration-micro) var(--ease-out-quart),
    transform var(--duration-micro) var(--ease-out-quart);
}
.line-chip:hover {
  background-color: oklch(50% 0.22 25);
}
.line-chip:active {
  transform: translateY(1px);
}
.line-chip[aria-expanded="true"] {
  background-color: oklch(42% 0.22 25);
  box-shadow: 0 0 0 2px var(--color-on-color);
}
.line-chip[data-state="reported-down"],
.line-chip[data-state="reported-running"] {
  background-color: var(--color-reported);
  color: oklch(28% 0.03 85);
}
/* WKD: separate carrier (not ZTM), themed with its own token so it never
   blends with tram/bus/SKM ZTM-red chips. Reported-* still wins above. */
.line-chip[data-tryb="wkd"] {
  background-color: var(--color-wkd);
}
.line-chip[data-tryb="wkd"]:hover {
  background-color: oklch(48% 0.19 275);
}
.line-chip[data-tryb="wkd"][aria-expanded="true"] {
  background-color: oklch(42% 0.19 275);
}
/* KM: separate carrier themed with its own orange token. */
.line-chip[data-tryb="km"] {
  background-color: var(--color-km);
}
.line-chip[data-tryb="km"]:hover {
  background-color: oklch(64% 0.18 65);
}
.line-chip[data-tryb="km"][aria-expanded="true"] {
  background-color: oklch(58% 0.19 65);
}
.line-chip:focus-visible {
  outline-offset: 2px;
}

/* Detail panel: surfaced as a white plate against the drenched body, signaling
   "details pulled forward". Wide enough to read a sentence comfortably. */
.other-disruptions-details {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: var(--space-xs);
  background-color: var(--color-on-color);
  color: oklch(28% 0.02 145);
  border-radius: 4px;
  padding: var(--space-sm) var(--space-md);
  width: 100%;
  max-width: 420px;
  text-align: left;
  animation: detail-in var(--duration-micro) var(--ease-out-quart);
}
@keyframes detail-in {
  from { opacity: 0; transform: translateY(-4px); }
  to   { opacity: 1; transform: translateY(0); }
}
.detail-header {
  margin: 0;
  font-size: var(--text-meta);
  font-weight: 700;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--color-ztm);
}
.detail-text {
  margin: 0;
  font-size: var(--text-body);
  font-weight: 400;
  line-height: 1.5;
  color: oklch(28% 0.02 145);
  text-wrap: balance;
}
.detail-link {
  font-size: var(--text-meta);
  font-weight: 600;
  letter-spacing: 0.04em;
  text-transform: uppercase;
  color: var(--color-ztm);
  text-decoration: none;
  display: inline-flex;
  align-items: center;
  gap: var(--space-xs);
  margin-top: var(--space-xs);
  border-bottom: 1px solid transparent;
  transition: border-color var(--duration-micro) var(--ease-out-quart);
}
.detail-link:hover {
  border-bottom-color: var(--color-ztm);
}
.detail-arrow {
  font-weight: 400;
}

/* "więcej" affordance inside the inline detail panel. Pops the article
   sheet with the full WTP body. Inline text-link register — no surface,
   no border — the panel already provides the container. */
.detail-more {
  font-family: var(--font-sans);
  font-size: var(--text-meta);
  font-weight: 500;
  letter-spacing: 0.01em;
  color: oklch(45% 0.02 145);
  background: transparent;
  border: none;
  padding: var(--space-xs) 0 0;
  margin-top: var(--space-xs);
  align-self: flex-start;
  cursor: pointer;
  display: inline-flex;
  align-items: baseline;
  gap: 6px;
  transition: color var(--duration-micro) var(--ease-out-quart);
}
.detail-more::after {
  content: '→';
  font-weight: 400;
  transition: transform var(--duration-micro) var(--ease-out-quart);
}
.detail-more:hover {
  color: oklch(28% 0.02 145);
}
.detail-more:hover::after {
  transform: translateX(2px);
}
.detail-more:focus-visible {
  outline: 2px solid currentColor;
  outline-offset: 4px;
  border-radius: 1px;
}

/* --- Bottom (info footer) ------------------------------------ */
.bottom {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: var(--space-sm);
  padding: var(--space-md) 0;
}
.timestamp {
  margin: 0;
  font-size: var(--text-meta);
  font-weight: 500;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--color-on-color-dim);
  font-variant-numeric: tabular-nums;
}
body[data-state="loading"] .timestamp { color: var(--color-on-loading); opacity: 0.7; }

.subscribed-status {
  margin: 0;
  font-size: var(--text-meta);
  font-weight: 500;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--color-on-color-dim);
  display: inline-flex;
  align-items: center;
  gap: var(--space-xs);
}
.subscribed-mark {
  font-weight: 700;
  color: var(--color-on-color);
}
body[data-state="loading"] .subscribed-status { color: var(--color-on-loading); }

.offline-badge {
  margin: 0;
  font-size: var(--text-meta);
  font-weight: 500;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--color-on-color);
  background-color: var(--color-on-color-faint);
  padding: var(--space-xs) var(--space-sm);
  border-radius: 2px;
}

/* Footer links (privacy policy). Discrete tertiary register — same weight as
   .timestamp, no decoration, underline only on hover. */
.footer-links {
  margin: 0;
  font-size: 0.6875rem;
  font-weight: 500;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--color-on-color-dim);
}
.footer-links a {
  text-decoration: none;
  border-bottom: 1px solid transparent;
  transition: border-color var(--duration-micro) var(--ease-out-quart);
}
.footer-links a:hover {
  border-bottom-color: var(--color-on-color-dim);
}
body[data-state="loading"] .footer-links { color: var(--color-on-loading); opacity: 0.7; }

/* --- Action zone (primary pill swap + secondary icon row) ---- */
.action-zone {
  display: flex;
  flex-direction: column;
  align-items: stretch;
  gap: var(--space-sm);
  padding: 0;
  position: relative;
}
.action-zone:not(:has(.action-primary > :not([hidden]):not([data-state="unsupported"]),
                      .action-row > :not([hidden]))) {
  display: none;
}
/* Primary slot holds one pill at a time: subscribe-pill OR share-primary-pill.
   Visibility toggled in JS via updateActionZone(). */
.action-primary {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: var(--space-xs);
  flex-wrap: wrap;
}
.action-primary:not(:has(> :not([hidden]):not([data-state="unsupported"]))) {
  display: none;
}
.action-row {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: var(--space-xs);
  flex-wrap: wrap;
}
.action-row:not(:has(> :not([hidden]))) {
  display: none;
}

/* Primary pills (subscribe + share-primary fallback): labeled, white surface,
   status-colored text. In disrupted state share-primary inverts to status-color
   background so the share affordance reads as "act now". */
.subscribe,
.share-primary {
  display: inline-flex;
  align-items: center;
  gap: var(--space-sm);
  font-family: var(--font-sans);
  font-size: var(--text-body);
  font-weight: 600;
  letter-spacing: 0.01em;
  color: var(--color-running);
  background-color: var(--color-on-color);
  border: none;
  padding: var(--space-sm) var(--space-md);
  min-height: 44px;
  border-radius: 2px;
  transition:
    transform var(--duration-micro) var(--ease-out-quart),
    background-color var(--duration-micro) var(--ease-out-quart),
    color var(--duration-micro) var(--ease-out-quart),
    opacity var(--duration-micro) var(--ease-out-quart);
}
body[data-state="m1-down"] .subscribe,
body[data-state="m2-down"] .subscribe,
body[data-state="both-down"] .subscribe,
body[data-state="m1-down"] .share-primary:not([data-state="disrupted"]),
body[data-state="m2-down"] .share-primary:not([data-state="disrupted"]),
body[data-state="both-down"] .share-primary:not([data-state="disrupted"]) {
  color: var(--color-disrupted);
}
body[data-state="m1-reported"] .subscribe,
body[data-state="m2-reported"] .subscribe,
body[data-state="both-reported"] .subscribe,
body[data-state="m1-reported"] .share-primary,
body[data-state="m2-reported"] .share-primary,
body[data-state="both-reported"] .share-primary {
  color: var(--color-reported);
}
body[data-state="loading"] .subscribe,
body[data-state="loading"] .share-primary {
  color: var(--color-on-loading);
}
.subscribe:hover,
.share-primary:hover { transform: translateY(-1px); }
.subscribe:active,
.share-primary:active { transform: translateY(0); }

/* Share-primary in disrupted state: pill inverts to status color background.
   Visual cue: "this is the moment to share". */
.share-primary[data-state="disrupted"] {
  background-color: var(--color-disrupted);
  color: var(--color-on-color);
}
.share-primary[data-state="disrupted"]:hover {
  background-color: oklch(46% 0.17 35);
}

.action-icon {
  width: 20px;
  height: 20px;
  flex-shrink: 0;
}

.subscribe[data-state="denied"],
.subscribe-icon[data-state="denied"] {
  opacity: 0.6;
  cursor: not-allowed;
}
.subscribe[data-state="unsupported"] {
  display: none;
}
.subscribe[disabled],
.share-primary[disabled] { opacity: 0.6; cursor: progress; }

/* Install + report + share: icon-only 44×44 squares. Secondary actions get a
   quieter surface than the subscribe CTA — no border, no fill, just the
   glyph. Hover/active reveals a faint tap target so the affordance
   stays discoverable. */
.install,
.report,
.share,
.subscribe-icon,
.settings {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 44px;
  height: 44px;
  padding: 0;
  background: transparent;
  border: none;
  border-radius: 2px;
  color: var(--color-on-color);
  cursor: pointer;
  transition:
    background-color var(--duration-micro) var(--ease-out-quart),
    color var(--duration-micro) var(--ease-out-quart),
    opacity var(--duration-micro) var(--ease-out-quart);
}
.install:hover,
.report:hover,
.share:hover,
.subscribe-icon:hover,
.settings:hover {
  background-color: var(--color-on-color-faint);
}
.install:active,
.report:active,
.share:active,
.subscribe-icon:active,
.settings:active {
  background-color: oklch(98% 0.005 145 / 0.32);
}
body[data-state="loading"] .install,
body[data-state="loading"] .report,
body[data-state="loading"] .share,
body[data-state="loading"] .subscribe-icon,
body[data-state="loading"] .settings {
  color: var(--color-on-loading);
}
body[data-state="loading"] .install:hover,
body[data-state="loading"] .report:hover,
body[data-state="loading"] .share:hover,
body[data-state="loading"] .subscribe-icon:hover,
body[data-state="loading"] .settings:hover {
  background-color: oklch(48% 0.012 250 / 0.1);
}
.install[data-state="unsupported"] { display: none; }
.install[disabled],
.report[disabled],
.share[disabled],
.subscribe-icon[disabled] { opacity: 0.6; cursor: progress; }

/* Status line below the action row — surfaces long subscribe-state
   messages ("Powiadomienia zablokowane w przeglądarce", errors).
   Lives outside the buttons so the buttons themselves stay compact. */
.action-status {
  margin: var(--space-xs) 0 0;
  font-size: var(--text-meta);
  font-weight: 500;
  line-height: 1.4;
  color: var(--color-on-color-dim);
  text-align: center;
}
body[data-state="loading"] .action-status {
  color: var(--color-on-loading);
}

/* --- Share popover (desktop fallback for navigator.share) ---- */
.share-popover {
  position: absolute;
  top: 100%;
  left: 50%;
  transform: translateX(-50%) translateY(-4px);
  transform-origin: top center;
  z-index: 30;
  background: var(--color-on-color);
  border-radius: 6px;
  box-shadow: 0 8px 24px oklch(20% 0.02 250 / 0.18);
  padding: var(--space-sm);
  min-width: 240px;
  opacity: 0;
  transition: opacity var(--duration-micro) var(--ease-out-quart),
              transform var(--duration-micro) var(--ease-out-quart);
  pointer-events: none;
  color: var(--color-on-loading);
}
.share-popover.open {
  opacity: 1;
  transform: translateX(-50%) translateY(0);
  pointer-events: auto;
}
/* Flip above the trigger when below-placement would overflow viewport.
   Toggled by JS in openSharePopover() based on getBoundingClientRect. */
.share-popover.flip-above {
  top: auto;
  bottom: 100%;
  transform-origin: bottom center;
  transform: translateX(-50%) translateY(4px);
}
.share-popover.flip-above.open {
  transform: translateX(-50%) translateY(0);
}
.share-popover-body { display: flex; flex-direction: column; gap: var(--space-xs); }
.share-popover-title {
  margin: 0 var(--space-sm);
  font-size: var(--text-meta);
  font-weight: 600;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: oklch(28% 0.02 145 / 0.7);
}
.share-channels { display: flex; flex-direction: column; gap: 2px; }
.share-channel {
  display: flex;
  align-items: center;
  gap: var(--space-sm);
  width: 100%;
  padding: var(--space-sm) var(--space-md);
  background: transparent;
  border: none;
  border-radius: 4px;
  font: 600 var(--text-body)/1.2 var(--font-sans);
  color: inherit;
  text-align: left;
  cursor: pointer;
  min-height: 40px;
  transition: background-color var(--duration-micro) var(--ease-out-quart);
}
.share-channel:hover,
.share-channel:focus-visible {
  background: oklch(28% 0.02 145 / 0.08);
  outline: none;
}
.share-channel-icon {
  width: 18px;
  height: 18px;
  flex-shrink: 0;
}
@media (max-width: 640px) {
  .share-popover { display: none !important; }
}

/* --- Re-share hint (arrived via ?ref=) ----------------------- */
.reshare-hint {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: var(--space-sm);
  margin: var(--space-sm) 0 0;
  padding: var(--space-xs) var(--space-sm);
  font-size: var(--text-meta);
  font-weight: 500;
  color: var(--color-on-color-dim);
  background: oklch(98% 0.005 145 / 0.06);
  border-radius: 4px;
}
.reshare-hint-text { flex: 1; }
.reshare-hint-share {
  font: inherit;
  font-weight: 600;
  color: var(--color-on-color);
  background: transparent;
  border: none;
  cursor: pointer;
  padding: 0;
  text-decoration: underline;
  text-underline-offset: 2px;
}
.reshare-hint-share:hover { text-decoration-thickness: 2px; }
.reshare-hint-dismiss {
  margin-left: auto;
  background: transparent;
  border: none;
  cursor: pointer;
  color: var(--color-on-color-faint);
  font-size: 18px;
  line-height: 1;
  padding: 0 4px;
}
.reshare-hint-dismiss:hover { color: var(--color-on-color-dim); }

/* --- Install instructions (iOS) ------------------------------ */
.install-instructions {
  display: flex;
  align-items: flex-start;
  gap: var(--space-md);
  background-color: var(--color-on-color-faint);
  border-radius: 4px;
  padding: var(--space-md);
  margin-top: var(--space-sm);
  width: min(100%, 420px);
}
.install-instructions-body {
  flex: 1;
  display: flex;
  flex-direction: column;
  gap: var(--space-sm);
}
.install-instructions-title {
  font-size: var(--text-label);
  font-weight: 600;
  letter-spacing: 0.04em;
  text-transform: uppercase;
  color: var(--color-on-color);
  margin: 0;
}
.install-steps {
  margin: 0;
  padding-left: 1.25em;
  font-size: var(--text-meta);
  color: var(--color-on-color-dim);
  line-height: 1.5;
  display: flex;
  flex-direction: column;
  gap: var(--space-xs);
}
.install-steps em {
  font-style: normal;
  font-weight: 500;
  color: var(--color-on-color);
}
.share-icon-inline {
  width: 14px;
  height: 14px;
  display: inline-block;
  vertical-align: -2px;
}
.install-instructions-close {
  font-size: 1.25rem;
  line-height: 1;
  color: var(--color-on-color-dim);
  padding: 0 var(--space-xs);
  flex-shrink: 0;
}
.install-instructions-close:hover { color: var(--color-on-color); }

/* --- Bottom sheet (report confirmation) ----------------------- */
/* Mobile-first sheet, anchors to bottom edge. Panel uses the loading surface
   (off-white) so it reads as a neutral confirmation layer over the drenched
   status color. Amber primary button signals the destination state. */
.sheet {
  position: fixed;
  inset: 0;
  z-index: 100;
  display: flex;
  align-items: flex-end;
  justify-content: center;
  visibility: hidden;
  pointer-events: none;
  transition: visibility 0s linear var(--duration-state);
}
.sheet.open {
  visibility: visible;
  pointer-events: auto;
  transition: visibility 0s;
}
.sheet-backdrop {
  position: absolute;
  inset: 0;
  background-color: oklch(20% 0.02 145 / 0.62);
  opacity: 0;
  transition: opacity var(--duration-state) var(--ease-out-quart);
}
.sheet-panel {
  position: relative;
  background-color: var(--color-loading);
  color: var(--color-on-loading);
  width: 100%;
  max-width: var(--maxw);
  display: flex;
  flex-direction: column;
  gap: var(--space-md);
  padding:
    var(--space-md)
    max(var(--space-md), env(safe-area-inset-right))
    max(var(--space-md), env(safe-area-inset-bottom))
    max(var(--space-md), env(safe-area-inset-left));
  border-top-left-radius: 12px;
  border-top-right-radius: 12px;
  box-shadow: 0 -8px 32px oklch(20% 0.02 145 / 0.18);
  transform: translateY(100%);
  transition: transform var(--duration-state) var(--ease-out-quart);
}
.sheet.open .sheet-backdrop { opacity: 1; }
.sheet.open .sheet-panel { transform: translateY(0); }

.sheet-grab {
  width: 36px;
  height: 4px;
  background-color: oklch(50% 0.01 145 / 0.28);
  border-radius: 2px;
  align-self: center;
  margin-top: -4px;
}

.sheet-title {
  margin: 0;
  font-size: var(--text-headline);
  font-weight: 600;
  line-height: 1.2;
  letter-spacing: -0.01em;
  text-align: center;
  text-wrap: balance;
}
.sheet-body {
  margin: 0;
  font-size: var(--text-meta);
  line-height: 1.5;
  color: oklch(42% 0.02 145);
  text-align: center;
  text-wrap: balance;
}
.sheet-turnstile {
  display: flex;
  justify-content: center;
  min-height: 70px;
}
.sheet-turnstile:empty::before {
  content: "";
  width: 300px;
  height: 65px;
  background-color: oklch(50% 0.01 145 / 0.08);
  border-radius: 4px;
}
.sheet-error {
  margin: 0;
  font-size: var(--text-meta);
  font-weight: 500;
  line-height: 1.4;
  color: var(--color-disrupted);
  text-align: center;
}

.sheet-actions {
  display: grid;
  grid-template-columns: 1fr 1.4fr;
  gap: var(--space-sm);
  margin-top: var(--space-xs);
}

/* --- Sheet form fields ---------------------------------------- */
/* Modal now collects Tryb + Linia + Claim. Sheet surface is off-white
   (--color-loading), so form colors invert vs the drenched main page:
   dark text on light bg. */
.sheet-field {
  display: flex;
  flex-direction: column;
  gap: var(--space-sm);
}
.sheet-field-label {
  margin: 0;
  font-size: var(--text-meta);
  font-weight: 500;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: oklch(42% 0.02 145);
}
.sheet-field-hint {
  margin: 0;
  font-size: var(--text-meta);
  font-weight: 500;
  color: var(--color-disrupted);
}

/* Segmented control for Tryb / Claim — three-button row (Tryb) or two-button
   row (Claim). Selected option inverted (dark bg, light text). */
.segmented {
  display: grid;
  grid-auto-flow: column;
  grid-auto-columns: 1fr;
  border: 1px solid oklch(50% 0.01 145 / 0.28);
  border-radius: 4px;
  overflow: hidden;
}
.segmented-option {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: var(--space-sm);
  padding: var(--space-sm) var(--space-md);
  min-height: 44px;
  font-family: var(--font-sans);
  font-size: var(--text-body);
  font-weight: 500;
  color: oklch(42% 0.02 145);
  background-color: transparent;
  border: none;
  border-right: 1px solid oklch(50% 0.01 145 / 0.28);
  transition:
    background-color var(--duration-micro) var(--ease-out-quart),
    color var(--duration-micro) var(--ease-out-quart);
}
.segmented-option:last-child {
  border-right: none;
}
.segmented-option:hover:not([aria-checked="true"]) {
  background-color: oklch(50% 0.01 145 / 0.08);
}
.segmented-option[aria-checked="true"] {
  background-color: oklch(28% 0.02 145);
  color: var(--color-loading);
}
.segmented-icon {
  width: 18px;
  height: 18px;
  flex-shrink: 0;
}
.segmented-label {
  font-size: var(--text-meta);
  font-weight: 500;
  letter-spacing: 0.02em;
}

/* Line input — metro uses dropdown (M1/M2 only), tram/bus use text input. */
.line-input-wrap {
  display: flex;
}
.line-select,
.line-input {
  width: 100%;
  padding: var(--space-sm) var(--space-md);
  min-height: 44px;
  font-family: var(--font-sans);
  font-size: var(--text-body);
  font-weight: 500;
  color: oklch(28% 0.02 145);
  background-color: oklch(96% 0.008 145);
  border: 1px solid oklch(50% 0.01 145 / 0.28);
  border-radius: 2px;
  transition: border-color var(--duration-micro) var(--ease-out-quart);
}
.line-select:focus,
.line-input:focus {
  border-color: oklch(50% 0.01 145 / 0.55);
  outline: none;
}
.line-input::placeholder {
  color: oklch(50% 0.01 145 / 0.55);
}
.sheet-cancel,
.sheet-confirm {
  font-family: var(--font-sans);
  font-size: var(--text-body);
  font-weight: 600;
  min-height: 48px;
  border-radius: 2px;
  padding: var(--space-sm) var(--space-md);
  transition:
    background-color var(--duration-micro) var(--ease-out-quart),
    color var(--duration-micro) var(--ease-out-quart),
    border-color var(--duration-micro) var(--ease-out-quart),
    opacity var(--duration-micro) var(--ease-out-quart),
    transform var(--duration-micro) var(--ease-out-quart);
}
.sheet-cancel {
  background-color: transparent;
  color: var(--color-on-loading);
  border: 2px solid oklch(50% 0.01 145 / 0.28);
}
.sheet-cancel:hover {
  background-color: oklch(50% 0.01 145 / 0.08);
  border-color: oklch(50% 0.01 145 / 0.4);
}
.sheet-confirm {
  background-color: var(--color-reported);
  color: oklch(28% 0.03 85);
  border: 2px solid var(--color-reported);
}
.sheet-confirm:hover:not([disabled]) {
  transform: translateY(-1px);
}
.sheet-confirm:active:not([disabled]) {
  transform: translateY(0);
}
.sheet-confirm[disabled] {
  opacity: 0.5;
  cursor: not-allowed;
}
.sheet-confirm[data-state="submitting"] {
  opacity: 0.7;
  cursor: progress;
}

body[data-sheet-open],
body[data-article-sheet-open] { overflow: hidden; }

/* --- Article sheet (full WTP message) ------------------------- */
/* Shares the .sheet scaffold with the report sheet. Rich-text content
   area scrolls; modified date sits under the title as small metadata. */
.article-sheet-modified {
  margin: 0;
  font-size: var(--text-meta);
  font-weight: 500;
  color: oklch(45% 0.02 145);
  text-align: center;
}
.article-sheet-content {
  font-size: var(--text-body);
  line-height: 1.6;
  color: oklch(28% 0.02 145);
  text-align: left;
  max-height: 56vh;
  overflow-y: auto;
  -webkit-overflow-scrolling: touch;
  padding: 0 var(--space-xs);
  /* Allow <p> spacing without breaking layout */
}
.article-sheet-content p {
  margin: 0 0 var(--space-sm);
}
.article-sheet-content p:last-child {
  margin-bottom: 0;
}
.article-sheet-content strong,
.article-sheet-content b {
  font-weight: 600;
  color: oklch(20% 0.02 145);
}
.article-sheet-content em,
.article-sheet-content i {
  font-style: italic;
}
.article-sheet-content a {
  color: var(--color-ztm);
  text-decoration: underline;
  text-underline-offset: 2px;
}
.article-sheet-content a:hover {
  text-decoration-thickness: 2px;
}
.article-sheet-content ul,
.article-sheet-content ol {
  margin: 0 0 var(--space-sm);
  padding-left: 1.5em;
}
.article-sheet-content li {
  margin: 0 0 var(--space-xs);
}

/* Metro impediment cards inside the sheet (one per WTP article touching the
   tapped line). Title carries signage weight; description reads as body;
   link is a quiet text affordance, not a button. */
.article-sheet-content .metro-impediment {
  padding: var(--space-sm) 0;
  border-top: 1px solid oklch(28% 0.02 145 / 0.12);
}
.article-sheet-content .metro-impediment:first-child {
  border-top: none;
  padding-top: 0;
}
.article-sheet-content .metro-impediment:last-child {
  padding-bottom: 0;
}
.article-sheet-content .metro-impediment-title {
  font-weight: 600;
  font-size: var(--text-body);
  line-height: 1.4;
  color: oklch(20% 0.02 145);
  margin: 0 0 var(--space-xs);
}
.article-sheet-content .metro-impediment-desc {
  font-size: var(--text-body);
  line-height: 1.5;
  color: oklch(28% 0.02 145);
  margin: 0 0 var(--space-sm);
}
.article-sheet-content .metro-impediment-link {
  display: inline-flex;
  align-items: baseline;
  gap: 6px;
  font-size: var(--text-meta);
  font-weight: 600;
  letter-spacing: 0.04em;
  text-transform: uppercase;
  color: var(--color-ztm);
  text-decoration: none;
  border-bottom: 1px solid transparent;
  transition: border-color var(--duration-micro) var(--ease-out-quart);
}
.article-sheet-content .metro-impediment-link:hover {
  border-bottom-color: var(--color-ztm);
}

/* Article sheet actions: single subtle text-only close. No border, no
   fill — the panel surface is enough container. Hover just deepens the
   label color so the tap target stays legible. */
#article-sheet .sheet-actions {
  grid-template-columns: 1fr;
}
#article-sheet .sheet-cancel {
  background: transparent;
  color: oklch(45% 0.02 145);
  border: none;
  font-weight: 500;
  min-height: 44px;
}
#article-sheet .sheet-cancel:hover {
  background: transparent;
  border: none;
  color: oklch(28% 0.02 145);
}

@media (min-width: 640px) {
  .sheet { align-items: center; }
  .sheet-panel {
    max-width: calc(min(var(--maxw), 480px));
    border-radius: 12px;
    margin-bottom: var(--space-2xl);
  }
}

/* --- Responsive: tighter on small screens --------------------- */
@media (max-width: 380px) {
  :root {
    --dot-size: clamp(64px, 26vw, 88px);
  }
  .headline { letter-spacing: -0.02em; }
  .dots { gap: var(--space-md); }
}

/* --- Reduced motion ------------------------------------------- */
@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after {
    animation-duration: 0.01ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 100ms !important;
    scroll-behavior: auto !important;
  }
  .dot[data-state="disrupted"] { animation: none; }
  .dot[data-state="running"] { animation: none; }
  .live-dot { animation: none; opacity: 1; }
}

/* --- Print (utility, not the primary surface) ----------------- */
@media print {
  body { background: white !important; color: black !important; }
}
