/*
  Design system for chnkukoolwal.vc (sandbox).

  Shares its DNA with the Avvyun homepage (https://new.avvyun.vc) — same
  serif+sans pairing, same warm bronze/oxblood colour family, same
  restraint, same motion philosophy. But the hierarchy is deliberately
  inverted: Avvyun is the institution (cream canvas, oxblood type, dark
  CTA). This is the person (oxblood/espresso canvas, cream type, light
  CTA) — same colour family, reciprocal roles. Not a copy, and not dark
  mode: a considered emotional inversion. Structurally it remains its
  own thing — a persistent editorial sidebar, mask-reveal typography,
  alternating editorial rows — none of it borrowed from Avvyun's
  component vocabulary.
*/

/* ---------- Tokens ---------- */
:root {
  /* Canvas — deep espresso/oxblood, the inversion of Avvyun's cream. */
  --c-bg: #241209;
  --c-bg-raised: #33190e;
  --c-bg-deep: #170a03;

  /* Type — warm cream primary, muted taupe secondary. */
  --c-text: #f4ebe0;
  --c-text-soft: #c2a891;
  --c-text-faint: rgba(244, 235, 224, 0.52);

  /* Accent — bronze/rose-gold, same family as Avvyun's tan, brightened
     so it still sings against a dark canvas. */
  --c-accent: #cc9f6d;
  --c-accent-strong: #e2b688;

  /* Hairlines on dark. */
  --c-border: rgba(244, 235, 224, 0.14);
  --c-border-strong: rgba(244, 235, 224, 0.28);

  /* CTA — light bronze fill + dark text: the reciprocal of Avvyun's
     dark-oxblood-fill + white text. */
  --c-cta-bg: var(--c-accent);
  --c-cta-bg-hover: var(--c-accent-strong);
  --c-cta-text: var(--c-bg-deep);

  --c-selection: rgba(204, 159, 109, 0.28);
  --c-focus: var(--c-accent);

  --font-serif: "Fraunces", "Merriweather", Georgia, serif;
  --font-sans: "Geist", -apple-system, BlinkMacSystemFont, "Segoe UI", Helvetica, Arial, sans-serif;

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

  --rail-w: 17rem;
  --gutter: clamp(1.25rem, 4vw, 3rem);
  --content-max: 40rem;

  --ease-reveal: cubic-bezier(0.19, 1, 0.22, 1);
  --ease-soft: cubic-bezier(0.4, 0, 0.2, 1);
  --dur-quick: 220ms;
  --dur-reveal: 850ms;
  --dur-reveal-lg: 1000ms;
  --dur-photo: 1200ms;

  /* ---- Liquid Glass material -----------------------------------------
     Same recipe as the reference implementation (nuvya.vc): a neutral
     frosted translucency, brighter along the top edge like light
     catching a physical curved surface, that picks up this site's own
     canvas rather than introducing a new hue. Built entirely from
     --c-text (cream) and --c-accent (bronze) at low opacity — dark
     canvas, so the mechanism ports directly without inversion. */
  --glass-fill: linear-gradient(165deg, rgba(244, 235, 224, 0.10) 0%, rgba(244, 235, 224, 0.03) 55%, rgba(244, 235, 224, 0.05) 100%);
  --glass-fill-soft: linear-gradient(165deg, rgba(244, 235, 224, 0.06) 0%, rgba(244, 235, 224, 0.015) 60%, rgba(244, 235, 224, 0.035) 100%);
  --glass-border: rgba(244, 235, 224, 0.14);
  --glass-border-top: rgba(244, 235, 224, 0.34);
  --glass-blur: blur(20px) saturate(140%);
  --glass-blur-soft: blur(13px) saturate(125%);
  --glass-shadow: 0 1px 1px rgba(244, 235, 224, 0.10) inset, 0 16px 40px rgba(9, 4, 1, 0.55), 0 0 0 1px rgba(204, 159, 109, 0.07);
  --glass-shadow-hover: 0 1px 1px rgba(244, 235, 224, 0.16) inset, 0 20px 50px rgba(9, 4, 1, 0.6), 0 0 0 1px rgba(204, 159, 109, 0.16), 0 0 28px rgba(204, 159, 109, 0.12);
  --glass-specular: radial-gradient(circle 220px at var(--pointer-x, 50%) var(--pointer-y, 0%), rgba(244, 235, 224, 0.14), transparent 70%);

  --radius-glass-sm: 12px;
  --radius-glass: 20px;
  --radius-glass-lg: 28px;
  --radius-pill: 999px;

  color-scheme: dark;
}

/* ---- Liquid Glass surface primitive ---------------------------------
   Applied to the floating nav (rail/mobile-bar/drawer) and to one
   isolated "raised" panel — deliberately not to every row or card, so
   glass reads as a distinct material rather than a decoration smeared
   everywhere. Listed by exact selector, same discipline as the
   reference implementation's own nuvya.css. */
.rail,
.mobile-bar,
.drawer__panel,
.block-raised {
  position: relative;
  background: var(--glass-fill);
  -webkit-backdrop-filter: var(--glass-blur);
  backdrop-filter: var(--glass-blur);
  border: 1px solid var(--glass-border);
  border-top-color: var(--glass-border-top);
  box-shadow: var(--glass-shadow);
  --pointer-x: 50%;
  --pointer-y: 0%;
  transition: box-shadow var(--dur-reveal) var(--ease-soft), border-color var(--dur-reveal) var(--ease-soft);
}
.rail::after,
.mobile-bar::after,
.drawer__panel::after,
.block-raised::after {
  content: "";
  position: absolute;
  inset: 0;
  border-radius: inherit;
  background: var(--glass-specular);
  opacity: 0;
  transition: opacity var(--dur-reveal) ease;
  pointer-events: none;
}
.rail:hover::after,
.mobile-bar:hover::after,
.drawer__panel:hover::after,
.block-raised:hover::after { opacity: 1; }

.block-raised {
  -webkit-backdrop-filter: var(--glass-blur-soft);
  backdrop-filter: var(--glass-blur-soft);
  background: var(--glass-fill-soft);
}

@supports not ((backdrop-filter: blur(1px)) or (-webkit-backdrop-filter: blur(1px))) {
  .rail, .mobile-bar, .drawer__panel, .block-raised { background: rgba(23, 10, 3, 0.88); }
}
@media (prefers-reduced-motion: reduce) {
  .rail, .mobile-bar, .drawer__panel, .block-raised,
  .rail::after, .mobile-bar::after, .drawer__panel::after, .block-raised::after {
    transition-duration: 1ms;
  }
}

/* Ambient atmosphere — a fixed, near-imperceptible glow using the site's
   own bronze accent, giving the glass surfaces above something to catch
   light from and reinforcing that the UI sits on a plane above the page
   rather than flat against it. Pure CSS, no per-frame cost. */
.atmosphere {
  position: fixed;
  inset: 0;
  z-index: 0;
  pointer-events: none;
  overflow: hidden;
}
.atmosphere::before {
  content: "";
  position: absolute;
  top: 18%;
  left: 8%;
  width: min(640px, 90vw);
  height: min(640px, 90vw);
  background: radial-gradient(circle, rgba(204, 159, 109, 0.10) 0%, transparent 68%);
  filter: blur(10px);
  animation: atmosphere-drift 40s var(--ease-soft) infinite alternate;
}
@keyframes atmosphere-drift {
  0% { transform: translate(0, 0) scale(1); }
  100% { transform: translate(3%, 4%) scale(1.08); }
}
@media (prefers-reduced-motion: reduce) {
  .atmosphere::before { animation: none; }
}

/* ---------- Reset ---------- */
*, *::before, *::after { box-sizing: border-box; }
* { margin: 0; }
html { -webkit-text-size-adjust: 100%; }

body {
  min-height: 100svh;
  background: var(--c-bg);
  color: var(--c-text);
  font-family: var(--font-sans);
  font-weight: 400;
  font-size: 1.0625rem;
  -webkit-font-smoothing: antialiased;
  text-rendering: optimizeLegibility;
}

img, picture, svg { display: block; max-width: 100%; }
a { text-decoration: none; color: inherit; }
ul, ol { list-style: none; padding: 0; }
button, input, textarea { font: inherit; color: inherit; background: none; border: 0; }
button { cursor: pointer; }

::selection { background: var(--c-selection); color: var(--c-text); }
:focus-visible { outline: 2px solid var(--c-focus); outline-offset: 3px; }

.skip-link {
  position: absolute;
  top: -100%;
  left: var(--space-4);
  background: var(--c-accent);
  color: var(--c-bg-deep);
  padding: 0.75rem 1.25rem;
  z-index: 200;
  font-size: 0.875rem;
}
.skip-link:focus { top: var(--space-2); }

/* ---------- Frame: persistent editorial sidebar + main column ---------- */
.frame { position: relative; z-index: 1; display: flex; align-items: stretch; min-height: 100svh; }

/* The rail is Chinku's equivalent of a floating nav: still a persistent
   sidebar (its own information architecture, not Nuvya's topbar pill),
   but detached from the viewport edge as a pane of glass rather than a
   flush panel with a hairline border. */
.rail {
  width: var(--rail-w);
  flex: none;
  position: sticky;
  top: var(--space-4);
  height: calc(100svh - var(--space-4) * 2);
  margin: var(--space-4) 0 var(--space-4) var(--space-4);
  padding: var(--space-6) var(--space-5) var(--space-5);
  display: flex;
  flex-direction: column;
  justify-content: space-between;
  border-radius: var(--radius-glass-lg);
}

.rail__word {
  font-family: var(--font-serif);
  font-style: italic;
  font-weight: 500;
  font-size: 1.25rem;
  color: var(--c-text);
}

.rail__nav { display: flex; flex-direction: column; gap: var(--space-4); margin-top: var(--space-9); }
.rail__nav a {
  position: relative;
  display: inline-flex;
  align-items: center;
  gap: 0.6rem;
  font-family: var(--font-sans);
  font-weight: 500;
  font-size: 0.95rem;
  color: var(--c-text-soft);
  transition: color var(--dur-quick) var(--ease-soft);
}
.rail__nav a::before {
  content: "";
  width: 4px;
  height: 4px;
  border-radius: 50%;
  background: transparent;
  transition: background var(--dur-quick) var(--ease-soft);
  flex: none;
}
.rail__nav a:hover, .rail__nav a[aria-current="page"] { color: var(--c-text); }
.rail__nav a:hover::before, .rail__nav a[aria-current="page"]::before { background: var(--c-accent); }

.rail__foot { display: flex; flex-direction: column; gap: var(--space-4); }
.rail__avvyun {
  font-family: var(--font-sans);
  font-weight: 500;
  font-size: 0.875rem;
  color: var(--c-cta-text);
  background: var(--c-cta-bg);
  padding: 0.75em 1.1em;
  border-radius: 2px;
  text-align: center;
  transition: background-color var(--dur-quick) ease;
}
.rail__avvyun:hover { background: var(--c-cta-bg-hover); }
.rail__note { font-size: 0.75rem; color: var(--c-text-faint); line-height: 1.5; }

.utility-links { display: flex; gap: var(--space-4); }
.utility-links a {
  font-family: var(--font-sans);
  font-weight: 500;
  font-size: 0.8125rem;
  color: var(--c-text-soft);
  transition: color var(--dur-quick) var(--ease-soft);
}
.utility-links a:hover { color: var(--c-text); }

.frame__main { flex: 1; min-width: 0; }

/* Mobile top bar — hidden on desktop, replaces the rail below the
   breakpoint. Floating glass pill, detached from the viewport edge like
   the rail is on desktop, rather than a flush full-width strip. */
.mobile-bar {
  display: none;
  position: sticky;
  top: var(--space-3);
  z-index: 80;
  align-items: center;
  justify-content: space-between;
  margin: var(--space-3) var(--gutter) 0;
  padding: var(--space-3) var(--space-5);
  border-radius: var(--radius-pill);
  transition: box-shadow var(--dur-reveal) var(--ease-soft), border-color var(--dur-reveal) var(--ease-soft);
}
.mobile-bar[data-scrolled="true"] {
  box-shadow: var(--glass-shadow-hover);
  border-color: rgba(244, 235, 224, 0.22);
}
.mobile-bar__word { font-family: var(--font-serif); font-style: italic; font-weight: 500; font-size: 1.1rem; color: var(--c-text); }

.drawer-toggle {
  position: relative;
  width: 2.75rem;
  height: 2.75rem;
  padding: 0;
  color: var(--c-text);
  flex: none;
}
.drawer-toggle .icon-open,
.drawer-toggle .icon-close {
  position: absolute;
  inset: 0;
  display: flex;
  align-items: center;
  justify-content: center;
  transition: opacity var(--dur-quick) ease, transform var(--dur-quick) ease;
}
.drawer-toggle svg { width: 22px; height: 22px; }
.drawer-toggle .icon-close { opacity: 0; transform: scale(0.85); }
.drawer-toggle[aria-expanded="true"] .icon-open { opacity: 0; transform: scale(0.85); }
.drawer-toggle[aria-expanded="true"] .icon-close { opacity: 1; transform: scale(1); }

@media (max-width: 900px) {
  .rail { display: none; }
  .mobile-bar { display: flex; }
}

/* Mobile drawer — a partial-width panel from the right, not a full-screen
   centred overlay. Sibling of everything else so it's never affected by
   an ancestor's backdrop-filter / transform. */
.drawer {
  position: fixed;
  inset: 0;
  z-index: 90;
  pointer-events: none;
}
.drawer__scrim {
  position: absolute;
  inset: 0;
  background: rgba(0, 0, 0, 0.45);
  -webkit-backdrop-filter: blur(6px);
  backdrop-filter: blur(6px);
  opacity: 0;
  transition: opacity var(--dur-reveal) var(--ease-soft);
}
.drawer__panel {
  position: absolute;
  top: 0; right: 0; bottom: 0;
  width: min(20rem, 84vw);
  padding: var(--space-8) var(--space-6) var(--space-6);
  display: flex;
  flex-direction: column;
  justify-content: space-between;
  border-radius: var(--radius-glass-lg) 0 0 var(--radius-glass-lg);
  border-right: none;
  transform: translateX(100%);
  transition: transform var(--dur-reveal) var(--ease-reveal);
}
.drawer[data-open="true"] { pointer-events: auto; }
.drawer[data-open="true"] .drawer__scrim { opacity: 1; }
.drawer[data-open="true"] .drawer__panel { transform: translateX(0); }

.drawer__list { display: flex; flex-direction: column; gap: var(--space-5); }
.drawer__list a { font-family: var(--font-serif); font-weight: 480; font-size: 1.6rem; color: var(--c-text); }
.drawer__foot { display: flex; flex-direction: column; gap: var(--space-4); }

@media (min-width: 901px) {
  .drawer { display: none; }
}

/* ---------- Content primitives ---------- */
.wrap { max-width: 68rem; margin-inline: auto; padding-inline: var(--gutter); }
.content { max-width: var(--content-max); }
.block { padding-block: var(--space-8); }
.block + .block { border-top: 1px solid var(--c-border); }
.block-raised { background: var(--c-bg-raised); }

@media (max-width: 720px) {
  .block { padding-block: var(--space-7); }
}

/* ---------- Type ---------- */
.caption {
  font-family: var(--font-sans);
  font-weight: 500;
  font-size: 0.8125rem;
  letter-spacing: 0.02em;
  color: var(--c-text-faint);
}

.signature-tagline {
  font-family: var(--font-serif);
  font-style: italic;
  font-weight: 460;
  font-size: clamp(2.1rem, 1.7rem + 2.1vw, 3.6rem);
  line-height: 1.18;
  letter-spacing: -0.005em;
  color: var(--c-text);
  max-width: 16ch;
}
.signature-tagline .accent { color: var(--c-accent); }

h1, h2, .h2 {
  font-family: var(--font-serif);
  font-weight: 520;
  font-size: clamp(1.6rem, 1.35rem + 1.1vw, 2.4rem);
  line-height: 1.18;
  letter-spacing: -0.008em;
  color: var(--c-text);
}

h3, .h3 {
  font-family: var(--font-serif);
  font-weight: 520;
  font-size: clamp(1.2rem, 1.1rem + 0.4vw, 1.45rem);
  line-height: 1.28;
  color: var(--c-text);
}

.lede {
  font-family: var(--font-sans);
  font-weight: 400;
  font-size: clamp(1rem, 0.96rem + 0.2vw, 1.1rem);
  line-height: 1.7;
  color: var(--c-text-soft);
}

p { max-width: 38em; }
.prose-body p {
  font-family: var(--font-sans);
  font-weight: 400;
  font-size: 1.03rem;
  line-height: 1.8;
  color: var(--c-text-soft);
}
.prose-body p + p { margin-top: var(--space-4); }
.prose-body a { border-bottom: 1px solid var(--c-border-strong); }
.prose-body a:hover { border-color: var(--c-text); }

.meta { font-family: var(--font-sans); font-weight: 500; font-size: 0.8125rem; color: var(--c-text-soft); }
.text-muted { color: var(--c-text-faint); }

/* ---------- Links ---------- */
.text-link {
  position: relative;
  display: inline-flex;
  align-items: center;
  gap: 0.4em;
  color: var(--c-text);
  border-bottom: 1px solid var(--c-border-strong);
  padding-bottom: 0.05em;
  transition: color var(--dur-quick) var(--ease-soft), border-color var(--dur-quick) var(--ease-soft);
}
.text-link:hover { color: var(--c-accent); border-color: var(--c-accent); }
.text-link .arrow { transition: transform var(--dur-quick) var(--ease-soft); }
.text-link:hover .arrow { transform: translateX(0.15em); }

/* ---------- Buttons ---------- */
.btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: var(--space-2);
  font-family: var(--font-sans);
  font-weight: 500;
  font-size: 0.9375rem;
  padding: 0.85em 1.5em;
  border-radius: 2px;
  transition: transform var(--dur-quick) var(--ease-soft), background-color var(--dur-quick) ease;
}
.btn-primary { background: var(--c-cta-bg); color: var(--c-cta-text); }
.btn-primary:hover { background: var(--c-cta-bg-hover); transform: translateY(-1px); }

/* ---------- Mask reveal — the site's own signature motion. Text/blocks
   are revealed by widening a clip-path, like a shutter opening, rather
   than fading up (Avvyun's language) or blurring in. ---------- */
.mask-reveal {
  clip-path: inset(0 100% -20% 0);
  transition: clip-path var(--dur-reveal) var(--ease-reveal);
}
.mask-reveal.is-visible { clip-path: inset(0 0 -20% 0); }

.fade-in {
  opacity: 0;
  transform: translateY(10px);
  transition: opacity var(--dur-reveal) var(--ease-soft), transform var(--dur-reveal) var(--ease-soft);
}
.fade-in.is-visible { opacity: 1; transform: none; }

/* Photos emerge from the canvas itself, not from a light sheet — a dark
   panel recedes as the image settles from a slight zoom. */
.photo-reveal {
  position: relative;
  overflow: hidden;
  background: var(--c-bg-deep);
}
.photo-reveal img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  transform: scale(1.08);
  transition: transform var(--dur-photo) var(--ease-reveal);
}
.photo-reveal::after {
  content: "";
  position: absolute;
  inset: 0;
  background: var(--c-bg);
  transform-origin: top;
  transition: transform var(--dur-photo) var(--ease-reveal);
}
.photo-reveal.is-visible img { transform: scale(1); }
.photo-reveal.is-visible::after { transform: scaleY(0); }

@media (prefers-reduced-motion: reduce) {
  .mask-reveal, .fade-in { clip-path: none !important; opacity: 1 !important; transform: none !important; transition: none !important; }
  .photo-reveal img { transform: none !important; transition: none !important; }
  .photo-reveal::after { transform: scaleY(0) !important; transition: none !important; }
  .drawer__panel, .drawer__scrim { transition: none !important; }
}

/* ---------- Unified inner-page motion engine ----------------------------
   ONE vocabulary for About/Experience/Building/Writing/Now — float, fade,
   stagger, divider-draw, and a small bounded parallax drift. No clip-path,
   no CSS masks, no opaque covering panels, no pinned/sticky scroll-scrub,
   no scene overlap: every element stays in normal document flow and at
   its true final layout position at all times. An entrance transform only
   ever displaces an element FROM that position and animates it home —
   scroll never "reveals" anything through a mask or a moving cover.
   Same trigger the homepage's .fade-in/.mask-reveal already use (one
   IntersectionObserver + .is-visible, see site.js) — opt-in classes only,
   homepage markup/CSS untouched — and the SAME classes at every viewport
   width, so mobile gets the identical choreography, not a fallback.
   Per-item stagger: an inline --rd custom property (seconds, e.g.
   style="--rd:0.24") baked into each primitive's own transition-delay —
   one eyebrow → heading → body → metadata → cards cadence reused
   identically on every chapter of every page. */
.motion-fade,
.motion-float,
.motion-float-lg,
.motion-float-sm,
.motion-float-left,
.motion-float-right,
.motion-scale {
  transition-delay: calc(var(--rd, 0) * 1s);
}
.motion-fade { opacity: 0; transition: opacity var(--dur-reveal) var(--ease-soft); transition-delay: calc(var(--rd, 0) * 1s); }
.motion-fade.is-visible { opacity: 1; }

.motion-float {
  opacity: 0;
  transform: translateY(var(--fy, 36px));
  transition: opacity var(--dur-reveal) var(--ease-reveal), transform var(--dur-reveal) var(--ease-reveal);
}
.motion-float.is-visible { opacity: 1; transform: none; }

/* Heading-scale float — the largest element on the page: more travel, a
   touch of scale settle, a longer duration befitting its size. */
.motion-float-lg {
  opacity: 0;
  transform: translateY(var(--fy, 52px)) scale(.98);
  transition: opacity var(--dur-reveal-lg) var(--ease-reveal), transform var(--dur-reveal-lg) var(--ease-reveal);
}
.motion-float-lg.is-visible { opacity: 1; transform: none; }

.motion-float-sm {
  opacity: 0;
  transform: translateY(20px);
  transition: opacity var(--dur-reveal) var(--ease-reveal), transform var(--dur-reveal) var(--ease-reveal);
}
.motion-float-sm.is-visible { opacity: 1; transform: none; }

.motion-float-left {
  opacity: 0;
  transform: translateX(-32px);
  transition: opacity var(--dur-reveal) var(--ease-reveal), transform var(--dur-reveal) var(--ease-reveal);
}
.motion-float-left.is-visible { opacity: 1; transform: none; }

.motion-float-right {
  opacity: 0;
  transform: translateX(32px);
  transition: opacity var(--dur-reveal) var(--ease-reveal), transform var(--dur-reveal) var(--ease-reveal);
}
.motion-float-right.is-visible { opacity: 1; transform: none; }

.motion-scale {
  opacity: 0;
  transform: translateY(24px) scale(.97);
  transition: opacity var(--dur-reveal) var(--ease-reveal), transform var(--dur-reveal) var(--ease-reveal);
}
.motion-scale.is-visible { opacity: 1; transform: none; }

/* Divider draw — the only acceptable "reveal" behaviour for a line: it
   grows its own width from 0 to 100%, never anything covering content. */
.motion-divider {
  display: block;
  height: 1px;
  width: 100%;
  background: var(--c-accent);
  transform: scaleX(0);
  transform-origin: left;
  opacity: 0;
  margin-bottom: var(--space-6);
  transition: transform 900ms var(--ease-reveal), opacity 900ms var(--ease-reveal);
  transition-delay: calc(var(--rd, 0) * 1s);
}
.motion-divider.is-visible { transform: scaleX(1); opacity: .7; }
.motion-divider--center { transform-origin: center; }

/* Bounded continuous parallax (site.js) — applied on top of an already-
   settled element via the standalone `translate` property, which
   composes independently of the `transform` the entrance animations
   above use, so it can never fight or be mistaken for a reveal: the
   element is fully visible and at its true layout position throughout,
   scroll only ever nudges it a few px either way. */
@media (prefers-reduced-motion: reduce) {
  .motion-parallax { translate: none !important; }
  .motion-fade, .motion-float, .motion-float-lg, .motion-float-sm,
  .motion-float-left, .motion-float-right, .motion-scale {
    opacity: 1 !important; transform: none !important; transition: none !important;
  }
  .motion-divider { transform: scaleX(1) !important; opacity: .7 !important; transition: none !important; }
}

/* Timeline spine (Experience) — a restrained scroll-linked fill beside
   the row-list standing in for progress through the career history.
   JS-driven height (site.js); reduced-motion sets it complete below. */
.timeline { position: relative; }
.timeline__spine {
  display: none;
  position: absolute;
  top: 0.5rem;
  bottom: 0.5rem;
  left: calc(8rem + var(--gutter) * 0.5);
  width: 1px;
  background: var(--c-border);
}
.timeline__spine-fill {
  display: block;
  width: 100%;
  height: 0;
  background: var(--c-accent);
  transform-origin: top;
  opacity: 0.7;
}
@media (min-width: 620px) {
  .timeline__spine { display: block; }
}

@media (prefers-reduced-motion: reduce) {
  .timeline__spine-fill { transition: none !important; height: 100% !important; }
}

/* Experience — a career travelled, not a slideshow. Each row floats into
   place once (.motion-float, staggered) exactly like every other list on
   the site; independently of that one-shot entrance, whichever row is
   nearest the focus line gets a continuous, fully reversible .is-focused
   state as the user scrolls — a small accent-colour tint and a 4px nudge
   on its own title, and a light opacity lift on its content (never touching
   the row's own opacity, which .motion-float owns for the entrance) — so
   rows never disappear, never heavily dim, and nothing is ever wiped or
   masked. Legible at every position, emphasised only at its "turn". */
.timeline .row > .meta,
.timeline .row > div {
  opacity: .82;
  transition: opacity 240ms linear;
}
.timeline .row.is-focused > .meta,
.timeline .row.is-focused > div { opacity: 1; }

.timeline .row__title {
  color: var(--c-text);
  transition: color 240ms ease, transform var(--dur-quick) var(--ease-soft);
}
.timeline .row.is-focused .row__title { color: var(--c-accent); transform: translateX(4px); }
/* Hover echoes the same nudge, a touch further, taking precedence over
   the focus state when both apply. */
.timeline .row:hover .row__title { transform: translateX(8px); }

/* About/Experience — education cards get a small independent hover lift
   on top of their .motion-float entrance. */
.edu-card:hover { transform: translateY(-5px); transition: transform 260ms var(--ease-soft); }

@media (prefers-reduced-motion: reduce) {
  .timeline .row > .meta, .timeline .row > div, .timeline .row__title {
    transition: none !important; opacity: 1 !important; transform: none !important; color: var(--c-text) !important;
  }
}

/* ---------- Masthead (home) ---------- */
.masthead__grid {
  display: grid;
  gap: var(--space-7);
  align-items: end;
}
@media (min-width: 760px) {
  .masthead__grid { grid-template-columns: 1.3fr 0.9fr; }
}

.masthead__role { margin-top: var(--space-5); max-width: 32ch; }

.masthead__photo { aspect-ratio: 4 / 5; max-width: 22rem; margin-left: auto; }
@media (min-width: 760px) {
  .masthead__photo { margin-top: var(--space-8); }
}

/* ---------- Editorial focus rows (current focus / building) ---------- */
.focus-primary {
  padding-top: var(--space-6);
  border-top: 1px solid var(--c-border);
  display: grid;
  gap: var(--space-4);
}
@media (min-width: 700px) {
  .focus-primary { grid-template-columns: 9rem 1fr; gap: var(--gutter); }
}
.focus-primary__title { margin-top: 0.4rem; }
.focus-primary__title a:hover { color: var(--c-accent); }

.focus-secondary {
  display: grid;
  gap: var(--space-6);
  margin-top: var(--space-7);
}
@media (min-width: 700px) {
  .focus-secondary { grid-template-columns: repeat(2, 1fr); }
}
.focus-secondary > div { padding-top: var(--space-4); border-top: 1px solid var(--c-border); }
.focus-secondary h3 { margin-top: 0.35rem; }
.focus-secondary h3 a:hover { color: var(--c-accent); }

/* ---------- Rows (timeline / now / writing index / contact) ---------- */
.row-list { display: grid; }
.row {
  display: grid;
  gap: var(--space-2) var(--gutter);
  padding-block: var(--space-4);
  border-top: 1px solid var(--c-border);
}
.row-list > :last-child { border-bottom: 1px solid var(--c-border); }
@media (min-width: 620px) {
  .row { grid-template-columns: 8rem 1fr; align-items: baseline; }
}
.row__title { font-family: var(--font-serif); font-weight: 500; font-size: 1.08rem; }
a.row { transition: color var(--dur-quick) ease; }
a.row:hover .row__title { color: var(--c-accent); }

.row--split { grid-template-columns: 1fr auto; align-items: baseline; }

/* ---------- Theme list (writing themes / what I care about) ---------- */
.theme-list { margin-top: var(--space-4); display: grid; gap: 0.65rem; }
.theme-list li { padding-left: 1.3em; position: relative; color: var(--c-text-soft); }
.theme-list li::before { content: "·"; position: absolute; left: 0.2em; color: var(--c-accent); font-size: 1.4em; line-height: 0.7; }

/* ---------- Footer (minimal — the rail already carries navigation) ---------- */
.site-footer {
  max-width: 68rem;
  margin-inline: auto;
  padding-inline: var(--gutter);
  padding-block: var(--space-9) var(--space-8);
  border-top: 1px solid var(--c-border);
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-3) var(--space-6);
  align-items: center;
  justify-content: space-between;
}
.site-footer .meta { display: flex; gap: var(--space-5); }
.site-footer a:hover { color: var(--c-accent); }

@media (max-width: 720px) {
  .site-footer { padding-block: var(--space-7) var(--space-6); }
}

/* ---------- 404 ---------- */
.not-found {
  min-height: 80svh;
  display: flex;
  align-items: center;
  padding-block: var(--space-9);
}
.not-found p.lede { margin-top: var(--space-3); }
