/* Geeks of the World — hub
 *
 * Every app here does the same thing: starts plausible, escalates. That's the
 * product mechanic (the API takes level 1-5; /escalate returns five rungs), so
 * it's also the design system. Colour is not decoration here — a card's hue IS
 * its position on the absurdity ramp, cool at level 1 and hot at level 5.
 */

:root {
  --ink:        #191327;  /* deep violet, not black — this is a late-night page */
  --ink-raised: #221A38;
  --ink-line:   #33294F;
  --text:       #EDE8F5;
  --text-muted: #9B90B5;

  /* The escalation ramp. Cool → hot, five rungs, used functionally. */
  --lv1: #6C8CD5;
  --lv2: #7B6FD0;
  --lv3: #B75FC4;
  --lv4: #E8467C;
  --lv5: #FF6B35;

  --display: 'Bricolage Grotesque', 'Helvetica Neue', Helvetica, Arial, sans-serif;
  --body: system-ui, -apple-system, 'Segoe UI', Roboto, sans-serif;
  --mono: ui-monospace, SFMono-Regular, 'SF Mono', Menlo, Consolas, monospace;

  --gutter: clamp(1.25rem, 5vw, 4rem);
}

*, *::before, *::after { box-sizing: border-box; }

html { -webkit-text-size-adjust: 100%; }

body {
  margin: 0;
  background: var(--ink);
  color: var(--text);
  font-family: var(--body);
  font-size: 17px;
  line-height: 1.55;
  -webkit-font-smoothing: antialiased;
}

a { color: inherit; }

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

.wrap {
  max-width: 1120px;
  margin: 0 auto;
  padding-inline: var(--gutter);
}

/* ---------- masthead ---------------------------------------------------- */

.masthead {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 1rem;
  padding-block: 1.5rem;
  border-bottom: 1px solid var(--ink-line);
}

.wordmark {
  font-family: var(--display);
  font-weight: 800;
  font-size: 1.0625rem;
  letter-spacing: -0.02em;
  text-decoration: none;
}

.masthead .count {
  font-family: var(--mono);
  font-size: 0.75rem;
  color: var(--text-muted);
  letter-spacing: 0.06em;
  text-transform: uppercase;
}

/* ---------- hero: the escalation ---------------------------------------- */

.hero { padding-block: clamp(3rem, 10vw, 6.5rem) clamp(2.5rem, 7vw, 5rem); }

.hero .eyebrow {
  font-family: var(--mono);
  font-size: 0.75rem;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--text-muted);
  margin: 0 0 1.5rem;
}

/* The line that rewrites itself. Sized to hold the longest rung so the meter
   below never jumps as the text changes — min-height rather than a fixed
   height so long strings can still wrap on narrow screens. Short rungs leave
   space underneath; the meter sits close so that reads as headroom rather
   than a gap. */
.ladder {
  font-family: var(--display);
  font-weight: 700;
  font-size: clamp(1.75rem, 5.2vw, 3.25rem);
  line-height: 1.12;
  letter-spacing: -0.025em;
  margin: 0;
  min-height: 3.4em;
  color: var(--rung, var(--lv1));
  transition: color 600ms ease;
}

.ladder .caret {
  display: inline-block;
  width: 0.06em;
  height: 0.9em;
  margin-left: 0.06em;
  background: currentColor;
  vertical-align: -0.08em;
  animation: blink 1.1s steps(1) infinite;
}

@keyframes blink { 50% { opacity: 0; } }

/* The meter. A readout, so it's set in mono and labelled like an instrument. */
.meter {
  display: flex;
  align-items: center;
  gap: 0.75rem;
  margin-top: 1rem;
  font-family: var(--mono);
  font-size: 0.75rem;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--text-muted);
}

.rungs { display: flex; gap: 4px; }

.rungs span {
  width: 34px;
  height: 5px;
  border-radius: 1px;
  background: var(--ink-line);
  transition: background 400ms ease;
}

.rungs span.on { background: var(--rung, var(--lv1)); }

.hero .lede {
  max-width: 34ch;
  margin: 2.5rem 0 0;
  font-size: clamp(1.0625rem, 2vw, 1.25rem);
  color: var(--text-muted);
}

.hero .lede strong { color: var(--text); font-weight: 600; }

/* ---------- app grid ----------------------------------------------------- */

.shelf { padding-block: 0 clamp(3rem, 8vw, 5rem); }

.shelf h2 {
  font-family: var(--mono);
  font-size: 0.75rem;
  font-weight: 500;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--text-muted);
  margin: 0 0 1.25rem;
  padding-top: 2rem;
  border-top: 1px solid var(--ink-line);
  display: flex;
  flex-wrap: wrap;
  gap: 0.25rem 1rem;
}

/* Section blurb rides along on the heading rule — same size, quieter, so it
   reads as a caption rather than a second heading. */
.shelf h2 span {
  color: #6E6488;
  letter-spacing: 0.08em;
}

/* Sections after the first sit closer; the rule already separates them. */
.shelf + .shelf { padding-top: 0; }

.grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(min(100%, 264px), 1fr));
  gap: 1px;
  background: var(--ink-line);
  border: 1px solid var(--ink-line);
}

/* Cards butt against each other on a hairline grid — the shelf reads as one
   object rather than twelve floating tiles. */
.card {
  display: flex;
  flex-direction: column;
  gap: 0.5rem;
  padding: 1.5rem 1.375rem 1.375rem;
  background: var(--ink-raised);
  position: relative;
  transition: background 200ms ease;
}

/* The card holds two links — open the app, or open its App Store page — so it
   can't be one big anchor. The title link stretches over the whole card
   instead, and the store badge sits above it. Whole-card click still works,
   and the tab order is title then store rather than one opaque target. */
.card-link { text-decoration: none; color: inherit; }

.card-link::after {
  content: '';
  position: absolute;
  inset: 0;
}

.card::before {
  content: '';
  position: absolute;
  inset: 0 auto 0 0;
  width: 3px;
  background: var(--rung);
  opacity: 0.55;
  transition: opacity 200ms ease;
}

.card:hover, .card:focus-within { background: #281F42; }
.card:hover::before, .card:focus-within::before { opacity: 1; }

/* App art. The icons are strong, busy illustrations, so they stay small and
   sit above the title rather than competing with the hero. The ramp bar on the
   card's left edge remains what organises the shelf. */
.thumb {
  width: 56px;
  height: 56px;
  border-radius: 12px;
  overflow: hidden;
  flex: none;
  margin-bottom: 0.375rem;
  background: rgba(255, 255, 255, 0.04);
}

.thumb img {
  display: block;
  width: 100%;
  height: 100%;
  object-fit: cover;
}

/* Apps with no source art get a monogram in their ramp colour. */
.thumb.mono {
  display: grid;
  place-items: center;
  font-family: var(--display);
  font-weight: 800;
  font-size: 1.5rem;
  color: var(--rung);
  border: 1px solid var(--ink-line);
}

.card h3 {
  font-family: var(--display);
  font-size: 1.125rem;
  font-weight: 700;
  letter-spacing: -0.015em;
  margin: 0;
}

.card p { margin: 0; font-size: 0.9375rem; color: var(--text-muted); }

.card .tag {
  margin-top: auto;
  padding-top: 0.875rem;
  font-family: var(--mono);
  font-size: 0.6875rem;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--rung);
  align-self: flex-start;
}

/* Above the stretched title link so it stays independently clickable. */
a.tag.store {
  position: relative;
  z-index: 1;
  text-decoration: none;
  border-bottom: 1px solid transparent;
  transition: border-color 150ms ease;
}

a.tag.store:hover, a.tag.store:focus-visible { border-bottom-color: currentColor; }

.tag .ext { margin-left: 0.35em; }

/* ---------- bundle ------------------------------------------------------- */

.bundle {
  border: 1px solid var(--ink-line);
  padding: clamp(1.5rem, 4vw, 2.5rem);
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: space-between;
  gap: 1.5rem;
  margin-bottom: clamp(3rem, 8vw, 5rem);
}

.bundle h2 {
  font-family: var(--display);
  font-size: clamp(1.25rem, 3vw, 1.75rem);
  font-weight: 700;
  letter-spacing: -0.02em;
  margin: 0 0 0.375rem;
}

.bundle p { margin: 0; color: var(--text-muted); max-width: 46ch; }

.btn {
  display: inline-block;
  padding: 0.75rem 1.5rem;
  background: var(--text);
  color: var(--ink);
  font-weight: 600;
  font-size: 0.9375rem;
  text-decoration: none;
  border-radius: 2px;
  white-space: nowrap;
  transition: transform 150ms ease;
}

.btn:hover { transform: translateY(-1px); }

/* ---------- footer ------------------------------------------------------- */

footer {
  border-top: 1px solid var(--ink-line);
  padding-block: 2rem 3rem;
  font-size: 0.875rem;
  color: var(--text-muted);
  display: flex;
  flex-wrap: wrap;
  gap: 0.5rem 1.5rem;
  justify-content: space-between;
}

footer p { margin: 0; }

/* ---------- reduced motion ----------------------------------------------
   The hero animation is the one moving thing on the page. With motion
   reduced, the ladder holds its final rung instead of cycling — the joke
   still lands, it just doesn't move. Handled in JS too; this covers the
   caret and transitions. */

@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after {
    animation-duration: 0.01ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 0.01ms !important;
  }
  .ladder .caret { display: none; }
}
