/* ==========================================================================
   Components
   ========================================================================== */

/* ── band colour mapping ───────────────────────────────────────────────────
   One source of truth for score → colour, consumed by badges, cards, the day
   scrubber and the map legend alike.

   The fallback is wrapped in :where() so it carries zero specificity. Declaring
   it as a plain `.card { --band: ... }` gave it the same specificity as
   `[data-band="prime"]` but a later source position, so every card silently
   resolved to the grey no-data colour regardless of its score. */
:where(.card, .day, .week__day, .hero, .spark, .crag-tip, [data-band]) {
  --band: var(--cond-none);
}

[data-band="prime"] { --band: var(--cond-prime); }
[data-band="good"]  { --band: var(--cond-good); }
[data-band="mixed"] { --band: var(--cond-mixed); }
[data-band="poor"]  { --band: var(--cond-poor); }
[data-band="out"]   { --band: var(--cond-out); }
[data-band="none"]  { --band: var(--cond-none); }

/* ── search ──────────────────────────────────────────────────────────────── */
.search {
  position: relative;
  flex: 0 1 22rem;
  min-width: 11rem;
}

.search__input {
  width: 100%;
  padding: var(--space-2) var(--space-3) var(--space-2) 2.25rem;
  background: var(--bg-inset);
  border: 1px solid var(--line-mid);
  border-radius: var(--radius-chip);
  font-size: var(--text-sm);
  color: var(--ink-loud);
  transition: border-color var(--duration-fast), box-shadow var(--duration-fast);
}

.search__input::placeholder { color: var(--ink-faint); }

.search__input:hover { border-color: var(--line-loud); }

.search__input:focus {
  outline: none;
  border-color: var(--accent);
  box-shadow: var(--glow-accent);
}

.search__icon {
  position: absolute;
  left: 0.75rem;
  top: 50%;
  translate: 0 -50%;
  color: var(--ink-faint);
  pointer-events: none;
  display: flex;
}

.search__clear {
  position: absolute;
  right: 0.4rem;
  top: 50%;
  translate: 0 -50%;
  display: none;
  padding: var(--space-1);
  border-radius: 50%;
  color: var(--ink-muted);
  line-height: 0;
}

.search__clear:hover { color: var(--ink-loud); background: var(--line-soft); }
.search:has(.search__input:not(:placeholder-shown)) .search__clear { display: block; }

/* ── chips ───────────────────────────────────────────────────────────────── */
.chip-row {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-2);
}

.chip {
  --chip-bg: transparent;
  --chip-ink: var(--ink-muted);
  --chip-line: var(--line-mid);
  display: inline-flex;
  align-items: center;
  gap: 0.375rem;
  padding: 0.3rem 0.7rem;
  background: var(--chip-bg);
  color: var(--chip-ink);
  border: 1px solid var(--chip-line);
  border-radius: var(--radius-chip);
  font-size: var(--text-xs);
  font-weight: 500;
  white-space: nowrap;
  transition:
    background-color var(--duration-fast) var(--ease-out-soft),
    color var(--duration-fast) var(--ease-out-soft),
    border-color var(--duration-fast) var(--ease-out-soft),
    transform var(--duration-fast) var(--ease-out-soft);
}

.chip:hover {
  --chip-ink: var(--ink-loud);
  --chip-line: var(--line-loud);
  --chip-bg: var(--line-soft);
}

.chip:active { transform: scale(0.96); }

.chip[aria-pressed="true"] {
  --chip-bg: var(--accent-quiet);
  --chip-ink: var(--accent);
  --chip-line: color-mix(in oklch, var(--accent) 55%, transparent);
}

.chip__count {
  font-family: var(--font-mono);
  font-size: var(--text-2xs);
  opacity: 0.6;
}

/* ── icon buttons ────────────────────────────────────────────────────────── */
.icon-btn {
  display: inline-grid;
  place-items: center;
  width: 2.125rem;
  height: 2.125rem;
  border: 1px solid var(--line-mid);
  border-radius: var(--radius-sm);
  color: var(--ink-muted);
  background: var(--bg-inset);
  transition: color var(--duration-fast), border-color var(--duration-fast),
              background-color var(--duration-fast), transform var(--duration-fast);
}

.icon-btn:hover {
  color: var(--ink-loud);
  border-color: var(--line-loud);
  background: var(--bg-raised);
}

.icon-btn:active { transform: scale(0.94); }
.icon-btn[aria-busy="true"] { color: var(--accent); }
.icon-btn[aria-busy="true"] svg { animation: spin 1.1s linear infinite; }

@keyframes spin { to { rotate: 360deg; } }

/* ── day scrubber ─────────────────────────────────────────────────────────
   The single most important addition: conditions are a question about *which
   day*, and the old UI only ever showed today. Each button carries the
   continent-wide best score for that day, so the shape of the week is legible
   before you click anything. */
.days {
  display: flex;
  gap: 2px;
  padding: 3px;
  background: var(--bg-inset);
  border: 1px solid var(--line-mid);
  border-radius: var(--radius-md);
}

.day {
  position: relative;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 2px;
  min-width: 3.1rem;
  padding: 0.3rem 0.45rem 0.4rem;
  border-radius: var(--radius-sm);
  color: var(--ink-muted);
  transition: background-color var(--duration-fast), color var(--duration-fast);
}

.day:hover { background: var(--line-soft); color: var(--ink-base); }

.day[aria-pressed="true"] {
  background: var(--bg-raised);
  color: var(--ink-loud);
  box-shadow: inset 0 0 0 1px var(--line-loud);
}

.day__name {
  font-size: var(--text-2xs);
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 0.06em;
}

.day__date {
  font-family: var(--font-mono);
  font-size: var(--text-2xs);
  opacity: 0.65;
}

/* A luminance bar rather than a number: it compares at a glance. */
.day__bar {
  width: 100%;
  height: 3px;
  border-radius: 999px;
  background: var(--band);
  opacity: 0.85;
  transition: background-color var(--duration-base) var(--ease-out-soft);
}

.day[aria-pressed="true"] .day__bar { opacity: 1; height: 4px; }

/* ── crag cards ──────────────────────────────────────────────────────────── */
.card {
  position: relative;
  display: grid;
  grid-template-columns: auto 1fr auto;
  align-items: center;
  gap: var(--space-3);
  width: 100%;
  padding: var(--space-3);
  text-align: left;
  border-radius: var(--radius-md);
  color: inherit;
  transition: background-color var(--duration-fast) var(--ease-out-soft);
}

.card::before {
  content: "";
  position: absolute;
  inset: 0;
  border-radius: inherit;
  box-shadow: inset 0 0 0 1px transparent;
  transition: box-shadow var(--duration-fast);
  pointer-events: none;
}

.card:hover { background: var(--bg-raised); }
.card:hover::before { box-shadow: inset 0 0 0 1px var(--line-mid); }

.card[aria-current="true"] {
  background: var(--bg-raised);
}

.card[aria-current="true"]::before {
  box-shadow: inset 0 0 0 1px color-mix(in oklch, var(--band) 60%, transparent);
}

/* Rank number, set in mono so the column stays put as the list re-sorts. */
.card__rank {
  font-family: var(--font-mono);
  font-size: var(--text-xs);
  color: var(--ink-faint);
  min-width: 1.6rem;
  text-align: right;
  font-variant-numeric: tabular-nums;
}

.card__body { min-width: 0; }

.card__name {
  display: flex;
  align-items: center;
  gap: 0.4rem;
  font-size: var(--text-sm);
  font-weight: 600;
  color: var(--ink-loud);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

.card__meta {
  display: flex;
  align-items: center;
  gap: 0.4rem;
  font-size: var(--text-2xs);
  color: var(--ink-faint);
  white-space: nowrap;
  min-width: 0;
}

/* The place name is the expendable part: rock type, aspect and elevation are
   what the user is comparing on, so they must survive truncation. */
.card__where {
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
}

.card__specs { flex-shrink: 0; }
.card__sep { opacity: 0.4; flex-shrink: 0; }

.card__score {
  display: flex;
  flex-direction: column;
  align-items: flex-end;
  gap: 2px;
}

.card__value {
  font-family: var(--font-mono);
  font-variant-numeric: tabular-nums;
  font-size: var(--text-lg);
  font-weight: 600;
  line-height: 1;
  color: var(--band);
}

/* 7-day shape, inline. Lets you spot "bad today, great Friday" in the list. */
.spark {
  display: flex;
  gap: 1.5px;
  align-items: flex-end;
  height: 14px;
}

.spark__bar {
  width: 3px;
  border-radius: 1px;
  background: var(--bar, var(--cond-none));
  min-height: 2px;
  transition: height var(--duration-base) var(--ease-out-soft);
}

.spark__bar[data-active="true"] { outline: 1px solid var(--line-loud); outline-offset: 1px; }

/* ── badges ──────────────────────────────────────────────────────────────── */
.badge {
  display: inline-flex;
  align-items: center;
  gap: 0.3rem;
  padding: 0.15rem 0.45rem;
  border-radius: var(--radius-sm);
  font-size: var(--text-2xs);
  font-weight: 600;
  letter-spacing: 0.02em;
  background: color-mix(in oklch, var(--band) 20%, transparent);
  color: var(--band);
  border: 1px solid color-mix(in oklch, var(--band) 35%, transparent);
}

.badge--warn {
  background: color-mix(in oklch, var(--warn) 18%, transparent);
  color: var(--warn);
  border-color: color-mix(in oklch, var(--warn) 35%, transparent);
}

.badge--danger {
  background: color-mix(in oklch, var(--danger) 18%, transparent);
  color: var(--danger);
  border-color: color-mix(in oklch, var(--danger) 35%, transparent);
}

.badge--quiet {
  background: var(--line-soft);
  color: var(--ink-faint);
  border-color: var(--line-mid);
}

/* Confidence dots — three-state, readable without colour alone. */
.conf {
  display: inline-flex;
  gap: 2px;
  align-items: center;
}

.conf__pip {
  width: 4px; height: 4px;
  border-radius: 50%;
  background: var(--line-loud);
}

.conf__pip[data-on="true"] { background: var(--ink-muted); }

/* ── legend ──────────────────────────────────────────────────────────────── */
.legend {
  padding: var(--space-3) var(--space-4);
  background: var(--bg-overlay);
  backdrop-filter: blur(14px) saturate(1.2);
  border: 1px solid var(--line-mid);
  border-radius: var(--radius-md);
  box-shadow: var(--shadow-raise);
}

.legend__ramp {
  display: flex;
  height: 6px;
  border-radius: 999px;
  overflow: hidden;
  margin: var(--space-2) 0 var(--space-1);
  min-width: 12rem;
}

.legend__ramp span { flex: 1; }

.legend__scale {
  display: flex;
  justify-content: space-between;
  font-family: var(--font-mono);
  font-size: var(--text-2xs);
  color: var(--ink-faint);
}

/* ── status pill ─────────────────────────────────────────────────────────── */
.status {
  display: flex;
  align-items: center;
  gap: 0.5rem;
  padding: 0.35rem 0.7rem;
  background: var(--bg-overlay);
  backdrop-filter: blur(14px);
  border: 1px solid var(--line-mid);
  border-radius: var(--radius-chip);
  font-size: var(--text-2xs);
  color: var(--ink-muted);
  box-shadow: var(--shadow-raise);
}

.status__dot {
  width: 6px; height: 6px;
  border-radius: 50%;
  background: var(--ok);
  flex-shrink: 0;
}

.status[data-state="warming"] .status__dot {
  background: var(--warn);
  animation: pulse 1.6s ease-in-out infinite;
}

.status[data-state="error"] .status__dot { background: var(--danger); }

@keyframes pulse { 50% { opacity: 0.25; } }

/* ── detail sheet ────────────────────────────────────────────────────────── */
.sheet {
  position: absolute;
  top: var(--space-4);
  right: var(--space-4);
  bottom: var(--space-4);
  width: min(27rem, calc(100% - 2 * var(--space-4)));
  /* Above Leaflet's own controls, which sit at z-index 800 — otherwise the zoom
     buttons and the attribution strip punch through the panel. */
  z-index: 1100;
  display: flex;
  flex-direction: column;
  background: var(--bg-overlay);
  backdrop-filter: blur(22px) saturate(1.3);
  border: 1px solid var(--line-mid);
  border-radius: var(--radius-lg);
  box-shadow: var(--shadow-float);
  opacity: 0;
  translate: 1.5rem 0;
  visibility: hidden;
  /* Closing: hold visibility until the fade finishes. Opening (below): flip it
     immediately. Transitioning visibility symmetrically leaves the panel
     visibility:hidden for the first frames, and an invisible element cannot take
     focus — so focusing the close button on open failed silently. */
  transition:
    opacity var(--duration-base) var(--ease-out-expo),
    translate var(--duration-base) var(--ease-out-expo),
    visibility 0s linear var(--duration-base);
}

.sheet[data-open="true"] {
  opacity: 1;
  translate: 0 0;
  visibility: visible;
  transition:
    opacity var(--duration-base) var(--ease-out-expo),
    translate var(--duration-base) var(--ease-out-expo),
    visibility 0s;
}

.sheet__head {
  display: grid;
  grid-template-columns: 1fr auto;
  gap: var(--space-3);
  padding: var(--space-5) var(--space-5) var(--space-4);
  border-bottom: 1px solid var(--line-soft);
}

.sheet__title { font-size: var(--text-xl); }

.sheet__where {
  font-size: var(--text-xs);
  color: var(--ink-muted);
  margin-top: 0.15rem;
}

.sheet__body {
  flex: 1;
  min-height: 0;
  padding: var(--space-4) var(--space-5) var(--space-6);
  display: flex;
  flex-direction: column;
  gap: var(--space-5);
}

/* Hero score — the one place with real scale contrast. */
.hero {
  display: grid;
  grid-template-columns: auto 1fr;
  align-items: center;
  gap: var(--space-4);
}

.hero__value {
  font-family: var(--font-mono);
  font-variant-numeric: tabular-nums;
  font-size: var(--text-score);
  font-weight: 600;
  line-height: 0.85;
  letter-spacing: -0.04em;
  color: var(--band);
}

.hero__of {
  font-size: var(--text-sm);
  color: var(--ink-faint);
  letter-spacing: 0;
}

.hero__label { display: flex; flex-direction: column; gap: 0.3rem; align-items: flex-start; }
.hero__when { font-size: var(--text-xs); color: var(--ink-muted); }

/* Driver list — the "why", which the old popup never explained. */
.drivers { display: flex; flex-direction: column; gap: var(--space-2); }

.driver {
  display: grid;
  grid-template-columns: auto 1fr;
  gap: var(--space-3);
  align-items: start;
  padding: var(--space-3);
  background: var(--bg-inset);
  border: 1px solid var(--line-soft);
  border-left: 2px solid var(--sev, var(--line-mid));
  border-radius: var(--radius-sm);
}

.driver[data-severity="good"] { --sev: var(--ok); }
.driver[data-severity="warn"] { --sev: var(--warn); }
.driver[data-severity="bad"]  { --sev: var(--danger); }

.driver__icon { color: var(--sev); display: flex; padding-top: 1px; }
.driver__label { font-size: var(--text-sm); font-weight: 600; color: var(--ink-loud); }
.driver__detail { font-size: var(--text-xs); color: var(--ink-muted); }

/* Fact grid */
.facts {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(6.5rem, 1fr));
  gap: 1px;
  background: var(--line-soft);
  border: 1px solid var(--line-soft);
  border-radius: var(--radius-sm);
  overflow: hidden;
}

.fact {
  padding: var(--space-3);
  background: var(--bg-base);
}

.fact__key {
  font-size: var(--text-2xs);
  text-transform: uppercase;
  letter-spacing: var(--tracking-wide);
  color: var(--ink-faint);
}

.fact__val {
  font-family: var(--font-mono);
  font-variant-numeric: tabular-nums;
  font-size: var(--text-md);
  font-weight: 600;
  color: var(--ink-loud);
  margin-top: 0.15rem;
}

.fact__note { font-size: var(--text-2xs); color: var(--ink-faint); }

/* Week strip inside the sheet */
.week { display: flex; gap: var(--space-1); }

.week__day {
  flex: 1;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 3px;
  padding: var(--space-2) 0.1rem;
  border-radius: var(--radius-sm);
  background: var(--bg-inset);
  border: 1px solid transparent;
  transition: border-color var(--duration-fast), background-color var(--duration-fast);
}

.week__day:hover { background: var(--bg-raised); border-color: var(--line-mid); }

.week__day[aria-pressed="true"] {
  border-color: color-mix(in oklch, var(--band) 65%, transparent);
  background: var(--bg-raised);
}

.week__name { font-size: var(--text-2xs); color: var(--ink-faint); text-transform: uppercase; letter-spacing: 0.05em; }
.week__score { font-family: var(--font-mono); font-size: var(--text-sm); font-weight: 600; color: var(--band); }
.week__wx { font-size: var(--text-2xs); color: var(--ink-faint); }

/* Season bars — reuses the historic 8a.nu activity share. */
.season { display: flex; align-items: flex-end; gap: 2px; height: 2.5rem; }

.season__month {
  flex: 1;
  display: flex;
  flex-direction: column;
  justify-content: flex-end;
  align-items: center;
  gap: 3px;
  height: 100%;
}

.season__bar {
  width: 100%;
  border-radius: 2px 2px 0 0;
  background: var(--line-mid);
  min-height: 2px;
}

.season__month[data-peak="true"] .season__bar { background: var(--accent); }
.season__month[data-now="true"] .season__bar { outline: 1px solid var(--ink-faint); outline-offset: 1px; }
.season__label { font-size: 0.5625rem; color: var(--ink-faint); }

.section__head {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: var(--space-2);
  margin-bottom: var(--space-2);
}

.note {
  font-size: var(--text-xs);
  color: var(--ink-muted);
  padding: var(--space-3);
  background: var(--accent-quiet);
  border-radius: var(--radius-sm);
  border-left: 2px solid var(--accent);
}

.note--plain {
  background: var(--bg-inset);
  border-left-color: var(--line-loud);
  color: var(--ink-faint);
}

/* ── empty & loading states ──────────────────────────────────────────────── */
.empty {
  padding: var(--space-6) var(--space-5);
  text-align: center;
  color: var(--ink-faint);
  font-size: var(--text-sm);
}

.boot {
  position: fixed;
  inset: 0;
  z-index: 2000;
  display: grid;
  place-content: center;
  justify-items: center;
  gap: var(--space-4);
  background: var(--bg-deep);
  transition: opacity var(--duration-slow) var(--ease-out-expo), visibility var(--duration-slow);
}

/* `display: grid` overrides the UA's `[hidden] { display: none }` so the overlay
   can fade rather than vanish. That leaves a full-viewport fixed box in the
   layout, so pointer-events must be disabled explicitly — otherwise any hiccup
   in the visibility transition turns it into an invisible click-blocker over the
   entire app. */
.boot[hidden] {
  opacity: 0;
  visibility: hidden;
  display: grid;
  pointer-events: none;
}

.boot__title { font-size: var(--text-2xl); letter-spacing: -0.03em; }
.boot__msg { font-size: var(--text-sm); color: var(--ink-muted); }

.boot__track {
  width: min(20rem, 60vw);
  height: 3px;
  border-radius: 999px;
  background: var(--line-soft);
  overflow: hidden;
}

.boot__fill {
  height: 100%;
  width: 0;
  border-radius: 999px;
  background: linear-gradient(90deg, var(--cond-poor), var(--cond-good), var(--cond-prime));
  transition: width var(--duration-slow) var(--ease-out-soft);
}

/* ── leaflet overrides ───────────────────────────────────────────────────── */
.leaflet-container {
  background: var(--bg-inset);
  font-family: var(--font-ui);
  outline: none;
}

.leaflet-tile-pane { filter: var(--map-tiles-filter); }

.leaflet-control-zoom {
  border: 1px solid var(--line-mid) !important;
  border-radius: var(--radius-sm) !important;
  overflow: hidden;
  box-shadow: var(--shadow-raise) !important;
}

.leaflet-control-zoom a {
  background: var(--bg-overlay) !important;
  backdrop-filter: blur(14px);
  color: var(--ink-base) !important;
  border-color: var(--line-soft) !important;
  width: 2rem !important;
  height: 2rem !important;
  line-height: 2rem !important;
  font-size: 1.1rem !important;
}

.leaflet-control-zoom a:hover {
  background: var(--bg-raised) !important;
  color: var(--ink-loud) !important;
}

.leaflet-control-attribution {
  background: var(--bg-overlay) !important;
  color: var(--ink-faint) !important;
  font-size: 10px !important;
  padding: 1px 6px !important;
  border-top-left-radius: var(--radius-sm);
}

.leaflet-control-attribution a { color: var(--ink-muted) !important; }

/* Tooltip on hover — quick identification without committing to a click. */
.leaflet-tooltip.crag-tip {
  background: var(--bg-overlay);
  backdrop-filter: blur(14px);
  border: 1px solid var(--line-mid);
  border-left: 2px solid var(--band);
  border-radius: var(--radius-sm);
  box-shadow: var(--shadow-raise);
  color: var(--ink-base);
  padding: 0.35rem 0.55rem;
  font-size: var(--text-xs);
  white-space: nowrap;
}

.leaflet-tooltip.crag-tip::before { display: none; }

.crag-tip__name { font-weight: 600; color: var(--ink-loud); }

.crag-tip__score {
  font-family: var(--font-mono);
  font-weight: 600;
  color: var(--band);
}

/* ==========================================================================
   Responsive component overrides
   --------------------------------------------------------------------------
   These live here rather than in layout.css because media queries contribute
   no specificity: components.css is loaded last, so a `.sheet` rule in a media
   query in layout.css would lose to the plain `.sheet` rule above.
   ========================================================================== */

@media (max-width: 640px) {
  /* Narrower day buttons so a full week fits without horizontal scrolling. */
  .day {
    min-width: 2.6rem;
    padding: 0.3rem 0.25rem 0.4rem;
  }

  /* A true bottom sheet. Absolute positioning confines it to .map-region, which
     is only ~450 px tall on a phone — far too little room for the drivers, the
     week strip and the fact grid. */
  .sheet {
    position: fixed;
    top: auto;
    left: var(--space-2);
    right: var(--space-2);
    bottom: var(--space-2);
    width: auto;
    max-height: 80dvh;
    translate: 0 1.5rem;
  }

  .sheet[data-open="true"] { translate: 0 0; }

  .sheet__head { padding: var(--space-4) var(--space-4) var(--space-3); }
  .sheet__body { padding: var(--space-4) var(--space-4) var(--space-5); }

  /* Two columns rather than auto-fit, which collapses to one at this width. */
  .facts { grid-template-columns: repeat(2, 1fr); }
}
