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

:root {
  --cream: #F7F4EF;
  --stone: #E4DFD6;
  /* Secondary text. 6.6:1 on white, 5.5:1 on the chip grey, 5.0:1 on stone —
     clears WCAG AA on every surface in this palette. */
  --warm-gray: #635C55;
  /* Decorative only: dots, borders, icon strokes. Never text. */
  --faint: #9A9189;
  --charcoal: #252220;
  --text: #3D3830;
  --open: #2D7A52;     --open-bg: #E6F4EE;
  --closed: #B83030;   --closed-bg: #FCEAEA;
  --winter: #3A68A0;   --winter-bg: #EAF0F8;
  --restricted: #C97010; --restricted-bg: #FEF0DC;
  --blocked: #C97010;   --blocked-bg: #FEF0DC;
  --snow: #6B5EA8;     --snow-bg: #F0EDF8;
  --unknown: #888078;  --unknown-bg: #EFECE7;
  --sidebar: 320px;
}

body {
  font-family: 'DM Sans', system-ui, sans-serif;
  background: var(--cream);
  color: var(--text);
  height: 100dvh;
  display: flex;
  flex-direction: column;
  overflow: hidden;
}

/* ─── BRAND ─── */
/* There is no top bar. A full-width band would cut the map off and cost 56px
   of it on every screen, so the branding rides on the panel instead: its own
   card above the list, with the map running to the top of the viewport. */
.panel-brand {
  flex-shrink: 0;
  padding: 13px 15px 12px;
  display: flex;
  flex-direction: column;
  gap: 3px;
}
.brand-line { display: flex; align-items: baseline; gap: 7px; }
.logo-title {
  font-family: 'Fraunces', serif;
  font-size: 21px;
  font-weight: 600;
  line-height: 1.1;
  color: var(--charcoal);
  white-space: nowrap;
}
.logo-sub {
  font-size: 10px;
  font-weight: 600;
  color: var(--warm-gray);
  letter-spacing: 1.6px;
  text-transform: uppercase;
  white-space: nowrap;
}
.source-link {
  font-size: 11px;
  color: var(--warm-gray);
  text-decoration: none;
  white-space: nowrap;
  letter-spacing: 0.2px;
}
.source-link:hover { color: var(--charcoal); text-decoration: underline; }

/* ─── INSTALL BANNER ─── */
.install-banner {
  display: none;
  align-items: center;
  gap: 10px;
  padding: 10px 14px;
  background: #fff;
  border-bottom: 1px solid var(--stone);
  flex-shrink: 0;
  animation: slideDown 0.3s ease-out;
}
@keyframes slideDown { from { transform: translateY(-100%); opacity:0; } to { transform: translateY(0); opacity:1; } }
.install-banner.visible { display: flex; }
.install-icon { border-radius: 10px; flex-shrink: 0; }
.install-text {
  flex: 1; min-width: 0;
  display: flex; flex-direction: column; gap: 1px;
}
.install-text strong { font-size: 13px; color: var(--charcoal); }
.install-text span { font-size: 11px; color: var(--warm-gray); line-height: 1.3; }
.install-btn {
  background: var(--charcoal); color: #fff;
  border: none; border-radius: 8px;
  padding: 7px 14px; font-family: inherit;
  font-size: 12px; font-weight: 600;
  cursor: pointer; white-space: nowrap;
  flex-shrink: 0;
}
.install-btn:active { opacity: 0.8; }
.install-close {
  background: none; border: none;
  color: var(--warm-gray); font-size: 16px;
  cursor: pointer; padding: 4px;
  line-height: 1; flex-shrink: 0;
}

/* ─── LAYOUT ─── */
/* The map fills the body and the panel floats over it, rather than the two
   sitting either side of a hard divider. */
.app-body { display:flex; flex:1; overflow:hidden; position:relative; }

/* ─── SIDEBAR ─── */
/* Mobile: the aside is itself the white bottom sheet.
   Desktop: it is a transparent gutter and the cards inside carry the surface. */
aside {
  width: var(--sidebar);
  flex-shrink: 0;
  background: #fff;
  display: flex;
  flex-direction: column;
  overflow: hidden;
  z-index: 800;
  transition: transform 0.28s cubic-bezier(.4,0,.2,1);
}

/* Holds the filters, search and list. On desktop it is the second card. */
.panel-main {
  flex: 1;
  min-height: 0;
  display: flex;
  flex-direction: column;
  overflow: hidden;
}

/* The drag handle only exists for the mobile sheet */
.sheet-drag-area { display: none; }

@media (min-width: 701px) {
  aside {
    position: absolute;
    top: 0; left: 0; bottom: 0;
    /* Card width plus the gutter either side of it */
    width: calc(var(--sidebar) + 28px);
    height: auto;
    padding: 14px;
    gap: 10px;
    background: transparent;
    /* Card shadows spill past the padding, so this cannot clip */
    overflow: visible;
    /* The gutter is see-through: it must not swallow drags and clicks meant
       for the map underneath it. */
    pointer-events: none;
  }
  aside > * { pointer-events: auto; }

  .panel-brand,
  .panel-main {
    background: #fff;
    border-radius: 14px;
    border: 1px solid rgba(37,34,32,0.07);
    box-shadow:
      0 1px 2px rgba(37,34,32,0.05),
      0 8px 28px rgba(37,34,32,0.13);
  }
}

/* ─── SIDEBAR SEARCH ─── */
.sidebar-search {
  position: relative;
  padding: 10px 13px;
  border-bottom: 1px solid var(--stone);
  flex-shrink: 0;
}
.sidebar-search svg {
  position: absolute;
  left: 22px;
  top: 50%;
  transform: translateY(-50%);
  color: var(--warm-gray);
  pointer-events: none;
}
#search {
  width: 100%;
  box-sizing: border-box;
  background: var(--cream);
  border: 1.5px solid var(--stone);
  border-radius: 8px;
  padding: 7px 32px 7px 32px;
  font-family: inherit;
  font-size: 14px;
  color: var(--text);
  outline: none;
  transition: border-color 0.15s, background 0.15s;
}
#search::placeholder { color: var(--warm-gray); }
@media (max-width:700px) { #search { font-size: 16px; } }
.search-clear {
  position: absolute;
  right: 20px;
  top: 50%;
  transform: translateY(-50%);
  background: none;
  border: none;
  color: var(--warm-gray);
  font-size: 14px;
  cursor: pointer;
  padding: 4px 6px;
  line-height: 1;
  display: none;
}
.search-clear.visible { display: block; }
#search:focus {
  background: #fff;
  border-color: var(--charcoal);
}

/* ─── FILTERS ─── */
.filters {
  padding: 13px 13px 10px;
  border-bottom: 1px solid var(--stone);
  flex-shrink: 0;
}
.filters-label {
  font-size: 11px; font-weight: 700; letter-spacing: 1.2px;
  text-transform: uppercase; color: var(--warm-gray); margin-bottom: 8px;
}
.chips { display:inline-flex; flex-wrap:wrap; gap:5px; vertical-align: middle; }
.chip {
  display:flex; align-items:center; gap:5px;
  padding: 4px 8px 4px 6px;
  border-radius: 20px; font-size: 12px; font-weight: 500;
  cursor: pointer; border: 1.5px solid transparent;
  transition: all 0.12s; user-select: none;
  background: #EDEAE5; color: var(--warm-gray);
  font-family: inherit;   /* it is a <button> — do not inherit UA styling */
}
.chip-dot { width:6px; height:6px; border-radius:50%; flex-shrink:0; }
.chip:hover:not(.active) { background: #E3DFD9; }
.chip.active.chip-open       { background:var(--open-bg);       color:var(--open);       border-color:var(--open); }
.chip.active.chip-closed     { background:var(--closed-bg);     color:var(--closed);     border-color:var(--closed); }
.chip.active.chip-winter     { background:var(--winter-bg);     color:var(--winter);     border-color:var(--winter); }
.chip.active.chip-restricted { background:var(--restricted-bg); color:var(--restricted); border-color:var(--restricted); }
.chip.active.chip-blocked    { background:var(--blocked-bg);    color:var(--blocked);    border-color:var(--blocked); }
.chip.active.chip-snow       { background:var(--snow-bg);       color:var(--snow);       border-color:var(--snow); }
.chip.active.chip-unknown    { background:var(--unknown-bg);    color:var(--unknown);    border-color:var(--unknown); }


.list-header {
  padding: 9px 13px 4px;
  display:flex; align-items:center; justify-content:space-between;
  flex-shrink: 0;
}
.list-count { font-size:12px; color:var(--warm-gray); font-weight:500; }

.sort-toggle { display:flex; gap:2px; }
.sort-btn {
  background: none; border: 1px solid transparent;
  padding: 2px 7px; border-radius: 10px;
  font-size: 12px; font-weight: 500; font-family: inherit;
  color: var(--warm-gray); cursor: pointer;
  transition: all 0.12s; user-select: none;
}
.sort-btn:hover:not(.active) { background: #EDEAE5; }
.sort-btn.active {
  background: var(--stone); color: var(--charcoal);
  border-color: #d5d0ca;
}

.pass-list {
  flex:1; overflow-y:auto; padding: 0 5px 16px;
}
.pass-list::-webkit-scrollbar { width:3px; }
.pass-list::-webkit-scrollbar-thumb { background:var(--stone); border-radius:3px; }

.region-label {
  font-size: 11px; font-weight: 700; letter-spacing: 1px;
  text-transform: uppercase; color: var(--warm-gray);
  padding: 7px 7px 2px;
}
.pass-item {
  /* a <button>, so the UA defaults have to be unset explicitly */
  width: 100%; text-align: left; background: none;
  font-family: inherit; font-size: inherit; color: inherit;
  display:flex; align-items:center; gap:8px;
  padding: 7px 8px; border-radius: 7px; cursor:pointer;
  border: 1px solid transparent; transition: background 0.1s;
  margin-bottom: 1px;
}
.pass-item:hover { background: var(--cream); }
.pass-item.active { background: var(--cream); border-color: var(--stone); }
.pass-status-dot { width:8px; height:8px; border-radius:50%; flex-shrink:0; transition: background 0.3s; }

/* ─── LOADING STATE ─── */
@keyframes pulse { 0%,100% { opacity:1; } 50% { opacity:0.4; } }
body.loading .pass-status-dot,
body.loading .pass-badge { animation: pulse 1.2s ease-in-out infinite; }
.loading-hint {
  font-size: 11px; color: var(--warm-gray); font-weight: 400;
  display: none; margin-left: 4px; vertical-align: middle;
}
body.loading .loading-hint { display: inline; }
/* Own line: inline, it wraps mid-phrase into the chips when it gets long */
.data-note {
  display: block;
  font-size: 11px; color: var(--warm-gray); font-weight: 400;
  margin-top: 6px;
}
.data-note:empty { display: none; }
.data-note.error { color: var(--closed); font-weight: 500; }
body.loading .data-note { display: none; }
.pass-info { flex:1; min-width:0; }
.pass-name {
  display: block;
  font-size: 14px; font-weight: 500;
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.pass-meta { display: block; font-size: 12px; color: var(--warm-gray); margin-top: 1px; }
.pass-badge {
  font-size: 11px; font-weight: 600; padding: 2px 6px;
  border-radius: 10px; white-space: nowrap; flex-shrink: 0;
}
.pass-temp {
  display: flex; align-items: center; gap: 3px;
  font-size: 11.5px; color: var(--warm-gray); flex-shrink: 0;
  font-variant-numeric: tabular-nums;
}
.wx-icon { flex-shrink: 0; opacity: 0.85; }
body.loading .pass-temp { visibility: hidden; }


/* ─── MAP ─── */
#map { flex:1; z-index:100; }

/* ─── LEAFLET POPUP ─── */
.leaflet-popup-content-wrapper {
  border-radius: 12px !important;
  box-shadow: 0 6px 28px rgba(0,0,0,0.13) !important;
  padding: 0 !important;
  border: 1px solid var(--stone) !important;
  overflow: hidden;
  font-family: 'DM Sans', sans-serif !important;
}
.leaflet-popup-content { margin:0 !important; width:auto !important; min-width:240px; max-width:300px; }
.leaflet-popup-tip { background:#fff !important; }
/* Leaflet ships this at 3.39:1 — below AA */
.leaflet-popup-close-button { color: var(--warm-gray) !important; }
.leaflet-popup-close-button:hover,
.leaflet-popup-close-button:focus { color: var(--charcoal) !important; }
.popup-body { padding:15px 16px 14px; }
/* Name and status sit on one line; the old full-width colour bar read as
   chrome from an older era and the status badge kept wrapping the stat row. */
.popup-head {
  display: flex; align-items: flex-start; justify-content: space-between;
  gap: 10px; margin-bottom: 9px;
}
.popup-heading { min-width: 0; }
.popup-name {
  font-family: 'Fraunces', serif;
  font-size: 17px; font-weight: 600; color: var(--charcoal);
  line-height: 1.2; margin-bottom: 2px;
}
.popup-canton {
  font-size: 11px; color: var(--warm-gray);
  letter-spacing: 0.6px; text-transform: uppercase;
  font-weight: 600;
}
.popup-row { display:flex; align-items:center; gap:8px; margin-bottom:9px; flex-wrap:wrap; }
.popup-alt {
  display:flex; align-items:center; gap:4px;
  font-size: 12px; color: var(--text); font-weight: 500;
}
.popup-status-badge {
  display:inline-flex; align-items:center; gap:5px;
  font-size: 11.5px; font-weight: 600;
  padding: 3px 9px; border-radius: 20px;
  flex-shrink: 0; white-space: nowrap;
}.popup-pills {
  display: flex; flex-wrap: wrap; gap: 5px;
  margin-top: 7px;
}
.popup-pill {
  font-size: 11.5px; color: var(--warm-gray);
  background: var(--cream); border: 1px solid var(--stone);
  border-radius: 20px; padding: 2px 8px; white-space: nowrap;
}
.popup-notes {
  font-size: 12px; color: var(--warm-gray);
  line-height: 1.55; border-top: 1px solid var(--stone);
  padding-top: 9px; margin-top: 7px;
}

/* ─── POPUP: live detail from TCS ─── */
.popup-route {
  font-size: 12px; color: var(--warm-gray);
  margin: 0 0 9px; line-height: 1.4;
}

/* Next few hours. Shown inline rather than on hover so it works on touch.
   Only a top rule: whatever follows (notes, or the restrictions panel) draws
   its own, and having both produced a double separator. */
.popup-forecast {
  display: flex; gap: 2px; justify-content: space-between;
  margin: 0; padding: 9px 2px;
  border-top: 1px solid var(--stone);
}
/* The next section draws its own top rule, so its margin would stack on top of
   this padding and leave more space below the strip than above it. */
.popup-forecast + .popup-notes,
.popup-forecast + .popup-more { margin-top: 0; }
.fc {
  display: flex; flex-direction: column; align-items: center; gap: 2px;
  flex: 1; min-width: 0;
}
.fc-time { font-size: 10px; color: var(--warm-gray); font-variant-numeric: tabular-nums; }
.fc-temp { font-size: 11.5px; font-weight: 500; color: var(--text); font-variant-numeric: tabular-nums; }
.popup-winter {
  display: flex; align-items: center; gap: 5px;
  font-size: 12px; color: var(--warm-gray);
  margin-top: 7px; line-height: 1.4;
}
.popup-winter svg { flex-shrink: 0; opacity: 0.7; }

.popup-more {
  margin-top: 9px; border-top: 1px solid var(--stone); padding-top: 8px;
}
.popup-more summary {
  font-size: 12px; font-weight: 600; color: var(--charcoal);
  cursor: pointer; list-style: none; user-select: none;
}
.popup-more summary::-webkit-details-marker { display: none; }
.popup-more summary::after { content: ' ▾'; color: var(--warm-gray); }
.popup-more[open] summary::after { content: ' ▴'; }
.popup-more-block { margin-top: 8px; }
.popup-more-block strong {
  display: block; font-size: 10.5px; font-weight: 700;
  text-transform: uppercase; letter-spacing: 0.7px;
  color: var(--warm-gray); margin-bottom: 3px;
}
.popup-more-block p { font-size: 12px; line-height: 1.5; color: var(--text); }
.popup-nav-btns {
  display: flex; gap: 5px; margin-top: 10px;
}
.popup-maps-btn {
  display: flex; align-items: center; justify-content: center; gap: 4px;
  flex: 1; padding: 6px 6px;
  background: transparent; color: var(--charcoal);
  border: 1.5px solid var(--stone); border-radius: 7px; cursor: pointer;
  font-family: inherit; font-size: 12px; font-weight: 500;
  transition: background 0.15s, border-color 0.15s;
  white-space: nowrap;
}
.popup-maps-btn:hover { background: var(--stone); border-color: var(--faint); }

/* ─── MAP CONTROLS ─── */
.map-controls {
  position: absolute; top: 10px; right: 10px; z-index: 900;
  display: flex; flex-direction: column; align-items: flex-end; gap: 6px;
}
.map-btn {
  width: 34px; height: 34px;
  display: flex; align-items: center; justify-content: center;
  background: #fff; color: var(--charcoal);
  border: 1px solid var(--stone); border-radius: 9px;
  cursor: pointer; font-family: inherit;
  box-shadow: 0 1px 2px rgba(37,34,32,0.06), 0 4px 12px rgba(37,34,32,0.12);
}
.map-btn:hover { background: var(--cream); }
.map-btn.active { background: var(--charcoal); color: #fff; border-color: var(--charcoal); }

/* This has to beat the display rule below: [hidden] is only a UA stylesheet
   rule, so an author `display: flex` silently overrides it and the menu can
   never hide. */
.layers-menu[hidden] { display: none; }
.layers-menu {
  display: flex; flex-direction: column; gap: 2px;
  background: #fff; padding: 4px;
  border: 1px solid var(--stone); border-radius: 10px;
  box-shadow: 0 1px 2px rgba(37,34,32,0.06), 0 8px 24px rgba(37,34,32,0.16);
}
.layer-opt {
  background: none; border: none; border-radius: 7px;
  padding: 7px 14px; min-width: 104px; text-align: left;
  font-family: inherit; font-size: 13px; font-weight: 500;
  color: var(--text); cursor: pointer; white-space: nowrap;
}
.layer-opt:hover { background: var(--cream); }
.layer-opt.active { background: var(--charcoal); color: #fff; font-weight: 600; }

/* ─── ROUTE PANEL (mobile bottom sheet) ─── */
.route-close-btn {
  position: absolute; top: 52px; right: 10px; z-index: 900;
  background: white; border: 1px solid var(--stone);
  border-radius: 50%; width: 30px; height: 30px;
  display: none; align-items: center; justify-content: center;
  cursor: pointer; box-shadow: 0 2px 8px rgba(0,0,0,0.1);
  font-size: 14px; color: var(--text);
}

.empty-state {
  padding:36px 14px; text-align:center; color:var(--warm-gray); font-size:14px; line-height:1.6;
}

/* ─── MOBILE OVERLAY ─── */
.sidebar-overlay {
  display: none;
  position: fixed;
  inset: 0;
  background: rgba(0,0,0,0.4);
  z-index: 700;
}

/* ─── BOTTOM SHEET ─── */
.sheet-handle-bar {
  display: none;
  width: 36px; height: 4px;
  background: #D0CAC2;
  border-radius: 2px;
  margin: 0 auto 0;
  flex-shrink: 0;
}

/* ─── RESPONSIVE ─── */
@media (max-width: 700px) {
  :root { --sidebar: 290px; }

  /* The sheet already carries the surface, so the brand is a plain block at
     the top of it rather than a second card. Kept compact: it is above the
     chips, so every pixel here is one the peeking sheet grows by. */
  .panel-brand { padding: 2px 14px 9px; }
  .logo-title { font-size: 18px; }
  .source-link { font-size: 10px; }

  .sidebar-overlay { display: none !important; } /* no overlay on mobile - sheet is inline */

  .sheet-handle-bar { display: block; }

  /* A popup is nearly full width here and its own close button sits top-right,
     so both map controls stack down the left instead. Map controls render
     above popups in Leaflet, so this has to be solved by placement. */
  .map-controls { left: 10px; right: auto; align-items: flex-start; }
  .route-close-btn { left: 10px; right: auto; top: 52px; }

  #map { width: 100%; }

  /* Give map breathing room above the peeking sheet */
  .app-body { padding-bottom: 0; }
  #map {
    /* Map fills full height; sheet overlays on top */
    height: 100%;
  }

  aside {
    position: fixed;
    bottom: 0;
    left: 0;
    right: 0;
    width: 100%;
    height: 45dvh; /* default, overridden by JS */
    border-right: none;
    border-top: 1px solid var(--stone);
    border-radius: 16px 16px 0 0;
    box-shadow: 0 -4px 24px rgba(0,0,0,0.12);
    z-index: 900;
    overflow: hidden;
    transform: none;
    transition: height 0.3s cubic-bezier(.4,0,.2,1);
    max-height: 75dvh;
  }

  /* Sheet inner layout */
  aside .filters {
    flex-shrink: 0;
  }
  aside .list-header {
    flex-shrink: 0;
  }
  aside .pass-list {
    flex: 1;
    min-height: 0;
    overflow-y: auto;
  }

  /* handle touch area */
  .sheet-drag-area,
  .panel-brand,
  .filters,
  .sidebar-search {
    cursor: ns-resize;
    touch-action: none;
    user-select: none;
  }
  .sheet-drag-area {
    padding: 10px 0 4px;
    display: flex;
    align-items: center;
    justify-content: center;
    flex-shrink: 0;
  }
}

@media (max-width: 400px) {
  .logo-title { font-size: 17px; }
}

/* ─── ACCESSIBILITY ─── */

/* Visible only to screen readers */
.sr-only {
  position: absolute; width: 1px; height: 1px;
  padding: 0; margin: -1px; overflow: hidden;
  clip: rect(0 0 0 0); clip-path: inset(50%); white-space: nowrap;
}

/* 77 list rows sit between the top of the page and the map in tab order */
.skip-link {
  position: absolute; left: 8px; top: -60px; z-index: 3000;
  background: #fff; color: var(--charcoal);
  padding: 9px 14px; border-radius: 0 0 8px 8px;
  font-size: 13px; font-weight: 600; text-decoration: none;
  border: 2px solid var(--charcoal);
  transition: top 0.15s;
}
.skip-link:focus { top: 0; }

/* Keyboard focus has to be visible everywhere it can land */
:focus-visible {
  outline: 3px solid var(--charcoal);
  outline-offset: 2px;
  border-radius: 4px;
}
.pass-item:focus-visible { outline-offset: -2px; }
#map:focus-visible, .leaflet-container:focus-visible { outline-offset: -3px; }

@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after {
    animation-duration: 0.01ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 0.01ms !important;
    scroll-behavior: auto !important;
  }
  /* The loading pulse is decoration; keep the dot legible instead */
  body.loading .pass-status-dot,
  body.loading .pass-badge { animation: none !important; opacity: 1 !important; }
}
