/* Starship Tracker - public dashboard
   Independent of the admin stylesheet on purpose: this page faces the open
   internet and should be able to evolve without touching the control panel. */

:root {
  --ink: #f2f5fb;
  --ink-soft: #b9c3d6;
  --ink-faint: #8d97ad;
  --bg: #05070d;
  --bg-raised: #131826;
  --line: rgba(255, 255, 255, 0.09);
  --line-strong: rgba(255, 255, 255, 0.18);
  /* Interactive boundaries need 3:1; a translucent white hairline over a
     photograph never reaches it, so controls get an opaque edge. */
  --edge: #5d6880;
  --accent: #6ea2ff;
  --accent-warm: #ffb454;
  --go: #43e39b;
  --hold: #ffd166;
  --bad: #ff7a7a;

  --shell: min(1140px, 100% - 2.5rem);
  --radius: 14px;
  --radius-lg: 20px;

  --font: Inter, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto,
    "Helvetica Neue", Arial, sans-serif;
  --mono: "SF Mono", ui-monospace, "JetBrains Mono", "Cascadia Mono",
    "Roboto Mono", Consolas, monospace;

  /* One spacing scale. Every gap on the page is a step on this ladder, so the
     rhythm is readable instead of nine near-identical values. */
  --sp-1: 0.25rem;
  --sp-2: 0.5rem;
  --sp-3: 0.75rem;
  --sp-4: 1rem;
  --sp-5: 1.5rem;
  --sp-6: 2rem;
  --sp-7: 3rem;
  --sp-8: 4.5rem;

  /* Type roles are genuinely separated so the lower page has an order.
     Minimums stay readable on a phone: body never drops below 16px. */
  --step--2: 0.8125rem;                              /* true fine print */
  --step--1: clamp(0.875rem, 0.85rem + 0.12vw, 0.9375rem);  /* labels, meta */
  --step-0: clamp(1rem, 0.97rem + 0.15vw, 1.0625rem);        /* body */
  --step-1: clamp(1.0625rem, 1rem + 0.3vw, 1.25rem);         /* note, names */
  --step-2: clamp(1.25rem, 1.1rem + 0.7vw, 1.75rem);         /* headings */
  /* The mission name leads; the clock is a clear step below it at every
     width. Two elements tied at one size gave the hero no entry point. */
  --hero: clamp(2.25rem, 1.45rem + 4vw, 4.25rem);            /* mission name */
  --display: clamp(1.75rem, 1.2rem + 2.6vw, 2.75rem);        /* countdown */
}

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

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

body {
  margin: 0;
  min-height: 100vh;
  background: var(--bg);
  color: var(--ink);
  font-family: var(--font);
  font-size: var(--step-0);
  line-height: 1.6;
  -webkit-font-smoothing: antialiased;
  display: flex;
  flex-direction: column;
}

h1, h2, h3 { margin: 0; line-height: 1.1; letter-spacing: -0.025em; font-weight: 640; }
p { margin: 0; }

a { color: var(--accent); }
a:focus-visible,
button:focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: 3px;
  border-radius: 6px;
}

img { max-width: 100%; display: block; }

.shell { width: var(--shell); margin-inline: auto; }

.sr-only {
  position: absolute;
  width: 1px; height: 1px;
  padding: 0; margin: -1px;
  overflow: hidden;
  clip-path: inset(50%);
  white-space: nowrap;
  border: 0;
}

.skip-link {
  position: absolute;
  left: var(--sp-4);
  top: -3rem;
  z-index: 10;
  padding: var(--sp-2) var(--sp-4);
  background: var(--accent);
  color: #05070d;
  font-weight: 620;
  border-radius: 0 0 8px 8px;
  transition: top 0.15s ease;
}
.skip-link:focus { top: 3px; }

/* ---------- Masthead ---------- */

.masthead {
  border-bottom: 1px solid var(--line);
  padding-block: var(--sp-4);
  position: relative;
  z-index: 3;
}
.masthead .shell {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--sp-3);
  /* Never wrap: wrapping doubled the header height and flipped the live
     indicator from right- to left-aligned, which read as a collapse. */
  flex-wrap: nowrap;
}
.wordmark {
  display: flex;
  align-items: center;
  gap: var(--sp-3);
  font-weight: 620;
  letter-spacing: -0.015em;
  font-size: var(--step-0);
  min-width: 0;
}
.wordmark .label {
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.wordmark .glyph {
  width: 2rem;
  height: 2rem;
  flex: none;
  border-radius: 9px;
  display: grid;
  place-items: center;
  color: #fff;
  background: linear-gradient(145deg, #6ea2ff 0%, #8b6bff 55%, #b45bff 100%);
  box-shadow:
    0 0 0 1px rgba(255, 255, 255, 0.1) inset,
    0 3px 12px rgba(110, 162, 255, 0.28);
}
.wordmark .glyph .mark { width: 62%; height: 62%; display: block; }
.live {
  display: inline-flex;
  align-items: center;
  gap: var(--sp-2);
  flex: none;
  font-size: var(--step--2);
  color: var(--ink-faint);
  letter-spacing: 0.08em;
  text-transform: uppercase;
}
.live .pulse {
  width: 0.5rem;
  height: 0.5rem;
  flex: none;
  border-radius: 50%;
  background: var(--go);
  box-shadow: 0 0 0 0 rgba(67, 227, 155, 0.6);
  animation: pulse 2.4s ease-out infinite;
}
@keyframes pulse {
  0% { box-shadow: 0 0 0 0 rgba(67, 227, 155, 0.55); }
  70% { box-shadow: 0 0 0 0.6rem rgba(67, 227, 155, 0); }
  100% { box-shadow: 0 0 0 0 rgba(67, 227, 155, 0); }
}

/* ---------- Hero ---------- */

.hero {
  position: relative;
  isolation: isolate;
  padding-block: clamp(var(--sp-7), 8vw, var(--sp-8));
  overflow: hidden;
}
.hero-media {
  position: absolute;
  inset: 0;
  z-index: -2;
  width: 100%;
  height: 100%;
  object-fit: cover;
  object-position: 72% 42%;
}
/* The scrim protects the copy column and then gets out of the way, instead of
   flooding the whole frame. The photograph survives in the open right-hand
   third, which is the only place it is doing any work. */
.hero-scrim {
  position: absolute;
  inset: 0;
  z-index: -1;
  background:
    linear-gradient(
      90deg,
      rgba(5, 7, 13, 0.95) 0%,
      rgba(5, 7, 13, 0.9) 30%,
      rgba(5, 7, 13, 0.62) 52%,
      rgba(5, 7, 13, 0.24) 74%,
      rgba(5, 7, 13, 0.1) 100%
    ),
    linear-gradient(
      180deg,
      rgba(5, 7, 13, 0.5) 0%,
      rgba(5, 7, 13, 0) 26%,
      rgba(5, 7, 13, 0) 58%,
      var(--bg) 100%
    );
}
.hero.no-media .hero-scrim {
  background:
    radial-gradient(90% 70% at 18% -10%, rgba(110, 162, 255, 0.22), transparent 62%),
    radial-gradient(70% 60% at 88% 8%, rgba(155, 107, 255, 0.16), transparent 60%);
}
.hero .shell { position: relative; }
.hero-copy { max-width: 46rem; }

.eyebrow {
  font-size: var(--step--1);
  letter-spacing: 0.16em;
  text-transform: uppercase;
  font-weight: 620;
  color: var(--accent);
  margin-bottom: var(--sp-3);
}
/* Real mission names run long. A tight measure manufactured extra lines and
   pushed the countdown and CTA below the fold. */
.mission {
  font-size: var(--hero);
  max-width: 22ch;
  overflow-wrap: break-word;
}
.mission:has(+ *) { hyphens: auto; }
.whenline {
  margin-top: var(--sp-4);
  color: var(--ink);
  font-size: var(--step-1);
  font-weight: 500;
}
/* Keeps the final location segment from being orphaned, without marking
   upstream text as trusted HTML to inject a non-breaking space. */
.nowrap-tail { text-wrap: pretty; }

.whereline {
  margin-top: var(--sp-1);
  color: var(--ink-soft);
  font-size: var(--step--1);
}

/* ---------- Countdown ---------- */

/* An equal-width track: four identical boxes, sized by the grid rather than
   by whichever label happens to be longest. */
.countdown {
  margin-top: var(--sp-6);
  display: grid;
  grid-template-columns: repeat(4, minmax(0, 1fr));
  gap: var(--sp-3);
  max-width: 34rem;
}
.unit {
  background: rgba(8, 11, 20, 0.78);
  border: 1px solid var(--edge);
  border-radius: var(--radius);
  padding: var(--sp-4) var(--sp-2);
  text-align: center;
  min-width: 0;
  backdrop-filter: blur(10px);
}
.unit .n {
  display: block;
  font-family: var(--mono);
  font-size: var(--display);
  font-weight: 600;
  font-variant-numeric: tabular-nums;
  letter-spacing: -0.03em;
  line-height: 1;
}
.unit .l {
  display: block;
  margin-top: var(--sp-2);
  font-size: var(--step--2);
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--ink-faint);
}
/* Part of the numeral system, not a label floating above it. */
.cd-sign {
  grid-column: 1 / -1;
  font-family: var(--mono);
  font-size: var(--step-1);
  font-weight: 600;
  letter-spacing: 0.08em;
  color: var(--ink-soft);
  margin-bottom: calc(var(--sp-1) * -1);
}
/* Counting up after liftoff must not look like counting down. */
.countdown.elapsed .unit .n { color: var(--accent-warm); }
.countdown.elapsed .cd-sign { color: var(--accent-warm); }
.countdown.elapsed .unit { border-color: rgba(255, 180, 84, 0.45); }

.chips {
  display: flex;
  gap: var(--sp-2);
  flex-wrap: wrap;
  margin-top: var(--sp-5);
  align-items: center;
}
.chip {
  display: inline-flex;
  align-items: center;
  gap: var(--sp-2);
  padding: 0.36rem 0.85rem;
  border-radius: 100px;
  font-size: var(--step--1);
  font-weight: 560;
  border: 1px solid var(--edge);
  background: rgba(8, 11, 20, 0.75);
  color: var(--ink-soft);
  backdrop-filter: blur(8px);
}
.chip.go { color: var(--go); border-color: rgba(67, 227, 155, 0.4); }
.chip.hold { color: var(--hold); border-color: rgba(255, 209, 102, 0.4); }
.chip.bad { color: var(--bad); border-color: rgba(255, 122, 122, 0.4); }

/* One colour axis on this page: the launch status chip. Date precision is a
   different fact and gets a different encoding - plain text - because two
   semantic axes sharing one palette produced an amber "To Be Confirmed" badge
   directly above a green "confirmed" sentence. */
.note {
  margin-top: var(--sp-4);
  font-size: var(--step-1);
  max-width: 52ch;
  color: var(--ink-soft);
}
/* Only a date too vague to rely on earns emphasis, and it earns it with
   weight and a rule rather than by borrowing the status colour. */
.note.loose {
  color: var(--ink);
  font-weight: 560;
  border-left: 2px solid var(--hold);
  padding-left: var(--sp-3);
}

.actions { display: flex; gap: var(--sp-3); flex-wrap: wrap; margin-top: var(--sp-5); }
.cta {
  display: inline-flex;
  align-items: center;
  gap: var(--sp-2);
  padding: 0.8rem 1.45rem;
  border-radius: 100px;
  background: var(--accent);
  color: #05070d;
  font-weight: 620;
  font-size: var(--step-0);
  text-decoration: none;
  transition: transform 0.15s ease, background 0.15s ease;
}
.cta:hover { background: #8ab5ff; transform: translateY(-1px); }
.cta.ghost {
  background: rgba(8, 11, 20, 0.6);
  color: var(--ink);
  border: 1px solid var(--edge);
}
.cta.ghost:hover { background: rgba(255, 255, 255, 0.06); }

/* ---------- Sections ---------- */

.section { padding-block: var(--sp-7); }
.section-head {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: var(--sp-4);
  margin-bottom: var(--sp-5);
  flex-wrap: wrap;
}
.section-head h2 { font-size: var(--step-2); }
.section-head .count { font-size: var(--step--2); color: var(--ink-faint); }

/* Capped so the mission name and its date stay visually paired. At full
   shell width the gap between them reached 54% of the card. */
.launches {
  display: grid; gap: var(--sp-2); list-style: none; padding: 0; margin: 0;
  max-width: 46rem;
}
.launch {
  display: grid;
  grid-template-columns: minmax(0, 1fr) auto;
  gap: var(--sp-2) var(--sp-5);
  align-items: center;
  padding: var(--sp-4) var(--sp-5);
  background: var(--bg-raised);
  border: 1px solid var(--line);
  border-radius: var(--radius);
  transition: border-color 0.15s ease;
}
.launch > * { min-width: 0; }
/* No hover affordance: the card is not a link and nothing responds. */
.launch .name { font-weight: 580; font-size: var(--step-1); }
.launch .meta { color: var(--ink-faint); font-size: var(--step--1); margin-top: var(--sp-1); }
.launch .when {
  font-family: var(--mono);
  font-size: var(--step--1);
  color: var(--ink-soft);
  font-variant-numeric: tabular-nums;
  text-align: right;
}
.launch .when .rel { display: block; color: var(--ink-faint); margin-top: var(--sp-1); }

.empty {
  padding: var(--sp-7) var(--sp-5);
  text-align: center;
  color: var(--ink-faint);
  background: var(--bg-raised);
  border: 1px dashed var(--line-strong);
  border-radius: var(--radius-lg);
}

/* ---------- Footer ---------- */

.foot {
  margin-top: auto;
  border-top: 1px solid var(--line);
  padding-block: var(--sp-5) var(--sp-6);
  color: var(--ink-faint);
  font-size: var(--step--2);
}
.foot .shell {
  display: flex;
  justify-content: space-between;
  gap: var(--sp-4);
  flex-wrap: wrap;
}
.foot a {
  color: var(--ink-soft);
  /* A 17px-tall target fails the 24px minimum. */
  display: inline-block;
  padding-block: 4px;
  min-height: 24px;
}

/* ---------- Standalone message pages ---------- */

/* Composed at display scale. The headline used to inherit a section-heading
   size because .message h1 outranked .mission, leaving a small grey cluster
   adrift in 70% empty black. */
.message {
  flex: 1;
  display: grid;
  place-items: center;
  padding: var(--sp-8) var(--sp-5);
  text-align: center;
}
.message-inner { max-width: 34rem; }
.message h1 {
  font-size: var(--hero);
  line-height: 1.05;
  margin-block: var(--sp-3) var(--sp-4);
}
.message .lede { color: var(--ink-soft); font-size: var(--step-1); }
.message .actions { justify-content: center; margin-top: var(--sp-6); }

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

/* Below this the copy spans the full width, so a left-weighted scrim no longer
   protects it. Switch to a vertical-dominant treatment and re-centre the crop
   so the vehicle is not hidden behind the headline. */
@media (max-width: 820px) {
  /* Crop onto the bright part of the frame. A 0.58-aspect window at 62%
     showed only the dark tower, so the picture had nothing to give. */
  .hero-media { object-position: 76% 42%; }
  /* Heavy behind the copy, then genuinely light: the band below the text is
     where the photograph gets to be a photograph. Flooding the whole
     rectangle left a maximum luminance of 0.07 - a black plate with a ghost. */
  .hero-scrim {
    background:
      linear-gradient(
        180deg,
        rgba(5, 7, 13, 0.9) 0%,
        rgba(5, 7, 13, 0.88) 46%,
        rgba(5, 7, 13, 0.66) 68%,
        rgba(5, 7, 13, 0.3) 86%,
        rgba(5, 7, 13, 0.82) 100%
      );
  }
  /* Room below the copy for the image to read. */
  .hero { padding-bottom: var(--sp-8); }
  /* Sized to the numeral, not to the shell. Letting the row stretch to the
     full width turned each box into a blank plate with a small number adrift
     in it - 75% empty at 768px. */
  .countdown {
    grid-template-columns: repeat(4, minmax(0, 6.5rem));
    justify-content: start;
    max-width: none;
  }
}

@media (max-width: 560px) {
  :root { --shell: min(1140px, 100% - 1.75rem); }
  .mission { max-width: 100%; }

  /* On a phone the copy fills the frame, so a photograph behind it has to be
     scrimmed into oblivion to keep the text legible - which is how the hero
     ended up a black plate with a ghost in it. Giving the image its own band
     resolves the conflict: the picture reads as a picture, and the copy sits
     on solid ground at full contrast. */
  .hero { padding-top: 0; }
  .hero-media {
    position: relative;
    inset: auto;
    z-index: 0;
    height: 190px;
    object-position: 72% 46%;
    margin-bottom: var(--sp-6);
  }
  .hero-scrim {
    top: 0;
    height: 190px;
    z-index: 1;
    /* Only a fade into the page at the lower edge; the image itself is clear. */
    background: linear-gradient(
      180deg,
      rgba(5, 7, 13, 0) 0%,
      rgba(5, 7, 13, 0) 52%,
      rgba(5, 7, 13, 0.55) 82%,
      var(--bg) 100%
    );
  }
  .hero.no-media .hero-scrim { height: auto; inset: 0; z-index: -1; }
  .hero .shell { position: relative; z-index: 2; }
  .countdown {
    grid-template-columns: repeat(2, minmax(0, 7.25rem));
    justify-content: start;
    gap: var(--sp-2);
    max-width: none;
  }
  .unit { padding: var(--sp-3) var(--sp-2); }

  /* Stack the card and let the date wrap. The nowrap date column used to give
     the card an intrinsic floor wider than its own container, which clipped it
     off the screen below 358px. */
  .launch { grid-template-columns: minmax(0, 1fr); gap: var(--sp-2); padding: var(--sp-4); }
  .launch .when { text-align: left; white-space: normal; }
  .launch .when .rel { display: inline; margin-top: 0; margin-left: var(--sp-2); }

  .live .text { display: none; }
  .foot .shell { flex-direction: column; gap: var(--sp-2); }
}

@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after {
    animation-duration: 0.001ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 0.001ms !important;
  }
}

@media (prefers-contrast: more) {
  :root {
    --ink-soft: #dde3ee;
    --ink-faint: #b6c0d2;
    --line-strong: rgba(255, 255, 255, 0.34);
  }
}

@media print {
  .hero-media, .hero-scrim, .live, .cta, .skip-link { display: none !important; }
  body { background: #fff; color: #000; }
}
