/* v0.0.40 */
:root {
  --paper: #F5F5F7;
  --ink: #14171C;
  --surface: #FFFFFF;
  --muted: #7A828E;
  --hairline: #ECECEF;

  --tube-a: #E8433B;
  --tube-b: #B3181E;
  --traffic-a: #F5A623;
  --traffic-b: #C17D0A;
  --weather-a: #4FB6E8;
  --weather-b: #1C7DBB;
  --air-a: #9AA5B1;
  --air-b: #6B7684;
  --petrol-a: #0BA97C;
  --petrol-b: #0E7C5A;
  --social-a: #6C7BFF;
  --social-b: #3B4CDB;
  --news-a: #FF5A5F;
  --news-b: #D6262C;
  --crime-a: #8B5FA8;
  --crime-b: #5C3A78;
  --ae-a: #2FA8A0;
  --ae-b: #1D7570;
  --links-a: #D9A441;
  --links-b: #A6741E;

  --shops-a: #4A6FA5;
  --shops-b: #2D4A75;
  --uv-a: #F5C518;
  --uv-b: #D99A0A;

  --band-good: #0BA97C;
  --band-mid: #E8A33D;
  --band-bad: #D6262C;

  --font-display: "Poppins", sans-serif;
  --font-body: "Inter", -apple-system, BlinkMacSystemFont, sans-serif;

  --radius: 20px;
}

* { box-sizing: border-box; }

html {
  overflow-x: hidden;
}

.top-banner {
  width: 100%;
  position: fixed;
  top: 0;
  left: 0;
  right: 0;
  z-index: 10;
  display: flex;
  flex-wrap: nowrap;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
  background: #113f8a;
  padding: 10px 16px;
}
.top-banner-text {
  color: #fff;
  font-family: var(--font-display);
  font-weight: 700;
  font-size: 15px;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.top-banner .info-btn,
.top-banner .info-btn svg {
  color: #fff !important;
  flex-shrink: 0;
}
.top-banner .info-btn:hover,
.top-banner .info-btn:focus-visible {
  color: #fff !important;
  opacity: 0.8;
}

body {
  margin: 0;
  background: var(--paper);
  color: var(--ink);
  font-family: var(--font-body);
  -webkit-font-smoothing: antialiased;
  overflow-x: hidden;
}

/* ---------- page: single flex column; .brand sits at the bottom on both
   mobile and desktop, just styled differently per breakpoint ---------- */
.page {
  display: flex;
  flex-direction: column;
  min-height: 100vh;
  padding-top: 50px; /* clears the fixed top-banner (~46px tall) */
}

/* ---------- brand bar ---------- */
.brand {
  order: 1; /* bottom, both mobile and desktop */
  display: flex;
  align-items: center;
  justify-content: center;
  max-width: 480px;
  width: 100%;
  margin: 0 auto;
  padding: 4px 16px 16px;
}
.brand-text {
  display: block;
  width: 100%;
  text-align: center;
  font-family: var(--font-display);
  font-weight: 600;
  font-size: 18px;
}
.brand-text a {
  color: inherit;
  text-decoration: underline;
  text-underline-offset: 3px;
}
.brand-text a:hover,
.brand-text a:focus-visible {
  text-decoration-thickness: 2px;
}

.info-btn {
  all: unset;
  box-sizing: border-box;
  width: 26px;
  height: 26px;
  flex: 0 0 auto;
  display: flex;
  align-items: center;
  justify-content: center;
  color: var(--muted);
  cursor: pointer;
  border-radius: 50%;
}
.info-btn svg {
  width: 20px;
  height: 20px;
}
.info-btn:hover,
.info-btn:focus-visible {
  color: var(--ink);
}

/* ---------- feed (mobile-first: single column, portrait default) ---------- */
.feed {
  max-width: 560px;
  margin: 0 auto;
  padding: 12px 16px 40px;
  display: grid;
  grid-template-columns: repeat(2, 1fr);
  align-items: stretch;
  gap: 12px;
  width: 100%;
}

.card {
  background: var(--surface);
  border-radius: var(--radius);
  overflow: hidden;
  box-shadow: 0 4px 16px rgba(20, 23, 28, 0.07);
  display: flex;
  flex-direction: column;
  height: 270px;
}

/* Mobile: icon sits in the top-left corner sharing a row with just the
   eyebrow label; stat/sub break onto their own full-width lines below,
   rather than being squeezed into a narrow column beside the icon. This
   is done with CSS Grid + display:contents (unwrapping .card-head-text
   so its children become direct grid items) rather than changing the
   HTML, so the desktop override below can cleanly revert to the original
   inline row layout without needing separate markup. */
.card-head {
  all: unset;
  box-sizing: border-box;
  display: grid;
  grid-template-columns: auto 1fr auto;
  align-items: center;
  column-gap: 8px;
  row-gap: 4px;
  width: 100%;
  min-height: 118px; /* reserves space for a 2-line stat regardless of
    whether this card's actual text wraps that far, so header height
    stays consistent across cards with different text lengths */
  cursor: pointer;
  padding: 14px;
}
.card-head-text {
  display: contents;
}

.badge {
  grid-column: 1;
  grid-row: 1;
  flex: 0 0 auto;
  width: 28px;
  height: 28px;
  border-radius: 50%;
  overflow: hidden;
  display: flex;
  align-items: center;
  justify-content: center;
  color: #fff;
}
.badge svg { width: 15px; height: 15px; }
.badge--lg { width: 52px; height: 52px; }
.badge--lg svg { width: 26px; height: 26px; }

.card--tube .badge    { background: linear-gradient(135deg, var(--tube-a), var(--tube-b)); }
.card--traffic .badge { background: linear-gradient(135deg, var(--traffic-a), var(--traffic-b)); }
.card--weather .badge { background: linear-gradient(135deg, var(--weather-a), var(--weather-b)); }
.card--air .badge     { background: linear-gradient(135deg, var(--air-a), var(--air-b)); } /* JS sets the real colour per AQI band once loaded */
.card--petrol .badge  { background: linear-gradient(135deg, var(--petrol-a), var(--petrol-b)); }
.card--social .badge  { background: linear-gradient(135deg, var(--social-a), var(--social-b)); }
.card--news .badge    { background: linear-gradient(135deg, var(--news-a), var(--news-b)); }
.card--crime .badge   { background: linear-gradient(135deg, var(--crime-a), var(--crime-b)); }
.card--ae .badge      { background: linear-gradient(135deg, var(--ae-a), var(--ae-b)); }
.card--links .badge   { background: linear-gradient(135deg, var(--links-a), var(--links-b)); }
.card--shops .badge   { background: linear-gradient(135deg, var(--shops-a), var(--shops-b)); }
.card--uv .badge      { background: linear-gradient(135deg, var(--uv-a), var(--uv-b)); }

/* Soft colour-tinted header backgrounds, matching each card's own accent —
   a light wash rather than the full saturated badge colour. Air's tint is
   static (its badge colour shifts dynamically per AQI band via JS; the
   header tint doesn't follow that, to keep this simple). */
.card--tube .card-head, .card--tube .panel-head       { background: color-mix(in srgb, var(--tube-a) 12%, white); }
.card--traffic .card-head, .card--traffic .panel-head { background: color-mix(in srgb, var(--traffic-a) 12%, white); }
.card--weather .card-head, .card--weather .panel-head { background: color-mix(in srgb, var(--weather-a) 12%, white); }
.card--air .card-head, .card--air .panel-head         { background: color-mix(in srgb, var(--air-a) 12%, white); }
.card--petrol .card-head, .card--petrol .panel-head   { background: color-mix(in srgb, var(--petrol-a) 12%, white); }
.card--social .card-head, .card--social .panel-head   { background: color-mix(in srgb, var(--social-a) 12%, white); }
.card--news .card-head, .card--news .panel-head       { background: color-mix(in srgb, var(--news-a) 12%, white); }
.card--crime .card-head, .card--crime .panel-head     { background: color-mix(in srgb, var(--crime-a) 12%, white); }
.card--ae .card-head, .card--ae .panel-head           { background: color-mix(in srgb, var(--ae-a) 12%, white); }
.card--links .card-head, .card--links .panel-head     { background: color-mix(in srgb, var(--links-a) 12%, white); }
.card--uv .card-head, .card--uv .panel-head            { background: color-mix(in srgb, var(--uv-a) 12%, white); }
.card--shops .card-head, .card--shops .panel-head     { background: color-mix(in srgb, var(--shops-a) 12%, white); }

.card-eyebrow {
  grid-column: 2;
  grid-row: 1;
  font-size: 11.5px;
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 0.06em;
  color: var(--muted);
}

.card-stat {
  grid-column: 1 / -1;
  grid-row: 2;
  font-family: var(--font-display);
  font-weight: 700;
  font-size: 17px;
  line-height: 1.25;
}

.card-sub {
  grid-column: 1 / -1;
  grid-row: 3;
  font-size: 12.5px;
  color: var(--muted);
}

.chevron {
  grid-column: 3;
  grid-row: 1;
  flex: 0 0 auto;
  width: 18px;
  height: 18px;
  color: var(--muted);
}

/* ---------- inline preview: content always visible (not just at desktop
   widths). Real content, not decoration. ---------- */
.card-preview {
  display: block;
  flex: 1 1 auto;
  min-height: 0;
  overflow: hidden;
  cursor: pointer;
  padding: 0 14px 14px;
}
.card-preview--social {
  min-height: 0;
}

/* ---------- fullscreen panel ---------- */
.panel {
  display: none;
}
.panel.is-open {
  display: flex;
  flex-direction: column;
  position: fixed;
  inset: 0;
  z-index: 100;
  width: 100%;
  max-width: 100vw;
  overflow-x: hidden;
  background: var(--surface);
  overflow-y: auto;
  -webkit-overflow-scrolling: touch;
  animation: expandIn 0.22s ease;
}

/* Info panel: same fullscreen open/close mechanism as the data cards, but
   reads as a backdrop + centered dialog rather than a full-bleed page,
   since it's a short note rather than a scrollable list of content. */
.panel--info.is-open {
  align-items: center;
  justify-content: center;
  background: rgba(20, 23, 28, 0.55);
  padding: 24px;
}
.info-content {
  background: var(--surface);
  border-radius: var(--radius);
  padding: 32px 24px 24px;
  max-width: 360px;
  width: 100%;
  text-align: center;
}
.info-content p {
  margin: 0 0 12px;
  font-size: 14.5px;
  line-height: 1.5;
}
.info-content p:last-child {
  margin-bottom: 0;
  color: var(--muted);
}
.info-content a {
  color: var(--ink);
  font-weight: 600;
}
.info-content h3 {
  margin: 0 0 12px;
  font-family: var(--font-display);
  font-weight: 700;
  font-size: 17px;
}

.source-btn {
  all: unset;
  box-sizing: border-box;
  width: 22px;
  height: 22px;
  flex: 0 0 auto;
  border-radius: 50%;
  border: 1.5px solid var(--hairline);
  color: var(--muted);
  font-family: var(--font-body);
  font-size: 12px;
  font-weight: 700;
  display: flex;
  align-items: center;
  justify-content: center;
  cursor: pointer;
}
.source-btn:hover,
.source-btn:focus-visible {
  color: var(--ink);
  border-color: var(--muted);
}
@keyframes expandIn {
  from { opacity: 0; transform: scale(0.97); }
  to   { opacity: 1; transform: scale(1); }
}
@media (prefers-reduced-motion: reduce) {
  .panel.is-open { animation: none; }
}

.panel-close {
  all: unset;
  position: fixed;
  top: 16px;
  right: 16px;
  width: 36px;
  height: 36px;
  border-radius: 50%;
  background: var(--paper);
  display: flex;
  align-items: center;
  justify-content: center;
  cursor: pointer;
  color: var(--ink);
  z-index: 2;
}
.panel-close svg { width: 16px; height: 16px; }

.panel-head {
  display: flex;
  align-items: center;
  gap: 14px;
  padding: 20px 56px 28px 20px;
}
.panel-title {
  font-family: var(--font-display);
  font-weight: 700;
  font-size: 16px;
}

.panel-body {
  padding: 20px 20px 32px;
  max-width: 560px;
  width: 100%;
  margin: 0 auto;
}
.panel-body--social {
  padding: 0 8px 32px;
}

/* ---------- rows (petrol stations / headlines / forecast / departures) ---------- */
.rows {
  list-style: none;
  margin: 0 0 14px;
  padding: 0;
  border-top: 1px solid var(--hairline);
}
.rows li {
  padding: 12px 0;
  border-bottom: 1px solid var(--hairline);
}

.row {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 10px;
}
.row-icon {
  flex: 0 0 auto;
  width: 20px;
  height: 20px;
  color: var(--weather-b);
  align-self: center;
}
.row-name {
  flex: 1;
  min-width: 0;
  display: flex;
  align-items: center;
  gap: 8px;
}
.station-name {
  font-size: 14.5px;
  font-weight: 500;
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.station-price {
  font-family: var(--font-display);
  font-size: 15px;
  font-weight: 600;
  flex: 0 0 auto;
}
.station-price.band-good { color: var(--band-good); }
.station-price.band-mid { color: var(--band-mid); }
.station-price.band-bad { color: var(--band-bad); }
.row.is-cheapest .station-price { color: var(--petrol-a); }
.row.is-cheapest .station-name::before { content: "● "; color: var(--petrol-a); }
.row.is-next .station-price { color: var(--tube-a); }
.row.is-next .station-name::before { content: "● "; color: var(--tube-a); }
.row.is-severe .station-name::before { content: "● "; color: var(--traffic-b); }

.rows--news a {
  color: var(--ink);
  text-decoration: none;
  font-size: 14.5px;
  font-weight: 500;
  line-height: 1.4;
  display: block;
}
.rows--news a:hover,
.rows--news a:focus-visible {
  text-decoration: underline;
}
.rows--news li .card-foot {
  margin-top: 3px;
}

/* Expandable traffic disruption rows */
.row-toggle {
  all: unset;
  box-sizing: border-box;
  display: block;
  width: 100%;
  cursor: pointer;
}
.row-toggle .row {
  align-items: center;
}
.chevron-small {
  flex: 0 0 auto;
  width: 16px;
  height: 16px;
  color: var(--muted);
  margin-left: 8px;
  transition: transform 0.15s ease;
}
.row-toggle[aria-expanded="true"] .chevron-small {
  transform: rotate(180deg);
}
.row-detail {
  padding: 4px 0 8px 4px;
  font-size: 13px;
  color: var(--muted);
  line-height: 1.5;
}
.row-detail p {
  margin: 0 0 4px;
}
.row-detail strong {
  color: var(--ink);
  font-weight: 600;
}

/* compact preview variant: same row markup, tighter spacing, no top rule
   (the card-head already provides visual separation above it) */
.rows--preview {
  border-top: none;
  margin: 0;
}
.rows--preview li {
  padding: 8px 0;
}
.rows--preview li:last-child {
  border-bottom: none;
}

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

.section-label {
  font-size: 11.5px;
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 0.06em;
  color: var(--muted);
  margin: 18px 0 4px;
}
.section-label:first-child {
  margin-top: 0;
}

.group-heading {
  font-family: var(--font-display);
  font-weight: 700;
  font-size: 16px;
  margin: 22px 0 2px;
}
.group-heading--icon {
  display: flex;
  align-items: center;
  gap: 8px;
}
.group-heading-icon {
  height: 22px;
  width: 22px;
  object-fit: contain;
  flex: 0 0 auto;
}
.line-status {
  all: unset;
  box-sizing: border-box;
  display: inline-flex;
  align-items: center;
  gap: 6px;
  font-family: var(--font-body);
  font-size: 12.5px;
  font-weight: 600;
  margin: 4px 0 12px;
  padding: 4px 10px;
  border-radius: 100px;
  background: var(--paper);
  color: var(--muted);
  cursor: default;
}
.line-status.has-detail {
  cursor: pointer;
}
.line-status::before {
  content: "";
  width: 7px;
  height: 7px;
  border-radius: 50%;
  background: currentColor;
  flex: 0 0 auto;
}
.line-status .chevron-small {
  display: none;
  color: currentColor;
  margin-left: 2px;
}
.line-status.has-detail .chevron-small {
  display: block;
}
.line-status[aria-expanded="true"] .chevron-small {
  transform: rotate(180deg);
}
.line-status-detail {
  margin: -8px 0 12px;
  padding: 10px 12px;
  border-radius: 10px;
  background: var(--paper);
  font-size: 13px;
  color: var(--muted);
  line-height: 1.5;
}
.line-status-detail p {
  margin: 0 0 4px;
}
.line-status-detail p:last-child {
  margin-bottom: 0;
}
.line-status-detail strong {
  color: var(--ink);
  font-weight: 600;
}
.line-status.status-good { color: var(--band-good); }
.line-status.status-mid  { color: var(--band-mid); }
.line-status.status-bad  { color: var(--band-bad); }

.section-label--station-break {
  margin-top: 32px;
  padding-top: 14px;
  border-top: 1px solid var(--hairline);
}
.group-heading:first-child {
  margin-top: 0;
}

.retailer-logo {
  display: block;
  height: 20px;
  width: auto;
  margin: 40px 0 4px;
  padding-top: 14px;
  border-top: 1px solid var(--hairline);
  object-fit: contain;
}
.retailer-logo:first-child {
  margin-top: 0;
  padding-top: 0;
  border-top: none;
}
.retailer-logo--text {
  height: auto;
  font-family: var(--font-display);
  font-weight: 700;
  font-size: 20px;
  letter-spacing: 0.02em;
}
/* Aldi's logo appeared tiny at the shared 20px height — likely built-in
   padding in the SVG itself around a smaller mark. Sized up on its own
   as a first attempt; can't visually confirm without seeing the file. */
.retailer-logo--aldi {
  height: 42px;
}

.group-heading + .section-label {
  margin-top: 6px;
}

/* Petrol map — Leaflet is lazy-loaded into this on first expand */
.petrol-map {
  width: 100%;
  height: 220px;
  border-radius: 14px;
  overflow: hidden;
  background: var(--paper);
  margin-bottom: 4px;
}
.crime-map-el {
  height: 440px;
}
#petrol-map {
  height: 420px;
}
#traffic-map {
  height: 420px;
}
.rows--compact li {
  padding: 6px 0;
}
.rows--compact .station-name {
  font-size: 12.5px;
}
.rows--compact .station-price {
  font-size: 12.5px;
}
.petrol-map .leaflet-popup-content {
  font-family: var(--font-body);
  font-size: 13px;
  line-height: 1.5;
}
.petrol-map .price-pin {
  background: var(--surface);
  border: 2px solid var(--petrol-a);
  border-radius: 8px;
  padding: 3px 7px;
  font-family: var(--font-display);
  font-weight: 600;
  font-size: 12px;
  white-space: nowrap;
  box-shadow: 0 2px 6px rgba(20, 23, 28, 0.15);
}
.petrol-map .price-pin.is-cheapest {
  background: var(--petrol-a);
  color: #fff;
  border-color: var(--petrol-a);
}
.petrol-map .crime-dot {
  width: 12px;
  height: 12px;
  border-radius: 50%;
  background: var(--crime-a);
  border: 2px solid #fff;
  box-shadow: 0 1px 4px rgba(20, 23, 28, 0.3);
}

/* Weather hourly strip */
.hourly-strip {
  display: flex;
  gap: 14px;
  overflow-x: auto;
  padding: 4px 2px 12px;
  scroll-snap-type: x proximity;
}
.hourly-item {
  flex: 0 0 auto;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 4px;
  min-width: 44px;
  scroll-snap-align: center;
}
.hourly-item.is-now {
  font-weight: 700;
}
.hourly-item.is-now .hourly-time {
  color: var(--ink);
}
.hourly-time {
  font-size: 11px;
  color: var(--muted);
}
.hourly-icon {
  width: 22px;
  height: 22px;
  color: var(--weather-b);
}
.hourly-temp {
  font-family: var(--font-display);
  font-weight: 600;
  font-size: 13px;
}
.hourly-temp.band-good { color: var(--band-good); }
.hourly-temp.band-mid { color: var(--band-mid); }
.hourly-temp.band-bad { color: var(--band-bad); }
.card-stat.band-good { color: var(--band-good); }
.card-stat.band-mid { color: var(--band-mid); }
.card-stat.band-bad { color: var(--band-bad); }

.card-foot {
  font-size: 12px;
  color: var(--muted);
  margin: 0;
}

.text-link {
  color: var(--ink);
  font-weight: 600;
}

/* ---------- focus states ---------- */
.card-head:focus-visible,
.panel-close:focus-visible,
a:focus-visible {
  outline: 2px solid var(--ink);
  outline-offset: 2px;
}

/* ---------- desktop: 3-col grid, inline previews, brand moves to bottom.
   900px chosen (rather than the 640px tablet step) because 3 real columns
   of these cards need more like a small-laptop width to not feel cramped —
   a tablet portrait (~768-820px) still reads as "mobile" here. ---------- */
@media (min-width: 900px) {
  /* Petrol map matches traffic's size exactly, per request — same
     dimensions on mobile and this same 2x desktop treatment. */
  #petrol-map {
    height: 840px;
  }
  #panel-petrol .panel-body {
    max-width: 1080px;
  }

  /* Traffic map at 2x size on desktop — both dimensions, not just height,
     since a road-flow map reads better wide than tall. Widens just this
     one panel's content column beyond the shared 560px default. */
  #traffic-map {
    height: 840px;
  }
  #panel-traffic .panel-body {
    max-width: 1080px;
  }

  .brand {
    padding: 12px 16px 40px;
    max-width: none;
  }

  .feed {
    max-width: 1080px;
    grid-template-columns: repeat(3, 1fr);
    gap: 20px;
    padding: 32px 24px 24px;
  }

  /* More horizontal room per card at this width, so a taller card reads
     as spacious rather than cramped. */
  .card {
    height: 340px;
  }

  /* Revert to the original inline row layout: icon + vertical text stack
     side by side, rather than mobile's icon-and-eyebrow-then-full-width
     stat/sub arrangement. Desktop cards are wide enough that the inline
     layout doesn't need the extra room the mobile restructure creates. */
  .card-head {
    display: flex;
    align-items: center;
    column-gap: 12px;
    min-height: auto;
    padding: 16px 16px 10px;
  }
  .card-head-text {
    display: flex;
    flex-direction: column;
    gap: 2px;
    flex: 1;
    min-width: 0;
  }
  .badge {
    width: 44px;
    height: 44px;
  }
  .badge svg { width: 22px; height: 22px; }
  .card-stat {
    font-size: 18px;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
  }
  .chevron {
    width: 20px;
    height: 20px;
  }

  .card-preview {
    overflow-y: auto;
    cursor: default;
    padding: 0 16px 16px;
  }
}
