.site-nav {
  position: sticky;
  top: 0;
  z-index: 10;
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: space-between;
  row-gap: var(--space-1);
  padding: var(--space-2) var(--space-3);
  background: rgba(18, 19, 26, 0.75);
  backdrop-filter: blur(8px);
  border-bottom: 1px solid var(--color-hairline);
}
.site-nav__brand {
  font-family: var(--font-display);
  font-weight: 700;
  color: var(--color-text-primary);
  white-space: nowrap;
}
.site-nav__links { display: flex; flex-wrap: wrap; gap: var(--space-3); }
.site-nav__links a {
  color: var(--color-text-muted);
  font-size: var(--fs-small);
}
.site-nav__links a.is-active,
.site-nav__links a:hover { color: var(--color-signal); }

.divider {
  position: relative;
  max-width: var(--max-width);
  margin: 0 auto;
  padding: 0 var(--space-3);
}
/* A pool of stage light around each bar cluster, so the dividers sit in
   something instead of floating in an empty band between sections. */
.divider::before {
  content: '';
  position: absolute;
  left: 50%;
  top: 50%;
  width: min(68rem, 95%);
  height: 18rem;
  transform: translate(-50%, -50%);
  z-index: -1;
  pointer-events: none;
  opacity: 0.55;
  background-image:
    radial-gradient(48% 55% at 28% 50%, var(--color-pink-glow), transparent 72%),
    radial-gradient(48% 55% at 72% 50%, var(--color-signal-glow), transparent 72%);
}
.waveform-canvas {
  width: 100%;
  display: block;
  /* One GPU pass for the whole bar field, rather than a shadow per segment. */
  filter: drop-shadow(0 0 7px var(--color-pink-glow));
}

.sparkle-field {
  position: absolute;
  left: 0;
  right: 0;
  top: -5rem;
  bottom: -5rem;
  z-index: -1;
  pointer-events: none;
}
.sparkle-field span {
  position: absolute;
  width: 3px;
  height: 3px;
  border-radius: 50%;
  background: var(--color-lavender);
  box-shadow: 0 0 8px 2px rgba(183, 156, 255, 0.45);
  animation: sparkle-twinkle 5.5s ease-in-out infinite;
}
.sparkle-field span:nth-child(3n + 1) {
  background: var(--color-pink);
  box-shadow: 0 0 6px 1px var(--color-pink-glow);
}
.sparkle-field span:nth-child(3n + 2) {
  background: var(--color-signal);
  box-shadow: 0 0 6px 1px var(--color-signal-glow);
}

.hero-content { position: relative; z-index: 1; max-width: var(--content-width); }
.hero-content .eyebrow-line { font-family: var(--font-mono); color: var(--color-pink); font-size: var(--fs-small); }
.hero-content h1 {
  background: linear-gradient(100deg, var(--color-pink) 0%, var(--color-lavender) 45%, var(--color-signal) 100%);
  -webkit-background-clip: text;
  background-clip: text;
  -webkit-text-fill-color: transparent;
  color: var(--color-text-primary);
}
.hero-content .tagline { font-size: 1.15rem; color: var(--color-text-muted); max-width: var(--content-width); text-wrap: balance; }
.whole { white-space: nowrap; }
.hero-actions { display: flex; flex-wrap: wrap; gap: var(--space-2); margin-top: var(--space-3); }

.section--hero {
  isolation: isolate;
}
.section--hero::before {
  content: '';
  position: absolute;
  inset: -10%;
  z-index: -2;
  opacity: 0.5;
  background-image:
    radial-gradient(38rem 26rem at 12% 18%, var(--color-pink-glow), transparent 60%),
    radial-gradient(34rem 24rem at 88% 12%, var(--color-signal-glow), transparent 60%),
    radial-gradient(30rem 22rem at 50% 95%, rgba(183, 156, 255, 0.25), transparent 60%);
  filter: blur(10px);
  animation: aurora-drift 22s ease-in-out infinite alternate;
  pointer-events: none;
}
.section--hero::after {
  content: '';
  position: absolute;
  inset: 0;
  z-index: -1;
  opacity: 0.06;
  background-image: repeating-radial-gradient(circle at 80% 50%, var(--color-text-primary) 0, var(--color-text-primary) 1px, transparent 1px, transparent 6px);
  pointer-events: none;
}
@keyframes aurora-drift {
  0% { transform: translate(0, 0) scale(1); }
  100% { transform: translate(-3%, 2%) scale(1.06); }
}

/* Above the name, like every other field on the site sits above the section
   it names. */
.hero-waveform { position: relative; z-index: 1; margin-bottom: var(--space-4); }

.hero-sparkles { position: absolute; inset: 0; z-index: 0; pointer-events: none; overflow: hidden; }
.hero-sparkles span {
  position: absolute;
  width: 3px;
  height: 3px;
  border-radius: 50%;
  background: var(--color-pink);
  box-shadow: 0 0 6px 1px var(--color-pink-glow);
  animation: sparkle-twinkle 4.5s ease-in-out infinite;
}
.hero-sparkles span:nth-child(1) { top: 18%; left: 22%; background: var(--color-pink); animation-delay: 0s; }
.hero-sparkles span:nth-child(2) { top: 32%; left: 68%; background: var(--color-signal); box-shadow: 0 0 6px 1px var(--color-signal-glow); animation-delay: 0.8s; }
.hero-sparkles span:nth-child(3) { top: 58%; left: 38%; background: var(--color-lavender); box-shadow: 0 0 6px 1px rgba(183, 156, 255, 0.35); animation-delay: 1.6s; }
.hero-sparkles span:nth-child(4) { top: 12%; left: 80%; background: var(--color-pink); animation-delay: 2.3s; }
.hero-sparkles span:nth-child(5) { top: 70%; left: 12%; background: var(--color-signal); box-shadow: 0 0 6px 1px var(--color-signal-glow); animation-delay: 3.1s; }
.hero-sparkles span:nth-child(6) { top: 44%; left: 90%; background: var(--color-lavender); box-shadow: 0 0 6px 1px rgba(183, 156, 255, 0.35); animation-delay: 3.8s; }
@keyframes sparkle-twinkle {
  0%, 100% { opacity: 0; transform: scale(0.6); }
  50% { opacity: 1; transform: scale(1.4); }
}

/* --- about --- */

/* The photo goes beside the paragraph rather than in the hero, so the hero
   field and the name keep the width they were laid out for. On a phone it
   stacks above the text, smaller, since beside it the text column would be a
   few words wide. */
.about-body {
  display: grid;
  grid-template-columns: auto minmax(0, 1fr);
  gap: var(--space-4);
  align-items: start;
}
/* The studio backdrop was white. It was replaced with a pale wash of the
   pink and lavender tokens before export, so this only has to draw the ring:
   the padding shows the gradient behind the image, and border-radius clips
   the photo inside it. */
.headshot img {
  width: 200px;
  height: 200px;
  padding: 3px;
  border-radius: 50%;
  background: linear-gradient(135deg, var(--color-pink), var(--color-lavender) 55%, var(--color-signal));
  box-shadow: 0 0 24px -4px var(--color-pink-glow);
}
@media (max-width: 40rem) {
  .about-body { grid-template-columns: minmax(0, 1fr); gap: var(--space-3); }
  .headshot img { width: 144px; height: 144px; }
}

.card {
  background: var(--color-surface);
  border: 1px solid var(--color-hairline);
  border-radius: var(--radius-md);
  padding: var(--space-3);
  transition: border-color 0.2s ease, box-shadow 0.2s ease, transform 0.2s ease;
}
.card:hover {
  border-color: var(--color-pink);
  box-shadow: 0 0 0 1px var(--color-pink-glow), 0 8px 24px -12px var(--color-pink-glow);
  transform: translateY(-2px);
}
.card h3 { margin-bottom: var(--space-1); }

/* The whole card is the link, so the inner text has to opt out of link colour. */
.card--link {
  display: flex;
  flex-direction: column;
  color: inherit;
  text-decoration: none;
}
.card--link:hover { text-decoration: none; }
.card--link p { flex: 1; }
.card__cue {
  margin-top: var(--space-2);
  font-family: var(--font-mono);
  font-size: 0.75rem;
  color: var(--color-signal);
}
.card--link:hover .card__cue { color: var(--color-pink); }

.noscript-note {
  border: 1px solid var(--color-pink);
  border-radius: var(--radius-sm);
  padding: var(--space-2);
  color: var(--color-text-primary);
}

.page-title { font-size: var(--fs-h2); }
.section--page-head { padding-bottom: var(--space-4); }

/* No scroll-margin here: html already sets scroll-padding-top, and the two
   stack, which parked headings ~120px below the nav instead of just under it. */
.reading-group { margin-bottom: var(--space-6); }
.reading-group h2 { font-size: var(--fs-h3); color: var(--color-pink); margin-bottom: var(--space-1); }
.reading-group__note {
  max-width: var(--content-width);
  margin-bottom: var(--space-3);
}
.reading-back { margin-top: var(--space-2); }

.not-found-links { list-style: none; padding: 0; margin: var(--space-3) 0 0; display: grid; gap: var(--space-1); }

.reading-position {
  max-width: var(--content-width);
  margin: 0 0 var(--space-3);
  padding: var(--space-2) var(--space-3);
  border-left: 2px solid var(--color-pink);
  background: var(--color-surface);
  border-radius: 0 var(--radius-sm) var(--radius-sm) 0;
  color: var(--color-text-primary);
}

.decoder-note { max-width: var(--content-width); }
.decoder-label {
  margin: var(--space-3) 0 var(--space-1);
  font-family: var(--font-mono);
  font-size: 0.75rem;
  color: var(--color-text-muted);
}

.key-table {
  border-collapse: collapse;
  margin: var(--space-2) 0 var(--space-3);
  max-width: var(--content-width);
  width: 100%;
}
.key-table th, .key-table td {
  text-align: left;
  padding: var(--space-1) var(--space-2) var(--space-1) 0;
  border-bottom: 1px solid var(--color-hairline);
}
.key-table th { color: var(--color-text-muted); font-weight: 500; font-size: var(--fs-small); }
.key-table td { color: var(--color-text-muted); }
.key-sym { color: var(--color-pink); font-family: var(--font-mono); }

.worked {
  font-family: var(--font-mono);
  font-size: 1.1rem;
  color: var(--color-signal);
  letter-spacing: 0.1em;
}
.worked .brk { color: var(--color-pink); }

.decoder-pad { display: flex; flex-wrap: wrap; gap: var(--space-2); margin-bottom: var(--space-2); }
.decoder-pad .btn { display: flex; flex-direction: column; align-items: flex-start; }
.decoder-pad .btn span {
  font-family: var(--font-mono);
  font-size: 0.7rem;
  font-weight: 400;
  opacity: 0.75;
}
.btn--quiet { border-color: var(--color-hairline); color: var(--color-text-muted); box-shadow: none; }
.btn--quiet:hover { background: var(--color-surface); border-color: var(--color-text-muted); color: var(--color-text-primary); }

.strip {
  font-family: var(--font-mono);
  font-size: 1.15rem;
  color: var(--color-signal);
  background: var(--color-surface);
  border: 1px solid var(--color-hairline);
  border-radius: var(--radius-sm);
  padding: var(--space-2);
  word-break: break-word;
  min-height: 1.5em;
}
.strip.is-empty { color: var(--color-text-muted); font-size: var(--fs-small); }

.decoded {
  font-family: var(--font-display);
  font-size: var(--fs-h2);
  letter-spacing: 0.06em;
  color: var(--color-pink);
  word-break: break-word;
  margin: 0;
}
.decoded.has-unknown { color: var(--color-text-muted); }

.alphabet {
  list-style: none;
  margin: 0 0 var(--space-3);
  padding: 0;
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(7rem, 1fr));
  gap: var(--space-1) var(--space-2);
}
.alphabet li { color: var(--color-text-muted); }
.alphabet b { color: var(--color-text-primary); margin-right: 0.4rem; }
.alphabet code { font-family: var(--font-mono); color: var(--color-signal); }

.progress-list {
  list-style: none;
  margin: 0 0 var(--space-3);
  padding: 0;
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(11rem, 1fr));
  gap: var(--space-1) var(--space-2);
}
.progress-list li {
  font-family: var(--font-mono);
  font-size: var(--fs-small);
  color: var(--color-text-muted);
  letter-spacing: 0.12em;
}
.progress-list li.is-found { color: var(--color-signal); }
.blank {
  display: inline-block;
  width: 0.5em;
  height: 0.85em;
  margin-right: 0.12em;
  border: 1px solid currentColor;
  border-radius: 1px;
  opacity: 0.55;
  vertical-align: baseline;
}
.blank-gap { display: inline-block; width: 0.6em; }
/* The unsolved cipher is the page's open question, so it is set larger than
   the checklist above it rather than as one more row. */
#final-slot, #radio-slot {
  font-size: 1.15rem;
  letter-spacing: 0.22em;
  color: var(--color-text-primary);
}
#final-slot.is-found, #radio-slot.is-found { color: var(--color-signal); }
.progress-reward {
  max-width: var(--content-width);
  padding: var(--space-2) var(--space-3);
  border-left: 2px solid var(--color-pink);
  background: var(--color-surface);
  border-radius: 0 var(--radius-sm) var(--radius-sm) 0;
  color: var(--color-text-primary);
}

.fp-grid { display: grid; gap: var(--space-3); }
.fp-panel {
  background: var(--color-surface);
  border: 1px solid var(--color-hairline);
  border-radius: var(--radius-md);
  padding: var(--space-3);
}
.fp-panel h2 {
  font-size: 0.75rem;
  font-family: var(--font-mono);
  font-weight: 500;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--color-text-muted);
  margin-bottom: var(--space-1);
}
.fp-value {
  font-family: var(--font-mono);
  font-size: 1.05rem;
  color: var(--color-signal);
  margin-bottom: var(--space-2);
  word-break: break-word;
}
.fp-code {
  font-family: var(--font-display);
  font-size: var(--fs-h2);
  letter-spacing: 0.22em;
  color: var(--color-pink);
}
.fp-prose { max-width: var(--content-width); margin-bottom: 0; }
.fp-prose + .fp-prose { margin-top: var(--space-2); }

/* Shown when the script that fills this page never ran, which on a page called
   fingerprint.js usually means a content blocker caught it. The entrance is CSS
   rather than JavaScript for the obvious reason: the case it announces is
   JavaScript not running. */
.fp-blocked {
  border-color: var(--color-pink);
  animation: fp-blocked-in 420ms ease-out both;
}
.fp-blocked .fp-value { color: var(--color-pink); }
@keyframes fp-blocked-in {
  from { opacity: 0; transform: translateY(12px); }
  to { opacity: 1; transform: none; }
}
@media (prefers-reduced-motion: reduce) {
  .fp-blocked { animation: none; }
}
.fp-signature { padding: 0; margin: var(--space-2) 0 var(--space-3); }
.fp-disclaimer {
  max-width: var(--content-width);
  border: 1px solid var(--color-pink);
  border-radius: var(--radius-sm);
  padding: var(--space-2);
  color: var(--color-text-primary);
}

.ethics-note { max-width: var(--content-width); }

.footer-hint {
  margin-top: var(--space-3);
  font-family: var(--font-mono);
  font-size: 0.75rem;
  color: var(--color-text-muted);
}
/* These sentences are the way in to the decoder, and readers were walking past
   them. Two things were wrong. The links were the same colour as the prose
   around them with no underline, so nothing said they were links at all, and
   the opacity on this paragraph dropped them to 3.69:1, under the 4.5:1 body
   text needs. The opacity is gone rather than overridden, because opacity
   applies to the whole subtree and a child cannot climb back out of it.

   The underline is not decoration. Colour on its own is the one cue a
   colour-blind reader may not get, so the links have to be marked some other
   way too.

   The nav-style margin on .site-footer a is for the link row above; inside a
   sentence it pushes the full stop away from the word it belongs to. It needs
   the descendant selector: `.footer-hint a` ties `.site-footer a` at (0,1,1)
   and loses on source order, which is why an earlier attempt at this did
   nothing and the gap stayed. */
.site-footer .footer-hint a {
  margin: 0;
  color: var(--color-pink);
  text-decoration: underline;
  text-underline-offset: 0.25em;
}
.reading-list {
  list-style: none;
  margin: 0;
  padding: 0;
  border-left: 2px solid var(--color-hairline);
}
.reading-list li {
  padding: 0 0 var(--space-3) var(--space-3);
  max-width: var(--content-width);
}
.reading-list a { font-weight: 500; }
.reading-meta {
  display: block;
  margin-top: 0.15rem;
  font-family: var(--font-mono);
  font-size: 0.75rem;
  color: var(--color-text-muted);
}

.section-lede { max-width: var(--content-width); margin-bottom: var(--space-4); }

.tile {
  background: var(--color-surface);
  border: 1px solid var(--color-hairline);
  border-radius: var(--radius-md);
  padding: var(--space-3);
  transition: border-color 0.2s ease, box-shadow 0.2s ease, transform 0.2s ease;
}
/* Only a tile that links somewhere lifts. A plain tile that lifted on hover
   looked clickable and then did nothing when clicked. */
.tile--link:hover,
.tile--link:focus-visible {
  border-color: var(--color-signal);
  box-shadow: 0 0 0 1px var(--color-signal-glow), 0 8px 24px -12px var(--color-signal-glow);
  transform: translateY(-2px);
}
.tile__tags { display: flex; flex-wrap: wrap; gap: var(--space-1); margin-top: var(--space-2); }
.tile__tag {
  font-family: var(--font-mono);
  font-size: 0.75rem;
  color: var(--color-pink);
  border: 1px solid var(--color-hairline);
  border-radius: var(--radius-sm);
  padding: 0.15rem 0.5rem;
}
.tile__note {
  font-family: var(--font-mono);
  font-size: 0.75rem;
  color: var(--color-text-muted);
  margin-top: var(--space-2);
}

.timeline-item {
  position: relative;
  padding: 0 0 var(--space-4) var(--space-3);
}
.timeline-item::before {
  content: '';
  position: absolute;
  left: -6px;
  top: 0.35rem;
  width: 10px;
  height: 10px;
  border-radius: 50%;
  background: var(--color-pink);
  box-shadow: 0 0 8px 1px var(--color-pink-glow);
}
.timeline-item time {
  display: block;
  font-family: var(--font-mono);
  font-size: var(--fs-small);
  color: var(--color-pink);
  margin-bottom: var(--space-1);
}
.timeline-item ul { margin: var(--space-1) 0 0; padding-left: 1.1rem; }
/* The folded older roles: one quieter line, so the eye stays on recent work. */
.timeline-item--earlier h3 { font-size: var(--fs-body); color: var(--color-text-muted); }
.timeline-item--earlier p { margin: var(--space-1) 0 0; font-size: var(--fs-small); }
.timeline-item li { margin-bottom: 0.35rem; }

.btn {
  display: inline-block;
  /* Set explicitly so this works on <button> as well as <a>. A real button
     otherwise inherits the UA's light ButtonFace background and its own
     smaller font, which left near-white text on a near-white fill. */
  background: transparent;
  font-family: var(--font-body);
  font-size: 1rem;
  font-weight: 600;
  cursor: pointer;
  padding: 0.65rem 1.25rem;
  border-radius: var(--radius-sm);
  border: 1px solid var(--color-pink);
  color: var(--color-text-primary);
  text-decoration: none;
  box-shadow: 0 0 12px -6px var(--color-pink-glow);
  transition: border-color 0.2s ease, background 0.2s ease, color 0.2s ease, box-shadow 0.2s ease;
}
.btn:hover {
  background: linear-gradient(100deg, var(--color-pink), var(--color-signal));
  border-color: var(--color-signal);
  color: var(--color-ink);
  text-decoration: none;
  box-shadow: 0 0 24px -4px var(--color-pink-glow);
}
/* One filled button per view, so the eye has a single first move. Ink on this
   pink measures about 9.9:1. Hover falls through to .btn:hover above, whose
   two selectors outrank this one, so the fill visibly shifts toward signal. */
.btn--primary {
  background: var(--color-pink);
  color: var(--color-ink);
  box-shadow: 0 0 20px -6px var(--color-pink-glow);
}

.contact-routes { margin: 0 0 var(--space-3); }
.contact-routes > div { margin-bottom: var(--space-2); }
.contact-routes dt {
  font-family: var(--font-mono);
  font-size: var(--fs-small);
  color: var(--color-pink);
}
/* An address has no spaces to break at, so a narrow screen may split it
   anywhere rather than push the page sideways. */
.contact-routes dd { margin: 0.15rem 0 0; overflow-wrap: anywhere; }

.site-footer {
  border-top: 1px solid var(--color-hairline);
  padding: var(--space-4) var(--space-3);
  text-align: center;
  color: var(--color-text-muted);
}
/* --- radio --- */

.radio-intro {
  max-width: var(--content-width);
  color: var(--color-text-muted);
}
.radio-list {
  list-style: none;
  margin: var(--space-3) 0 0;
  padding: 0;
  max-width: var(--content-width);
  border-top: 1px solid var(--color-hairline);
}
.radio-list li { border-bottom: 1px solid var(--color-hairline); }
.radio-list button {
  display: flex;
  flex-wrap: wrap;
  gap: 0 var(--space-2);
  align-items: baseline;
  width: 100%;
  padding: var(--space-2) var(--space-1);
  background: transparent;
  border: 0;
  border-left: 2px solid transparent;
  color: var(--color-text-primary);
  font-family: inherit;
  font-size: 1rem;
  text-align: left;
  cursor: pointer;
}
.radio-list button:hover { background: var(--color-surface); }
.radio-list li.is-current button {
  border-left-color: var(--color-pink);
  color: var(--color-signal);
}
.radio-track-artist {
  font-family: var(--font-mono);
  font-size: var(--fs-small);
  color: var(--color-text-muted);
}
.radio-status {
  font-family: var(--font-mono);
  font-size: var(--fs-small);
  color: var(--color-text-muted);
}
/* YouTube's terms do not allow playing the audio with the video hidden, so the
   player stays visible and at its own aspect ratio rather than being reduced to
   a background audio source. */
.radio-stage {
  margin-top: var(--space-3);
  max-width: 34rem;
  aspect-ratio: 16 / 9;
  border: 1px solid var(--color-hairline);
  border-radius: var(--radius-sm);
  overflow: hidden;
}
.radio-stage iframe, .radio-stage > div { width: 100%; height: 100%; border: 0; }

.radio-controls {
  position: fixed;
  right: var(--space-3);
  bottom: var(--space-3);
  z-index: 20;
  display: flex;
  flex-direction: column;
  gap: var(--space-1);
  padding: var(--space-2);
  background: var(--color-surface);
  border: 1px solid var(--color-hairline);
  border-radius: var(--radius-sm);
  box-shadow: 0 8px 28px rgb(0 0 0 / 0.45);
}
.radio-now {
  margin: 0;
  font-family: var(--font-mono);
  font-size: 0.75rem;
  color: var(--color-text-muted);
  max-width: 18rem;
}
.radio-buttons {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--space-1);
}
.radio-buttons button {
  padding: 0.35rem 0.7rem;
  background: transparent;
  border: 1px solid var(--color-pink);
  border-radius: var(--radius-sm);
  color: var(--color-text-primary);
  font-family: var(--font-mono);
  font-size: 0.8rem;
  cursor: pointer;
}
.radio-buttons button:hover { background: var(--color-pink-glow); }
.radio-volume {
  display: flex;
  align-items: center;
  gap: 0.4rem;
  font-family: var(--font-mono);
  font-size: 0.7rem;
  color: var(--color-text-muted);
}
.radio-volume input { width: 6rem; accent-color: var(--color-pink); }
#radio-lock[aria-pressed="true"] {
  border-color: var(--color-signal);
  color: var(--color-signal);
}
.radio-lock-note {
  margin: 0;
  max-width: 18rem;
  font-family: var(--font-mono);
  font-size: 0.7rem;
  line-height: 1.5;
  color: var(--color-text-muted);
}

@media (max-width: 36rem) {
  .radio-controls { left: var(--space-2); right: var(--space-2); bottom: var(--space-2); }
  .radio-now { max-width: none; }
}

/* The one field with no heading under it. Every other bar cluster on the site
   names the section it introduces; this one has nothing to introduce, which is
   the only clue it needs. */
.footer-signal {
  position: relative;
  max-width: var(--max-width);
  margin: 0 auto var(--space-4);
}
.site-footer a { color: var(--color-pink); margin: 0 var(--space-1); }
.site-footer a:hover { color: var(--color-signal); text-decoration: underline; }

@media (max-width: 36rem) {
  /* Seven links wrap to three rows here. Sticky, that is ~13% of a phone
     screen occupied permanently, so let it scroll away instead. */
  .site-nav { position: static; padding: var(--space-1) var(--space-2); }
  .site-nav__brand { font-size: 0.9rem; }
  .site-nav__links { gap: var(--space-2); font-size: 0.75rem; }
}

/* --- how I work --- */

/* Text for screen readers only, such as the note that a link opens a new tab. */
.visually-hidden {
  position: absolute;
  width: 1px;
  height: 1px;
  margin: -1px;
  padding: 0;
  overflow: hidden;
  clip-path: inset(50%);
  white-space: nowrap;
  border: 0;
}

/* The tile becomes a link once its write-up exists, the same way a research
   card does, so the whole card is the target rather than a small "read more". */
.tile--link {
  display: flex;
  flex-direction: column;
  color: inherit;
  text-decoration: none;
}
.tile--link:hover { text-decoration: none; }
.tile--link p { flex: 1; }
/* Same label treatment as the beat headings on work.html, so the marker on the
   tile and the page it opens read as one thing. align-self keeps it the width
   of its text inside the flex column. */
.tile__depth {
  align-self: flex-start;
  margin-bottom: var(--space-1);
  font-family: var(--font-mono);
  font-size: 0.75rem;
  font-weight: 500;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--color-pink);
}
.tile__cue {
  margin-top: var(--space-2);
  font-family: var(--font-mono);
  font-size: 0.75rem;
  color: var(--color-signal);
}
.tile--link:hover .tile__cue { color: var(--color-pink); }

.work-group { margin-bottom: var(--space-6); }
.work-group h2 { font-size: var(--fs-h3); color: var(--color-pink); margin-bottom: var(--space-1); }
.work-group .tile__tags { margin-bottom: var(--space-3); }
/* Each beat is labelled rather than run together, because the labels are the
   argument: a reader scanning for the rejected option should find it. */
.work-beat { max-width: var(--content-width); margin-bottom: var(--space-3); }
.work-beat h3 {
  font-family: var(--font-mono);
  font-size: 0.75rem;
  font-weight: 500;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--color-text-muted);
  margin-bottom: var(--space-1);
}
.work-beat p { margin: 0; }
/* A beat can run to several paragraphs, and without this they close up into
   one block that is hard to scan. */
.work-beat p + p { margin-top: var(--space-2); }

/* --- résumé --- */

.resume-head { margin-bottom: var(--space-5); }
/* Same mono line as the hero's, so the role reads the same way on both pages. */
.resume-head .eyebrow-line {
  margin-bottom: var(--space-1);
  font-family: var(--font-mono);
  font-size: var(--fs-small);
  color: var(--color-pink);
}
.resume-head .page-title { margin-bottom: var(--space-1); }
.resume-place { margin-bottom: var(--space-3); }
/* Two columns where they fit, one on a phone. The widest entries, the LinkedIn
   address and the SKKU label, measure about 17.3rem, so 18rem columns never
   break an address mid-word to fit two across. The min() lets one column
   shrink below that on a phone narrower than 18rem instead of overflowing. */
.resume-contact {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(min(18rem, 100%), 1fr));
  gap: 0 var(--space-3);
  max-width: var(--content-width);
}
.resume-download { margin: 0; }
/* Korean breaks between any two syllables by default, which split the SKKU
   name across two lines on paper. keep-all breaks it only at spaces, the way
   Korean is set in print. */
.resume [lang="ko"] { word-break: keep-all; }

.resume-section {
  max-width: var(--content-width);
  margin-bottom: var(--space-4);
}
/* Labels rather than headlines. The page is one document, and at full h2 size
   the two sections read as separate pages stacked together. */
.resume-section h2 {
  padding-bottom: var(--space-1);
  border-bottom: 1px solid var(--color-hairline);
  margin-bottom: var(--space-3);
  font-family: var(--font-mono);
  font-size: var(--fs-small);
  font-weight: 500;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--color-text-muted);
}

/* --- print --- */

/* For the printed résumé only. The colours come from its print tokens in
   tokens.css, and this block removes what only works on a screen: the footer's
   bar field, which on a résumé sheet is a band of bars with no explanation next
   to it, and the nav, footer links and download button, none of which can be
   followed on paper. Other pages print as before, fields included. */
@page { margin: 16mm 18mm; }

@media print {
  .page-resume { font-size: 10pt; line-height: 1.45; }
  .page-resume::before,
  .page-resume .site-nav,
  .page-resume .site-footer,
  .page-resume .noscript-note,
  .page-resume .resume-download { display: none; }

  .page-resume .section { max-width: none; padding: 0; }
  .page-resume .page-title { font-size: 22pt; }
  .page-resume h3 { font-size: 12pt; }
  .resume-head { margin-bottom: 1.5rem; }
  .resume-section { max-width: none; margin-bottom: 1rem; }

  /* The timeline rail goes. Browsers drop backgrounds when printing, so the
     dots vanished, and the border was cut into stubs wherever a role moved to
     the next sheet. */
  .resume .timeline { border-left: 0; }
  .resume .timeline-item::before { display: none; }
  /* A role split across two sheets separates its dates and title from its
     bullets, so each one moves to the next page whole. */
  .resume .timeline-item { padding: 0 0 1rem; break-inside: avoid; }
  .resume h1, .resume h2, .resume h3 { break-after: avoid; }
}
