/* Light-first, dark by toggle. Near-black on warm off-white; pure #000/#fff
   nowhere. Every colour on the page comes from the one ramp in format.js: the
   numeral, the 0-100 track, the chart's price trace, the colour-scale legend,
   the red/green 24h stat, and the cycle-high/cycle-low marker glyphs. Halvings
   and everything else stay on the neutral foreground. */

@font-face {
  font-family: "Outfit";
  font-style: normal;
  font-weight: 100 900;
  font-display: swap;
  src: url("fonts/outfit-latin-var.woff2") format("woff2");
}

@font-face {
  font-family: "Inter";
  font-style: normal;
  font-weight: 100 900;
  font-display: swap;
  src: url("fonts/inter-latin-var.woff2") format("woff2");
}

:root[data-theme="light"] {
  --bg: #faf9f7;
  --fg: #1c1b19;
  --muted: #807b73;
  --hairline: #e5e2db;
  --halo-opacity: 0.07;   /* light mode: faint tint wash, never a bloom */
}

:root[data-theme="dark"] {
  --bg: #131311;
  --fg: #eceae4;
  --muted: #8f8b82;
  --hairline: #2a2925;
  --halo-opacity: 0.2;    /* dark mode: the glow is the Huly quality */
}

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

html { scroll-behavior: smooth; }

body {
  background: var(--bg);
  color: var(--fg);
  font-family: "Inter", system-ui, sans-serif;
  font-size: 15px;
  line-height: 1.6;
  -webkit-font-smoothing: antialiased;
  overflow-x: clip;
}

/* ---- top bar (scrolls away, not sticky) ------------------------------- */

.topbar {
  display: flex;
  align-items: center;
  justify-content: space-between;
  max-width: 1080px;
  margin: 0 auto;
  padding: 20px 24px;
}

.brand {
  display: flex;
  align-items: center;
  gap: 9px;
}

/* Takes the page foreground, so it inverts with the theme. */
.mark {
  color: var(--fg);
  flex: none;
}

.wordmark {
  font-family: "Outfit", sans-serif;
  font-weight: 600;
  font-size: 14px;
  letter-spacing: 0.01em;
}

#theme-toggle {
  display: grid;
  place-items: center;
  width: 34px;
  height: 34px;
  border: 1px solid var(--hairline);
  border-radius: 50%;
  background: none;
  color: var(--muted);
  cursor: pointer;
  transition: color 0.2s, box-shadow 0.2s;
}

#theme-toggle:hover {
  color: var(--fg);
  box-shadow: 0 0 12px 0 color-mix(in srgb, var(--fg) 12%, transparent);
}

/* The icon shows the mode you are currently in: sun in light, moon in dark.
   Keying it off data-theme rather than a click handler means it is correct on
   the very first paint, including when the theme comes from localStorage or
   the system preference. */
#theme-toggle .icon-moon {
  display: none;
}

:root[data-theme="dark"] #theme-toggle .icon-sun {
  display: none;
}

:root[data-theme="dark"] #theme-toggle .icon-moon {
  display: block;
}

/* ---- staleness banner ------------------------------------------------- */

.banner {
  background: var(--hairline);
  color: var(--fg);
  font-size: 13px;
  text-align: center;
  padding: 10px 24px;
}

/* ---- hero ------------------------------------------------------------- */

main {
  max-width: 1080px;
  margin: 0 auto;
  padding: 0 24px;
}

.hero {
  text-align: center;
  padding: clamp(40px, 8vh, 88px) 0 8px;
}

h1 {
  font-family: "Outfit", sans-serif;
  font-weight: 800;
  font-size: clamp(44px, 7vw, 64px);
  letter-spacing: -0.02em;
  line-height: 0.95;
  margin-bottom: 24px;
}

.intro {
  color: var(--muted);
  max-width: 60ch;
  margin: 0 auto 14px;
}

.unavailable {
  text-align: center;
  color: var(--muted);
  padding: 80px 0 120px;
}

/* ---- the value and the track ------------------------------------------ */

.value-section {
  text-align: center;
  padding: clamp(24px, 5vh, 56px) 0 8px;
}

.value-wrap {
  position: relative;
  display: inline-block;
}

.value {
  /* Rendered at its final position and colour on load — no count-up. */
  position: relative;
  font-family: "Outfit", sans-serif;
  font-weight: 700;
  font-size: clamp(96px, 20vw, 120px);
  letter-spacing: -0.03em;
  line-height: 1;
  font-variant-numeric: tabular-nums;
  z-index: 1;
}

.value-halo {
  position: absolute;
  inset: -48px -72px;
  background: radial-gradient(closest-side,
              var(--halo-color, transparent), transparent 72%);
  opacity: var(--halo-opacity);
  pointer-events: none;
}

/* One subtle ambient shimmer on the value, dark mode only. */
@keyframes shimmer {
  0%, 100% { opacity: var(--halo-opacity); }
  50% { opacity: calc(var(--halo-opacity) * 1.6); }
}

:root[data-theme="dark"] .value-halo {
  animation: shimmer 7s ease-in-out infinite;
}

.track {
  display: flex;
  align-items: center;
  gap: 12px;
  max-width: 520px;
  margin: 28px auto 0;
}

.track-end {
  font-size: 12px;
  color: var(--muted);
  font-variant-numeric: tabular-nums;
}

.track-rail {
  position: relative;
  flex: 1;
  height: 14px;
  display: flex;
  align-items: center;
}

.track-bar {
  width: 100%;
  height: 3px;
  border-radius: 2px;
  /* The gradient itself is set from JS off the same ramp as the numeral. */
}

.track-marker {
  /* The whole track is coloured, so the marker cannot rely on hue: a solid
     disc in the page foreground with a ring — shape contrast, not colour. */
  position: absolute;
  top: 50%;
  width: 12px;
  height: 12px;
  border-radius: 50%;
  background: var(--fg);
  border: 2px solid var(--bg);
  box-shadow: 0 0 0 1px var(--fg);
  transform: translate(-50%, -50%);
}

.updated {
  font-size: 13px;
  color: var(--muted);
  margin-top: 18px;
}

/* ---- stats ------------------------------------------------------------ */

.stats {
  display: flex;
  justify-content: center;
  gap: clamp(32px, 7vw, 88px);
  padding: clamp(32px, 6vh, 56px) 0;
  text-align: center;
}

.stat {
  position: relative;   /* the containing block for .stat-note */
  display: flex;
  flex-direction: column;
  gap: 6px;
}

.stat-label {
  font-size: 11px;
  font-weight: 500;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--muted);
}

.stat-figure {
  font-family: "Outfit", "Inter", sans-serif;
  font-size: 22px;
  font-weight: 600;
  font-variant-numeric: tabular-nums;
  /* The flash below paints a background behind the figure; the padding keeps
     that wash off the glyphs, and the negative margin keeps it from moving
     anything. Reserved here rather than added with the class, so applying the
     class cannot reflow the row. */
  padding: 2px 8px;
  margin: -2px -8px;
  border-radius: 6px;
}

/* Only shown when the price is not live: names the day the close is from,
   rather than letting a committed number pass for a current one.

   Taken out of flow on purpose. It appears on load and usually disappears a
   moment later when the first poll answers, and in flow that would resize the
   stats row and shove the chart down the page just as someone starts reading.
   Absolute means it can come and go without moving anything. */
.stat-note {
  position: absolute;
  top: 100%;
  /* Centred on the figure and allowed to be wider than it: the stat column is
     only as wide as "BTC PRICE", which would otherwise break the qualifier
     across two lines mid-date. */
  left: 50%;
  transform: translateX(-50%);
  white-space: nowrap;
  margin-top: 3px;
  font-size: 11px;
  line-height: 1.3;
  color: var(--muted);
}

.stat-note:empty {
  display: none;
}

/* The live-price flash.

   This fires most minutes on a page that may be left open all day, so it is
   built to be missable: a wash of the ramp's own green or red behind the
   figure and gone again inside half a second. Background only — no movement,
   no change of size, nothing that would pull the eye back to it. The colour
   arrives as --flash-color from app.js so it comes from the one ramp like
   every other colour here.

   prefers-reduced-motion needs no rule of its own: the global block below
   switches every animation off, and app.js additionally skips adding the
   class at all, so the number simply changes. */
@keyframes stat-flash {
  from { background-color: color-mix(in srgb,
                                     var(--flash-color) 22%, transparent); }
  to { background-color: transparent; }
}

.stat-figure.flash {
  animation: stat-flash 0.5s ease-out;
}

/* ---- chart ------------------------------------------------------------ */

.chart-section {
  padding-bottom: 24px;
}

.chart-controls {
  display: flex;
  justify-content: space-between;
  align-items: center;
  flex-wrap: wrap;
  gap: 10px;
  padding: 8px 0 14px;
}

.pill-group {
  display: flex;
  gap: 4px;
}

.pill {
  font-family: "Inter", sans-serif;
  font-size: 12.5px;
  font-weight: 500;
  color: var(--muted);
  background: none;
  border: none;
  border-radius: 999px;
  padding: 5px 13px;
  cursor: pointer;
  transition: color 0.2s, background-color 0.2s, box-shadow 0.2s;
}

.pill:hover {
  color: var(--fg);
  box-shadow: 0 0 10px 0 color-mix(in srgb, var(--fg) 10%, transparent);
}

.pill.active {
  color: var(--bg);
  background: var(--fg);
}

.chart {
  width: 100%;
  /* Taller than the plot alone needs: chart.js reserves the bottom of this box
     for the x-axis labels and the range strip below them. */
  height: 470px;
}

.chart-action {
  display: grid;
  place-items: center;
  width: 28px;
  height: 28px;
  border: 1px solid var(--hairline);
  border-radius: 8px;
  background: none;
  color: var(--muted);
  cursor: pointer;
  transition: color 0.2s, box-shadow 0.2s;
}

.chart-action:hover {
  color: var(--fg);
  box-shadow: 0 0 10px 0 color-mix(in srgb, var(--fg) 10%, transparent);
}

.chart-action .icon-collapse {
  display: none;
}

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

/* ECharts' own panel styling is switched off in chart.js — transparent, no
   border, no padding — so everything visible here is ours, and both themes
   come from the same custom properties as the rest of the page. */
.chart-tooltip .tt {
  font-family: "Inter", system-ui, sans-serif;
  font-size: 12px;
  font-weight: 400;
  line-height: 1.45;
  color: var(--fg);
  background: var(--bg);
  border: 1px solid var(--hairline);
  border-radius: 8px;
  padding: 8px 10px;
  box-shadow: 0 4px 16px 0 color-mix(in srgb, var(--fg) 8%, transparent);
}

.chart-tooltip .tt-date {
  font-weight: 500;
  margin-bottom: 4px;
  white-space: nowrap;
}

.chart-tooltip .tt-row {
  display: flex;
  justify-content: space-between;
  gap: 14px;
  white-space: nowrap;
}

.chart-tooltip .tt-key {
  color: var(--muted);
}

.chart-tooltip .tt-value {
  font-variant-numeric: tabular-nums;
}

/* ---- chart legend ----------------------------------------------------- */

.chart-legend {
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  gap: 6px 16px;
  padding: 14px 0 0;
}

.legend-item {
  display: inline-flex;
  align-items: center;
  gap: 7px;
  font-family: "Inter", sans-serif;
  font-size: 12.5px;
  color: var(--muted);
  background: none;
  border: none;
  padding: 2px 0;
  cursor: pointer;
  transition: color 0.2s, opacity 0.2s;
}

.legend-item:hover {
  color: var(--fg);
}

/* Off state: the whole entry dims, so what is showing is legible at a glance
   without any extra chrome. */
.legend-item[aria-pressed="false"] {
  opacity: 0.4;
}

.legend-item[aria-pressed="false"] .legend-mark {
  filter: grayscale(1);
}

.legend-mark {
  width: 14px;
  height: 8px;
  border-radius: 2px;
  flex: none;
}

/* Filled from the ramp by app.js, so it stays the same colour source as the
   series it stands for. */
.legend-mark-index {
  background: var(--muted);
}

/* Marker entries carry the glyph itself rather than a colour block. Its colour
   is set from the ramp by app.js for highs and lows; halvings inherit the
   legend item's own muted colour through currentColor. */
.legend-glyph {
  display: grid;
  place-items: center;
  width: 13px;
  height: 13px;
  border-radius: 0;
}

.legend-glyph svg {
  display: block;
}

/* ---- colour-scale legend ---------------------------------------------- */

.scale-legend {
  max-width: 320px;
  margin: 16px auto 0;
}

.scale-strip {
  height: 7px;
  border-radius: 999px;
}

.scale-ticks {
  display: flex;
  justify-content: space-between;
  font-size: 11px;
  color: var(--muted);
  font-variant-numeric: tabular-nums;
  padding-top: 5px;
}

.scale-caption {
  font-size: 11.5px;
  line-height: 1.5;
  color: var(--muted);
  text-align: center;
  padding-top: 8px;
}

/* ---- full-screen chart ------------------------------------------------ */

/* One class covers both mechanisms: the Fullscreen API where it exists, and
   a fixed-position fallback for Safari on iPhone, which has no element
   fullscreen at all. app.js applies the class in either case, which also
   avoids a :fullscreen / :-webkit-full-screen selector list — one unknown
   pseudo-class there would invalidate the whole rule in every browser. */
.chart-section.chart-expanded {
  position: fixed;
  /* Long-hand rather than `inset: 0`, which Safari only understood from 14.5.
     On an older iPhone the shorthand is dropped whole, and a fixed element with
     no offsets sizes to its content and sits at its static position — which
     looks exactly like an overlay that fails to cover the page. */
  top: 0;
  right: 0;
  bottom: 0;
  left: 0;
  /* Belt and braces against the same failure: with an explicit viewport height
     the box cannot come out short even if the offsets are ignored. dvh tracks
     the viewport shrinking as phone toolbars appear, vh does not, so vh is the
     fallback and dvh wins wherever it is understood. */
  width: 100%;
  height: 100vh;
  height: 100dvh;
  z-index: 60;
  padding: 12px 16px;
  background: var(--bg);
  /* The reveal animation leaves a transform on this section. On an ancestor a
     transform would make the fixed positioning above resolve against it rather
     than the viewport; on the element itself it does not, but clearing it keeps
     the box exactly where the offsets put it. */
  transform: none;
  opacity: 1;
  /* Hands every touch gesture inside the overlay to the chart: in full screen
     there is no page to scroll or zoom, and the chart's own pinch-zoom is
     enabled here. iOS Safari ignores this for page zoom, which is why
     fullscreen.js also refuses its gesture events. */
  touch-action: none;
  /* One finger pans and a press held still moves the crosshair (chart.js), so
     the browser must not answer the same hold with a selection or a callout
     menu of its own — on iOS that would fire at roughly the moment the hold
     registers, on exactly the gesture the chart is waiting for. */
  -webkit-touch-callout: none;
  -webkit-user-select: none;
  user-select: none;
}

/* Full screen means the chart, not the chart and all its apparatus: the series
   and marker toggles, the colour-scale legend and its caption all go. The
   control row above the plot stays — with the range strip hidden as well and no
   inside zoom on touch, those pills are the only way left to change the
   window. */
.chart-section.chart-expanded .chart-legend,
.chart-section.chart-expanded .scale-legend {
  display: none;
}

.chart-section.chart-expanded .chart {
  /* Only the control row and this section's own padding sit outside the plot
     now, so the offset is much smaller than it was with the legends showing. */
  height: calc(100vh - 92px);
  height: calc(100dvh - 92px);
}

.chart-section.chart-expanded .icon-expand {
  display: none;
}

.chart-section.chart-expanded .icon-collapse {
  display: block;
}

/* The page behind must not scroll under the fallback overlay. */
body.chart-expanded-lock {
  overflow: hidden;
}

/* ---- about ------------------------------------------------------------ */

.about {
  max-width: 62ch;
  margin: 0 auto;
  padding: clamp(40px, 8vh, 72px) 0;
  color: var(--muted);
}

.about h2 {
  font-family: "Outfit", sans-serif;
  font-size: 16px;
  font-weight: 600;
  color: var(--fg);
  margin: 28px 0 8px;
}

.about h2:first-child { margin-top: 0; }

.about code {
  font-size: 13px;
  background: var(--hairline);
  padding: 2px 6px;
  border-radius: 4px;
}

/* ---- footer (Huly-style: muted, airy, nothing boxed) ------------------ */

footer {
  display: flex;
  justify-content: center;
  align-items: center;
  gap: 12px;
  padding: 96px 24px 72px;
  font-size: 13px;
  color: var(--muted);
}

/* ---- scroll reveals --------------------------------------------------- */

.reveal {
  opacity: 0;
  transform: translateY(14px);
  transition: opacity 0.6s ease, transform 0.6s ease;
}

.reveal.visible {
  opacity: 1;
  transform: none;
}

/* ---- motion off ------------------------------------------------------- */

@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after {
    animation: none !important;
    transition: none !important;
  }
  html { scroll-behavior: auto; }
  .reveal { opacity: 1; transform: none; }
}

/* ---- mobile (designed at 375px first, then loosened) ------------------ */

@media (max-width: 640px) {
  .stats {
    flex-direction: column;
    /* Wider than the 24px this was: stacked, the out-of-flow .stat-note falls
       into the gap under the price, and at 24px it sat nearer the next stat's
       label than the figure it belongs to. */
    gap: 34px;
  }

  .chart { height: 300px; }

  .chart-controls { justify-content: center; }
}
