/*
 * Lifted from the app's src/theme.ts so the page and the product read as one
 * thing: petrol ink, one loud amber numeral, mint for cheap and coral for dear.
 */
:root {
  --ink: #080d0c;
  --surface: #111917;
  --surface-hi: #18231f;
  --hair: #24322e;
  --text: #edf3f0;
  --muted: #7e918b;
  --amber: #ffc145;
  --mint: #56e39f;
  --coral: #ff7a6b;
  --on-accent: #080d0c;

  --rail: 400px;
  --ease-out: cubic-bezier(0.16, 1, 0.3, 1);

  /* Whichever way we're pointing right now. Set per-reveal in JS. */
  --accent: var(--amber);
  --accent-soft: rgba(255, 193, 69, 0.16);

  color-scheme: dark;
}

* { box-sizing: border-box; }

html, body {
  height: 100%;
  margin: 0;
  overflow: hidden;
  background: var(--ink);
  color: var(--text);
  font: 15px/1.5 -apple-system, BlinkMacSystemFont, 'Segoe UI', Inter, Roboto, sans-serif;
  -webkit-font-smoothing: antialiased;
}

/* Prices are compared by eye down a column; they must not jitter. */
.num { font-variant-numeric: tabular-nums; }

button { font: inherit; color: inherit; border: 0; background: none; cursor: pointer; }
a { color: var(--accent); }

:focus-visible {
  outline: 2px solid var(--amber);
  outline-offset: 3px;
  border-radius: 6px;
}

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

#map {
  position: fixed;
  inset: 0;
  z-index: 0;
  background: var(--ink);
}

/* OpenStreetMap only publishes a light basemap, so the page makes its own dark
   one: invert flips land and water, and the hue rotation puts the colours back
   the right way round afterwards. Cheaper than a second tile provider, and it
   leaves the markers as the only saturated thing on screen.

   A tileset that is already dark says so (MAP_TILE_DARK), and gets a gentle
   cool-down instead — inverting it twice would just hand back the light one. */
#map[data-tiles='invert'] .leaflet-tile-pane {
  filter: invert(1) hue-rotate(180deg) brightness(0.82) contrast(0.85) saturate(0.32);
}
#map[data-tiles='dark'] .leaflet-tile-pane {
  filter: saturate(0.55) brightness(0.82) contrast(1.05);
}

.leaflet-container { background: var(--ink); font: inherit; }

.leaflet-control-attribution {
  background: rgba(8, 13, 12, 0.82) !important;
  color: var(--muted) !important;
  font-size: 10.5px !important;
  backdrop-filter: blur(8px);
}
.leaflet-control-attribution a { color: #9fb2ab !important; }

.leaflet-control-zoom { display: none; }

/* -------------------------------------------------------------- markers -- */

.pin { position: relative; }

.pin__dot {
  position: absolute;
  left: 50%;
  top: 50%;
  width: 14px;
  height: 14px;
  margin: -7px 0 0 -7px;
  border-radius: 50%;
  background: var(--pin, var(--amber));
  box-shadow: 0 0 0 3px rgba(8, 13, 12, 0.85), 0 4px 14px rgba(0, 0, 0, 0.6);
}

/* Two rings, offset in time, so the winner keeps breathing after it lands. */
.pin--hero .pin__ring {
  position: absolute;
  left: 50%;
  top: 50%;
  width: 22px;
  height: 22px;
  margin: -11px 0 0 -11px;
  border-radius: 50%;
  border: 2px solid var(--pin, var(--amber));
  animation: ping 2.4s var(--ease-out) infinite;
}
.pin--hero .pin__ring:nth-child(2) { animation-delay: 1.2s; }

@keyframes ping {
  0%   { transform: scale(0.5); opacity: 0.9; }
  70%  { transform: scale(3.4); opacity: 0; }
  100% { transform: scale(3.4); opacity: 0; }
}

.pin--hero .pin__dot {
  width: 18px;
  height: 18px;
  margin: -9px 0 0 -9px;
  box-shadow: 0 0 0 4px rgba(8, 13, 12, 0.9), 0 0 26px var(--pin, var(--amber)),
    0 6px 18px rgba(0, 0, 0, 0.7);
}

/* The price, on the map, at the place charging it — but held back until the
   card's number has finished climbing, or the map gives the answer away while
   the reveal is still building to it.

   Deliberately carries no animation fill mode: if the tab is in the background
   and the keyframes never run, the tag must still be sitting there visible
   rather than held at its opening frame. */
.pin__tag { display: none; }
.pin[data-tagged='true'] .pin__tag {
  position: absolute;
  left: 50%;
  bottom: 16px;
  transform: translateX(-50%);
  padding: 4px 10px;
  border-radius: 999px;
  background: var(--pin, var(--amber));
  color: var(--ink);
  font-size: 13px;
  font-weight: 750;
  letter-spacing: -0.01em;
  font-variant-numeric: tabular-nums;
  white-space: nowrap;
  box-shadow: 0 8px 22px rgba(0, 0, 0, 0.7);
  display: block;
  animation: tag-pop 0.5s var(--ease-out);
}

@keyframes tag-pop {
  from { opacity: 0; transform: translateX(-50%) translateY(8px) scale(0.6); }
}

.pin--ghost .pin__dot {
  width: 9px;
  height: 9px;
  margin: -4.5px 0 0 -4.5px;
  opacity: 0.5;
}

/* --------------------------------------------------------------- shell --- */

.shell {
  position: relative;
  z-index: 500;
  height: 100%;
  display: grid;
  grid-template-columns: var(--rail) 1fr;
  pointer-events: none;
}
.shell > * { pointer-events: auto; }

.rail {
  height: 100%;
  overflow-y: auto;
  overscroll-behavior: contain;
  padding: 26px 22px 22px;
  background: linear-gradient(180deg, rgba(8, 13, 12, 0.94), rgba(8, 13, 12, 0.86));
  backdrop-filter: blur(22px);
  border-right: 1px solid var(--hair);
  display: flex;
  flex-direction: column;
  gap: 22px;
}

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

.brandline {
  display: flex;
  align-items: center;
  gap: 9px;
  font-size: 11px;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--muted);
}
.brandline::before {
  content: '';
  width: 8px;
  height: 8px;
  border-radius: 50%;
  background: var(--mint);
  box-shadow: 0 0 12px var(--mint);
  animation: pulse 3s ease-in-out infinite;
}
@keyframes pulse { 50% { opacity: 0.35; } }

h1 {
  margin: 10px 0 0;
  font-size: 44px;
  line-height: 0.94;
  letter-spacing: -0.035em;
  font-weight: 700;
}
h1 em {
  font-style: normal;
  color: var(--amber);
}

.tagline {
  margin: 12px 0 0;
  color: var(--muted);
  font-size: 14px;
  max-width: 34ch;
}

/* ------------------------------------------------------------- controls -- */

.label {
  font-size: 11px;
  letter-spacing: 0.13em;
  text-transform: uppercase;
  color: var(--muted);
  margin-bottom: 9px;
}

.grades { display: flex; flex-wrap: wrap; gap: 6px; }

.grade {
  padding: 7px 13px;
  border-radius: 999px;
  border: 1px solid var(--hair);
  background: var(--surface);
  color: var(--muted);
  font-size: 13px;
  font-weight: 550;
  transition: all 0.18s var(--ease-out);
}
.grade:hover { border-color: #3a4c46; color: var(--text); }
.grade[aria-pressed='true'] {
  background: var(--text);
  border-color: var(--text);
  color: var(--ink);
}

.asks { display: grid; gap: 10px; }

.ask {
  position: relative;
  display: flex;
  align-items: center;
  gap: 13px;
  padding: 15px 17px;
  border-radius: 16px;
  border: 1px solid var(--hair);
  background: var(--surface-hi);
  text-align: left;
  overflow: hidden;
  transition: transform 0.18s var(--ease-out), border-color 0.18s, background 0.18s;
}
.ask:hover:not(:disabled) { transform: translateY(-2px); border-color: var(--tint); }
.ask:active:not(:disabled) { transform: translateY(0) scale(0.99); }
.ask:disabled { opacity: 0.45; cursor: progress; }

.ask__emoji { font-size: 26px; line-height: 1; }
.ask__title { font-weight: 650; font-size: 15.5px; letter-spacing: -0.01em; }
.ask__sub { font-size: 12.5px; color: var(--muted); margin-top: 2px; }

.ask--dear { --tint: var(--coral); }
.ask--cheap { --tint: var(--mint); }
.ask__title { color: var(--tint); }

/* A hairline of the button's own colour, so the pair reads cheap/dear at a glance. */
.ask::before {
  content: '';
  position: absolute;
  left: 0;
  top: 0;
  bottom: 0;
  width: 3px;
  background: var(--tint);
}

/* --------------------------------------------------------- runners-up ---- */

.runners { display: grid; gap: 6px; }

.runner {
  display: grid;
  grid-template-columns: 20px 1fr auto;
  align-items: center;
  gap: 11px;
  padding: 10px 12px;
  border-radius: 11px;
  border: 1px solid transparent;
  background: var(--surface);
  text-align: left;
  transition: background 0.16s, border-color 0.16s;
}
.runner:hover { background: var(--surface-hi); border-color: var(--hair); }
.runner[aria-current='true'] { border-color: var(--accent); background: var(--accent-soft); }

.runner__rank { font-size: 12px; color: var(--muted); font-weight: 650; }
.runner__who { font-size: 13.5px; font-weight: 550; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.runner__where { font-size: 11.5px; color: var(--muted); }
.runner__price { font-size: 14px; font-weight: 650; color: var(--accent); }

.foot {
  margin-top: auto;
  padding-top: 16px;
  border-top: 1px solid var(--hair);
  font-size: 11.5px;
  line-height: 1.6;
  color: var(--muted);
}
.foot p { margin: 0 0 7px; }
.foot a { color: #9fb2ab; }

.mute {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  padding: 5px 10px;
  border-radius: 999px;
  border: 1px solid var(--hair);
  font-size: 11.5px;
  color: var(--muted);
}
.mute:hover { color: var(--text); }

/* --------------------------------------------------------------- card ---- */

.stage {
  position: relative;
  display: flex;
  align-items: flex-end;
  justify-content: center;
  padding: 0 32px 32px;
  pointer-events: none;
}

.card {
  width: min(560px, 100%);
  /* A tall card on a short phone would otherwise run off the top of the
     screen, taking the price with it. */
  max-height: calc(100% - 16px);
  overflow-y: auto;
  overscroll-behavior: contain;
  padding: 22px 24px 20px;
  border-radius: 22px;
  border: 1px solid var(--hair);
  background: rgba(17, 25, 23, 0.9);
  backdrop-filter: blur(26px) saturate(1.2);
  box-shadow: 0 24px 70px rgba(0, 0, 0, 0.72);
  pointer-events: auto;

  opacity: 0;
  transform: translateY(26px) scale(0.97);
  transition: opacity 0.42s var(--ease-out), transform 0.42s var(--ease-out);
}
.card[data-shown='true'] { opacity: 1; transform: none; }
.card[hidden] { display: none; }

/*
 * The card arrives in pieces rather than all at once — badge, then who, then
 * the number, then the reasoning. Half a second of choreography is the
 * difference between a result appearing and a result being announced.
 *
 * Done with transitions rather than keyframes on purpose. A transition that
 * never gets to run still leaves the element at its finished style, so a card
 * revealed into a background tab is fully readable the moment anyone looks at
 * it; keyframes with a backwards fill would hold it at the opening frame,
 * which here means invisible.
 */
.card > * {
  opacity: 0;
  transform: translateY(12px);
  transition: opacity 0.4s var(--ease-out), transform 0.4s var(--ease-out);
}
.card[data-shown='true'] > * {
  opacity: 1;
  transform: none;
}
.card[data-shown='true'] > *:nth-child(1) { transition-delay: 0.04s; }
.card[data-shown='true'] > *:nth-child(2) { transition-delay: 0.13s; }
.card[data-shown='true'] > *:nth-child(3) { transition-delay: 0.26s; }
.card[data-shown='true'] > *:nth-child(4) { transition-delay: 0.34s; }
.card[data-shown='true'] > *:nth-child(5) { transition-delay: 0.42s; }

.card__top {
  display: flex;
  align-items: center;
  gap: 12px;
  margin-bottom: 16px;
}

.crown {
  display: inline-flex;
  align-items: center;
  gap: 7px;
  padding: 6px 13px 6px 9px;
  border: 1px solid color-mix(in srgb, var(--accent) 34%, transparent);
  border-radius: 999px;
  background: var(--accent-soft);
  color: var(--accent);
  font-size: 11.5px;
  font-weight: 650;
  letter-spacing: 0.06em;
  text-transform: uppercase;
}

.close {
  margin-left: auto;
  width: 30px;
  height: 30px;
  border-radius: 50%;
  color: var(--muted);
  font-size: 17px;
  line-height: 1;
}
.close:hover { background: var(--surface-hi); color: var(--text); }

.card__body { display: flex; align-items: center; gap: 18px; }

/* The logos we have are unlicensed placeholders, so every forecourt gets the
   same treatment: initials on a tile tinted from its own name. */
.mono {
  flex: none;
  width: 62px;
  height: 62px;
  border-radius: 17px;
  display: grid;
  place-items: center;
  font-size: 21px;
  font-weight: 750;
  letter-spacing: -0.02em;
  color: var(--ink);
}

.who { min-width: 0; flex: 1; }
.who__name {
  font-size: 19px;
  font-weight: 680;
  letter-spacing: -0.02em;
  line-height: 1.2;
}
.who__where { font-size: 13px; color: var(--muted); margin-top: 3px; }
.who__tags { display: flex; gap: 5px; margin-top: 7px; flex-wrap: wrap; }
.tag {
  font-size: 10.5px;
  padding: 2.5px 7px;
  border-radius: 5px;
  background: var(--surface-hi);
  border: 1px solid var(--hair);
  color: var(--muted);
}

.price { text-align: right; flex: none; }
.price__big {
  font-size: 52px;
  font-weight: 750;
  letter-spacing: -0.045em;
  line-height: 1;
  color: var(--accent);
  /* Anchored right so the punch grows away from the name beside it. */
  transform-origin: 100% 50%;
}

/* The number stops climbing and lands. Purely decorative — the figure is
   already written and correct before this ever plays. */
.price__big[data-land='true'] { animation: price-land 0.62s var(--ease-out); }

@keyframes price-land {
  0% { transform: scale(1.34); text-shadow: 0 0 44px var(--accent); }
  58% { transform: scale(0.975); text-shadow: 0 0 22px var(--accent); }
  100% { transform: scale(1); text-shadow: 0 0 0 transparent; }
}
.price__big span { font-size: 25px; letter-spacing: -0.02em; }
.price__unit { font-size: 11.5px; color: var(--muted); margin-top: 4px; }

.verdict {
  margin: 17px 0 0;
  padding: 13px 15px;
  border-radius: 13px;
  background: var(--accent-soft);
  border: 1px solid color-mix(in srgb, var(--accent) 26%, transparent);
  font-size: 14.5px;
  font-weight: 550;
  line-height: 1.4;
}

.stats {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: 1px;
  margin-top: 16px;
  background: var(--hair);
  border: 1px solid var(--hair);
  border-radius: 13px;
  overflow: hidden;
}
.stat { background: var(--surface); padding: 11px 13px; }
.stat__k {
  font-size: 10px;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--muted);
  margin-bottom: 4px;
}
.stat__v { font-size: 16.5px; font-weight: 650; letter-spacing: -0.02em; }
.stat__n { font-size: 11px; color: var(--muted); margin-top: 2px; }

.card__foot {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
  margin-top: 14px;
  font-size: 11.5px;
  color: var(--muted);
}
.card__foot a { font-weight: 600; text-decoration: none; }
.card__foot a:hover { text-decoration: underline; }

/* -------------------------------------------------------------- flash ---- */

/* One beat of the accent colour blooming out of the middle of the screen at
   the moment of the reveal. Sits under the confetti and over everything else. */
.flash {
  position: fixed;
  inset: 0;
  /* Above the map, below the rail and the card — see #confetti. */
  z-index: 300;
  pointer-events: none;
  opacity: 0;
  background: radial-gradient(ellipse at 50% 58%, var(--accent), transparent 62%);
}
.flash[data-fire='true'] { animation: flash 0.62s ease-out; }

@keyframes flash {
  0% { opacity: 0; }
  14% { opacity: 0.34; }
  100% { opacity: 0; }
}

/* ----------------------------------------------------------- confetti ---- */

/*
 * Deliberately behind the rail and the card rather than over them.
 *
 * Poppers thrown in front of the result bury it: the badge and the brand name
 * are small text, and a scrap of foil across either one costs more than the
 * celebration adds. Sat behind, the same volley reads as going off around the
 * card — which is the composition anyway — and the answer stays legible the
 * whole way through.
 */
#confetti {
  position: fixed;
  inset: 0;
  z-index: 310;
  pointer-events: none;
}

/* ------------------------------------------------------------- loading --- */

.state {
  position: fixed;
  inset: 0;
  z-index: 1200;
  display: grid;
  place-items: center;
  background: var(--ink);
  text-align: center;
  padding: 24px;
}
.state[hidden] { display: none; }
.state__pump { font-size: 46px; animation: bob 1.8s ease-in-out infinite; }
@keyframes bob { 50% { transform: translateY(-9px); } }
.state p { color: var(--muted); margin: 14px 0 0; }

/* -------------------------------------------------------------- mobile --- */

@media (max-width: 860px) {
  .shell { grid-template-columns: 1fr; grid-template-rows: auto 1fr; }

  .rail {
    border-right: 0;
    border-bottom: 1px solid var(--hair);
    padding: 18px 18px 16px;
    gap: 16px;
    max-height: 52vh;
  }
  h1 { font-size: 34px; }
  .tagline { display: none; }
  .foot { display: none; }

  /* On a phone the card is a sheet over everything rather than a box inside
     the map column: pinned to the viewport, it is sized by the screen instead
     of by whatever height the rail happens to have grown to. */
  .stage { padding: 0; }
  .card {
    position: fixed;
    left: 12px;
    right: 12px;
    bottom: 12px;
    width: auto;
    z-index: 600;
    max-height: calc(100vh - 24px);
    max-height: calc(100dvh - 24px);
    padding: 18px 18px 16px;
    border-radius: 20px;
  }

  /* Once there is an answer on screen, the rail has done its job. Capping it
     is what keeps a band of actual map visible between the two — without this
     the sheet and the rail meet in the middle and the location, which is the
     entire point of flying there, is covered up. */
  .shell[data-revealed='true'] .rail { max-height: 26vh; }
  .card__body { gap: 13px; }
  .mono { width: 50px; height: 50px; border-radius: 14px; font-size: 17px; }
  .who__name { font-size: 16.5px; }
  .price__big { font-size: 40px; }
  .price__big span { font-size: 20px; }

  /* Three across even on a phone: stacking them costs ~120px of height, which
     is the difference between the card fitting and the price scrolling away.
     The tightened gaps below buy the last few pixels needed on a small phone,
     where the card would otherwise clamp and scroll by its own footer. */
  .verdict { margin-top: 13px; padding: 11px 13px; font-size: 13.5px; }
  .stats { margin-top: 12px; }
  .card__foot { margin-top: 11px; }
  .stat { padding: 9px 9px; }
  .stat__k { font-size: 9px; letter-spacing: 0.06em; }
  .stat__v { font-size: 14px; }
  .stat__n { font-size: 10px; }
}

/* Movement here is decoration on top of an answer that is legible without it.
   Strip the travel and the spin; keep the state changes. */
@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after {
    animation-duration: 0.01ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 0.01ms !important;
    /* The reveal's cascade is staged with delays, so clearing durations alone
       would still hold each part of the card back by up to four-tenths. */
    transition-delay: 0ms !important;
    animation-delay: 0ms !important;
  }
}
