/* Archivo (Omnibus-Type, SIL OFL 1.1). One variable file carries both the
   normal and narrow widths through font-stretch, so the interface and the
   condensed year readout come from a single 88 KB download. Self-hosted
   rather than linked: the app is meant to run from file://, where a CDN
   stylesheet would not load. Latin subset only — nothing in the data or the
   interface goes above U+00FF, and the dashes sit in latin's U+2000 range. */

@font-face {
  font-family: "Archivo";
  font-style: normal;
  font-weight: 400 700;
  font-stretch: 62% 125%;
  font-display: swap;
  src: url("../fonts/archivo-latin-var.woff2") format("woff2");
}

/* Chronologie du métro de Paris — app styles.
   One palette, two themes: every colour is a custom property redefined under
   prefers-color-scheme, so nothing hard-codes a light-mode assumption. Metro
   line colours are data, not theme, and come from the GeoJSON. */

:root {
  --bg: #edeff2;          /* glazed tile white, cool rather than cream */
  --panel: #ffffff;
  --ink: #10151c;         /* near-black carrying the plate's blue bias */
  --muted: #5a6473;
  --faint: #8b94a3;
  --border: #dbdfe6;
  --water: #c2d5e6;
  --park: #c6d9b4;
  --land: #e7e5df;
  --accent: #0e3a76;      /* the enamel station plate, unchanged since 1900 */
  --highlight: #e3a23c;   /* Guimard amber, the one warm note */
  --shadow: 0 1px 2px rgba(0, 0, 0, .06), 0 6px 20px rgba(0, 0, 0, .06);
  --dim: .1;
  --radius: 10px;
}

@media (prefers-color-scheme: dark) {
  :root {
    --bg: #10141a;
    --panel: #171c24;
    --ink: #e8ebef;
    --muted: #939cab;
    --faint: #6d7788;
    --border: #262d38;
    --water: #1d3a50;
    --park: #1e2c1d;
    --land: #191c20;
    --accent: #7fb0ee;
    --highlight: #e3a23c;
    --shadow: 0 1px 2px rgba(0, 0, 0, .4), 0 6px 20px rgba(0, 0, 0, .35);
    --dim: .14;
  }
}

* { box-sizing: border-box; }

html, body {
  margin: 0;
  padding: 0;
  height: 100%;
}

body {
  background: var(--bg);
  color: var(--ink);
  font: 15px/1.5 "Archivo", -apple-system, BlinkMacSystemFont, "Segoe UI",
        Helvetica, Arial, sans-serif;
  font-stretch: 100%;
  -webkit-font-smoothing: antialiased;
  overflow: hidden;
}

#app {
  display: grid;
  grid-template-rows: auto auto minmax(0, 1fr);
  height: 100%;
  max-width: 1400px;
  margin: 0 auto;
  padding: 18px 20px 14px;
  gap: 14px;
}

/* ---------- header ---------- */

header {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 16px;
  flex-wrap: wrap;
}

h1 {
  margin: 0;
  font-size: 21px;
  font-weight: 600;
  letter-spacing: -.01em;
}

.subtitle {
  color: var(--muted);
  font-size: 13px;
}

/* ---------- timeline ---------- */

.timeline {
  display: grid;
  grid-template-columns: auto auto minmax(0, 1fr) auto;
  align-items: center;
  gap: 14px;
  /* the scrubber adds its own left margin, see .scrubber */
  background: var(--panel);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  box-shadow: var(--shadow);
  padding: 12px 16px;
}

.transport {
  display: flex;
  align-items: center;
  gap: 6px;
}

#play {
  width: 40px;
  height: 40px;
  flex: none;
  border: none;
  border-radius: 50%;
  background: var(--accent);
  color: #fff;
  cursor: pointer;
  display: grid;
  place-items: center;
  transition: transform .12s ease, filter .12s ease;
}

#play:hover { filter: brightness(1.08); }
#play:active { transform: scale(.94); }
#play svg { width: 15px; height: 15px; fill: currentColor; }

/* A direction toggle rather than a second transport, so it sits smaller than
   play and latches on when time is running backwards. */
#reverse {
  width: 30px;
  height: 30px;
  flex: none;
  border: 1px solid var(--border);
  border-radius: 50%;
  background: var(--panel);
  color: var(--muted);
  cursor: pointer;
  display: grid;
  place-items: center;
  transition: transform .12s ease, color .12s ease, border-color .12s ease;
}

#reverse:hover { color: var(--accent); border-color: var(--accent); }
#reverse:active { transform: scale(.92); }
#reverse svg { width: 14px; height: 14px; fill: currentColor; }

#reverse[aria-pressed="true"] {
  background: var(--accent);
  border-color: var(--accent);
  color: #fff;
}

/* The flag sits under the year rather than beside it, so turning it on never
   shifts the year readout sideways mid-playback. */
.year-box {
  display: flex;
  flex-direction: column;
  justify-content: center;
  margin-left: 4px;
}

.year-flag {
  font-size: 10px;
  text-transform: uppercase;
  letter-spacing: .08em;
  font-weight: 600;
  color: var(--faint);
  visibility: hidden;
  margin-top: -2px;
}

.year-flag.on { visibility: visible; }

.year {
  font-variant-numeric: tabular-nums;
  font-stretch: 76%;       /* the narrow cut, from the same variable file */
  font-size: 32px;
  font-weight: 700;
  letter-spacing: 0;
  min-width: 4.2ch;
}

/* The scrubber is a plain div; the track, ticks and handle are absolutely
   positioned inside it and driven from JS. */
.scrubber {
  position: relative;
  height: 46px;
  margin-left: 18px;
  cursor: pointer;
  touch-action: none;      /* we handle the drag ourselves via pointer events */
  user-select: none;
  -webkit-user-select: none;
}

.scrubber:focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: 4px;
  border-radius: 6px;
}

.track, .fill {
  position: absolute;
  top: 19px;
  height: 4px;
  border-radius: 2px;
  pointer-events: none;
}

.track { left: 9px; right: 9px; background: var(--border); }
.fill { left: 9px; background: var(--accent); }

/* Past today the timeline stops being a record. The track takes the same
   dashed language as the planned lines on the map, drawn over the fill so
   that scrubbing into the future does not paint it solid again. */
.future {
  position: absolute;
  top: 19px;
  height: 4px;
  right: 9px;
  border-radius: 2px;
  pointer-events: none;
  display: none;
  background: repeating-linear-gradient(
    90deg, var(--panel) 0 2px, transparent 2px 5px);
}

.today {
  position: absolute;
  top: 13px;
  width: 1px;
  height: 16px;
  background: var(--faint);
  pointer-events: none;
  display: none;
}

.future.on, .today.on { display: block; }

.tick {
  position: absolute;
  top: 31px;
  font-size: 10px;
  color: var(--faint);
  transform: translateX(-50%);
  pointer-events: none;
  font-variant-numeric: tabular-nums;
}

/* Sits between the track and the label rather than crossing the track. */
.tick::before {
  content: "";
  position: absolute;
  left: 50%;
  top: -7px;
  width: 1px;
  height: 5px;
  background: var(--border);
}

.handle {
  position: absolute;
  top: 21px;
  width: 16px;
  height: 16px;
  margin: -8px 0 0 -8px;
  border-radius: 50%;
  background: var(--panel);
  border: 3px solid var(--accent);
  box-shadow: 0 1px 4px rgba(0, 0, 0, .25);
  pointer-events: none;
  transition: transform .1s ease;
}

.scrubber.dragging .handle { transform: scale(1.2); }

.speed {
  display: flex;
  gap: 2px;
  background: var(--bg);
  border: 1px solid var(--border);
  border-radius: 7px;
  padding: 2px;
}

.speed button {
  border: none;
  background: none;
  color: var(--muted);
  font: inherit;
  font-size: 12px;
  padding: 3px 9px;
  border-radius: 5px;
  cursor: pointer;
}

.speed button[aria-pressed="true"] {
  background: var(--panel);
  color: var(--ink);
  box-shadow: 0 1px 2px rgba(0, 0, 0, .12);
}

/* ---------- main: legend rail + map ---------- */

main {
  display: grid;
  grid-template-columns: 190px minmax(0, 1fr);
  gap: 14px;
  min-height: 0;
}

.rail {
  display: flex;
  flex-direction: column;
  gap: 10px;
  min-height: 0;
}

.panel {
  background: var(--panel);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  box-shadow: var(--shadow);
}

#legend {
  padding: 12px;
  overflow-y: auto;
  min-height: 0;
  flex: 1;
}

.legend-title {
  font-size: 11px;
  text-transform: uppercase;
  letter-spacing: .08em;
  color: var(--faint);
  margin-bottom: 9px;
}

.chip {
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 4px 5px;
  border-radius: 6px;
  cursor: default;
  transition: opacity .15s ease, background .15s ease;
  font-size: 13px;
}

.chip:hover { background: var(--bg); }
.chip.dim { opacity: var(--dim); }

.swatch {
  width: 14px;
  height: 14px;
  border-radius: 3px;
  flex: none;
}

/* Hollow, mirroring the hollow stations on the map. */
.chip.planned .swatch {
  background: none !important;
  border: 2px solid;
}

.chip.planned .label { font-style: italic; }

.legend-note {
  display: none;
  margin-top: 10px;
  font-size: 11px;
  line-height: 1.35;
  color: var(--faint);
}

.legend-note.on { display: block; }

.count {
  margin-left: auto;
  color: var(--faint);
  font-size: 11px;
  font-variant-numeric: tabular-nums;
}

.notes {
  padding: 10px 12px;
  font-size: 12px;
  color: var(--muted);
}

.notes summary {
  cursor: pointer;
  color: var(--ink);
  font-size: 12px;
  list-style: none;
}

.notes summary::-webkit-details-marker { display: none; }
.notes summary::before { content: "› "; color: var(--faint); }
.notes[open] summary::before { content: "⌄ "; }
.notes p { margin: 8px 0 0; }
.notes b { color: var(--ink); font-weight: 600; }

/* ---------- map ---------- */

#map {
  position: relative;
  overflow: hidden;
  background: var(--land);
}

#map svg { display: block; width: 100%; height: 100%; }

.water { fill: var(--water); stroke: none; }
.parks { fill: var(--park); stroke: none; }

.line {
  fill: none;
  stroke-width: 2.0;
  stroke-linecap: round;
  stroke-linejoin: round;
  vector-effect: non-scaling-stroke;  /* keeps width constant under zoom */
  cursor: pointer;
}

.station {
  /* stroke colour is set per station in app.js: its own hue, shaded */
  stroke-width: .8;
  vector-effect: non-scaling-stroke;
  cursor: pointer;
}

/* The casing sits under .line and 1px wider, leaving half a pixel of shaded
   edge on each side. Twelve of the twenty-two RATP colours fall below the 3:1
   contrast floor against the land tone — line 1's yellow reaches only 1.19:1 —
   and this restores a readable edge without altering a single brand hue.
   Line and casing together come to 3px, against 2.6px for the bare line before
   casing existed: the edge costs almost nothing in apparent weight. */
.casing {
  fill: none;
  stroke-width: 3.0;
  stroke-linecap: round;
  stroke-linejoin: round;
  vector-effect: non-scaling-stroke;
  pointer-events: none;
}

/* Projected track. Dashed rather than faded, because the fade is already
   spoken for by the hover dim: a dimmed built line and a highlighted planned
   one would be the same grey. The casing takes the same pattern, or it would
   read as a solid line under a dotted one.

   vector-effect holds the stroke width constant under zoom but not the dash
   spacing, so the pattern is set in the same units the stroke is drawn in and
   checked at both ends of the zoom range. */
.line.planned, .casing.planned {
  stroke-dasharray: 5 4;
  stroke-linecap: butt;
}

/* A station that does not exist yet is a ring, not a dot. The fill is the
   land tone rather than none, so the dashed line does not run through it. */
.station.planned {
  fill: var(--land) !important;
  stroke-width: 1.3;
}

.hidden { opacity: 0 !important; pointer-events: none; }

.zoom-controls {
  position: absolute;
  right: 10px;
  bottom: 10px;
  display: flex;
  flex-direction: column;
  gap: 4px;
}

.zoom-controls button {
  width: 30px;
  height: 30px;
  border: 1px solid var(--border);
  background: var(--panel);
  color: var(--ink);
  border-radius: 7px;
  font-size: 15px;
  line-height: 1;
  cursor: pointer;
  box-shadow: var(--shadow);
}

.zoom-controls button:hover { background: var(--bg); }

.hint {
  position: absolute;
  left: 12px;
  bottom: 10px;
  font-size: 11px;
  color: var(--faint);
  pointer-events: none;
}

/* ---------- tooltip ---------- */

#tooltip {
  position: fixed;
  z-index: 20;
  pointer-events: none;
  opacity: 0;
  transition: opacity .12s ease;
  background: var(--panel);
  color: var(--ink);
  border: 1px solid var(--border);
  border-radius: 7px;
  box-shadow: var(--shadow);
  padding: 7px 10px;
  font-size: 12.5px;
  max-width: 240px;
}

#tooltip.on { opacity: 1; }
#tooltip .t-name { font-weight: 600; }
#tooltip .t-meta { color: var(--muted); font-size: 11.5px; margin-top: 2px; }

/* ---------- narrow screens ---------- */

@media (max-width: 760px) {
  body { overflow: auto; }

  #app {
    grid-template-rows: auto auto auto auto;
    height: auto;
    min-height: 100%;
    padding: 12px 12px 16px;
  }

  header { gap: 4px; }
  h1 { font-size: 18px; }

  .timeline {
    grid-template-columns: auto minmax(0, 1fr);
    grid-template-areas: "play year" "scrub scrub" "speed speed";
    gap: 18px 12px;
    padding: 12px;
  }

  .transport { grid-area: play; }
  .year-box { grid-area: year; margin-left: 0; }
  .year { font-size: 26px; }
  /* stacked here, so the horizontal offset would only misalign it */
  .scrubber { grid-area: scrub; height: 50px; margin-left: 0; }
  .track, .fill, .future { top: 21px; }
  .today { top: 15px; }
  .tick { top: 33px; }
  .handle { top: 23px; width: 20px; height: 20px; margin: -10px 0 0 -10px; }
  .speed { grid-area: speed; justify-self: start; }

  /* legend moves above the map as a horizontal strip */
  main {
    grid-template-columns: minmax(0, 1fr);
    grid-template-areas: "legend" "map" "notes";
    gap: 10px;
  }

  .rail { display: contents; }
  #legend { grid-area: legend; overflow-x: auto; overflow-y: hidden; flex: none; }
  .notes { grid-area: notes; }
  #map { grid-area: map; height: 58vh; min-height: 320px; }

  .legend-title { display: none; }

  #legend .chips {
    display: flex;
    gap: 6px;
    flex-wrap: nowrap;
  }

  .chip {
    flex: none;
    border: 1px solid var(--border);
    padding: 5px 9px;
    white-space: nowrap;
  }

  .count { display: none; }
  .hint { display: none; }
}

/* ---------- visibility + highlight ----------
   Year changes and highlighting are class toggles, so the fades are done by CSS
   rather than by transitioning 740 nodes in JS. #app.no-anim kills the
   transition while the user is scrubbing, where instant feedback is wanted. */

.line, .station, .casing { transition: opacity .28s ease; }
.line.dim, .station.dim, .casing.dim { opacity: var(--dim); }
#app.no-anim .line,
#app.no-anim .station,
#app.no-anim .casing { transition: none; }

/* ---------- language switch ---------- */

.header-end {
  display: flex;
  align-items: center;
  gap: 12px;
}

.lang {
  display: flex;
  gap: 2px;
  background: var(--panel);
  border: 1px solid var(--border);
  border-radius: 7px;
  padding: 2px;
}

.lang button {
  border: none;
  background: none;
  color: var(--muted);
  font: inherit;
  font-size: 11px;
  font-weight: 600;
  letter-spacing: .03em;
  padding: 3px 8px;
  border-radius: 5px;
  cursor: pointer;
}

.lang button[aria-pressed="true"] {
  background: var(--accent);
  color: #fff;
}

/* ---------- credits ----------
   ODbL requires the attribution to travel with the map itself, so this sits on
   the map rather than in the README. It stays visible at every width — unlike
   .hint, which is decorative and hides on narrow screens. */

.credits-btn {
  position: absolute;
  right: 48px;                /* clear of the zoom stack at right: 10px */
  bottom: 10px;
  width: 28px;
  height: 28px;
  border: 1px solid var(--border);
  border-radius: 50%;
  background: var(--panel);
  color: var(--muted);
  font: inherit;
  font-size: 14px;
  line-height: 1;
  cursor: pointer;
  display: grid;
  place-items: center;
  box-shadow: var(--shadow);
}

.credits-btn:hover { color: var(--accent); border-color: var(--accent); }

#credits {
  border: none;
  border-radius: var(--radius);
  padding: 0;
  max-width: 460px;
  width: calc(100% - 32px);
  background: var(--panel);
  color: var(--ink);
  box-shadow: 0 10px 50px rgba(0, 0, 0, .3);
}

#credits::backdrop { background: rgba(0, 0, 0, .45); }

.credits-head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 16px;
  padding: 16px 18px;
  border-bottom: 1px solid var(--border);
}

.credits-head h2 {
  margin: 0;
  font-size: 15px;
  font-weight: 600;
}

#credits-close {
  border: none;
  background: none;
  color: var(--muted);
  font-size: 20px;
  line-height: 1;
  cursor: pointer;
  padding: 0 4px;
}

#credits-close:hover { color: var(--ink); }

.credits-body {
  padding: 16px 18px 20px;
  font-size: 13px;
  line-height: 1.55;
  max-height: 60vh;
  overflow-y: auto;
}

.credits-body p { margin: 0 0 12px; }
.credits-body p:last-child { margin-bottom: 0; }
.credits-body b { font-weight: 600; }
.credits-body a { color: var(--accent); }

.credits-by {
  border-bottom: 1px solid var(--border);
  padding-bottom: 12px;
}

.credits-note {
  color: var(--muted);
  font-size: 12px;
  border-top: 1px solid var(--border);
  padding-top: 12px;
}
