/* Every color, type size, radius and shadow is a token of :root; the rules
   below only name them. Light by default, dark with the system: the 3D scene
   (scene.ts) and the charts (timeline.ts) read the same tokens. All contrasts
   are checked for WCAG AA in both themes: text 4.5:1, controls and marks 3:1. */
:root {
  color-scheme: light dark;

  /* Surfaces: an ivory page, near-white cards, cream wells. */
  --bg: #faf9f5;
  --surface-1: #ffffff;
  --surface-2: #f5f4ed;
  --surface-3: #f0eee6;
  --surface-raised: #ffffff;
  --stage: #f3f1ea;
  --hairline: rgba(31, 30, 29, 0.08);
  --hairline-strong: rgba(31, 30, 29, 0.15);
  /* The edge of a field or a handle: 3:1 against what surrounds it. */
  --line-control: #8e8c87;

  --ink: #141413;
  --ink-2: #3d3d3a;
  --ink-3: #6b6a64;
  --danger: #d1242f;
  --warn: #8a5c00;
  --gain: #207029;
  --star: #a97500;
  /* A developer's popular repo, flagged by a flame as on GitHub. */
  --flame: #bc4c00;
  /* The podium of a ranking: gold, silver and bronze, deep enough to part
     from the tiles' lines, the silver a cool slate; and the share of its
     color in a podium tile's surface. */
  --rank-1: #b07d00;
  --rank-2: #64748b;
  --rank-3: #b0612d;
  --rank-wash: 10%;
  /* A repo tile's growth: from its base to this share of its color, along
     a curve that keeps the middle of the scale quiet and lets the fastest
     stand out. */
  --heat-base: var(--surface-1);
  --heat-color: #2da44e;
  --heat-max: 100%;
  --heat-curve: 1.2;

  /* One accent, GitHub's blue (Primer): the main button, a selection, the
     focus. Each theme sets it here once. */
  --accent: #0969da;
  --accent-strong: #0969da;
  --accent-ink: #0550ae;
  --accent-fill: #0969da;
  --accent-fill-hover: #0550ae;
  --accent-fill-active: #033d8b;
  --on-accent: #ffffff;
  --accent-tint: rgba(9, 105, 218, 0.1);
  --accent-tint-strong: rgba(9, 105, 218, 0.18);
  --focus-ring: var(--accent-strong);

  /* Charts: one blue series, a warm linked hover (validated as a pair, and
     against the stars' gold, for color vision deficiencies), recessive grid
     and axes. A star history takes the stars' gold, over its wash. */
  --mark: #0969da;
  --mark-hover: #cf222e;
  --mark-muted: #dcd8cd;
  --mark-soft: #54aeff;
  --mark-wash: rgba(9, 105, 218, 0.1);
  --star-wash: rgba(169, 117, 0, 0.12);
  --grid: rgba(31, 30, 29, 0.06);
  --axis: rgba(31, 30, 29, 0.22);
  --band: rgba(31, 30, 29, 0.05);

  /* 3D scene. Ramps run from a day's few commits (or stars) to the busiest,
     as a list read by scene.ts: one hue each, monotone lightness, their low
     end at 2:1 at least against a tile at zero. The commits take Primer's
     blues, the stars a gold that brightens with their count. A day not
     loaded yet is a tile darker than a day confirmed at zero. */
  --scene-plinth: #d6d0c2;
  --scene-grid: #e2ddd1;
  --scene-unknown: #e4e0d6;
  --scene-zero: #f8f6f1;
  --scene-dim: #e9e5dc;
  --scene-highlight: var(--mark-hover);
  --scene-ramp: #54aeff, #218bff, #0969da, #0550ae, #033d8b;
  --scene-stars: #c99919, #d4a730, #dfb542, #eac352, #f5d162;
  --scene-sky: #fffaf2;
  --scene-ground: #f4ede0;
  --scene-key: #fffaf3;
  --scene-rim: #fff8f0;
  --scene-sky-intensity: 1.5;
  --scene-key-intensity: 2.2;
  --scene-rim-intensity: 0.35;
  --scene-shadow: #2e2a24;
  --scene-shadow-opacity: 0.16;
  --stage-vignette: rgba(46, 42, 36, 0.06);

  /* Shapes and depth: generous radii, soft and diffuse shadows. */
  --radius-xs: 4px;
  --radius-sm: 8px;
  --radius: 12px;
  --radius-lg: 16px;
  --radius-pill: 999px;
  --shadow-sm: 0 1px 2px rgba(20, 20, 19, 0.04);
  --shadow-md: 0 1px 2px rgba(20, 20, 19, 0.04), 0 4px 20px rgba(20, 20, 19, 0.05);
  --shadow-lg: 0 12px 40px rgba(20, 20, 19, 0.12), 0 2px 6px rgba(20, 20, 19, 0.05);

  /* Type: a serif for titles and the source's name, a plain sans for the
     interface. System faces only: the page loads no font. */
  --font-serif: "Tiempos Headline", "Copernicus", ui-serif, "New York", Georgia, "Times New Roman", serif;
  --font-sans: ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;
  --font-mono: ui-monospace, "SF Mono", SFMono-Regular, Menlo, Consolas, monospace;
  --text-xs: 11px;
  --text-sm: 12px;
  --text-md: 13px;
  --text-base: 14px;
  --text-lg: 16px;
  --text-xl: 20px;
  --text-2xl: 24px;
  --text-hero: clamp(2.5rem, 3.2vw, 2.75rem);

  --ease-out: cubic-bezier(0.2, 0.8, 0.2, 1);
  --ease-in-out: cubic-bezier(0.65, 0, 0.35, 1);
  --topbar: 72px;

  accent-color: var(--accent-strong);
  font-family: var(--font-sans);
  background: var(--bg);
}

/* Dark: the system's, unless the page is set to light (theme.ts), or the
   page's own choice. The two blocks hold the same tokens. */
@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) {
    /* Warm charcoal: the page, raised cards, a deeper well for the stage. */
    --bg: #262624;
    --surface-1: #30302e;
    --surface-2: #292927;
    --surface-3: #33332f;
    --surface-raised: #30302e;
    --stage: #1f1e1d;
    --hairline: rgba(250, 249, 245, 0.07);
    --hairline-strong: rgba(250, 249, 245, 0.13);
    --line-control: #82807b;

    --ink: #faf9f5;
    --ink-2: #c2c0b6;
    --ink-3: #9c9a92;
    --danger: #ff7b72;
    --warn: #dcaa3c;
    --gain: #3fb950;
    --star: #e3b341;
    --flame: #db6d28;
    --rank-1: #f0b429;
    --rank-2: #b1bac4;
    --rank-3: #d18447;
    /* No wash: on charcoal, it would take the gain and the names under 4.5:1. */
    --rank-wash: 0%;
    --heat-base: var(--surface-2);
    --heat-color: #2a9a45;
    --heat-max: 100%;
    --heat-curve: 1.6;

    --accent: #4493f8;
    --accent-strong: #4493f8;
    --accent-ink: #79c0ff;
    --accent-fill: #1f6feb;
    --accent-fill-hover: #1158c7;
    --accent-fill-active: #0d419d;
    --accent-tint: rgba(68, 147, 248, 0.16);
    --accent-tint-strong: rgba(68, 147, 248, 0.28);

    --mark: #4493f8;
    --mark-hover: #e2484f;
    --mark-muted: #4a4844;
    --mark-soft: #1f6feb;
    --mark-wash: rgba(68, 147, 248, 0.14);
    --star-wash: rgba(227, 179, 65, 0.14);
    --grid: rgba(250, 249, 245, 0.06);
    --axis: rgba(250, 249, 245, 0.2);
    --band: rgba(250, 249, 245, 0.06);

    --scene-plinth: #2a2926;
    --scene-grid: #3a3935;
    --scene-unknown: #2d2c29;
    --scene-zero: #353430;
    --scene-dim: #262523;
    --scene-ramp: #1f6feb, #388bfd, #58a6ff, #79c0ff, #a5d6ff;
    --scene-stars: #c99919, #d4a730, #dfb542, #eac352, #f5d162;
    --scene-sky: #fff3e6;
    --scene-ground: #141312;
    --scene-key: #fffaf3;
    --scene-rim: #d6e4ff;
    --scene-sky-intensity: 1.8;
    --scene-key-intensity: 3.2;
    --scene-rim-intensity: 0.5;
    --scene-shadow: #000000;
    --scene-shadow-opacity: 0.32;
    --stage-vignette: rgba(10, 9, 8, 0.45);

    --shadow-sm: 0 1px 2px rgba(0, 0, 0, 0.2);
    --shadow-md: 0 1px 2px rgba(0, 0, 0, 0.2), 0 4px 20px rgba(0, 0, 0, 0.18);
    --shadow-lg: 0 12px 40px rgba(0, 0, 0, 0.45), 0 2px 6px rgba(0, 0, 0, 0.25);
  }
}

:root[data-theme="dark"] {
  color-scheme: dark;

  /* Warm charcoal: the page, raised cards, a deeper well for the stage. */
  --bg: #262624;
  --surface-1: #30302e;
  --surface-2: #292927;
  --surface-3: #33332f;
  --surface-raised: #30302e;
  --stage: #1f1e1d;
  --hairline: rgba(250, 249, 245, 0.07);
  --hairline-strong: rgba(250, 249, 245, 0.13);
  --line-control: #82807b;

  --ink: #faf9f5;
  --ink-2: #c2c0b6;
  --ink-3: #9c9a92;
  --danger: #ff7b72;
  --warn: #dcaa3c;
  --gain: #3fb950;
  --star: #e3b341;
  --flame: #db6d28;
  --rank-1: #f0b429;
  --rank-2: #b1bac4;
  --rank-3: #d18447;
  /* No wash: on charcoal, it would take the gain and the names under 4.5:1. */
  --rank-wash: 0%;
  --heat-base: var(--surface-2);
  --heat-color: #2a9a45;
  --heat-max: 100%;
  --heat-curve: 1.6;

  --accent: #4493f8;
  --accent-strong: #4493f8;
  --accent-ink: #79c0ff;
  --accent-fill: #1f6feb;
  --accent-fill-hover: #1158c7;
  --accent-fill-active: #0d419d;
  --accent-tint: rgba(68, 147, 248, 0.16);
  --accent-tint-strong: rgba(68, 147, 248, 0.28);

  --mark: #4493f8;
  --mark-hover: #e2484f;
  --mark-muted: #4a4844;
  --mark-soft: #1f6feb;
  --mark-wash: rgba(68, 147, 248, 0.14);
  --star-wash: rgba(227, 179, 65, 0.14);
  --grid: rgba(250, 249, 245, 0.06);
  --axis: rgba(250, 249, 245, 0.2);
  --band: rgba(250, 249, 245, 0.06);

  --scene-plinth: #2a2926;
  --scene-grid: #3a3935;
  --scene-unknown: #2d2c29;
  --scene-zero: #353430;
  --scene-dim: #262523;
  --scene-ramp: #1f6feb, #388bfd, #58a6ff, #79c0ff, #a5d6ff;
  --scene-stars: #c99919, #d4a730, #dfb542, #eac352, #f5d162;
  --scene-sky: #fff3e6;
  --scene-ground: #141312;
  --scene-key: #fffaf3;
  --scene-rim: #d6e4ff;
  --scene-sky-intensity: 1.8;
  --scene-key-intensity: 3.2;
  --scene-rim-intensity: 0.5;
  --scene-shadow: #000000;
  --scene-shadow-opacity: 0.32;
  --stage-vignette: rgba(10, 9, 8, 0.45);

  --shadow-sm: 0 1px 2px rgba(0, 0, 0, 0.2);
  --shadow-md: 0 1px 2px rgba(0, 0, 0, 0.2), 0 4px 20px rgba(0, 0, 0, 0.18);
  --shadow-lg: 0 12px 40px rgba(0, 0, 0, 0.45), 0 2px 6px rgba(0, 0, 0, 0.25);
}

:root[data-theme="light"] { color-scheme: light; }

* { box-sizing: border-box; }
[hidden] { display: none !important; }

html, body { min-height: 100%; }

body {
  margin: 0;
  color: var(--ink);
  background: var(--bg);
  font-size: var(--text-base);
  line-height: 1.45;
  -webkit-font-smoothing: antialiased;
}

button, input, select { font: inherit; color: inherit; }
button { cursor: pointer; }
h2, p, figure { margin: 0; }

::selection { background: var(--accent-tint-strong); }

.icon-sprite { position: absolute; width: 0; height: 0; overflow: hidden; }

.icon {
  width: 18px;
  height: 18px;
  flex: none;
  fill: none;
  stroke: currentColor;
  stroke-width: 1.6;
  stroke-linecap: round;
  stroke-linejoin: round;
}

/* Focus wears the accent as a ring set off from the element; a selection is
   a tint inside it, so keyboard focus never reads as a selected filter. */
:focus-visible { outline: 2px solid var(--focus-ring); outline-offset: 2px; }

.micro {
  color: var(--ink-3);
  font: 500 var(--text-sm)/1.3 var(--font-sans);
  letter-spacing: 0.01em;
}

/* ---------- Shell ---------- */

.app { min-height: 100dvh; }

/* One row of 72 px; a second one when the banner wraps below the form. */
.topbar {
  position: relative;
  z-index: 20;
  min-height: var(--topbar);
  display: flex;
  align-items: center;
  gap: 24px;
  padding: 12px 24px;
}

.brand {
  display: flex;
  align-items: center;
  gap: 12px;
  color: var(--ink);
  font: 500 var(--text-xl)/1 var(--font-serif);
  letter-spacing: -0.01em;
  text-decoration: none;
  border-radius: var(--radius-sm);
}

.brand-mark {
  height: 20px;
  display: flex;
  align-items: flex-end;
  gap: 2px;
}

.brand-mark i {
  width: 3px;
  height: var(--h);
  border-radius: 2px 2px 0 0;
  background: var(--ink);
}

.brand-mark i:nth-child(1) { --h: 8px; opacity: 0.4; }
.brand-mark i:nth-child(2) { --h: 13px; opacity: 0.65; }
.brand-mark i:nth-child(3) { --h: 20px; background: var(--accent); }
.brand-mark i:nth-child(4) { --h: 11px; opacity: 0.65; }
.brand-mark i:nth-child(5) { --h: 6px; opacity: 0.4; }

/* The form and the banner share the row while both keep a useful width;
   otherwise the banner takes a full row below the form. The status floats
   under both, never over the banner. */
.topbar-slot {
  position: relative;
  flex: 1;
  min-width: 0;
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 12px 16px;
}
.topbar-slot > .search { flex: 1 1 400px; min-width: 0; }
/* Wide enough beside the form for its title and some names, or on its own line. */
.topbar-slot > .trending { flex: 1 1 480px; min-width: 0; }
/* The Promote banner follows Trending's, on the next line when the row is full. */
.topbar-slot > .promote { flex: 1 1 360px; min-width: 0; }

/* ---------- Search form ---------- */

/* The composer: one rounded box holding the field and its send button, 4 px
   inside its edge (padding and border): 48 px high, as the banner. */
.search {
  display: flex;
  align-items: center;
  gap: 8px;
  max-width: 640px;
  padding: 3px;
  border: 1px solid var(--line-control);
  border-radius: var(--radius-lg);
  background: var(--surface-raised);
  box-shadow: var(--shadow-md);
  transition: border-color 160ms, box-shadow 160ms;
}

.search:hover { border-color: var(--ink-3); }

.search:has(input:focus) {
  border-color: var(--focus-ring);
  box-shadow: var(--shadow-md), 0 0 0 3px var(--accent-tint);
}

.field { display: grid; gap: 8px; min-width: 0; }

.field-label { position: absolute; width: 1px; height: 1px; overflow: hidden; clip-path: inset(50%); }
.field-repo { flex: 1; min-width: 0; }

.field-box {
  position: relative;
  height: 40px;
  display: flex;
  align-items: center;
  padding: 0 4px 0 12px;
  font-size: var(--text-lg);
}

.field-prefix { color: var(--ink-3); white-space: nowrap; }

input {
  min-width: 0;
  flex: 1;
  height: 100%;
  border: 0;
  outline: 0;
  padding: 0 0 0 2px;
  background: transparent;
}

input::placeholder { color: var(--ink-3); }
input:focus-visible { outline: none; }

.submit {
  height: 40px;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 8px;
  padding: 0 12px 0 16px;
  border: 0;
  /* Concentric with the composer: its radius less the 4 px inset. */
  border-radius: calc(var(--radius-lg) - 4px);
  color: var(--on-accent);
  background: var(--accent-fill);
  font-size: var(--text-base);
  font-weight: 600;
  box-shadow: var(--shadow-sm);
  transition: background 160ms, transform 160ms var(--ease-out);
}

.submit:hover { background: var(--accent-fill-hover); }
.submit:active { background: var(--accent-fill-active); transform: scale(0.98); }
.submit:disabled { cursor: progress; }
.submit .icon { stroke-width: 2; transition: transform 200ms var(--ease-out); }
.submit:hover .icon { transform: translateX(3px); }

/* Generate and Stop share one cell, and the spinner takes the arrow's width:
   the button, and the field beside it, keep one width while a skyline loads. */
.button-label { display: grid; }
.button-label > span { grid-area: 1 / 1; }
.label-stop, .app.is-loading .label-generate { visibility: hidden; }
.app.is-loading .label-stop { visibility: visible; }

.submit-spinner { display: none; width: 18px; height: 14px; justify-content: center; align-items: flex-end; gap: 2px; }
.submit-spinner i {
  width: 3px;
  height: 14px;
  border-radius: 1px;
  background: currentColor;
  transform-origin: bottom;
  animation: equalize 600ms var(--ease-in-out) infinite alternate;
}
.submit-spinner i:nth-child(2) { animation-delay: -200ms; }
.submit-spinner i:nth-child(3) { animation-delay: -400ms; }
@keyframes equalize { from { transform: scaleY(0.25); } to { transform: scaleY(1); } }
.app.is-loading .submit-spinner { display: inline-flex; }
.app.is-loading .submit-arrow { display: none; }

.status {
  position: absolute;
  top: calc(100% + 8px);
  left: 0;
  padding: 8px 12px;
  border: 1px solid var(--hairline-strong);
  border-radius: var(--radius);
  color: var(--ink);
  background: var(--surface-raised);
  box-shadow: var(--shadow-lg);
  font-size: var(--text-md);
  white-space: nowrap;
  opacity: 0;
  transform: translateY(-4px);
  pointer-events: none;
  transition: opacity 180ms, transform 220ms var(--ease-out);
}

.status.is-error { color: var(--danger); }
.app.is-loading .status,
.app.is-stopped .status,
.app.has-error .status { opacity: 1; transform: none; }

/* The old-data note shows over the page for a few seconds; its date stays on the stage. */
.status.is-stale { opacity: 1; transform: none; }

/* ---------- Trending banner ---------- */

/* Beside the form, the names of the day's GitHub Trending ranking pass from
   right to left, as on a news ticker. */
.trending {
  height: 48px;
  display: flex;
  align-items: center;
  border: 1px solid var(--hairline-strong);
  border-radius: var(--radius-lg);
  background: var(--surface-2);
  overflow: hidden;
  container-type: inline-size;
}

.trending-title {
  flex: none;
  height: 100%;
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 0 12px 0 16px;
  border-right: 1px solid var(--hairline);
  color: var(--ink-2);
  font: 600 var(--text-sm)/1 var(--font-sans);
  white-space: nowrap;
}

.trending-day { color: var(--ink-3); font-weight: 500; font-variant-numeric: tabular-nums; }
/* The title opens the Trending page. */
.trending-title-link { display: inline-flex; align-items: center; gap: 4px; color: inherit; text-decoration: none; border-radius: var(--radius-xs); }
/* An arrow going up, in the green of the stars gained. */
.trending-title-link .icon { width: 14px; height: 14px; color: var(--gain); stroke-width: 1.8; }
/* A loudspeaker, in the accent. */
.promote .trending-title-link .icon { color: var(--accent); }
/* A promoted repo's share after its name; an example's name opens nothing. */
.promote span.trending-link { cursor: inherit; }
.promote span.trending-link:hover { background: none; }
.trending-list .promote-share { grid-area: 2 / 3; color: var(--ink-3); font: 500 var(--text-sm)/1 var(--font-sans); font-variant-numeric: tabular-nums; }
.trending-title-link:hover { color: var(--accent-ink); text-decoration: underline; text-underline-offset: 3px; }
/* A narrow banner keeps its room for the names. */
@container (max-width: 600px) {
  .trending-day { display: none; }
}
/* On a phone, the title and its menus tighten. */
@container (max-width: 420px) {
  .trending-title { gap: 4px; padding: 0 8px 0 12px; font-size: var(--text-xs); }
  .trending-title select { height: 24px; padding: 0 16px 0 8px; background-position: right 9px center, right 5px center, 0 0; }
  .trending-pause { margin: 0 4px 0 0; }
}

/* The kind and the period of the ranking shown, as GitHub's own menus. */
.trending-title select {
  appearance: none;
  height: 28px;
  padding: 0 20px 0 8px;
  border: 1px solid var(--line-control);
  border-radius: var(--radius-sm);
  /* A small filled chevron, drawn from two triangles in the muted ink. */
  background:
    linear-gradient(45deg, transparent 50%, var(--ink-3) 50%) no-repeat right 11px center / 4px 4px,
    linear-gradient(135deg, var(--ink-3) 50%, transparent 50%) no-repeat right 7px center / 4px 4px,
    var(--surface-raised);
  color: var(--ink-2);
  font: inherit;
  font-weight: 500;
  cursor: pointer;
  transition: color 160ms, border-color 160ms;
}
.trending-title select:hover { color: var(--ink); border-color: var(--ink-3); }
.trending-title option { background: var(--surface-raised); color: var(--ink); }

/* Where the browser lets the page draw the open menu, it takes the banner's
   look instead of the system's: a panel of the page's theme under its button,
   the ranking shown in the accent, a greyed one dimmed. Elsewhere the system
   menu stays. */
@supports (appearance: base-select) {
  .trending-title select,
  .trending-title ::picker(select) { appearance: base-select; }
  .trending-title select { min-height: 0; align-items: center; }
  .trending-title select::picker-icon { display: none; }
  .trending-title select:open { color: var(--ink); border-color: var(--ink-3); }
  .trending-title ::picker(select) {
    margin-top: 4px;
    padding: 4px;
    border: 1px solid var(--hairline-strong);
    border-radius: var(--radius);
    background: var(--surface-raised);
    box-shadow: var(--shadow-lg);
    opacity: 0;
    transform: translateY(-4px);
    transition: opacity 140ms, transform 180ms var(--ease-out), display 180ms allow-discrete, overlay 180ms allow-discrete;
  }
  .trending-title select:open::picker(select) { opacity: 1; transform: none; }
  @starting-style {
    .trending-title select:open::picker(select) { opacity: 0; transform: translateY(-4px); }
  }
  .trending-title option {
    gap: 8px;
    padding: 8px 12px 8px 8px;
    border-radius: var(--radius-sm);
    background: transparent;
    color: var(--ink-2);
    cursor: pointer;
  }
  .trending-title option:hover,
  .trending-title option:focus-visible { outline: none; background: var(--surface-3); color: var(--ink); }
  .trending-title option:checked { color: var(--accent-ink); }
  .trending-title option:disabled { background: transparent; color: var(--ink-3); opacity: 0.5; cursor: default; }
  @media (prefers-reduced-motion: reduce) {
    .trending-title ::picker(select) { transform: none; transition: none; }
  }
}

/* Positioned, so the names' visually hidden units stay inside its clip: a
   still list (reduced motion) never widens the page. */
.trending-viewport {
  position: relative;
  flex: 1;
  min-width: 0;
  height: 100%;
  padding-left: 12px;
  overflow: hidden;
  mask-image: linear-gradient(90deg, transparent, black 12px, black calc(100% - 12px), transparent);
  /* The names follow a horizontal drag: a vertical swipe still scrolls the
     page, and no text gets selected. */
  touch-action: pan-y;
  user-select: none;
  -webkit-user-select: none;
}

/* By hand only, when the list stays put. */
.trending:not(.is-moving) .trending-viewport {
  overflow-x: auto;
  scrollbar-width: thin;
  scrollbar-color: var(--hairline-strong) transparent;
}

.trending-track { width: max-content; height: 100%; display: flex; }
.trending.is-moving .trending-track { will-change: transform; }
.trending.is-moving .trending-viewport { cursor: grab; }
.trending.is-dragging .trending-viewport,
.trending.is-dragging .trending-link { cursor: grabbing; }

.trending-list {
  flex: none;
  display: flex;
  align-items: center;
  margin: 0;
  padding: 0;
  list-style: none;
}

.trending-list li { display: flex; align-items: center; }

/* Every name is followed by its separator, so the copy joins the list with
   the same gap; the last one shows only while the list loops. */
.trending-list li::after {
  content: "";
  width: 4px;
  height: 4px;
  margin: 0 16px;
  border-radius: 50%;
  background: var(--ink-3);
  opacity: 0.6;
}
.trending:not(.is-moving) .trending-list li:last-child::after { display: none; }

/* Three rows of fixed heights keep every name on one line, centred: the rank
   above the image and the stars of the day above the stars, on one line, then
   the image, name and stars, then an empty row as high as the first. */
.trending-link {
  display: inline-grid;
  grid-template-columns: 18px auto auto;
  grid-template-rows: 8px 18px 8px;
  column-gap: 8px;
  align-items: center;
  padding: 0 4px;
  border-radius: var(--radius-xs);
  color: var(--ink);
  font: 500 var(--text-md)/1 var(--font-sans);
  text-decoration: none;
  white-space: nowrap;
  transition: color 160ms, background 160ms;
}

.trending-link:hover { background: var(--surface-3); }
/* The repo whose skyline is shown: its rank and stars take the accent too. */
.trending-link[aria-current="page"] { color: var(--accent-ink); background: var(--accent-tint); }
.trending-link[aria-current="page"] :is(.trending-rank, .source-stars) { color: inherit; }

/* A repo's image: a square, cropped from a social preview when it has one. */
.trending-image {
  grid-area: 2 / 1;
  width: 18px;
  height: 18px;
  border-radius: var(--radius-xs);
  object-fit: cover;
  background: var(--surface-3);
}
.trending-link.is-profile .trending-image { border-radius: 50%; }
.trending-name { grid-area: 2 / 2; }
.trending-link .source-stars { grid-area: 2 / 3; }
.trending-rank,
.trending-today { font: 600 9px/8px var(--font-sans); font-variant-numeric: tabular-nums; }
.trending-rank { grid-area: 1 / 1; color: var(--ink-3); }
.trending-today { grid-area: 1 / 3; justify-self: end; color: var(--gain); }
.trending-link:focus-visible { outline-offset: -2px; }

.trending-pause {
  flex: none;
  width: 32px;
  height: 32px;
  display: grid;
  place-items: center;
  margin: 0 8px 0 4px;
  padding: 0;
  border: 0;
  border-radius: var(--radius-sm);
  color: var(--ink-2);
  background: transparent;
  transition: color 160ms, background 160ms;
}

.trending-pause:hover { color: var(--ink); background: var(--surface-3); }
.trending-pause .icon { width: 14px; height: 14px; stroke-width: 1.8; }
.trending-pause .icon-play,
.trending.is-paused .trending-pause .icon-pause { display: none; }
.trending.is-paused .trending-pause .icon-play { display: block; }

/* ---------- Theme switch ---------- */

/* System, Light or Dark, at the end of the top bar: one button showing the
   theme in use, which opens into a pill of the three, the theme in use drawn
   as a selection. The pill unfolds leftwards over the bar from the button,
   each icon sliding out from under it to its place. */
.theme-menu { position: relative; flex: none; width: 36px; height: 36px; }
.theme-toggle,
.theme-switch {
  border: 1px solid var(--hairline-strong);
  border-radius: var(--radius);
  background: var(--surface-2);
}
.theme-toggle {
  width: 36px;
  height: 36px;
  display: grid;
  place-items: center;
  padding: 0;
  color: var(--ink-2);
  transition: color 160ms, background 160ms;
}
.theme-toggle:hover { color: var(--ink); background: var(--surface-3); }
.theme-toggle .icon { display: none; width: 16px; height: 16px; }
:root:not([data-theme]) .theme-toggle .theme-icon-system,
:root[data-theme="light"] .theme-toggle .theme-icon-light,
:root[data-theme="dark"] .theme-toggle .theme-icon-dark { display: block; }

.theme-switch {
  position: absolute;
  top: 0;
  right: 0;
  z-index: 1;
  display: flex;
  gap: 2px;
  padding: 3px;
  box-shadow: var(--shadow-md);
  /* Closed, it is the button's square, and out of reach. */
  clip-path: inset(0 0 0 calc(100% - 36px) round var(--radius));
  visibility: hidden;
  transition: clip-path 220ms var(--ease-out), visibility 0s 220ms;
}
.theme-menu.is-open .theme-switch {
  clip-path: inset(0 round var(--radius));
  visibility: visible;
  transition: clip-path 220ms var(--ease-out), visibility 0s;
}

.theme-switch button {
  width: 28px;
  height: 28px;
  display: grid;
  place-items: center;
  padding: 0;
  border: 1px solid transparent;
  /* Concentric with the pill: its radius less the 4 px inset. */
  border-radius: calc(var(--radius) - 4px);
  color: var(--ink-3);
  background: transparent;
  opacity: 0;
  /* Under the button, 30 px a slot, until the pill opens. */
  transform: translateX(var(--from, 0px));
  transition: transform 220ms var(--ease-out), opacity 160ms, color 160ms, background 160ms, border-color 160ms;
}
.theme-switch [data-theme-choice="system"] { --from: 60px; }
.theme-switch [data-theme-choice="light"] { --from: 30px; }
.theme-menu.is-open .theme-switch button { opacity: 1; transform: none; }

.theme-switch button:hover { color: var(--ink); background: var(--surface-3); }
.theme-switch .icon { width: 16px; height: 16px; }

/* The theme in use, from the first paint: read on the root's attribute,
   which the inline script of index.html sets before the page shows. It
   stands where the button's icon was as the pill opens. */
:root:not([data-theme]) .theme-switch [data-theme-choice="system"],
:root[data-theme="light"] .theme-switch [data-theme-choice="light"],
:root[data-theme="dark"] .theme-switch [data-theme-choice="dark"] {
  border-color: var(--accent-strong);
  color: var(--accent-ink);
  background: var(--accent-tint);
  opacity: 1;
}

@media (prefers-reduced-motion: reduce) {
  .theme-switch, .theme-menu.is-open .theme-switch, .theme-switch button { transition: none; }
}

/* ---------- Account menu ---------- */

/* Before the theme switch, 8 px from it, once accounts are available: Sign
   in, or the account's picture, which opens a panel of the account and its
   watchlist below the bar. */
.account-menu { position: relative; flex: none; display: flex; margin-right: -16px; }
.account-sign-in,
.account-toggle {
  height: 36px;
  border: 1px solid var(--hairline-strong);
  border-radius: var(--radius);
  color: var(--ink);
  background: var(--surface-2);
  transition: background 160ms;
}
.account-sign-in { padding: 0 14px; font: 600 var(--text-md)/1 var(--font-sans); white-space: nowrap; }
.account-toggle { width: 36px; display: grid; place-items: center; padding: 0; }
.account-sign-in:hover,
.account-toggle:hover { background: var(--surface-3); }
.account-avatar,
.account-initial { grid-area: 1 / 1; width: 24px; height: 24px; border-radius: 50%; }
.account-avatar { object-fit: cover; }
.account-initial {
  display: grid;
  place-items: center;
  color: var(--on-accent);
  background: var(--accent-fill);
  font: 600 var(--text-sm)/1 var(--font-sans);
}

/* Its right edge on the bar's, under the theme switch: 8 px and 36 px. */
.account-panel {
  position: absolute;
  top: calc(100% + 8px);
  right: -44px;
  z-index: 1;
  width: 360px;
  max-width: calc(100vw - 32px);
  display: grid;
  gap: 12px;
  padding: 16px;
  border: 1px solid var(--hairline-strong);
  border-radius: var(--radius);
  background: var(--surface-raised);
  box-shadow: var(--shadow-lg);
  opacity: 0;
  transform: translateY(-4px);
  visibility: hidden;
  transition: opacity 160ms, transform 220ms var(--ease-out), visibility 0s 220ms;
}
.account-menu.is-open .account-panel {
  opacity: 1;
  transform: none;
  visibility: visible;
  transition: opacity 160ms, transform 220ms var(--ease-out), visibility 0s;
}

.account-who { display: grid; gap: 2px; min-width: 0; }
.account-name { font-weight: 600; overflow-wrap: anywhere; }
.account-email { color: var(--ink-3); font-size: var(--text-md); overflow-wrap: anywhere; }
.account-heading { display: flex; justify-content: space-between; gap: 8px; padding-top: 12px; border-top: 1px solid var(--hairline); }
.account-count { font-variant-numeric: tabular-nums; }
/* The search, then the medals to filter by: Top 1, 2 and 3, each with its
   podium's color. */
.account-tools { display: grid; gap: 8px; }
.account-search {
  height: 32px;
  padding: 0 10px;
  border: 1px solid var(--line-control);
  border-radius: var(--radius-sm);
  color: var(--ink);
  background: var(--surface-1);
  font-size: var(--text-md);
}
.account-search::placeholder { color: var(--ink-3); }
.account-medals { display: flex; flex-wrap: wrap; gap: 6px; }
:is(.account-medal, .account-item-medal)[data-rank="1"] { --medal: var(--rank-1); }
:is(.account-medal, .account-item-medal)[data-rank="2"] { --medal: var(--rank-2); }
:is(.account-medal, .account-item-medal)[data-rank="3"] { --medal: var(--rank-3); }
.account-medal {
  height: 26px;
  display: inline-flex;
  align-items: center;
  gap: 6px;
  padding: 0 10px;
  border: 1px solid var(--hairline-strong);
  border-radius: var(--radius-pill);
  color: var(--ink-2);
  background: var(--surface-2);
  font: 600 var(--text-xs)/1 var(--font-sans);
  transition: color 160ms, background 160ms, border-color 160ms;
}
.account-medal:hover { color: var(--ink); background: var(--surface-3); }
.account-medal[aria-pressed="true"] { border-color: var(--medal); color: var(--ink); background: color-mix(in srgb, var(--medal) 16%, var(--surface-2)); }
.account-medal::before,
.account-item-medal::before { content: ""; flex: none; width: 8px; height: 8px; border-radius: 50%; background: var(--medal); }
/* Past half the screen's height, the list scrolls; 4 px around its items
   keep their focus ring in view. */
.account-watchlist { max-height: 50vh; overflow-y: auto; margin: -4px; padding: 4px; list-style: none; }
.account-watchlist:empty { display: none; }
/* Its icon, its name leading to its website or GitHub, its remove button;
   under the name, its About on two lines at most, then a line a medal it won. */
.account-item { display: grid; grid-template-columns: 20px minmax(0, 1fr) auto; align-items: center; column-gap: 4px; }
.account-item-icon { width: 20px; height: 20px; border-radius: var(--radius-xs); background: var(--surface-3); object-fit: cover; }
.account-item-icon.is-round { border-radius: 50%; }
.account-item-icon.is-broken { visibility: hidden; }
.account-item-about,
.account-item-medal { grid-column: 2 / -1; padding: 0 6px 4px; font-size: var(--text-xs); }
.account-item-about {
  display: -webkit-box;
  margin: 0;
  color: var(--ink-2);
  line-height: 15px;
  overflow: hidden;
  overflow-wrap: anywhere;
  -webkit-box-orient: vertical;
  -webkit-line-clamp: 2;
}
.account-item-medal { display: flex; align-items: center; gap: 6px; color: var(--ink-3); font-variant-numeric: tabular-nums; }
.account-item-name {
  min-width: 0;
  display: flex;
  align-items: center;
  gap: 6px;
  padding: 6px;
  border-radius: var(--radius-sm);
  color: var(--ink);
  text-decoration: none;
}
.account-item-label { min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.account-item-name .icon { width: 12px; height: 12px; color: var(--ink-3); }
.account-item-name:hover { color: var(--accent-ink); background: var(--surface-3); }
.account-item-name:hover .icon { color: currentColor; }
.account-item-remove {
  flex: none;
  width: 28px;
  height: 28px;
  display: grid;
  place-items: center;
  padding: 0;
  border: 0;
  border-radius: var(--radius-sm);
  color: var(--ink-3);
  background: transparent;
  transition: color 160ms, background 160ms;
}
.account-item-remove:hover { color: var(--ink); background: var(--surface-3); }
.account-item-remove .icon { width: 14px; height: 14px; }
.account-empty, .account-nomatch { color: var(--ink-3); font-size: var(--text-md); }

.account-actions,
.account-confirm-delete { padding-top: 12px; border-top: 1px solid var(--hairline); }
.account-actions { display: flex; justify-content: space-between; gap: 8px; }
.account-sign-out,
.account-delete,
.account-confirm-buttons button {
  height: 32px;
  padding: 0 12px;
  border: 1px solid var(--hairline-strong);
  border-radius: var(--radius-sm);
  color: var(--ink);
  background: var(--surface-2);
  font: 600 var(--text-md)/1 var(--font-sans);
  transition: background 160ms;
}
.account-sign-out:hover,
.account-delete:hover,
:is(.account-confirm-buttons, .account-confirm) button:hover { background: var(--surface-3); }
/* Deleting the account asks in place of the actions; removing an item, under
   its name: Cancel, then the action, in the danger color. */
.account-confirm-delete { display: grid; gap: 10px; }
.account-confirm-delete p { font-size: var(--text-md); }
.account-confirm-buttons { display: flex; justify-content: flex-end; gap: 8px; }
.account-confirm { grid-column: 2 / -1; display: flex; flex-wrap: wrap; align-items: center; gap: 6px; padding: 0 6px 6px; }
.account-confirm-text { margin-right: auto; color: var(--ink-2); font-size: var(--text-xs); }
.account-confirm button {
  height: 26px;
  padding: 0 10px;
  border: 1px solid var(--hairline-strong);
  border-radius: var(--radius-sm);
  color: var(--ink);
  background: var(--surface-2);
  font: 600 var(--text-xs)/1 var(--font-sans);
}
.account-delete,
:is(.account-confirm, .account-confirm-buttons) .account-confirm-yes { color: var(--danger); }

@media (prefers-reduced-motion: reduce) {
  .account-panel, .account-menu.is-open .account-panel, .account-medal { transition: none; }
}

/* ---------- Workspace ---------- */

.workspace {
  min-height: calc(100dvh - var(--topbar));
  display: grid;
  grid-template-columns: minmax(0, 1fr) clamp(288px, 24vw, 344px);
  /* The 3D keeps its former 320px minimum; the 86px period strip adds to it. */
  grid-template-rows: minmax(406px, 1fr) auto;
  grid-template-areas:
    "stage insights"
    "timeline insights";
  gap: 16px;
  padding: 4px 24px 24px;
  transition: opacity 300ms, filter 300ms;
}

/* Without a skyline, the workspace is faded and inert. Waiting for the period,
   the previous skyline fades the same way. Once the new plinth is up, only
   the 3D view is live: statistics, charts and the period strip wait for
   every day. */
.app[data-state="empty"] .workspace,
.app.is-loading:not(.is-building) .workspace { opacity: 0.45; filter: saturate(0.4); pointer-events: none; }
.insights, .timeline, .overview { transition: opacity 300ms; }
.app.is-building :is(.insights, .timeline, .overview),
.app.is-incomplete :is(.insights, .timeline, .overview) { opacity: 0.4; pointer-events: none; }

/* Texts left empty while a skyline loads keep their line, so the 3D frame,
   the charts and the key figures do not move when they fill. */
.insights-range, .kpi-sub, .section-sub { min-height: 1lh; }

.card {
  border: 1px solid var(--hairline);
  border-radius: var(--radius-lg);
  background: var(--surface-1);
  box-shadow: var(--shadow-sm);
}

.icon-button {
  width: 32px;
  height: 32px;
  display: grid;
  place-items: center;
  flex: none;
  padding: 0;
  border: 1px solid var(--hairline-strong);
  border-radius: var(--radius-sm);
  color: var(--ink-2);
  background: color-mix(in srgb, var(--surface-raised) 80%, transparent);
  backdrop-filter: blur(10px);
  transition: color 160ms, background 160ms, border-color 160ms;
}

.icon-button:hover { color: var(--ink); background: var(--surface-3); border-color: var(--line-control); }

/* Filter reminders share one look: accent outline, tint, and an × to clear. */
.focus-chip,
.ghost-button {
  height: 28px;
  display: inline-flex;
  align-items: center;
  gap: 4px;
  padding: 0 8px 0 12px;
  border: 1px solid var(--accent-strong);
  border-radius: var(--radius-pill);
  color: var(--ink);
  background: var(--accent-tint);
  font-size: var(--text-sm);
  font-weight: 600;
  white-space: nowrap;
  animation: chip-in 240ms var(--ease-out);
  transition: background 160ms;
}

.ghost-button { padding: 0 12px; }
.focus-chip .icon { width: 14px; height: 14px; color: var(--ink-2); }
.focus-chip:hover, .ghost-button:hover { background: var(--accent-tint-strong); }

@keyframes chip-in { from { opacity: 0; transform: translateY(-3px) scale(0.96); } }

/* ---------- Stage ---------- */

/* The 3D view fills the frame above the period strip. */
.stage {
  grid-area: stage;
  display: flex;
  flex-direction: column;
  overflow: hidden;
  background: var(--stage);
}

.stage-view { position: relative; flex: 1; min-height: 300px; }

.stage-canvas { position: absolute; inset: 0; }
.stage-canvas canvas { display: block; width: 100%; height: 100%; outline: none; }

.stage-view::after {
  content: "";
  position: absolute;
  inset: 0;
  z-index: 1;
  pointer-events: none;
  background:
    linear-gradient(color-mix(in srgb, var(--stage) 80%, transparent), transparent 88px),
    radial-gradient(120% 90% at 50% 45%, transparent 55%, var(--stage-vignette) 100%);
}

.stage-labels {
  position: absolute;
  inset: 0;
  z-index: 2;
  overflow: hidden;
  pointer-events: none;
}

.stage-label {
  position: absolute;
  top: 0;
  left: 0;
  height: 18px;
  padding: 4px 0 0 8px;
  border-left: 1px solid var(--axis);
  color: var(--ink-2);
  font-size: var(--text-xs);
  font-weight: 600;
  font-variant-numeric: tabular-nums;
  line-height: 1;
  white-space: nowrap;
  opacity: 0;
  transition: opacity 180ms;
  will-change: transform;
}

.stage-label.is-minor { color: var(--ink-3); font-weight: 500; }
.stage-label.is-visible { opacity: 1; }

.stage-top {
  position: absolute;
  z-index: 3;
  top: 16px;
  left: 20px;
  right: 16px;
  display: flex;
  align-items: flex-start;
  justify-content: space-between;
  gap: 12px;
  pointer-events: none;
}

.stage-top > * { pointer-events: auto; }

.stage-title {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 4px 12px;
  min-width: 0;
}

/* A long name never runs under the tools: the stars and the link icon wrap
   below it first, then the name itself breaks. */
.stage-owner {
  display: inline-flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 2px 8px;
  min-width: 0;
  color: var(--ink);
  font: 500 var(--text-xl)/1.2 var(--font-serif);
  letter-spacing: -0.01em;
  text-decoration: none;
  border-radius: var(--radius-sm);
}
#stage-owner-name { min-width: 0; overflow-wrap: anywhere; }

.stage-owner .icon-external { width: 14px; height: 14px; color: var(--ink-3); transition: color 160ms, transform 160ms var(--ease-out); }
.stage-owner:hover .icon-external { color: var(--ink); transform: translate(1px, -1px); }

/* A repo's image is a square, cropped from a social preview when it has one;
   a profile's avatar is round. The empty square keeps its place before the
   name while the image loads, or when there is none: the name never moves. */
.stage-owner-image {
  width: 24px;
  height: 24px;
  flex: none;
  overflow: hidden;
  border-radius: var(--radius-xs);
  background: var(--surface-3);
  box-shadow: 0 0 0 1px var(--hairline);
}
.stage-owner-image.is-profile { border-radius: 50%; }
.stage-owner-image img { display: block; width: 100%; height: 100%; object-fit: cover; }
.stage-owner-image img[hidden] { display: none; }

/* The star count beside a repo's name, here and in the Trending banner. */
.source-stars {
  display: inline-flex;
  align-items: center;
  gap: 4px;
  color: var(--ink-3);
  font: 500 var(--text-sm)/1 var(--font-sans);
  font-variant-numeric: tabular-nums;
  letter-spacing: 0;
  white-space: nowrap;
}
.source-stars[hidden] { display: none; }
.source-stars .icon { width: 12px; height: 12px; stroke-width: 1.8; color: var(--star); }

.stage-period { color: var(--ink-3); font: 500 var(--text-sm)/1.3 var(--font-sans); font-variant-numeric: tabular-nums; }

.stage-freshness,
.stage-update {
  flex-basis: 100%;
  color: var(--ink-2);
  font-size: var(--text-sm);
  line-height: 1.5;
}

.stage-update { display: flex; gap: 8px; align-items: baseline; }
.stage-update.is-active #stage-update-label::before {
  content: "";
  display: inline-block;
  width: 6px;
  height: 6px;
  margin-right: 8px;
  border-radius: 50%;
  background: currentColor;
  animation: update-pulse 1.4s ease-in-out infinite;
}
@keyframes update-pulse { 50% { opacity: 0.35; } }

.visually-hidden {
  position: absolute;
  width: 1px;
  height: 1px;
  padding: 0;
  margin: -1px;
  overflow: hidden;
  clip: rect(0, 0, 0, 0);
  white-space: nowrap;
  border: 0;
}

.stage-tools { display: flex; gap: 8px; }

.stage-hint {
  position: absolute;
  z-index: 3;
  left: 20px;
  bottom: 16px;
  color: var(--ink-3);
  font: 500 var(--text-xs)/1.3 var(--font-sans);
  pointer-events: none;
  transition: opacity 400ms;
}

.stage-hint.is-dismissed { opacity: 0; }

/* A stopped or failed construction keeps its days, never as a result. */
.stage-incomplete {
  position: absolute;
  z-index: 3;
  left: 20px;
  bottom: 16px;
  display: none;
  height: 24px;
  align-items: center;
  margin: 0;
  padding: 0 12px;
  border: 1px solid color-mix(in srgb, var(--warn) 45%, transparent);
  border-radius: var(--radius-pill);
  color: var(--warn);
  background: color-mix(in srgb, var(--warn) 10%, var(--surface-raised));
  font-size: var(--text-sm);
  font-weight: 600;
  white-space: nowrap;
}

.app.is-incomplete .stage-incomplete { display: inline-flex; }
.app.is-incomplete .stage-hint { display: none; }

.stage:fullscreen { border-radius: 0; border: 0; }

/* Fullscreen sizes the frame, not the view: at any width, the view takes
   the screen above the period strip. */
.stage:fullscreen .stage-view { flex: 1; height: auto; }

/* ---------- Insights ---------- */

.insights {
  grid-area: insights;
  /* Its long lists scroll inside the column instead of stretching the grid. */
  contain: size;
  display: flex;
  flex-direction: column;
  gap: 24px;
  padding: 20px;
  overflow-y: auto;
  scrollbar-width: thin;
  scrollbar-color: var(--hairline-strong) transparent;
  mask-image: linear-gradient(black calc(100% - 28px), transparent);
}

.insights-head { display: grid; gap: 4px; }
.insights-range { color: var(--ink-2); font-size: var(--text-md); font-variant-numeric: tabular-nums; }

.total { display: flex; align-items: baseline; flex-wrap: wrap; gap: 4px 8px; margin-top: -4px; }

.total-value {
  font-size: var(--text-hero);
  font-weight: 600;
  font-variant-numeric: tabular-nums;
  letter-spacing: -0.035em;
  line-height: 1;
}

.total-unit { color: var(--ink-2); font: 400 var(--text-lg)/1.2 var(--font-serif); }

/* Key figures: one row each, label and context on the left, value on the right. */
.kpis { display: grid; gap: 4px; }

.kpi {
  width: 100%;
  min-width: 0;
  display: grid;
  grid-template-columns: minmax(0, 1fr) auto 18px;
  grid-template-areas:
    "label value state"
    "sub value state";
  align-items: center;
  column-gap: 12px;
  row-gap: 2px;
  padding: 12px 12px 12px 16px;
  border: 1px solid transparent;
  border-radius: var(--radius);
  text-align: left;
  background: var(--surface-2);
  transition: background 160ms, border-color 160ms;
}

.kpi-label { grid-area: label; color: var(--ink-2); font-size: var(--text-md); font-weight: 500; }
.kpi-sub { grid-area: sub; overflow: hidden; color: var(--ink-3); font-size: var(--text-sm); font-variant-numeric: tabular-nums; text-overflow: ellipsis; white-space: nowrap; }
.kpi-value { grid-area: value; font-size: var(--text-lg); font-weight: 600; font-variant-numeric: tabular-nums; letter-spacing: -0.01em; white-space: nowrap; }
.kpi-state { grid-area: state; display: grid; place-items: center; color: var(--ink-3); }
.kpi-state .icon { width: 16px; height: 16px; }
.kpi-state .icon-selected { display: none; }

.kpi.is-toggle:hover:not(:disabled) { background: var(--surface-3); border-color: var(--hairline-strong); }
.kpi.is-toggle:hover:not(:disabled) .kpi-state { color: var(--ink); }
.kpi.is-toggle:disabled { cursor: default; }
.kpi.is-toggle:disabled .kpi-state { visibility: hidden; }

.kpi[aria-pressed="true"],
.kpi[aria-pressed="true"]:hover:not(:disabled) { border-color: var(--accent-strong); background: var(--accent-tint); }
.kpi[aria-pressed="true"] .kpi-label,
.kpi[aria-pressed="true"] .kpi-state { color: var(--ink); }
/* On the tint, the context takes the second ink: 4.5:1 in both themes. */
.kpi[aria-pressed="true"] .kpi-sub { color: var(--ink-2); }
.kpi[aria-pressed="true"] .icon-idle { display: none; }
.kpi[aria-pressed="true"] .icon-selected { display: block; }

.breakdown { display: grid; gap: 8px; }

.bar-list { display: grid; gap: 2px; margin: 0; padding: 0; list-style: none; }

.bar-row {
  position: relative;
  width: 100%;
  height: 28px;
  display: grid;
  grid-template-columns: 40px minmax(0, 1fr) 56px;
  align-items: center;
  gap: 12px;
  padding: 0 8px;
  border: 1px solid transparent;
  border-radius: var(--radius-sm);
  text-align: left;
  background: transparent;
  transition: background 140ms, border-color 140ms;
}

li.bar-row { cursor: default; }
.bar-row:hover, .bar-row.is-lit { background: var(--band); }

.bar-row.is-active,
.bar-row.is-active:hover { border-color: var(--accent-strong); background: var(--accent-tint); }

.bar-label { color: var(--ink-2); font-size: var(--text-sm); font-variant-numeric: tabular-nums; }

.bar-track { height: 8px; border-radius: var(--radius-xs); background: var(--band); }

.bar-fill {
  display: block;
  height: 100%;
  border-radius: var(--radius-xs);
  background: var(--mark-soft);
  transform: scaleX(0);
  transform-origin: left;
  transition: transform 700ms var(--ease-out), background 140ms;
}

.bar-row.is-peak .bar-fill { background: var(--mark); }
.bar-row.is-lit .bar-fill { background: var(--mark-hover); }
.bar-row.is-lit .bar-label, .bar-row.is-active .bar-label { color: var(--ink); }

.bar-value {
  color: var(--ink-2);
  font-size: var(--text-sm);
  font-variant-numeric: tabular-nums;
  text-align: right;
}

/* ---------- Timeline ---------- */

.timeline {
  grid-area: timeline;
  min-width: 0;
  display: grid;
  gap: 12px;
  padding: 16px 20px;
}

.chart-panel { display: flex; flex-direction: column; gap: 4px; min-width: 0; }

.chart-head {
  min-height: 32px;
  display: flex;
  align-items: flex-start;
  justify-content: space-between;
  gap: 16px;
}

.chart-heading { display: grid; gap: 2px; min-width: 0; }
.section-title { font: 500 var(--text-lg)/1.3 var(--font-serif); letter-spacing: -0.005em; }
.section-sub { color: var(--ink-3); font-size: var(--text-sm); font-variant-numeric: tabular-nums; }

.chart-actions { display: flex; align-items: center; gap: 8px; flex: none; }
.chart-actions .icon-button { width: 32px; height: 32px; background: transparent; border-color: transparent; }
.chart-actions .icon-button:hover { background: var(--surface-3); border-color: var(--hairline-strong); }
.chart-actions .icon-button .icon { width: 16px; height: 16px; }

.chart-body { position: relative; }
.chart-body-bars { height: 136px; }
.chart-body-cumulative { height: 96px; }
.chart-canvas { position: absolute; inset: 0; display: block; width: 100%; height: 100%; border-radius: var(--radius-xs); }

.fullscreen-only { display: none; }

.chart-panel:fullscreen {
  gap: 12px;
  padding: 28px 32px;
  background: var(--surface-1);
}

.chart-panel:fullscreen .section-title { font-size: var(--text-2xl); }
.chart-panel:fullscreen .chart-body { flex: 1; height: auto; }
.chart-panel:fullscreen .fullscreen-only { display: inline-flex; }

.overview { padding: 8px 20px 16px; }

.overview-track {
  position: relative;
  height: 40px;
  border-radius: var(--radius-sm);
  background: var(--band);
  cursor: crosshair;
  touch-action: none;
  user-select: none;
}

.overview-canvas { display: block; width: 100%; height: 100%; }

.brush {
  position: absolute;
  top: -1px;
  bottom: -1px;
  min-width: 6px;
  border: 1px solid var(--accent-strong);
  border-radius: var(--radius-sm);
  background: var(--accent-tint);
  cursor: grab;
}

.overview-track.is-dragging .brush { cursor: grabbing; }

.brush.is-full { border-color: var(--hairline-strong); background: transparent; }

.brush-handle {
  position: absolute;
  top: 50%;
  width: 8px;
  height: 24px;
  margin-top: -12px;
  border: 1px solid var(--line-control);
  border-radius: var(--radius-xs);
  background: var(--surface-raised);
  box-shadow: var(--shadow-sm);
  cursor: ew-resize;
  transition: transform 140ms var(--ease-out);
}

.brush-handle:hover { transform: scaleY(1.15); }
.brush-handle[data-handle="left"] { left: -5px; }
.brush-handle[data-handle="right"] { right: -5px; }

.overview-periods { position: relative; height: 24px; margin-top: 4px; }

.period {
  position: absolute;
  top: 0;
  height: 24px;
  padding: 0;
  overflow: hidden;
  border: 1px solid transparent;
  border-radius: var(--radius-sm);
  color: var(--ink-3);
  background: transparent;
  font-size: var(--text-xs);
  font-variant-numeric: tabular-nums;
  white-space: nowrap;
  transition: color 140ms, background 140ms, border-color 140ms;
}

.period:hover { color: var(--ink); background: var(--band); }
.period[aria-pressed="true"] { color: var(--ink); font-weight: 600; border-color: var(--accent-strong); background: var(--accent-tint); }

/* ---------- Tooltip ---------- */

.tooltip {
  position: fixed;
  z-index: 50;
  top: 0;
  left: 0;
  display: grid;
  gap: 2px;
  max-width: 280px;
  padding: 8px 12px;
  border: 1px solid var(--hairline-strong);
  border-radius: var(--radius);
  background: color-mix(in srgb, var(--surface-raised) 96%, transparent);
  backdrop-filter: blur(12px);
  box-shadow: var(--shadow-lg);
  pointer-events: none;
  opacity: 0;
  transition: opacity 120ms;
}

.tooltip.is-visible { opacity: 1; }
.tooltip-value { font-size: var(--text-lg); font-weight: 600; font-variant-numeric: tabular-nums; letter-spacing: -0.01em; }
.tooltip-label { color: var(--ink-2); font-size: var(--text-sm); font-variant-numeric: tabular-nums; }

.tooltip-extra {
  margin-top: 4px;
  padding-top: 8px;
  border-top: 1px solid var(--hairline);
  color: var(--ink-3);
  font-size: var(--text-sm);
  font-variant-numeric: tabular-nums;
}

/* ---------- Trending page ---------- */

/* On /trending and /trending/promote, the page takes the whole workspace:
   the skyline's views step aside, and the workspace is live without a
   skyline. */
.app:is([data-page="trending"], [data-page="promote"]) .workspace {
  grid-template-columns: minmax(0, 1fr);
  grid-template-rows: auto;
  grid-template-areas: "trending";
  opacity: 1;
  filter: none;
  pointer-events: auto;
}
.app:is([data-page="trending"], [data-page="promote"]) .workspace > :is(#stage, .insights, .timeline) { display: none; }

.trending-page, .promote-page {
  grid-area: trending;
  display: grid;
  grid-template-rows: auto minmax(0, 1fr) auto;
  gap: 16px;
  min-height: calc(100dvh - var(--topbar) - 28px);
  padding: 20px 24px 24px;
}

.trending-page-head { display: flex; flex-wrap: wrap; align-items: center; justify-content: space-between; gap: 12px; }
.trending-page-choices { display: flex; flex-wrap: wrap; gap: 8px; }
.trending-page-heading { display: flex; flex-wrap: wrap; align-items: baseline; gap: 4px 12px; }
.trending-page-title { margin: 0; color: var(--ink); font: 500 var(--text-2xl)/1.15 var(--font-serif); letter-spacing: -0.01em; }
.trending-page-time { color: var(--ink-3); font-size: var(--text-md); font-variant-numeric: tabular-nums; }

/* The measure, the list and the period, as the theme switch: one pill each,
   the choice in the accent. Hidden on Devs, the measures keep their room, so
   that Repos and Devs stay under the pointer. */
.trending-measures.is-hidden { visibility: hidden; }
.trending-periods {
  display: flex;
  gap: 2px;
  padding: 3px;
  border: 1px solid var(--hairline-strong);
  border-radius: var(--radius);
  background: var(--surface-2);
}
.trending-periods button {
  height: 28px;
  padding: 0 12px;
  border: 1px solid transparent;
  border-radius: calc(var(--radius) - 4px);
  color: var(--ink-2);
  background: transparent;
  font: 600 var(--text-sm)/1 var(--font-sans);
  transition: color 160ms, background 160ms, border-color 160ms;
}
.trending-periods button:hover { color: var(--ink); background: var(--surface-3); }
.trending-periods [aria-checked="true"] { border-color: var(--accent-strong); color: var(--accent-ink); background: var(--accent-tint); }

/* The tip of a measure, above the measures, as the card a small tile
   projects, on the measures' own ground to part it from the page: what the
   tiles' surface stands for, its head in the ink. */
.trending-page-choices { position: relative; }
.measure-tip {
  position: absolute;
  z-index: 40;
  display: grid;
  gap: 2px;
  width: max-content;
  max-width: 260px;
  margin: 0;
  padding: 8px 10px;
  border: 1px solid var(--hairline-strong);
  border-radius: var(--radius-sm);
  color: var(--ink-2);
  background: var(--surface-2);
  box-shadow: var(--shadow-lg);
  font: 500 var(--text-sm)/1.35 var(--font-sans);
  pointer-events: none;
  animation: tile-card-in 120ms var(--ease-out);
}
.measure-tip-head { color: var(--ink); font-weight: 600; }
@media (prefers-reduced-motion: reduce) {
  .measure-tip { animation: none; }
}

.treemap-frame { position: relative; min-height: 420px; }
/* The tiles' borders, clearer than the page's hairlines: a quarter of the
   ink. The tiles touch, square: each reaches 1 px past its box to the right
   and below, over its neighbours' borders, so that one line parts two; the
   map leaves that pixel free at its own right and bottom. */
.treemap { position: absolute; inset: 0 1px 1px 0; transition: opacity 200ms; --tile-border: color-mix(in srgb, var(--ink) 24%, transparent); }
.trending-page.is-loading .treemap, .promote-page.is-loading .treemap { opacity: 0.45; }
.treemap-status:empty { display: none; }
.treemap-status { margin: 0; color: var(--ink-2); font-size: var(--text-base); }

/* The Promote page: an Example badge while its repos are invented, and the
   promotions' count and what is in play. */
.promote-example {
  align-self: center;
  padding: 2px 8px;
  border-radius: 999px;
  color: var(--accent-ink);
  background: var(--accent-tint);
  font: 600 var(--text-xs)/1.4 var(--font-sans);
}
.promote-page-sub { flex-basis: 100%; margin: 0; color: var(--ink-3); font-size: var(--text-md); font-variant-numeric: tabular-nums; }
/* A promotion's tile, reached by the keyboard: its share, then its amount.
   Its 1 px border lies inside its box, whose surface stays exact. */
.promote-page .tile-item:focus-visible { outline: none; z-index: 2; }
.promote-page .tile-item:focus-visible > .tile { outline: 2px solid var(--focus-ring); outline-offset: -2px; }
.tile-share { font-weight: 600; }
.tile-amount { color: var(--ink-3); font-size: var(--text-xs); }
/* While the client simulates, their tile is dashed, in the accent, and the
   tiles glide to their new places as the amount changes. */
.promote-page .tile-item { transition: left 350ms ease, top 350ms ease, width 350ms ease, height 350ms ease; }
.promote-page .tile-item.is-preview { z-index: 1; }
.promote-page .tile-item.is-preview > .tile { border: 1px dashed var(--accent-strong); color: var(--accent-ink); background: var(--accent-tint); }
@media (prefers-reduced-motion: reduce) {
  .promote-page .tile-item { transition: none; }
}

/* The simulator: beside the treemap from 1024 px, under it below, the
   treemap kept in view. */
.promote-body { display: grid; grid-template-columns: minmax(0, 1fr); gap: 16px; min-height: 0; }
@media (min-width: 1024px) {
  .promote-page.has-panel .promote-body { grid-template-columns: minmax(0, 1fr) 340px; }
}
@media (max-width: 1023px) {
  .promote-page.has-panel .treemap-frame { min-height: min(420px, 42vh); }
}
.promote-panel {
  align-self: start;
  display: grid;
  gap: 12px;
  padding: 16px;
  border: 1px solid var(--hairline-strong);
  border-radius: var(--radius-lg);
  background: var(--surface-2);
}
.promote-panel-head { display: flex; align-items: center; justify-content: space-between; gap: 8px; }
.promote-panel-title { margin: 0; color: var(--ink); font: 500 var(--text-lg)/1.2 var(--font-serif); }
.promote-explain { margin: 0; color: var(--ink-2); font-size: var(--text-sm); line-height: 1.45; }
.promote-field { display: grid; gap: 4px; }
.promote-label { color: var(--ink-3); font: 600 var(--text-xs)/1.2 var(--font-sans); }
.promote-repo, .promote-amount {
  min-width: 0;
  height: 36px;
  padding: 0 10px;
  border: 1px solid var(--hairline-strong);
  border-radius: var(--radius);
  color: var(--ink);
  background: var(--surface-1);
  font: 500 var(--text-sm)/1 var(--font-sans);
}
.promote-amount-box { display: flex; align-items: center; gap: 6px; color: var(--ink-2); font-weight: 600; }
.promote-amount { flex: 1; font-variant-numeric: tabular-nums; }
.promote-slider { width: 100%; margin: 0; accent-color: var(--accent); }
.promote-error { margin: -6px 0 0; color: var(--danger); font-size: var(--text-xs); }
.promote-goals { display: flex; flex-wrap: wrap; align-items: center; gap: 6px; }
.promote-goals button {
  height: 28px;
  padding: 0 10px;
  border: 1px solid var(--hairline-strong);
  border-radius: var(--radius);
  color: var(--ink-2);
  background: transparent;
  font: 600 var(--text-xs)/1 var(--font-sans);
  cursor: pointer;
}
.promote-goals button:hover { color: var(--ink); background: var(--surface-3); }
.promote-kpis { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: 8px; margin: 0; }
.promote-kpis > div { min-width: 0; padding: 8px 10px; border-radius: var(--radius); background: var(--surface-1); }
.promote-kpis dt { color: var(--ink-3); font-size: var(--text-xs); }
.promote-kpis dd { margin: 2px 0 0; color: var(--ink); font: 600 var(--text-lg)/1.2 var(--font-sans); font-variant-numeric: tabular-nums; }
.promote-kpis .promote-including { color: var(--ink-3); font: 500 var(--text-xs)/1.3 var(--font-sans); }
.promote-kpis .promote-including:empty { display: none; }
.promote-dilution { margin: 0; color: var(--ink-2); font-size: var(--text-xs); line-height: 1.45; }
.promote-dilution:empty { display: none; }
.promote-pay-row { display: flex; flex-wrap: wrap; align-items: center; gap: 8px 12px; }
.promote-cta, .promote-pay {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  height: 36px;
  padding: 0 14px;
  border: 0;
  border-radius: var(--radius);
  color: var(--on-accent);
  background: var(--accent-fill);
  font: 600 var(--text-sm)/1 var(--font-sans);
  cursor: pointer;
}
.promote-cta:hover, .promote-pay:hover { background: var(--accent-fill-hover); }
.promote-cta:active, .promote-pay:active { background: var(--accent-fill-active); }
.promote-paid { margin: 0; color: var(--ink-2); font-size: var(--text-xs); }
.promote-paid:empty { display: none; }

.tile-item { position: absolute; }
.tile {
  position: absolute;
  inset: 0 -1px -1px 0;
  display: flex;
  min-width: 0;
  padding: 0;
  border: 1px solid var(--tile-border);
  border-radius: 0;
  color: var(--ink);
  background: var(--surface-1);
  font: 500 var(--text-sm)/1.2 var(--font-sans);
  text-align: left;
  overflow: hidden;
  transition: border-color 160ms, background 160ms, box-shadow 160ms;
}
.tile.is-repo { flex-direction: column; justify-content: space-between; gap: 4px; padding: 6px 8px; }
/* Hovered, by the pointer on it or on its bookmark. */
.tile-item:hover > .tile, .tile.is-selected { border-color: var(--accent-strong); background: var(--accent-tint); }
/* A repo's growth colors its surface on its period's scale (--heat, from 0
   to 1): as the color deepens, its growth, its stars gained and its
   secondary texts move toward the ink. Hovered or selected, it keeps that
   color under the accent's tint. */
.tile.has-growth {
  --growth-fill: color-mix(in oklab, var(--heat-color) calc(pow(var(--heat), var(--heat-curve)) * var(--heat-max)), var(--heat-base));
  background: var(--growth-fill);
}
.tile-item:hover > .tile.has-growth, .tile.has-growth.is-selected { background: linear-gradient(var(--accent-tint), var(--accent-tint)) var(--growth-fill); }
.tile.has-growth :is(.tile-gain, .tile-growth) { color: color-mix(in oklab, var(--ink) calc(var(--heat) * 100%), var(--gain)); }
.tile.has-growth :is(.tile-about, .tile-stars, .tile-forks, .tile-fork-growth) { color: color-mix(in oklab, var(--ink) calc(var(--heat) * 80%), var(--ink-2)); }
.tile.has-growth:not(.is-rank-1, .is-rank-2, .is-rank-3) .tile-rank, .tile.has-growth :is(.tile-forks, .tile-fork-growth) .icon {
  color: color-mix(in oklab, var(--ink) calc(var(--heat) * 100%), var(--ink-3));
}
/* The podium's rank keeps its color on a chip of the page's surface, which
   it reads on whatever the tile's color. */
.tile.has-growth:is(.is-rank-1, .is-rank-2, .is-rank-3) .tile-rank { padding: 0 4px; border-radius: var(--radius-xs); background: var(--surface-1); }
/* A colored border draws over its neighbours' lines; the focus ring over all. */
.tile-item:hover > .tile, .tile:is(.is-selected, .is-rank-1, .is-rank-2, .is-rank-3) { z-index: 1; }
.tile:focus-visible { outline-offset: -2px; z-index: 2; }
.tile-head, .tile-foot { display: flex; align-items: center; gap: 6px; min-width: 0; }
.tile-foot { justify-content: space-between; margin-top: auto; font-variant-numeric: tabular-nums; }
/* The repo's About, cut after the lines its tile has room for. */
.tile-about {
  display: -webkit-box;
  min-height: 0;
  color: var(--ink-2);
  font: 400 var(--text-xs)/15px var(--font-sans);
  overflow: hidden;
  overflow-wrap: anywhere;
  -webkit-box-orient: vertical;
}
.tile-name { min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.tile-image, .tile-avatar { flex: none; border-radius: var(--radius-xs); }
.tile-avatar { border-radius: 50%; }
.tile-gain { color: var(--gain); font-weight: 600; }
/* Forks gained, in a card or a dialog, in the gray of the forks. */
.tile-gain.is-forks, .detail-gain.is-forks { color: var(--ink-2); }
/* A repo's growth in the middle of its tile, as a quote's change, in the
   monospace at the size the page gives it, then a star in the same color
   for what grew: 0.9em with its gap, as growthSize counts it. Its fork
   growth under it, smaller, a fork after it, in the gray of the tile's
   forks, 0.1em of the first's size between them, as forkGrowthSize counts
   it. */
.tile-growth {
  position: absolute;
  inset: 0;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 0.1em;
  font-family: var(--font-mono);
  font-weight: 600;
  line-height: 1;
  letter-spacing: -0.03em;
  pointer-events: none;
}
.tile-growth-line { display: flex; align-items: center; gap: 0.1em; }
.tile-growth .icon { width: 0.8em; height: 0.8em; stroke-width: 1.8; }
/* A repo's stars and forks, after its stars gained: the rank keeps the far end. */
.tile-stars, .tile-forks { display: inline-flex; align-items: center; gap: 2px; color: var(--ink-2); font-size: var(--text-xs); }
.tile-stars .icon { width: 12px; height: 12px; color: var(--star); }
.tile-forks .icon { width: 12px; height: 12px; color: var(--ink-3); }
.tile-foot > .tile-rank { margin-left: auto; }
/* A developer's popular repo, on one line after its login. */
.tile-popular { display: flex; align-items: center; gap: 4px; min-width: 0; color: var(--ink-2); font: 500 var(--text-xs)/15px var(--font-sans); }
.tile-popular .icon { width: 12px; height: 12px; color: var(--ink-3); }
.tile-popular-name { min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.tile-person { min-width: 0; overflow: hidden; color: var(--ink-3); font-size: var(--text-xs); text-overflow: ellipsis; white-space: nowrap; }
.tile-rank { color: var(--ink-3); font-size: var(--text-xs); }
/* Too short for a foot, the name alone on its line, a smaller image before
   it; too narrow for an image, the name takes its room; tiny, the lines
   alone: the accessible name stays. */
.tile.is-short { padding-block: 0; justify-content: center; }
.tile.is-short .tile-foot { display: none; }
.tile.is-short .tile-image { width: 14px; height: 14px; }
.tile.is-narrow { padding-inline: 4px; }
.tile.is-narrow .tile-image { display: none; }
.tile.is-narrow .tile-foot { flex-wrap: wrap; row-gap: 0; }
.tile.is-tiny > * { display: none; }
/* The podium of the ranking, outlined 3 px wide whatever the tile's surface:
   the border holds its color on hover and selection, the rank too. Its
   surface takes a wash of that color, but on hover and selection. */
.tile:is(.is-rank-1, .is-rank-2, .is-rank-3) { border-color: var(--podium); box-shadow: inset 0 0 0 2px var(--podium); }
.tile-item:not(:hover) > .tile:is(.is-rank-1, .is-rank-2, .is-rank-3):not(.is-selected, .has-growth) { background: color-mix(in srgb, var(--podium) var(--rank-wash), var(--surface-1)); }
:is(.tile, .tile-card):is(.is-rank-1, .is-rank-2, .is-rank-3) .tile-rank { color: var(--podium); font-weight: 700; }
:is(.tile, .tile-card).is-rank-1 { --podium: var(--rank-1); }
:is(.tile, .tile-card).is-rank-2 { --podium: var(--rank-2); }
:is(.tile, .tile-card).is-rank-3 { --podium: var(--rank-3); }
/* The repo's star history, cumulated, as on stargithub: its line and a
   wash under it, just above the foot. */
.tile-spark { flex: none; margin-top: auto; overflow: visible; }
.tile-spark ~ .tile-foot { margin-top: 0; }
.tile-spark-area { fill: color-mix(in srgb, var(--star) 16%, transparent); }
.tile-spark-line { fill: none; stroke: var(--star); stroke-width: 1.5; stroke-linejoin: round; stroke-linecap: round; }
/* The watchlist button of a tile with room for it, always shown: 24 px high
   in its top right corner, centred on a short tile, over the tile and its
   focus ring. "Add to watchlist" from 220 px wide, its bookmark with a plus
   alone below; watched, "In watchlist" in the accent, the bookmark filled.
   The tile's name stops before it. */
.tile-watch {
  position: absolute;
  top: 4px;
  right: 4px;
  z-index: 3;
  width: 24px;
  height: 24px;
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 4px;
  padding: 0;
  border: 1px solid var(--hairline-strong);
  border-radius: var(--radius-sm);
  color: var(--ink-2);
  background: var(--surface-2);
  font: 600 var(--text-xs)/1 var(--font-sans);
  white-space: nowrap;
  transition: color 160ms, background 160ms, border-color 160ms;
}
.has-watch-label > .tile-watch { width: 124px; }
.tile.is-short + .tile-watch { top: 50%; margin-top: -12px; }
.tile-watch .icon { width: 14px; height: 14px; }
.tile-watch:hover { color: var(--ink); background: var(--surface-3); border-color: var(--line-control); }
/* Its own surface, not a tint that would pile up on a hovered tile's. */
.tile-watch[data-watched="true"] { border-color: var(--accent-strong); color: var(--accent-ink); }
:is(.tile-watch, .detail-watch)[data-watched="true"] .icon { fill: currentColor; }
.has-watch > .tile .tile-head { margin-right: 24px; }
.has-watch-label > .tile .tile-head { margin-right: 124px; }
@media (prefers-reduced-motion: reduce) {
  .tile-watch { transition: none; }
}
/* The card a small tile projects while it is hovered or focused: what a
   large tile shows, the name in full, the About on three lines at most, a
   repo's star history across it. Over the tiles and out of the pointer's
   way; a podium's in its color. */
.tile-card {
  position: absolute;
  z-index: 30;
  display: grid;
  gap: 4px;
  width: max-content;
  max-width: min(300px, 100%);
  padding: 8px 10px;
  border: 1px solid var(--hairline-strong);
  border-radius: var(--radius-sm);
  color: var(--ink);
  background: var(--surface-raised);
  box-shadow: var(--shadow-lg);
  font: 500 var(--text-sm)/1.2 var(--font-sans);
  pointer-events: none;
  animation: tile-card-in 120ms var(--ease-out);
}
@keyframes tile-card-in { from { opacity: 0; } }
.tile-card:is(.is-rank-1, .is-rank-2, .is-rank-3) { border-color: var(--podium); }
.tile-card .tile-about { -webkit-line-clamp: 3; }
.tile-card .tile-foot { gap: 16px; }
/* A repo's counts go to the next line whole, the rank still at the far end. */
.tile-card .tile-foot:has(.tile-gain, .tile-stars, .tile-forks) { flex-wrap: wrap; row-gap: 2px; }
.tile-card :is(.tile-gain, .tile-stars, .tile-forks) { white-space: nowrap; }
.tile-card-name, .tile-card .tile-popular-name { min-width: 0; overflow-wrap: anywhere; }
.tile-card .tile-popular-name { white-space: normal; }

/* The dialog of a repo or a developer, modal: the page behind is inert under
   a veil of the page's own color. It holds the 3D view, borrowed from the
   workspace, on three fifths of its width, above a repo's star history,
   beside what is known of it. */
:root:has(.skyline-dialog[open]) { overflow: hidden; }
.skyline-dialog {
  width: min(1160px, calc(100vw - 48px));
  height: min(800px, calc(100dvh - 48px));
  max-width: none;
  max-height: none;
  padding: 0;
  border: 1px solid var(--hairline-strong);
  border-radius: var(--radius-lg);
  color: var(--ink);
  background: var(--bg);
  box-shadow: var(--shadow-lg);
  overflow: hidden;
}
.skyline-dialog::backdrop { background: color-mix(in srgb, var(--bg) 72%, transparent); backdrop-filter: blur(3px); }
.skyline-dialog-frame { height: 100%; display: flex; flex-direction: column; }
.skyline-dialog-head { flex: none; display: flex; align-items: flex-start; gap: 12px; padding: 12px 12px 12px 20px; border-bottom: 1px solid var(--hairline); background: var(--surface-1); }
.skyline-dialog-source { flex: 1; min-width: 0; display: grid; gap: 16px; }
.skyline-dialog-status:empty { display: none; }
.skyline-dialog-status { flex: none; margin: 0; padding: 8px 20px; border-bottom: 1px solid var(--hairline); color: var(--ink-2); font-size: var(--text-md); font-variant-numeric: tabular-nums; }
.skyline-dialog-status.is-error { color: var(--danger); }
.skyline-dialog-body {
  flex: 1;
  min-height: 0;
  display: grid;
  grid-template-columns: minmax(0, 3fr) minmax(300px, 2fr);
}
.skyline-dialog-views { min-height: 0; display: grid; grid-template-columns: minmax(0, 1fr); grid-auto-rows: minmax(0, 1fr); gap: 12px; padding: 12px; }
.skyline-dialog-info { display: grid; grid-template-columns: minmax(0, 1fr); align-content: start; gap: 16px; min-height: 0; padding: 16px 20px; border-left: 1px solid var(--hairline); overflow-y: auto; }
/* The 3D view fills its column, or the upper half above a repo's star
   history, out of the workspace's areas; the period strip stays on the
   skyline's page. */
.skyline-dialog .stage { grid-area: auto; min-height: 0; }
.skyline-dialog .stage-view { flex: 1; height: auto; min-height: 0; }
.skyline-dialog .overview { display: none; }
/* Until the repo's first frame, nothing of another skyline shows. */
.app[data-state="empty"] .skyline-dialog .stage { visibility: hidden; }
/* A repo's stars, cumulated day by day (star-chart.ts), drawn as the charts
   of the skyline's page; Loading… in the middle until its history comes. */
.star-panel { min-height: 0; padding: 12px 16px 8px; }
.star-panel[hidden] { display: none; }
.star-panel .section-title { margin: 0; }
.star-panel-body { flex: 1; min-height: 0; }
.star-panel-status { position: absolute; inset: 0; display: grid; place-items: center; color: var(--ink-3); font-size: var(--text-md); }
.star-panel-status:empty { display: none; }

.detail-head { display: flex; align-items: center; gap: 12px; min-height: 32px; }
.detail-description { margin: 0; color: var(--ink-2); font-size: var(--text-base); line-height: 1.5; }
.detail-tags { display: flex; flex-wrap: wrap; gap: 6px; margin: 0; padding: 0; list-style: none; }
.detail-topic { padding: 2px 10px; border-radius: var(--radius-pill); color: var(--accent-ink); background: var(--accent-tint); font: 600 var(--text-sm)/18px var(--font-sans); }
/* The language and the license, before the topics: outlined on the page's
   surface, each with its icon, as tall as a topic. */
.detail-tag {
  display: inline-flex;
  align-items: center;
  gap: 4px;
  padding: 1px 10px 1px 8px;
  border: 1px solid var(--hairline-strong);
  border-radius: var(--radius-pill);
  color: var(--ink-2);
  background: var(--surface-2);
  font: 600 var(--text-sm)/18px var(--font-sans);
}
.detail-tag .icon { width: 14px; height: 14px; color: var(--ink-3); }
/* A language's official logo, filled in its brand's color, or in the ink's
   where that color would not stand out on the theme's surface
   (language-logos.ts). */
.detail-tag .language-logo { fill: light-dark(var(--logo-on-light), var(--logo-on-dark)); stroke: none; color: var(--ink-2); }
.detail-stars { display: flex; flex-wrap: wrap; align-items: baseline; gap: 8px 20px; margin: 0; font-variant-numeric: tabular-nums; }
/* A total and its gain, wrapped apart only when one line cannot hold them. */
.detail-count { display: inline-flex; flex-wrap: wrap; align-items: baseline; gap: 4px 12px; }
.detail-stars .detail-value { font-size: var(--text-xl); }
.detail-gain { color: var(--gain); font-size: var(--text-md); font-weight: 600; }
.detail-forks { display: inline-flex; align-items: center; gap: 4px; }
.detail-forks .icon { width: 16px; height: 16px; color: var(--ink-3); }
/* A developer's popular repo's About, under its name. */
.detail-popular { margin: 0; color: var(--ink-2); font-size: var(--text-md); line-height: 1.5; }
/* Its stars and forks, smaller than a repo's own. */
.detail-popular-counts { display: flex; flex-wrap: wrap; align-items: baseline; gap: 4px 16px; margin: 0; font-size: var(--text-base); font-variant-numeric: tabular-nums; }
.detail-podiums { margin: 0; color: var(--ink-2); font-size: var(--text-md); font-weight: 600; }
.detail-image { flex: none; border-radius: var(--radius-sm); }
.detail-image.is-round { border-radius: 50%; }
.detail-title { flex: 1; margin: 0; min-width: 0; font: 500 var(--text-xl)/1.2 var(--font-serif); overflow-wrap: anywhere; }
.detail-title a { color: var(--ink); text-decoration: none; }
.detail-title a:hover { color: var(--accent-ink); text-decoration: underline; text-underline-offset: 3px; }
.detail-total { display: inline-flex; align-items: center; gap: 4px; }
.detail-total .icon { width: 16px; height: 16px; color: var(--star); }
.detail-value { font-weight: 600; }
.detail-unit { color: var(--ink-3); font-size: var(--text-md); }
.detail-facts { display: flex; flex-wrap: wrap; gap: 12px 24px; margin: 0; }
.detail-fact { display: grid; gap: 2px; }
.detail-fact dt { color: var(--ink-3); font-size: var(--text-xs); font-weight: 600; letter-spacing: 0.04em; text-transform: uppercase; }
.detail-fact { min-width: 0; }
.detail-fact dd { margin: 0; color: var(--ink); font-size: var(--text-base); font-variant-numeric: tabular-nums; overflow-wrap: anywhere; }
.detail-fact a { color: var(--accent-ink); }
.detail-links { display: flex; flex-wrap: wrap; gap: 4px 16px; }
.detail-open,
.detail-watch {
  flex: none;
  height: 32px;
  padding: 0 14px;
  border: 1px solid var(--accent-fill);
  border-radius: var(--radius-sm);
  color: var(--on-accent);
  background: var(--accent-fill);
  font: 600 var(--text-md)/1 var(--font-sans);
  transition: background 160ms;
}
.detail-open:hover,
.detail-watch:hover { background: var(--accent-fill-hover); }
/* The bookmark of the dialog, as Details: its icon, then "Watchlist". */
.detail-watch { display: inline-flex; align-items: center; gap: 6px; }
.detail-watch .icon { width: 16px; height: 16px; }
/* The repo's owner, or the developer's popular repo, in a card of its own. */
.detail-owner, .detail-popular-repo { display: grid; grid-template-columns: minmax(0, 1fr); gap: 10px; padding: 14px 16px; border: 1px solid var(--hairline); border-radius: var(--radius-sm); background: var(--surface-1); }
.detail-owner-type, .detail-owner-more, .detail-popular-label { margin: 0; color: var(--ink-3); font-size: var(--text-xs); font-weight: 600; letter-spacing: 0.04em; text-transform: uppercase; }
.detail-popular-label { display: flex; align-items: center; gap: 4px; }
.detail-popular-label .icon { width: 14px; height: 14px; color: var(--flame); }
.detail-owner-head, .detail-popular-head { display: flex; align-items: center; gap: 10px; min-width: 0; }
.detail-owner-avatar, .detail-popular-image { flex: none; border-radius: var(--radius-xs); }
.detail-owner-avatar.is-round { border-radius: 50%; }
.detail-owner-name, .detail-popular-name { margin: 0; min-width: 0; font: 600 var(--text-md)/1.3 var(--font-sans); overflow-wrap: anywhere; }
.detail-owner-name a, .detail-popular-name a { color: var(--ink); text-decoration: none; }
.detail-owner-name a:hover, .detail-popular-name a:hover { color: var(--accent-ink); text-decoration: underline; text-underline-offset: 3px; }
/* A website's favicon, on the line of its name. */
.site-icon { width: 16px; height: 16px; margin-right: 6px; border-radius: 3px; vertical-align: -3px; }
.detail-owner-site, .detail-popular-site { justify-self: start; color: var(--accent-ink); font-size: var(--text-base); overflow-wrap: anywhere; }
.detail-repos { display: grid; grid-template-columns: minmax(0, 1fr); gap: 4px; margin: 0; padding: 0; list-style: none; }
.detail-repo {
  width: 100%;
  display: flex;
  align-items: center;
  gap: 12px;
  padding: 8px 10px;
  border: 1px solid var(--hairline);
  border-radius: var(--radius-sm);
  color: var(--ink);
  background: var(--surface-1);
  font: 500 var(--text-base)/1.2 var(--font-sans);
  text-align: left;
}
.detail-repo:hover { border-color: var(--accent-strong); background: var(--accent-tint); }
.detail-repo-name { flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }

@media (max-width: 700px) {
  .trending-page { padding: 16px; min-height: 0; }
  .treemap-frame { min-height: 0; height: 70vh; }
  /* The dialog takes the whole screen: the 3D view, a repo's star history,
     then what is known of it, scrolling together. */
  .skyline-dialog { width: 100vw; height: 100dvh; max-height: none; border: 0; border-radius: 0; }
  .skyline-dialog-head { padding: 10px 10px 10px 16px; }
  /* The image and the name on their line, Watchlist and Details below. */
  .detail-head { flex-wrap: wrap; }
  .detail-title { flex-basis: calc(100% - 44px); }
  .skyline-dialog-body { display: block; overflow-y: auto; }
  .skyline-dialog-views { grid-auto-rows: auto; gap: 0; padding: 0; }
  .skyline-dialog-info { border-left: 0; border-top: 1px solid var(--hairline); padding: 16px; overflow: visible; }
  .skyline-dialog .stage { height: 44dvh; border-radius: 0; border: 0; }
  .star-panel { height: 260px; border-width: 1px 0 0; border-radius: 0; box-shadow: none; }
}

/* ---------- Debug ---------- */

.perf-panel {
  position: fixed;
  z-index: 60;
  right: 12px;
  bottom: 12px;
  margin: 0;
  padding: 8px 12px;
  border: 1px solid var(--hairline-strong);
  border-radius: var(--radius-sm);
  color: var(--ink);
  background: color-mix(in srgb, var(--surface-raised) 88%, transparent);
  font: 500 var(--text-xs)/1.45 var(--font-mono);
  pointer-events: none;
}

/* ---------- Responsive ---------- */

@media (max-width: 1080px) {
  :root { --text-hero: 2.25rem; }
  .workspace { grid-template-columns: minmax(0, 1fr) 288px; }
}

@media (max-width: 860px) {
  .workspace {
    min-height: 0;
    grid-template-columns: minmax(0, 1fr);
    grid-template-rows: auto;
    grid-template-areas: "stage" "timeline" "insights";
  }

  .stage-view { flex: none; height: 52vh; min-height: 320px; }
  .insights { contain: none; overflow: visible; mask-image: none; }
}

@media (max-width: 640px) {
  .topbar {
    flex-wrap: wrap;
    gap: 12px;
    padding: 12px 16px;
  }

  .topbar-slot { order: 2; flex-basis: 100%; }
  /* The theme switch joins the brand's line, on its right, the account menu
     just before it. */
  .theme-menu { order: 1; margin-left: auto; }
  .account-menu { order: 1; margin-left: auto; margin-right: -4px; }
  .account-menu:not([hidden]) ~ .theme-menu { margin-left: 0; }
  /* The composer keeps its one line of 48 px, and the view below the
     height. */
  .search { max-width: none; }
  .status { right: 0; white-space: normal; }
  .workspace { padding: 4px 16px 16px; gap: 12px; }
  .stage-view { height: 50vh; min-height: 300px; }
  .stage-top { left: 16px; right: 12px; }
  .stage-owner { font-size: var(--text-lg); }
  .stage-hint { display: none; }
  .timeline { padding: 16px; }
  .overview { padding: 8px 16px 16px; }
  .insights { padding: 20px 16px; }
}

/* Too narrow for Generate beside the whole placeholder: the button shows
   its arrow only, or the spinner while a skyline loads, and keeps its name
   for assistive technologies. */
@media (max-width: 440px) {
  .submit { width: 40px; padding: 0; justify-content: center; }
  .button-label { position: absolute; width: 1px; height: 1px; overflow: hidden; clip-path: inset(50%); }
}

@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;
  }
  .stage-update.is-active #stage-update-label::before { animation: none; }
}
