/* ===========================================================================
   Deep Field
   ---------------------------------------------------------------------------
   The page is an observing log. Photography is the loudest thing on it;
   everything else — rules, labels, timestamps — is instrument panel, set in
   mono at small sizes and kept deliberately quiet.

   Structure
     1. Faces
     2. Tokens
     3. Themes
     4. Reset and base
     5. Rhythm and layout
     6. Masthead and navigation
     7. Spectrum (the topic index)
     8. Stories
     9. Gallery
    10. Launch table
    11. Panels, notes and the support line
    12. Advertising slots
    13. Footer
    14. Motion
    15. Narrow screens
   =========================================================================== */

/* -- 1. Faces ------------------------------------------------------------- */

@font-face {
  font-family: 'Newsreader';
  font-style: normal;
  font-weight: 200 800;
  font-display: swap;
  src: url('../fonts/newsreader-normal-200_800.woff2') format('woff2-variations'),
       url('../fonts/newsreader-normal-200_800.woff2') format('woff2');
}

@font-face {
  font-family: 'Newsreader';
  font-style: italic;
  font-weight: 200 800;
  font-display: swap;
  src: url('../fonts/newsreader-italic-200_800.woff2') format('woff2-variations'),
       url('../fonts/newsreader-italic-200_800.woff2') format('woff2');
}

@font-face {
  font-family: 'Plex Mono';
  font-style: normal;
  font-weight: 400;
  font-display: swap;
  src: url('../fonts/ibm-plex-mono-normal-400.woff2') format('woff2');
}

@font-face {
  font-family: 'Plex Mono';
  font-style: normal;
  font-weight: 500;
  font-display: swap;
  src: url('../fonts/ibm-plex-mono-normal-500.woff2') format('woff2');
}

@font-face {
  font-family: 'Plex Mono';
  font-style: normal;
  font-weight: 600;
  font-display: swap;
  src: url('../fonts/ibm-plex-mono-normal-600.woff2') format('woff2');
}

/* -- 2. Tokens ------------------------------------------------------------ */

:root {
  --serif: 'Newsreader', 'Iowan Old Style', 'Palatino Linotype', Palatino, Georgia, serif;
  --mono: 'Plex Mono', ui-monospace, 'SF Mono', Menlo, Consolas, monospace;

  /* A single fluid scale. Body sits at 17px because this site is read, not
     scanned, and 16px serif on a dark ground is a squint. */
  --t-xs: 0.6875rem;
  --t-sm: 0.8125rem;
  --t-base: 1.0625rem;
  --t-md: clamp(1.15rem, 1.06rem + 0.4vw, 1.35rem);
  --t-lg: clamp(1.4rem, 1.24rem + 0.75vw, 1.9rem);
  --t-xl: clamp(1.85rem, 1.5rem + 1.65vw, 2.9rem);
  --t-2xl: clamp(2.4rem, 1.75rem + 3vw, 4.1rem);
  --t-3xl: clamp(3rem, 1.9rem + 5.2vw, 6.2rem);

  --measure: 37rem;

  --gap-2: 0.25rem;
  --gap-3: 0.5rem;
  --gap-4: 0.75rem;
  --gap-5: 1rem;
  --gap-6: 1.5rem;
  --gap-7: 2rem;
  --gap-8: 3rem;
  --gap-9: 4.5rem;
  --gap-10: 7rem;

  --shell: 82rem;
  --shell-wide: 102rem;
  --pad: clamp(1.15rem, 4vw, 3.5rem);

  --ease: cubic-bezier(0.2, 0.7, 0.25, 1);
  --ease-out: cubic-bezier(0.16, 1, 0.3, 1);

  color-scheme: dark;
}

/* -- 3. Themes ------------------------------------------------------------ */
/* Night is the default: this is a publication about the sky after dark, and the
   photography is almost entirely light-on-black to begin with. */

:root,
[data-theme='night'] {
  --ink: #e9e6df;
  --ink-quiet: #9c9a94;
  --ink-faint: #6a6963;
  --ground: #08090b;
  --ground-raised: #101216;
  --ground-sunken: #050608;
  --rule: rgba(233, 230, 223, 0.14);
  --rule-strong: rgba(233, 230, 223, 0.28);
  --accent: var(--site-accent-night, #ffb15d);
  --accent-quiet: var(--site-accent-night-quiet, #c98a3f);
  --shade: rgba(255, 255, 255, 0.045);
  --frame: #000;
  --selection: rgba(255, 177, 93, 0.24);
  --sky: var(--starfield);
}

[data-theme='day'] {
  color-scheme: light;
  --ink: #16181c;
  --ink-quiet: #5c5f66;
  --ink-faint: #8b8e95;
  --ground: #f5f3ee;
  --ground-raised: #fffefb;
  --ground-sunken: #eae7e0;
  --rule: rgba(22, 24, 28, 0.16);
  --rule-strong: rgba(22, 24, 28, 0.34);
  --accent: var(--site-accent-day, #8a5310);
  --accent-quiet: var(--site-accent-day-quiet, #a8712c);
  --shade: rgba(22, 24, 28, 0.04);
  --frame: #d8d4cb;
  --selection: rgba(138, 83, 16, 0.18);
  --sky: none;
}

@media (prefers-color-scheme: light) {
  :root:not([data-theme]) {
    color-scheme: light;
    --ink: #16181c;
    --ink-quiet: #5c5f66;
    --ink-faint: #8b8e95;
    --ground: #f5f3ee;
    --ground-raised: #fffefb;
    --ground-sunken: #eae7e0;
    --rule: rgba(22, 24, 28, 0.16);
    --rule-strong: rgba(22, 24, 28, 0.34);
    --accent: var(--site-accent-day, #8a5310);
    --accent-quiet: var(--site-accent-day-quiet, #a8712c);
    --shade: rgba(22, 24, 28, 0.04);
    --frame: #d8d4cb;
    --selection: rgba(138, 83, 16, 0.18);
    --sky: none;
  }
}

/* The field.
   Twelve one-pixel points on four tiles of different, deliberately unrelated
   sizes, so the pattern never lines up with itself at any scroll position or
   viewport width. On a laptop that works out at somewhere around fifty visible
   points — enough that the ground reads as sky rather than as paper, and few
   enough that it is a texture and not a wallpaper. Two of them are very
   slightly warm, because real ones are.

   Defined once here and switched on by --sky in the night theme only; daylight
   gets a flat ground, the way a printed page would. */
:root {
  --starfield:
    radial-gradient(1.4px 1.4px at 11% 27%, rgba(255, 255, 255, 0.55), transparent 60%),
    radial-gradient(1px 1px at 68% 12%, rgba(255, 255, 255, 0.34), transparent 60%),
    radial-gradient(1px 1px at 39% 78%, rgba(255, 250, 240, 0.40), transparent 60%),
    radial-gradient(1px 1px at 84% 54%, rgba(255, 255, 255, 0.26), transparent 60%),
    radial-gradient(1.2px 1.2px at 24% 61%, rgba(255, 255, 255, 0.44), transparent 60%),
    radial-gradient(1px 1px at 57% 33%, rgba(255, 255, 255, 0.28), transparent 60%),
    radial-gradient(1px 1px at 92% 87%, rgba(240, 246, 255, 0.36), transparent 60%),
    radial-gradient(1px 1px at 6% 92%, rgba(255, 255, 255, 0.30), transparent 60%),
    radial-gradient(1.3px 1.3px at 73% 41%, rgba(255, 255, 255, 0.48), transparent 60%),
    radial-gradient(1px 1px at 45% 8%, rgba(255, 255, 255, 0.24), transparent 60%),
    radial-gradient(1px 1px at 18% 49%, rgba(255, 255, 255, 0.32), transparent 60%),
    radial-gradient(1px 1px at 88% 19%, rgba(255, 255, 255, 0.22), transparent 60%);
  --starfield-size:
    743px 611px, 743px 611px, 743px 611px,
    521px 487px, 521px 487px, 521px 487px,
    907px 829px, 907px 829px, 907px 829px,
    389px 431px, 389px 431px, 389px 431px;
}

/* Topic accents. `--topic` is set per element from the build; these two lines
   pick the right variant for the active theme. */
[data-topic] {
  --accent: var(--topic-night);
  --accent-quiet: var(--topic-night-quiet);
}

[data-theme='day'] [data-topic] {
  --accent: var(--topic-day);
  --accent-quiet: var(--topic-day-quiet);
}

@media (prefers-color-scheme: light) {
  :root:not([data-theme]) [data-topic] {
    --accent: var(--topic-day);
    --accent-quiet: var(--topic-day-quiet);
  }
}

/* -- 4. Reset and base ---------------------------------------------------- */

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

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

@media (prefers-reduced-motion: reduce) {
  html { scroll-behavior: auto; }
}

body {
  margin: 0;
  background-color: var(--ground);
  background-image: var(--sky, none);
  background-size: var(--starfield-size);
  color: var(--ink);
  font-family: var(--serif);
  font-size: var(--t-base);
  font-optical-sizing: auto;
  line-height: 1.58;
  -webkit-font-smoothing: antialiased;
  text-rendering: optimizeLegibility;
  overflow-x: hidden;
}

::selection { background: var(--selection); }

h1, h2, h3, h4, p, figure, blockquote, ul, ol, dl { margin: 0; }
ul, ol { padding: 0; list-style: none; }
img, svg, video { display: block; max-width: 100%; }
button { font: inherit; color: inherit; }

a {
  color: inherit;
  text-decoration: none;
}

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

.skip {
  position: absolute;
  left: -9999px;
  top: 0;
  z-index: 100;
  background: var(--ground-raised);
  color: var(--ink);
  padding: 0.75rem 1.25rem;
  border: 1px solid var(--rule-strong);
  font-family: var(--mono);
  font-size: var(--t-sm);
}

.skip:focus { left: 1rem; top: 1rem; }

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

/* The label voice: mono, small, tracked, never shouty. */
.label {
  font-family: var(--mono);
  font-size: var(--t-xs);
  font-weight: 500;
  letter-spacing: 0.13em;
  text-transform: uppercase;
  color: var(--ink-quiet);
}

.label--accent { color: var(--accent); }

/* Spectral notation keeps its own casing throughout. CSS uppercase is a
   transliteration, not a font choice: it turns Hβ into HB, Fe Kα into FE KA and
   the micro sign in 10 µm into a Greek capital Mu. Every one of those is a
   different quantity from the one meant. */
.label--band,
.unit { text-transform: none; letter-spacing: 0.06em; }

.mono { font-family: var(--mono); font-size: var(--t-sm); }

.dim { color: var(--ink-quiet); }

/* -- 5. Rhythm and layout ------------------------------------------------- */

.shell {
  width: 100%;
  max-width: var(--shell);
  margin-inline: auto;
  padding-inline: var(--pad);
}

.shell--wide { max-width: var(--shell-wide); }

.band { padding-block: var(--gap-9); }
.band--tight { padding-block: var(--gap-8); }
.band + .band { border-top: 1px solid var(--rule); }

.band__head {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: var(--gap-5);
  flex-wrap: wrap;
  padding-bottom: var(--gap-6);
  margin-bottom: var(--gap-7);
  border-bottom: 1px solid var(--rule);
}

.band__title {
  font-size: var(--t-lg);
  font-weight: 450;
  letter-spacing: -0.012em;
  line-height: 1.1;
}

.band__more {
  font-family: var(--mono);
  font-size: var(--t-sm);
  color: var(--ink-quiet);
  border-bottom: 1px solid var(--rule-strong);
  padding-bottom: 2px;
  transition: color 0.2s var(--ease), border-color 0.2s var(--ease);
}

.band__more:hover { color: var(--accent); border-color: var(--accent); }

.prose {
  max-width: var(--measure);
  font-size: var(--t-md);
  line-height: 1.66;
}

.prose p + p { margin-top: 1.15em; }
.prose a { color: var(--accent); border-bottom: 1px solid color-mix(in oklab, var(--accent) 45%, transparent); }
.prose a:hover { border-bottom-color: var(--accent); }
.prose h2 {
  font-size: var(--t-lg);
  font-weight: 450;
  margin-top: 2.2em;
  margin-bottom: 0.5em;
  letter-spacing: -0.012em;
}
.prose h3 {
  font-family: var(--mono);
  font-size: var(--t-sm);
  font-weight: 600;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--ink-quiet);
  margin-top: 2em;
  margin-bottom: 0.65em;
}
.prose ul { margin: 1.15em 0; }
.prose li { position: relative; padding-left: 1.4em; margin-bottom: 0.5em; }
.prose li::before {
  content: '';
  position: absolute;
  left: 0;
  top: 0.72em;
  width: 0.55em;
  height: 1px;
  background: var(--accent-quiet);
}

/* -- 6. Masthead and navigation ------------------------------------------- */

/* Transparent, unlike the sticky nav below it, so the field is the first thing
   under the wordmark rather than a band of flat ground. */
.masthead {
  position: relative;
  border-bottom: 1px solid var(--rule);
}

.masthead__inner {
  display: grid;
  grid-template-columns: auto 1fr auto;
  align-items: center;
  gap: var(--gap-6);
  padding-block: var(--gap-5);
}

.wordmark {
  display: inline-flex;
  align-items: center;
  gap: 0.6rem;
}

.wordmark__glyph {
  width: 1.75rem;
  height: 1.75rem;
  flex: none;
}

.wordmark__text {
  font-size: 1.35rem;
  font-weight: 500;
  letter-spacing: -0.02em;
  line-height: 1;
  white-space: nowrap;
}

.wordmark__text em {
  font-style: normal;
  color: var(--ink-quiet);
  font-weight: 300;
}

.masthead__meta {
  justify-self: center;
  display: flex;
  align-items: center;
  gap: var(--gap-5);
  font-family: var(--mono);
  font-size: var(--t-xs);
  letter-spacing: 0.09em;
  text-transform: uppercase;
  color: var(--ink-faint);
}

.masthead__meta span { white-space: nowrap; }

.masthead__tools {
  display: flex;
  align-items: center;
  gap: var(--gap-4);
}

.icon-button {
  display: grid;
  place-items: center;
  width: 2.15rem;
  height: 2.15rem;
  background: none;
  border: 1px solid var(--rule);
  cursor: pointer;
  color: var(--ink-quiet);
  transition: color 0.2s var(--ease), border-color 0.2s var(--ease), background 0.2s var(--ease);
}

.icon-button:hover {
  color: var(--ink);
  border-color: var(--rule-strong);
  background: var(--shade);
}

.icon-button svg { width: 1.05rem; height: 1.05rem; }

.nav {
  border-bottom: 1px solid var(--rule);
  background: var(--ground);
  position: sticky;
  top: 0;
  z-index: 40;
}

.nav__inner {
  display: flex;
  align-items: stretch;
  gap: 0;
  overflow-x: auto;
  scrollbar-width: none;
  -ms-overflow-style: none;
}

.nav__inner::-webkit-scrollbar { display: none; }

.nav__link {
  position: relative;
  display: flex;
  align-items: center;
  padding: 0.85rem 0;
  margin-right: var(--gap-6);
  font-family: var(--mono);
  font-size: var(--t-sm);
  letter-spacing: 0.04em;
  color: var(--ink-quiet);
  white-space: nowrap;
  transition: color 0.2s var(--ease);
}

.nav__link:last-child { margin-right: 0; }
.nav__link:hover { color: var(--ink); }

.nav__link::after {
  content: '';
  position: absolute;
  left: 0;
  right: 0;
  bottom: -1px;
  height: 2px;
  background: var(--accent);
  transform: scaleX(0);
  transform-origin: left;
  transition: transform 0.28s var(--ease-out);
}

.nav__link:hover::after,
.nav__link[aria-current]::after { transform: scaleX(1); }
.nav__link[aria-current] { color: var(--ink); }

/* -- 7. Spectrum ---------------------------------------------------------- */
/* The topic index is drawn as a spectrum, ordered radio to gamma, because that
   is how the topics are actually arranged. */

.spectrum-strip {
  display: flex;
  height: 3px;
  width: 100%;
}

.spectrum-strip span { flex: 1 1 0; }

.spectrum {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(16rem, 1fr));
  border-top: 1px solid var(--rule);
  border-left: 1px solid var(--rule);
}

.spectrum__cell {
  position: relative;
  border-right: 1px solid var(--rule);
  border-bottom: 1px solid var(--rule);
  display: flex;
  flex-direction: column;
  gap: var(--gap-3);
  padding: var(--gap-6) var(--gap-5) var(--gap-6);
  overflow: hidden;
  transition: background 0.3s var(--ease);
}

.spectrum__cell::before {
  content: '';
  position: absolute;
  inset: 0 0 auto 0;
  height: 3px;
  background: var(--accent);
  transform: scaleX(0.18);
  transform-origin: left;
  transition: transform 0.5s var(--ease-out);
}

.spectrum__cell:hover { background: var(--ground-raised); }
.spectrum__cell:hover::before { transform: scaleX(1); }

.spectrum__band {
  font-family: var(--mono);
  font-size: var(--t-xs);
  font-weight: 500;
  letter-spacing: 0.06em;
  color: var(--accent);
}

.readout dd { text-transform: none; }

.spectrum__name {
  font-size: var(--t-md);
  font-weight: 450;
  letter-spacing: -0.01em;
  line-height: 1.15;
}

.spectrum__blurb {
  font-size: var(--t-sm);
  line-height: 1.5;
  color: var(--ink-quiet);
  max-width: 30ch;
}

.spectrum__count {
  margin-top: auto;
  padding-top: var(--gap-4);
  font-family: var(--mono);
  font-size: var(--t-xs);
  color: var(--ink-faint);
  letter-spacing: 0.06em;
}

/* -- 8. Stories ----------------------------------------------------------- */

.lead {
  display: grid;
  grid-template-columns: minmax(0, 1.15fr) minmax(0, 1fr);
  gap: var(--gap-8);
  align-items: center;
}

.lead__frame {
  position: relative;
  overflow: hidden;
  background: var(--ground-sunken);
  aspect-ratio: 4 / 3;
}

.lead__frame img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  transition: transform 1.4s var(--ease-out), opacity 0.6s var(--ease);
}

.lead:hover .lead__frame img { transform: scale(1.035); }

.lead__body { display: flex; flex-direction: column; gap: var(--gap-5); }

/* The lead headline runs at 85% of the display scale, and steps down again as
   the headline gets longer — see titleLength() in render/components.mjs. The
   tracking opens back up slightly at the small end so the smaller setting does
   not read as cramped. */
.lead__title {
  font-size: calc(var(--t-2xl) * var(--title-scale, 0.85));
  font-weight: 400;
  line-height: 1.05;
  letter-spacing: -0.028em;
  text-wrap: balance;
}

.lead[data-length='long'] .lead__title { --title-scale: 0.79; letter-spacing: -0.024em; }
.lead[data-length='xlong'] .lead__title { --title-scale: 0.73; letter-spacing: -0.02em; line-height: 1.08; }

.lead__title a { background-image: linear-gradient(var(--accent), var(--accent)); background-size: 0 1px; background-repeat: no-repeat; background-position: 0 100%; transition: background-size 0.45s var(--ease-out); }
.lead__title a:hover { background-size: 100% 1px; }

.lead__dek {
  font-size: var(--t-md);
  line-height: 1.55;
  color: var(--ink-quiet);
  max-width: 42ch;
}

/* The byline strip: source, topic, timestamp. Reads as instrument metadata. */
.byline {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: 0.55rem 0.85rem;
  font-family: var(--mono);
  font-size: var(--t-xs);
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--ink-faint);
}

.byline__source { color: var(--ink-quiet); }

.byline__topic {
  position: relative;
  z-index: 1;
  display: inline-flex;
  align-items: center;
  gap: 0.4rem;
  color: var(--accent);
}

.byline__topic::before {
  content: '';
  width: 0.4rem;
  height: 0.4rem;
  background: var(--accent);
  border-radius: 50%;
  flex: none;
}

.byline__sep { color: var(--ink-faint); opacity: 0.5; }

.river {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(19rem, 1fr));
  border-top: 1px solid var(--rule);
  border-left: 1px solid var(--rule);
}

.story {
  position: relative;
  border-right: 1px solid var(--rule);
  border-bottom: 1px solid var(--rule);
  display: flex;
  flex-direction: column;
  gap: var(--gap-4);
  padding: var(--gap-6) var(--gap-5) var(--gap-7);
  transition: background 0.3s var(--ease);
}

.story:hover { background: var(--ground-raised); }

.story__frame {
  position: relative;
  overflow: hidden;
  background: var(--ground-sunken);
  aspect-ratio: 3 / 2;
  margin-bottom: var(--gap-2);
}

.story__frame img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  transition: transform 1.1s var(--ease-out);
}

.story:hover .story__frame img { transform: scale(1.04); }

.story__title {
  font-size: var(--t-md);
  font-weight: 450;
  line-height: 1.18;
  letter-spacing: -0.015em;
  text-wrap: pretty;
}

.story__title a::after {
  content: '';
  position: absolute;
  inset: 0;
}

.story:hover .story__title { color: var(--accent); }

.story__dek {
  font-size: 0.95rem;
  line-height: 1.5;
  color: var(--ink-quiet);
  display: -webkit-box;
  -webkit-line-clamp: 3;
  line-clamp: 3;
  -webkit-box-orient: vertical;
  overflow: hidden;
}

.story__foot { margin-top: auto; padding-top: var(--gap-4); }

/* A denser variant for archives and topic pages: no images, ruled rows. */
.ledger { border-top: 1px solid var(--rule); }

.ledger__row {
  position: relative;
  display: grid;
  grid-template-columns: 7.5rem minmax(0, 1fr) auto;
  gap: var(--gap-6);
  align-items: baseline;
  padding: var(--gap-5) 0;
  border-bottom: 1px solid var(--rule);
  transition: padding-left 0.35s var(--ease-out);
}

.ledger__row::before {
  content: '';
  position: absolute;
  left: -1rem;
  top: 0;
  bottom: 0;
  width: 2px;
  background: var(--accent);
  opacity: 0;
  transition: opacity 0.3s var(--ease);
}

.ledger__row:hover::before { opacity: 1; }
.ledger__row:hover { padding-left: 0.6rem; }

.ledger__stamp {
  font-family: var(--mono);
  font-size: var(--t-xs);
  letter-spacing: 0.06em;
  color: var(--ink-faint);
  white-space: nowrap;
  padding-top: 0.35em;
}

.ledger__title {
  font-size: var(--t-md);
  font-weight: 400;
  line-height: 1.25;
  letter-spacing: -0.012em;
}

.ledger__row:hover .ledger__title { color: var(--accent); }
.ledger__title a::after { content: ''; position: absolute; inset: 0; }

.ledger__dek {
  font-size: 0.95rem;
  color: var(--ink-quiet);
  line-height: 1.5;
  margin-top: 0.35em;
  max-width: 62ch;
}

.ledger__source {
  font-family: var(--mono);
  font-size: var(--t-xs);
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--ink-faint);
  white-space: nowrap;
  padding-top: 0.35em;
}

/* -- 9. Gallery ----------------------------------------------------------- */

.plates {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(21rem, 1fr));
  gap: var(--gap-7) var(--gap-6);
}

.plate {
  display: flex;
  flex-direction: column;
  gap: var(--gap-4);
}

.plate--tall { grid-row: span 2; }

.plate__frame {
  position: relative;
  overflow: hidden;
  background: var(--ground-sunken);
  cursor: zoom-in;
  border: 1px solid var(--frame);
}

.plate__frame img {
  width: 100%;
  height: auto;
  transition: transform 1.6s var(--ease-out), filter 0.5s var(--ease);
}

.plate__frame:hover img { transform: scale(1.03); }

.plate__frame::after {
  content: '';
  position: absolute;
  inset: 0;
  background: linear-gradient(to top, rgba(0, 0, 0, 0.4), transparent 42%);
  opacity: 0;
  transition: opacity 0.4s var(--ease);
  pointer-events: none;
}

.plate__frame:hover::after { opacity: 1; }

.plate__caption {
  display: flex;
  flex-direction: column;
  gap: var(--gap-3);
  padding-top: var(--gap-3);
  border-top: 1px solid var(--rule);
}

.plate__title {
  font-size: 1.05rem;
  font-weight: 450;
  line-height: 1.25;
  letter-spacing: -0.01em;
}

.plate__credit {
  font-family: var(--mono);
  font-size: var(--t-xs);
  letter-spacing: 0.07em;
  color: var(--ink-faint);
}

.plate__credit strong { color: var(--ink-quiet); font-weight: 500; }

/* The first plate on the gallery page runs full width — the sky deserves it. */
.plate--hero { grid-column: 1 / -1; }
.plate--hero .plate__frame { max-height: 76vh; }
.plate--hero .plate__frame img { width: 100%; max-height: 78vh; object-fit: cover; }
.plate--hero .plate__title { font-size: var(--t-lg); font-weight: 400; letter-spacing: -0.02em; }
.plate--hero .plate__caption { max-width: 52rem; }

.plate__body {
  font-size: 0.95rem;
  line-height: 1.55;
  color: var(--ink-quiet);
  max-width: 60ch;
}

/* Lightbox */
.viewer {
  position: fixed;
  inset: 0;
  z-index: 90;
  display: grid;
  grid-template-rows: 1fr auto;
  background: rgba(4, 5, 7, 0.96);
  backdrop-filter: blur(6px);
  opacity: 0;
  visibility: hidden;
  transition: opacity 0.35s var(--ease), visibility 0.35s;
}

.viewer[data-open] { opacity: 1; visibility: visible; }

.viewer__stage {
  display: grid;
  place-items: center;
  padding: clamp(1rem, 4vw, 3rem);
  overflow: hidden;
}

.viewer__stage img {
  max-width: 100%;
  max-height: 100%;
  width: auto;
  height: auto;
  object-fit: contain;
  transform: scale(0.985);
  opacity: 0;
  transition: transform 0.5s var(--ease-out), opacity 0.4s var(--ease);
}

.viewer[data-open] .viewer__stage img { transform: scale(1); opacity: 1; }

.viewer__bar {
  display: flex;
  align-items: flex-start;
  justify-content: space-between;
  gap: var(--gap-6);
  padding: var(--gap-5) clamp(1rem, 4vw, 3rem) var(--gap-6);
  border-top: 1px solid rgba(255, 255, 255, 0.12);
  color: #e9e6df;
}

.viewer__meta { max-width: 60ch; display: flex; flex-direction: column; gap: 0.4rem; }
.viewer__title { font-size: var(--t-md); font-weight: 450; letter-spacing: -0.012em; }
.viewer__credit { font-family: var(--mono); font-size: var(--t-xs); letter-spacing: 0.07em; color: rgba(233, 230, 223, 0.55); }
.viewer__actions { display: flex; gap: var(--gap-4); flex: none; }

.viewer__close,
.viewer__nav {
  background: none;
  border: 1px solid rgba(255, 255, 255, 0.18);
  color: #e9e6df;
  width: 2.4rem;
  height: 2.4rem;
  display: grid;
  place-items: center;
  cursor: pointer;
  transition: border-color 0.2s var(--ease), background 0.2s var(--ease);
}

.viewer__close:hover,
.viewer__nav:hover { border-color: rgba(255, 255, 255, 0.5); background: rgba(255, 255, 255, 0.07); }
.viewer__nav svg, .viewer__close svg { width: 1.1rem; height: 1.1rem; }

/* -- 10. Launch table ----------------------------------------------------- */

.pad {
  display: grid;
  grid-template-columns: 9.5rem minmax(0, 1fr) 15rem 6rem;
  gap: var(--gap-5);
  align-items: baseline;
  padding: var(--gap-5) 0;
  border-bottom: 1px solid var(--rule);
}

.pad--head {
  border-bottom: 1px solid var(--rule-strong);
  padding-block: var(--gap-3);
}

.pad__when {
  font-family: var(--mono);
  font-size: var(--t-sm);
  letter-spacing: 0.03em;
  color: var(--ink);
}

.pad__when small { display: block; font-size: var(--t-xs); color: var(--ink-faint); letter-spacing: 0.08em; margin-top: 0.25em; }

.pad__mission { font-size: 1.05rem; font-weight: 450; line-height: 1.25; letter-spacing: -0.01em; }
.pad__vehicle { font-family: var(--mono); font-size: var(--t-xs); letter-spacing: 0.07em; text-transform: uppercase; color: var(--ink-quiet); margin-top: 0.35em; }
/* Provider names are proper nouns and some of them are very long; setting
   "China Aerospace Science and Technology Corporation" in caps swallows the row. */
.pad__provider { text-transform: none; letter-spacing: 0.03em; }
.pad__site { font-family: var(--mono); font-size: var(--t-xs); letter-spacing: 0.05em; color: var(--ink-faint); line-height: 1.45; }

.pad__status {
  font-family: var(--mono);
  font-size: var(--t-xs);
  letter-spacing: 0.09em;
  text-transform: uppercase;
  justify-self: end;
  padding: 0.2rem 0.5rem;
  border: 1px solid var(--rule);
  color: var(--ink-quiet);
  white-space: nowrap;
}

.pad__status[data-tone='go'] { color: var(--accent); border-color: color-mix(in oklab, var(--accent) 50%, transparent); }
.pad__status[data-tone='hold'] { color: var(--ink-faint); }

.countdown {
  font-family: var(--mono);
  font-variant-numeric: tabular-nums;
  letter-spacing: 0.04em;
}

/* Add to calendar. Quiet until the row is hovered or the link is focused —
   there are sixteen of these on the page and sixteen buttons would be a toolbar,
   not a schedule. It is always in the tab order and always readable; hovering
   only brings it up to full contrast. */
.pad__cal {
  display: inline-flex;
  align-items: center;
  gap: 0.4rem;
  margin-top: 0.55rem;
  font-family: var(--mono);
  font-size: var(--t-xs);
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--ink-faint);
  border-bottom: 1px solid transparent;
  padding-bottom: 1px;
  transition: color 0.22s var(--ease), border-color 0.22s var(--ease);
}

.pad__cal svg { width: 0.85rem; height: 0.85rem; flex: none; }

.pad:hover .pad__cal { color: var(--ink-quiet); }

.pad__cal:hover,
.pad__cal:focus-visible {
  color: var(--accent);
  border-bottom-color: color-mix(in oklab, var(--accent) 55%, transparent);
}

/* Subscribe to the whole schedule. */
.subscribe {
  margin-top: var(--gap-7);
  padding-top: var(--gap-6);
  border-top: 1px solid var(--rule);
  max-width: 46rem;
}

.subscribe__note { font-size: 0.95rem; line-height: 1.6; color: var(--ink-quiet); }
.subscribe__note strong { color: var(--ink); font-weight: 450; }

.subscribe__actions {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: var(--gap-5);
  margin-top: var(--gap-5);
}

.subscribe__plain {
  font-family: var(--mono);
  font-size: var(--t-xs);
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--ink-faint);
  border-bottom: 1px solid var(--rule);
  padding-bottom: 2px;
  transition: color 0.22s var(--ease), border-color 0.22s var(--ease);
}

.subscribe__plain:hover { color: var(--ink); border-bottom-color: var(--rule-strong); }

/* -- 11. Panels, notes and the support line -------------------------------- */

.rail { display: flex; flex-direction: column; gap: var(--gap-7); }

.panel {
  border: 1px solid var(--rule);
  padding: var(--gap-6) var(--gap-5);
}

.panel__title {
  font-family: var(--mono);
  font-size: var(--t-xs);
  font-weight: 600;
  letter-spacing: 0.13em;
  text-transform: uppercase;
  color: var(--ink-quiet);
  padding-bottom: var(--gap-4);
  margin-bottom: var(--gap-5);
  border-bottom: 1px solid var(--rule);
}

.stack { display: flex; flex-direction: column; }

.stack__item {
  padding: var(--gap-4) 0;
  border-bottom: 1px dotted var(--rule);
  position: relative;
}

.stack__item:last-child { border-bottom: 0; padding-bottom: 0; }
.stack__item:first-child { padding-top: 0; }

.stack__title {
  font-size: 1rem;
  line-height: 1.3;
  font-weight: 450;
  letter-spacing: -0.008em;
}

.stack__item:hover .stack__title { color: var(--accent); }
.stack__title a::after { content: ''; position: absolute; inset: 0; }
.stack__meta { margin-top: 0.35rem; }

/* The support line. One sentence, a rule above it, no badge, no jar, no
   floating widget. It should read like the last line of a colophon. */
.support {
  display: flex;
  align-items: baseline;
  flex-wrap: wrap;
  gap: 0.6rem 1rem;
  font-size: 1rem;
  color: var(--ink-quiet);
  line-height: 1.6;
}

.support strong { font-weight: 450; color: var(--ink); }

.coffee {
  display: inline-flex;
  align-items: center;
  gap: 0.5rem;
  font-family: var(--mono);
  font-size: var(--t-sm);
  letter-spacing: 0.05em;
  color: var(--ink);
  border: 1px solid var(--rule-strong);
  padding: 0.4rem 0.85rem;
  transition: color 0.25s var(--ease), border-color 0.25s var(--ease), background 0.25s var(--ease);
  white-space: nowrap;
}

.coffee:hover {
  color: var(--accent);
  border-color: var(--accent);
  background: color-mix(in oklab, var(--accent) 8%, transparent);
}

.coffee svg { width: 0.95rem; height: 0.95rem; flex: none; }

/* Feed directory */
.feeds {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(19rem, 1fr));
  border-top: 1px solid var(--rule);
  border-left: 1px solid var(--rule);
}

.feeds__row {
  border-right: 1px solid var(--rule);
  border-bottom: 1px solid var(--rule);
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--gap-5);
  padding: var(--gap-5);
  transition: background 0.25s var(--ease);
}

.feeds__row:hover { background: var(--ground-raised); }
.feeds__name { font-size: 1.05rem; font-weight: 450; letter-spacing: -0.01em; }
.feeds__name span { display: block; font-family: var(--mono); font-size: var(--t-xs); letter-spacing: 0.07em; color: var(--ink-faint); margin-top: 0.3em; }

.feeds__link {
  font-family: var(--mono);
  font-size: var(--t-xs);
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--accent);
  border: 1px solid color-mix(in oklab, var(--accent) 38%, transparent);
  padding: 0.35rem 0.6rem;
  transition: background 0.25s var(--ease), border-color 0.25s var(--ease);
  white-space: nowrap;
}

.feeds__link:hover { border-color: var(--accent); background: color-mix(in oklab, var(--accent) 12%, transparent); }

/* The classification vocabulary, printed.
   This is the actual match list from src/topics.mjs rendered on the page — the
   site's one processing step, shown rather than described. Set in mono at label
   size so it reads as data; decisive terms sit at full contrast and the rest
   step back, which is exactly the weighting the classifier applies. */
.vocab { display: flex; flex-direction: column; gap: var(--gap-4); }

.vocab__row {
  display: flex;
  flex-wrap: wrap;
  gap: 0.4rem 0.5rem;
}

.vocab__term {
  font-family: var(--mono);
  font-size: var(--t-xs);
  letter-spacing: 0.04em;
  color: var(--ink-faint);
  border: 1px solid var(--rule);
  padding: 0.25rem 0.5rem;
  white-space: nowrap;
}

.vocab__term--strong {
  color: var(--ink-quiet);
  border-color: color-mix(in oklab, var(--accent) 32%, var(--rule));
}

/* Source credits table */
.credits { border-top: 1px solid var(--rule); }
.credits__row {
  display: grid;
  grid-template-columns: minmax(0, 1fr) auto;
  gap: var(--gap-5);
  align-items: baseline;
  padding: var(--gap-4) 0;
  border-bottom: 1px solid var(--rule);
}
.credits__name { font-size: 1rem; }
.credits__name a:hover { color: var(--accent); }
.credits__terms { font-family: var(--mono); font-size: var(--t-xs); letter-spacing: 0.07em; text-transform: uppercase; color: var(--ink-faint); text-align: right; }

/* -- 12. Advertising slots ------------------------------------------------ */
/* Reserved, labelled, and collapsed to nothing when no network is configured,
   so an unsold page has no holes in it. */

.ad {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: var(--gap-3);
  margin-block: var(--gap-7);
}

.ad:not([data-live]) { display: none; }

.ad__label {
  font-family: var(--mono);
  font-size: 0.625rem;
  letter-spacing: 0.18em;
  text-transform: uppercase;
  color: var(--ink-faint);
  opacity: 0.65;
}

.ad__frame {
  width: 100%;
  display: flex;
  justify-content: center;
  overflow: hidden;
}

.ad--leaderboard .ad__frame { min-height: 90px; }
.ad--rectangle .ad__frame { min-height: 250px; }
.ad--infeed { grid-column: 1 / -1; padding: var(--gap-6) var(--gap-5); margin-block: 0; border-right: 1px solid var(--rule); border-bottom: 1px solid var(--rule); }
.ad--infeed .ad__frame { min-height: 100px; }

@media (max-width: 46rem) {
  .ad--leaderboard .ad__frame { min-height: 50px; }
}

/* -- 13. Footer ----------------------------------------------------------- */

.footer {
  border-top: 1px solid var(--rule);
  margin-top: var(--gap-9);
  background: var(--ground-sunken);
}

.footer__grid {
  display: grid;
  grid-template-columns: minmax(0, 1.4fr) repeat(3, minmax(0, 1fr));
  gap: var(--gap-8) var(--gap-7);
  padding-block: var(--gap-8);
}

.footer__about { display: flex; flex-direction: column; gap: var(--gap-5); max-width: 34ch; }
.footer__about p { font-size: 0.95rem; line-height: 1.6; color: var(--ink-quiet); }

.footer__col h3 {
  font-family: var(--mono);
  font-size: var(--t-xs);
  font-weight: 600;
  letter-spacing: 0.13em;
  text-transform: uppercase;
  color: var(--ink-faint);
  margin-bottom: var(--gap-5);
}

.footer__col li { margin-bottom: 0.55rem; }
.footer__col a { font-size: 0.95rem; color: var(--ink-quiet); transition: color 0.2s var(--ease); }
.footer__col a:hover { color: var(--accent); }

/* The telemetry line: real build numbers, not decoration. */
.telemetry {
  display: flex;
  flex-wrap: wrap;
  gap: 0.5rem 1.5rem;
  padding-block: var(--gap-5);
  border-top: 1px solid var(--rule);
  font-family: var(--mono);
  font-size: var(--t-xs);
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--ink-faint);
}

.telemetry b { font-weight: 500; color: var(--ink-quiet); }

/* -- 14. Motion ----------------------------------------------------------- */

[data-reveal] {
  opacity: 0;
  transform: translateY(14px);
}

[data-reveal].is-revealed {
  opacity: 1;
  transform: none;
  transition: opacity 0.6s var(--ease), transform 0.7s var(--ease-out);
  transition-delay: var(--reveal-delay, 0ms);
}

.no-js [data-reveal] { opacity: 1; transform: none; }

/* Photographs settle in rather than snapping. Driven by a CSS animation, not a
   class the script adds: if the script fails to load, the images must still be
   visible, and an animation that ends at opacity 1 guarantees that. */
img[data-remote] { animation: settle 0.6s var(--ease) both; }

@keyframes settle {
  from { opacity: 0; }
  to { opacity: 1; }
}

@media (prefers-reduced-motion: reduce) {
  *,
  *::before,
  *::after {
    animation-duration: 0.01ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 0.01ms !important;
  }
  [data-reveal] { opacity: 1; transform: none; }
}

/* -- 15. Narrow screens --------------------------------------------------- */

@media (max-width: 62rem) {
  .lead { grid-template-columns: 1fr; gap: var(--gap-6); }
  .lead__frame { aspect-ratio: 16 / 10; }
  .footer__grid { grid-template-columns: repeat(2, minmax(0, 1fr)); }
  .with-rail { grid-template-columns: 1fr !important; }
  .pad { grid-template-columns: 8rem minmax(0, 1fr) auto; }
  .pad__site { display: none; }
}

@media (max-width: 46rem) {
  .masthead__inner { grid-template-columns: 1fr auto; gap: var(--gap-4); }
  .masthead__meta { display: none; }
  .band { padding-block: var(--gap-8); }
  .ledger__row { grid-template-columns: 1fr; gap: 0.4rem; }
  .ledger__stamp { padding-top: 0; }
  .ledger__source { padding-top: 0; }
  .ledger__row:hover { padding-left: 0; }
  .footer__grid { grid-template-columns: 1fr; gap: var(--gap-7); }
  .plate--hero .plate__frame { max-height: 60vh; }
  .plate--hero .plate__frame img { max-height: 60vh; }
  .pad { grid-template-columns: 1fr; gap: 0.35rem; padding-block: var(--gap-5); }
  .pad--head { display: none; }
  .pad__status { justify-self: start; margin-top: 0.35rem; }
  .viewer__bar { flex-direction: column; gap: var(--gap-5); }
}

/* -- Dateline -------------------------------------------------------------- */
/* The home page's h1. A newspaper puts the date above the fold and then gets
   out of the way; the same idea, at label size rather than headline size. */

.dateline {
  display: flex;
  align-items: center;
  gap: var(--gap-5);
  font-family: var(--mono);
  font-size: var(--t-xs);
  font-weight: 500;
  letter-spacing: 0.15em;
  text-transform: uppercase;
  color: var(--ink-quiet);
}

.dateline__rule {
  flex: 1 1 auto;
  height: 1px;
  background: var(--rule);
  min-width: 2rem;
}

.dateline span:last-child { color: var(--ink-faint); text-align: right; }

@media (max-width: 46rem) {
  .dateline span:last-child { display: none; }
}

/* -- Text-only stories ----------------------------------------------------- */
/* A card with no photograph is not a broken card. It is a text piece, and it
   gets the space a text piece would take in print: bigger headline, more dek. */

.story--text { justify-content: flex-start; }

.story--text .story__title {
  font-size: var(--t-lg);
  font-weight: 400;
  line-height: 1.12;
  letter-spacing: -0.022em;
}

.story--text .story__dek {
  -webkit-line-clamp: 6;
  line-clamp: 6;
  font-size: var(--t-base);
}

.story--text::before {
  content: '';
  display: block;
  width: 2.5rem;
  height: 2px;
  background: var(--accent);
  margin-bottom: var(--gap-3);
}

/* The byline should stay on one line at card width. */
.byline { gap: 0.35rem 0.6rem; letter-spacing: 0.06em; }
.byline__sep { margin-inline: -0.1rem; }

/* -- Topic header ---------------------------------------------------------- */
/* The readout balances the header and carries information rather than filling
   space: how much is held on this subject, how fresh it is, and where the
   subject sits in the spectrum. */

.topic-head {
  display: grid;
  grid-template-columns: minmax(0, 1fr) 16rem;
  gap: var(--gap-8);
  align-items: start;
}

.topic-head__title {
  font-size: var(--t-2xl);
  font-weight: 400;
  line-height: 1.02;
  letter-spacing: -0.03em;
  margin-top: var(--gap-4);
  max-width: 16ch;
}

.readout {
  border-top: 2px solid var(--accent);
  padding-top: var(--gap-4);
}

.readout > div {
  display: flex;
  justify-content: space-between;
  align-items: baseline;
  gap: var(--gap-4);
  padding: 0.55rem 0;
  border-bottom: 1px dotted var(--rule);
}

.readout dt {
  font-family: var(--mono);
  font-size: var(--t-xs);
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--ink-faint);
}

.readout dd {
  margin: 0;
  font-family: var(--mono);
  font-size: var(--t-sm);
  font-variant-numeric: tabular-nums;
  color: var(--ink);
}

@media (max-width: 62rem) {
  .topic-head { grid-template-columns: 1fr; gap: var(--gap-6); }
  .readout { max-width: 24rem; }
}

/* -- Notice ---------------------------------------------------------------- */
/* Used when the page is showing something it wants to be honest about — a
   schedule carried over from an earlier gather, most often. */

.notice {
  margin-top: var(--gap-6);
  padding: var(--gap-4) var(--gap-5);
  border-left: 2px solid var(--accent);
  background: var(--shade);
  font-size: 0.95rem;
  line-height: 1.55;
  color: var(--ink-quiet);
  max-width: 60ch;
}
