/* goblingains landing — the app's Eclipse ground, as a training week.
   Direction contract: the comment at the top of index.html. Tokens are the
   Eclipse values in frontend/src/theme/tokens.ts; keep them in step. */

@font-face {
  font-family: "Geist";
  src: url("/fonts/geist.woff2") format("woff2");
  font-weight: 100 900;
  font-display: swap;
}

@font-face {
  font-family: "Geist Mono";
  src: url("/fonts/geist-mono.woff2") format("woff2");
  font-weight: 100 900;
  font-display: swap;
}

/* Eclipse: true black, raised cards, one teal for the action and the logged
   tick, iron for strength, tide for cardio. */
:root {
  --bg: #000000;
  --card: #1b2123;
  --card-2: #2b3438;
  --ink: #f2f6f6;
  --ink-2: #c6cfcf;
  --dim: #929c9c;
  --line: #5d6d73;
  --rule: #2b3438;
  --accent: #00e5c0;
  --accent-hover: #4df0d4;
  --accent-ink: #00201b;
  --iron: #f0a86a;
  --tide: #6fd0ff;
  --warn: #ffc857;

  --sans: "Geist", system-ui, -apple-system, "Segoe UI", sans-serif;
  --mono: "Geist Mono", ui-monospace, "SF Mono", Menlo, monospace;

  --page: 1320px;
  --gutter: clamp(1.25rem, 4vw, 3rem);
  --section: clamp(4.5rem, 9vw, 7.5rem);
  --radius-card: 14px;
  --ease-out: cubic-bezier(0.16, 1, 0.3, 1);

  color-scheme: dark;
}

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

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

body {
  margin: 0;
  background: var(--bg);
  color: var(--ink);
  font-family: var(--sans);
  font-size: 1rem;
  line-height: 1.55;
  -webkit-font-smoothing: antialiased;
}

h1,
h2,
h3,
p,
ul,
ol,
dl,
dd,
figure {
  margin: 0;
}

ul,
ol {
  padding: 0;
  list-style: none;
}

img,
video {
  display: block;
  max-width: 100%;
  height: auto;
}

a {
  color: inherit;
}

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

/* Hidden until a keyboard user tabs onto it. */
.skip {
  position: absolute;
  left: var(--gutter);
  top: 0.75rem;
  z-index: 10;
  padding: 0.6rem 1rem;
  border-radius: 999px;
  background: var(--accent);
  color: var(--accent-ink);
  font-weight: 600;
  text-decoration: none;
  transform: translateY(-200%);
}

.skip:focus-visible {
  transform: none;
}

/* ── Header ─────────────────────────────────────────────────────────── */

.top {
  max-width: var(--page);
  margin: 0 auto;
  padding: 1.25rem var(--gutter);
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 1rem;
}

/* "goblin" recedes, "gains" carries the weight. */
.wordmark {
  font-size: 1.125rem;
  font-weight: 450;
  letter-spacing: -0.02em;
  color: var(--dim);
  text-decoration: none;
}

.wordmark b {
  font-weight: 700;
  color: var(--ink);
}

.top-nav {
  display: flex;
  align-items: center;
  gap: 1.25rem;
}

.link {
  font-size: 0.9375rem;
  color: var(--ink-2);
  text-decoration: none;
  padding: 0.5rem 0;
}

.link:hover {
  color: var(--ink);
}

/* ── Buttons: teal is the one action, never decoration ─────────────── */

.btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-height: 48px;
  padding: 0 1.5rem;
  border-radius: 999px;
  border: 1px solid transparent;
  font-size: 1rem;
  font-weight: 600;
  text-decoration: none;
  white-space: nowrap;
  transition: background-color 160ms var(--ease-out), border-color 160ms var(--ease-out), color 160ms var(--ease-out);
}

.btn-primary {
  background: var(--accent);
  color: var(--accent-ink);
}

.btn-primary:hover {
  background: var(--accent-hover);
}

.btn-ghost {
  border-color: var(--line);
  color: var(--ink);
}

.btn-ghost:hover {
  border-color: var(--ink-2);
}

.btn-small {
  min-height: 40px;
  padding: 0 1.1rem;
  font-size: 0.9375rem;
}

/* ── Hero: copy top left, the week across, the phone down the right ── */

.hero {
  max-width: var(--page);
  margin: 0 auto;
  padding: clamp(2.5rem, 6vw, 5rem) var(--gutter) var(--section);
  display: grid;
  grid-template-columns: minmax(0, 1fr) clamp(260px, 24vw, 320px);
  grid-template-areas:
    "copy  promo"
    "week  promo";
  column-gap: clamp(2.5rem, 5vw, 4.5rem);
  row-gap: clamp(2.5rem, 5vw, 4rem);
  align-items: start;
}

.hero-copy {
  grid-area: copy;
}

h1 {
  max-width: 13ch;
  font-size: clamp(2.6rem, 5.6vw, 5rem);
  font-weight: 620;
  line-height: 1.02;
  letter-spacing: -0.035em;
  text-wrap: balance;
}

.lede {
  max-width: 46ch;
  margin-top: 1.5rem;
  font-size: clamp(1.0625rem, 1.4vw, 1.25rem);
  line-height: 1.5;
  color: var(--ink-2);
  text-wrap: pretty;
}

.actions {
  display: flex;
  flex-wrap: wrap;
  gap: 0.75rem;
  margin-top: 2rem;
}

/* A state, not an action: amber dot, never teal. */
.status {
  display: flex;
  align-items: center;
  gap: 0.6rem;
  margin-top: 1.25rem;
  font-size: 0.9375rem;
  color: var(--dim);
}

.dot {
  flex: none;
  width: 8px;
  height: 8px;
  border-radius: 50%;
  background: var(--warn);
}

/* The promo: a 9:16 video at the right edge, taller than the board beside it.
   It has its own black stage and phone inside, so no phone frame around it. */
.promo {
  grid-area: promo;
  position: sticky;
  top: 1.5rem;
}

.promo-screen {
  display: block;
  width: 100%;
  height: auto;
  aspect-ratio: 1080 / 1920;
  border-radius: 24px;
  border: 1px solid var(--rule);
  background: var(--bg);
}

.phone-frame {
  padding: 9px;
  border-radius: 46px;
  background: #0b0e0f;
  border: 1px solid var(--card-2);
  box-shadow: 0 40px 90px -40px rgba(0, 229, 192, 0.22), 0 18px 40px -24px rgba(0, 0, 0, 0.9);
}

.phone-screen {
  width: 100%;
  aspect-ratio: 374 / 810;
  object-fit: cover;
  border-radius: 37px;
  background: var(--card);
}

.phone figcaption,
.shot figcaption {
  margin-top: 1rem;
  font-size: 0.875rem;
  color: var(--dim);
  text-align: center;
}

/* ── The week board ─────────────────────────────────────────────────── */

.week {
  grid-area: week;
  min-width: 0;
}

.week-title {
  max-width: none;
  font-size: 1rem;
  font-weight: 600;
  letter-spacing: -0.01em;
  color: var(--ink);
}

.week-title span {
  font-weight: 400;
  color: var(--dim);
}

/* What this way of training gets, one line under the week's title. */
.week-for {
  max-width: 68ch;
  margin-top: 0.375rem;
  font-size: 0.9375rem;
  color: var(--ink-2);
  text-wrap: pretty;
}

/* Four weeks, one board at a time. No JavaScript on this site (the CSP refuses
   it), so the tabs are radio buttons: each checked input shows its board and
   marks its label. Arrow keys move between them, as in any radio group. */
.week-radio {
  position: absolute;
  opacity: 0;
  pointer-events: none;
}

.week-pick {
  display: inline-flex;
  gap: 0.25rem;
  padding: 0.25rem;
  margin-bottom: 1.25rem;
  border-radius: 999px;
  background: var(--card);
  border: 1px solid var(--rule);
}

.week-pick label {
  min-height: 36px;
  display: inline-flex;
  align-items: center;
  padding: 0 1rem;
  border-radius: 999px;
  font-size: 0.875rem;
  font-weight: 600;
  color: var(--dim);
  cursor: pointer;
  transition: background-color 160ms var(--ease-out), color 160ms var(--ease-out);
}

.week-pick label:hover {
  color: var(--ink);
}

#week-hybrid:checked ~ .week-pick label[for="week-hybrid"],
#week-hyrox:checked ~ .week-pick label[for="week-hyrox"],
#week-crossfit:checked ~ .week-pick label[for="week-crossfit"],
#week-bodybuilding:checked ~ .week-pick label[for="week-bodybuilding"] {
  background: var(--card-2);
  color: var(--ink);
}

#week-hybrid:focus-visible ~ .week-pick label[for="week-hybrid"],
#week-hyrox:focus-visible ~ .week-pick label[for="week-hyrox"],
#week-crossfit:focus-visible ~ .week-pick label[for="week-crossfit"],
#week-bodybuilding:focus-visible ~ .week-pick label[for="week-bodybuilding"] {
  outline: 2px solid var(--accent);
  outline-offset: 2px;
}

.board {
  display: none;
}

#week-hybrid:checked ~ .board-hybrid,
#week-hyrox:checked ~ .board-hyrox,
#week-crossfit:checked ~ .board-crossfit,
#week-bodybuilding:checked ~ .board-bodybuilding {
  display: block;
}

/* Seven columns when they fit; a horizontal, snapping strip when they don't. */
.days {
  display: grid;
  grid-template-columns: repeat(7, minmax(6.5rem, 1fr));
  gap: 0.5rem;
  margin-top: 1rem;
  overflow-x: auto;
  overscroll-behavior-x: contain;
  scroll-snap-type: x mandatory;
  scrollbar-width: thin;
  scrollbar-color: var(--card-2) transparent;
  padding-bottom: 0.5rem;
}

.day {
  display: flex;
  flex-direction: column;
  gap: 0.5rem;
  min-width: 0;
  scroll-snap-align: start;
}

.day-date {
  font-family: var(--mono);
  font-size: 0.8125rem;
  font-variant-numeric: tabular-nums;
  color: var(--ink);
}

.day-date span {
  color: var(--dim);
  margin-right: 0.25rem;
}

.session {
  flex: 1;
  display: flex;
  flex-direction: column;
  gap: 0.5rem;
  padding: 0.75rem;
  border-radius: var(--radius-card);
  background: var(--card);
  border: 1px solid var(--rule);
}

/* The block-type chip, as the app draws it: hue text on its own 16% tint. */
.kind {
  align-self: flex-start;
  padding: 0.15rem 0.5rem;
  border-radius: 999px;
  font-size: 0.6875rem;
  font-weight: 600;
  line-height: 1.5;
}

.kind-strength {
  color: var(--iron);
  background: rgba(240, 168, 106, 0.16);
}

.kind-cardio {
  color: var(--tide);
  background: rgba(111, 208, 255, 0.16);
}

.session h3 {
  font-size: 0.9375rem;
  font-weight: 600;
  line-height: 1.25;
  letter-spacing: -0.01em;
}

.session ul {
  display: grid;
  gap: 0.2rem;
  font-size: 0.8125rem;
  line-height: 1.35;
  color: var(--ink-2);
}

.meta {
  margin-top: auto;
  padding-top: 0.6rem;
  border-top: 1px solid var(--rule);
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 0.5rem;
}

.meta time {
  font-family: var(--mono);
  font-size: 0.6875rem;
  font-variant-numeric: tabular-nums;
  color: var(--dim);
}

/* Words, not a time: set in the sans like the rest of the card's prose. */
.meta-note {
  font-size: 0.75rem;
  line-height: 1.3;
  color: var(--dim);
}

/* The logged tick. Final state is teal; on load each one fills in, Monday
   to Sunday, the week being logged. `backwards` holds the empty ring during
   the delay, so with motion off the page simply shows a logged week. */
.tick {
  position: relative;
  flex: none;
  width: 18px;
  height: 18px;
  border-radius: 50%;
  background: var(--accent);
  border: 1.5px solid var(--accent);
  animation: tick-fill 520ms var(--ease-out) backwards;
  animation-delay: calc(500ms + var(--i, 0) * 170ms);
}

.tick::after {
  content: "";
  position: absolute;
  left: 50%;
  top: 45%;
  width: 4px;
  height: 8px;
  border-right: 2px solid var(--accent-ink);
  border-bottom: 2px solid var(--accent-ink);
  transform: translate(-50%, -50%) rotate(45deg);
  animation: tick-check 520ms var(--ease-out) backwards;
  animation-delay: inherit;
}

/* The day's place in the week sets the delay. Inline style attributes would
   say it shorter, but the CSP (style-src 'self') refuses them. */
.day:nth-child(2) { --i: 1; }
.day:nth-child(3) { --i: 2; }
.day:nth-child(4) { --i: 3; }
.day:nth-child(5) { --i: 4; }
.day:nth-child(6) { --i: 5; }
.day:nth-child(7) { --i: 6; }

@keyframes tick-fill {
  from {
    background: transparent;
    border-color: var(--line);
    transform: scale(0.85);
  }
}

@keyframes tick-check {
  from {
    opacity: 0;
    clip-path: inset(0 0 100% 0);
  }
}

.day-rest {
  justify-content: flex-start;
}

/* An example day carries no date, only its weekday. */
.board-hyrox .day-date span,
.board-crossfit .day-date span {
  margin-right: 0;
}

.rest {
  flex: 1;
  display: grid;
  place-items: center;
  min-height: 6rem;
  border-radius: var(--radius-card);
  border: 1px dashed var(--card-2);
  font-size: 0.8125rem;
  color: var(--dim);
}

/* ── Proof sections: copy beside a real screen ──────────────────────── */

.proof {
  max-width: var(--page);
  margin: 0 auto;
  padding: var(--section) var(--gutter);
  border-top: 1px solid var(--rule);
  display: grid;
  grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
  gap: clamp(2.5rem, 6vw, 6rem);
  align-items: center;
}

h2 {
  max-width: 18ch;
  font-size: clamp(1.9rem, 3.4vw, 3rem);
  font-weight: 620;
  line-height: 1.08;
  letter-spacing: -0.03em;
  text-wrap: balance;
}

.proof-copy p {
  max-width: 52ch;
  margin-top: 1.5rem;
  font-size: 1.0625rem;
  color: var(--ink-2);
  text-wrap: pretty;
}

.shot img {
  width: 100%;
  border-radius: 20px;
  border: 1px solid var(--rule);
  background: var(--card);
}

/* Watch section: the logger clip, held to a phone's width. */
.proof-watch .phone {
  justify-self: center;
  width: min(100%, 300px);
}

/* ── Counts: four quiet figures between the week and the proof ─────── */

.counts {
  max-width: var(--page);
  margin: 0 auto;
  padding: clamp(2.5rem, 5vw, 3.5rem) var(--gutter);
  border-top: 1px solid var(--rule);
}

.counts-list {
  display: grid;
  grid-template-columns: repeat(4, minmax(0, 1fr));
}

/* The figure reads first, its label under it; the dt stays first for
   screen readers, so a list is announced label, then value. */
.counts-list > div {
  display: flex;
  flex-direction: column-reverse;
  justify-content: flex-end;
  gap: 0.5rem;
  padding: 0 1.5rem;
  border-left: 1px solid var(--rule);
}

.counts-list > div:first-child {
  padding-left: 0;
  border-left: 0;
}

.counts-list dd {
  font-size: clamp(1.75rem, 3.2vw, 2.5rem);
  font-weight: 600;
  letter-spacing: -0.03em;
  line-height: 1;
  font-variant-numeric: tabular-nums;
}

.counts-list dt {
  max-width: 24ch;
  font-size: 0.875rem;
  color: var(--dim);
  text-wrap: pretty;
}

/* The one authored moment here: the figures count up as the row scrolls in.
   No script runs on this site, so an animated integer property feeds a CSS
   counter; the figure in the markup is what shows without scroll timelines,
   and what a screen reader reads. */
@property --n {
  syntax: "<integer>";
  initial-value: 0;
  inherits: false;
}

.count-cycles { --to: 100; }
.count-exercises { --to: 680; }
.count-muscles { --to: 24; }

@supports (animation-timeline: view()) {
  .count {
    display: inline-block;
    position: relative;
    color: transparent;
  }

  .count::after {
    content: counter(n);
    counter-reset: n var(--n);
    position: absolute;
    inset: 0;
    color: var(--ink);
    animation: count-up linear both;
    animation-timeline: view();
    animation-range: entry 20% cover 45%;
  }
}

@keyframes count-up {
  from { --n: 0; }
  to { --n: var(--to); }
}

/* ── Close ──────────────────────────────────────────────────────────── */

.close {
  max-width: var(--page);
  margin: 0 auto;
  padding: var(--section) var(--gutter);
  display: grid;
  justify-items: start;
  gap: 1.5rem;
}

.close h2 {
  font-size: clamp(2.2rem, 4.6vw, 4rem);
  max-width: 14ch;
}

.close p {
  max-width: 50ch;
  font-size: 1.0625rem;
  color: var(--ink-2);
}

.close .btn {
  margin-top: 0.5rem;
}

/* ── Footer ─────────────────────────────────────────────────────────── */

.foot {
  max-width: var(--page);
  margin: 0 auto;
  padding: 2.5rem var(--gutter) 3rem;
  border-top: 1px solid var(--rule);
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  justify-content: space-between;
  gap: 1rem 2rem;
}

.wordmark-small {
  font-size: 1rem;
}

.foot nav {
  display: flex;
  flex-wrap: wrap;
  gap: 0.5rem 1.5rem;
  font-size: 0.875rem;
}

.foot nav a {
  color: var(--dim);
  text-decoration: none;
  padding: 0.25rem 0;
}

.foot nav a:hover {
  color: var(--ink);
}

/* ── Legal pages ────────────────────────────────────────────────────── */

.legal {
  max-width: 46rem;
  margin: 0 auto;
  padding: clamp(2.5rem, 6vw, 4.5rem) var(--gutter) var(--section);
}

.legal h1 {
  max-width: none;
  font-size: clamp(2.2rem, 4.5vw, 3.25rem);
}

.legal h2 {
  max-width: none;
  margin-top: 2.75rem;
  font-size: 1.25rem;
  font-weight: 600;
  letter-spacing: -0.01em;
  line-height: 1.3;
}

.legal p {
  max-width: 68ch;
  margin-top: 1rem;
  color: var(--ink-2);
}

.legal a {
  color: var(--ink);
  text-decoration-color: var(--line);
  text-underline-offset: 3px;
}

.legal a:hover {
  text-decoration-color: var(--ink);
}

.legal-alt {
  margin-top: 3rem;
  padding-top: 1.5rem;
  border-top: 1px solid var(--rule);
}

/* ── Setup page (/ai): the legal page's prose, with code to copy ───── */

.setup code {
  font-family: var(--mono);
  font-size: 0.875em;
  color: var(--ink);
  overflow-wrap: anywhere;
}

.setup pre {
  max-width: 68ch;
  margin-top: 1rem;
  padding: 0.875rem 1rem;
  background: var(--card);
  border: 1px solid var(--rule);
  border-radius: 10px;
  overflow-x: auto;
  line-height: 1.5;
}

.setup pre code {
  overflow-wrap: normal;
  white-space: pre;
}

.setup ul {
  max-width: 68ch;
  margin-top: 1rem;
  color: var(--ink-2);
}

.setup li {
  padding: 0.375rem 0 0.375rem 1rem;
  border-left: 2px solid var(--accent);
  margin-top: 0.5rem;
}

/* ── Narrow: stack, the phone after the copy, the week as a strip ──── */

@media (max-width: 900px) {
  .hero {
    grid-template-columns: minmax(0, 1fr);
    grid-template-areas:
      "copy"
      "promo"
      "week";
  }

  .promo {
    position: static;
    justify-self: center;
    width: min(300px, 74vw);
  }

  /* About two and a half days in view, so the strip reads as scrollable. */
  .days {
    grid-template-columns: repeat(7, minmax(10rem, 42vw));
    margin-right: calc(-1 * var(--gutter));
    padding-right: var(--gutter);
    scroll-padding-left: 0;
  }

  .proof {
    grid-template-columns: minmax(0, 1fr);
  }

  /* Copy first on every proof section, whichever side the screen sat on. */
  .proof-watch .proof-copy {
    order: -1;
  }

  .counts-list {
    grid-template-columns: repeat(2, minmax(0, 1fr));
    row-gap: 2rem;
  }

  .counts-list > div:nth-child(3) {
    padding-left: 0;
    border-left: 0;
  }
}

@media (max-width: 520px) {
  .top-nav .link {
    display: none;
  }

  /* Four tabs in a phone's width. */
  .week-pick {
    max-width: 100%;
  }

  .week-pick label {
    padding: 0 0.625rem;
  }
}

@media (prefers-reduced-motion: reduce) {
  .tick,
  .tick::after {
    animation: none;
  }

  .count {
    color: inherit;
  }

  .count::after {
    content: none;
  }

  .btn {
    transition: none;
  }
}
