/* ───────────────────────────────────────────────────────────────
   EunBi Oh — personal site styles
   Single shared stylesheet across index/research/writing/about/contact.

   Layout philosophy:
   - Fluid typography (clamp) so sizes scale smoothly with viewport
     instead of stepping at media-query boundaries.
   - Three real reflow breakpoints only (960 / 640 / 420) — used where
     the layout shape actually has to change.
   - Container caps at 1400px so the site doesn't stretch to the edges
     of ultra-wide monitors.
   - Touch-target rule for fingers (≥44px tap height).
   ─────────────────────────────────────────────────────────────── */

:root {
  /* Palette is from color theme.png. Five colors, mixed by role:
     - Ebony (sage) is the primary accent — used most.
     - Light Bronze (warm) is the secondary, for hero italic flourishes
       + card hover signals; sparing on purpose.
     - Tea Green (soft) tints search affordances (highlights, hover bg).
     - Cornsilk (light cream) elevates cards above the cream page.
     - Charcoal is held in reserve. */
  --accent:      #606D5D;  /* Ebony */
  --accent-warm: #D4A373;  /* Light Bronze */
  --accent-tint: #CCD5AE;  /* Tea Green */
  --accent-deep: #7A8554;  /* Deep Tea Green — darkened from --accent-tint so
                              it reads as a title color on cream. Used as
                              the Contact page's title color, completing
                              the 4-tab palette (Ebony / Charcoal / Bronze /
                              Deep Tea Green), all sourced from color theme.png. */
  --paper:       #f1ece0;
  --paper-deep:  #e7e0cf;
  --paper-card:  #FEFAE0;  /* Cornsilk — slightly lighter than paper */
  --ink:         #161311;
  --ink-soft:    #5C5D67;  /* Charcoal — reserved */
  --body:        #2a2520;
  --faded:       #766a5a;
  --rule:        #cabf9f;
  --rule-2:      #dad1b8;

  --f-display: "Newsreader", "Source Serif Pro", Georgia, serif;
  --f-italic:  "Instrument Serif", "Newsreader", Georgia, serif;
  --f-sans:    "IBM Plex Sans", -apple-system, system-ui, sans-serif;
  --f-mono:    "JetBrains Mono", ui-monospace, monospace;

  --container:  1180px;        /* max page width on ultra-wide screens — narrower
                                  so content sits as a centered column with
                                  symmetric margins, not a wide left-leaning band */
  --gutter:     clamp(24px, 5vw, 48px);
}

/* Landing-page text uses Charcoal (gray) instead of near-black ink. Scoped
   to the landing's content sections so the header stays consistent across
   pages (header text remains the standard dark ink everywhere). Accents
   (italic field name, period, NOW dot, section labels) keep their Ebony /
   Bronze colors via explicit var(--accent) / var(--accent-warm) usage. */
body[data-active="home"] .hero,
body[data-active="home"] .now,
body[data-active="home"] .featured,
body[data-active="home"] .about-teaser {
  --ink:  #5C5D67;   /* Charcoal */
  --body: #5C5D67;   /* same gray for paragraph body text */
}

*, *::before, *::after { box-sizing: border-box; }
html, body { margin: 0; padding: 0; }
html { -webkit-text-size-adjust: 100%; }     /* don't grow text on iOS rotate */
body {
  background: var(--paper);
  color: var(--body);
  font-family: var(--f-display);
  min-height: 100vh;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
  overflow-x: hidden;          /* belt-and-braces against any rogue overflow */
}
a { color: inherit; }
::selection { background: var(--accent); color: #fff; }

/* ─── header ─── */
.site-header {
  position: sticky; top: 0; z-index: 20;
  background: color-mix(in srgb, var(--paper) 95%, transparent);
  backdrop-filter: blur(10px);
  -webkit-backdrop-filter: blur(10px);
  border-bottom: 1px solid var(--rule);
  padding: 16px var(--gutter);
  display: grid;
  grid-template-columns: 1fr auto 1fr;
  align-items: center;
  gap: 16px;
  font-family: var(--f-sans);
  font-size: 12px;
  letter-spacing: 0.4px;
  color: var(--faded);
}
/* Home affordance: just an icon in the top-left. The wordmark text was
   removed — visitors learn the name from the hero/about/footer. The icon
   is the single click target for "back to home". */
.wordmark-row { display: flex; align-items: center; min-width: 0; }
.home-link {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 36px;
  height: 36px;
  color: var(--ink);
  text-decoration: none;
  transition: color .25s, transform .25s;
}
.home-link:hover { color: var(--accent); transform: translateY(-1px); }
.home-icon {
  width: 20px;
  height: 20px;
  flex-shrink: 0;
  display: block;
}

.nav { display: flex; gap: clamp(14px, 2.4vw, 28px); justify-content: center; }
.nav a {
  position: relative;
  color: var(--ink);
  text-decoration: none;
  font-family: var(--f-sans);
  font-size: 13px;
  font-weight: 500;
  text-transform: uppercase;
  letter-spacing: 1.4px;
  padding: 4px 2px;
  border-bottom: 1.5px solid transparent;
  transition: color .25s, border-color .25s;
}
.nav a:hover { color: var(--accent); }
.nav a.is-active {
  color: var(--accent);
  font-weight: 600;
  border-bottom-color: var(--accent);
}

/* ─── header right column: social icons + language toggle ─── */
.header-right {
  display: flex;
  align-items: center;
  justify-content: flex-end;
  gap: clamp(12px, 1.8vw, 22px);
  flex-wrap: wrap;
}
.social-icons {
  display: flex;
  align-items: center;
  justify-content: flex-end;
  gap: clamp(10px, 1.2vw, 16px);
}
.social-icons a {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 32px;
  height: 32px;
  color: var(--ink);
  text-decoration: none;
  transition: color .25s, transform .25s;
}
.social-icons a:hover {
  color: var(--accent);
  transform: translateY(-2px);
}
.social-icons svg { width: 20px; height: 20px; display: block; }

/* Language toggle — small text buttons, the active one accent + bold.
   HIDDEN while KR is disabled (2026-10, stale KO copy) — chrome.js getLang()
   is also pinned to "en". Delete the display:none line to bring it back. */
.lang-toggle { display: none; }
.lang-toggle-disabled-keep-styles {
  display: inline-flex;
  align-items: center;
  gap: 2px;
  font-family: var(--f-sans);
  font-size: 12px;
  letter-spacing: 1.2px;
}
.lang-toggle button {
  background: transparent;
  border: 0;
  padding: 4px 6px;
  color: var(--faded);
  cursor: pointer;
  text-transform: uppercase;
  font: inherit;
  letter-spacing: inherit;
  transition: color .25s;
}
.lang-toggle button:hover { color: var(--ink); }
.lang-toggle button.is-active {
  color: var(--accent);
  font-weight: 600;
}
.lang-toggle > span { color: var(--rule); }

/* ─── nav drawer (hamburger menu, shown ≤840px) ────────────────────────
   At wide widths the .nav row in the header holds all the page tabs.
   When the header runs out of horizontal room (≤840px), the .nav row is
   hidden and a hamburger button (.nav-toggle) appears in .header-right.
   Clicking the hamburger slides .nav-drawer in from the right; .nav-backdrop
   dims the page behind it. Default state below hides all three so the
   wide layout is untouched — they're only revealed inside the 840px
   media query at the end of this file. */
.nav-toggle {
  display: none;
  align-items: center;
  justify-content: center;
  width: 36px;
  height: 36px;
  padding: 0;
  background: transparent;
  border: 0;
  color: var(--ink);
  cursor: pointer;
  transition: color .25s, transform .25s;
}
.nav-toggle:hover { color: var(--accent); transform: translateY(-1px); }
.nav-toggle svg { width: 22px; height: 22px; display: block; }

.nav-drawer {
  display: none;
  position: fixed;
  top: 0;
  right: 0;
  height: 100vh;
  width: min(320px, 86vw);
  background: var(--paper);
  border-left: 1px solid var(--rule);
  padding: clamp(64px, 8vw, 84px) 28px 28px;
  transform: translateX(100%);
  transition: transform .35s cubic-bezier(.2,.7,.3,1);
  z-index: 40;
  box-shadow: -20px 0 40px -20px rgba(40, 30, 10, .35);
  overflow-y: auto;
}
.nav-drawer.is-open { transform: translateX(0); }

.nav-drawer-close {
  position: absolute;
  top: 18px;
  right: 18px;
  width: 36px;
  height: 36px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  background: transparent;
  border: 0;
  color: var(--ink);
  cursor: pointer;
  padding: 0;
  transition: color .25s, transform .25s;
}
.nav-drawer-close:hover { color: var(--accent); transform: rotate(90deg); }
.nav-drawer-close svg { width: 22px; height: 22px; display: block; }

.nav-stacked {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: 4px;
}
.nav-stacked a {
  position: relative;
  display: block;
  width: 100%;
  padding: 12px 0;
  color: var(--ink);
  text-decoration: none;
  font-family: var(--f-display);
  font-size: 20px;
  font-weight: 500;
  letter-spacing: -0.2px;
  border-bottom: 1px solid var(--rule-2);
  transition: color .25s, padding-left .25s;
}
.nav-stacked a:hover { color: var(--accent); padding-left: 6px; }
.nav-stacked a.is-active {
  color: var(--accent);
  font-weight: 600;
}
.nav-stacked a.is-active::after {
  content: "";
  position: absolute;
  right: 0;
  top: 50%;
  transform: translateY(-50%);
  width: 6px;
  height: 6px;
  border-radius: 50%;
  background: var(--accent);
}

.nav-backdrop {
  display: none;
  position: fixed;
  inset: 0;
  background: rgba(20, 15, 5, .35);
  z-index: 30;
  opacity: 0;
  transition: opacity .25s;
}
.nav-backdrop.is-open { opacity: 1; }

/* Drawer social icons — rendered into the drawer by chrome.js (alongside
   the stacked nav). Default-hidden because the header carries the icons
   at most widths; only revealed at ≤420px where the header sheds them
   to avoid wrapping. See the @media (max-width: 420px) block below. */
.drawer-social-icons {
  display: none;
}

/* ─── dynamic drawer-mode trigger (.is-cramped) ────────────────────────
   Added at runtime by chrome.js when the header detects it has wrapped
   (e.g., Korean nav labels push the cluster wider than English at
   intermediate widths between the 840px breakpoint and natural fit).
   Mirrors the @media (max-width: 840px) rules so drawer mode kicks in
   based on actual layout, not just viewport width. */
.site-header.is-cramped {
  grid-template-columns: auto 1fr;
  gap: 12px;
}
.site-header.is-cramped .nav        { display: none; }
.site-header.is-cramped .nav-toggle { display: inline-flex; }
.site-header.is-cramped .header-right {
  gap: clamp(8px, 1.6vw, 18px);
}
/* The drawer + backdrop live as siblings of .site-header in <body>, so
   the ~ sibling combinator reveals them when the header is cramped. */
.site-header.is-cramped ~ .nav-drawer   { display: block; }
.site-header.is-cramped ~ .nav-backdrop { display: block; pointer-events: none; }
.site-header.is-cramped ~ .nav-backdrop.is-open { pointer-events: auto; }

/* ─── hero (two-column grid: title | photo) ───────────────────────────
   Real sibling columns so the title can never collide with the photo.
   `minmax(0, 1fr)` lets column 1 shrink properly (without it, CSS grid
   refuses to shrink a cell below its content's intrinsic min size).
   The intro paragraph spans both columns underneath. */
.hero {
  max-width: var(--container);
  margin: 0 auto;
  padding: clamp(40px, 6vw, 84px) var(--gutter) clamp(36px, 4vw, 56px);
  display: grid;
  grid-template-columns: minmax(0, 1fr) clamp(200px, 26vw, 420px);
  grid-template-rows: auto auto;
  column-gap: clamp(24px, 4vw, 60px);
  row-gap: clamp(28px, 3.6vw, 44px);
  /* Vertically center title block with photo so the two read as a balanced
     pair — otherwise the (shorter) title sits at the photo's top edge with
     empty space below it. Inert at narrow widths (single-column stack). */
  align-items: center;
}
.hero-title    { grid-column: 1; grid-row: 1; min-width: 0; max-width: 100%; }
.portrait-wrap { grid-column: 2; grid-row: 1; }
.hero-intro    { grid-column: 1 / -1; grid-row: 2; }

/* Portrait — background-image div, not <img>.
   - Right-click menu doesn't include "Save Image" because there's no <img>.
   - oncontextmenu="return false" on the wrapper blocks the menu entirely.
   - user-select / drag / webkit-touch-callout block drag-to-desktop and
     iOS press-and-hold "Save to Photos".
   - Deters casual saves; total prevention is impossible. */
.portrait {
  background-image: url("assets/portrait.jpg");
  background-size: cover;
  background-position: center;
  background-repeat: no-repeat;
  width: 100%;
  aspect-ratio: 4 / 5;
  border-radius: 10px;
  box-shadow: 0 24px 60px -28px rgba(40, 30, 10, .45);
  user-select: none;
  -webkit-user-select: none;
  -moz-user-select: none;
  -ms-user-select: none;
  -webkit-user-drag: none;
  -webkit-touch-callout: none;
}

h1.hero-title {
  font-family: var(--f-display);
  font-weight: 500;
  /* Sized for an academic headline rather than a designer display. About
     45% smaller than the original at laptop widths — still confident but
     no longer dominates the page. */
  font-size: clamp(34px, 6vw, 88px);
  line-height: 1.02;
  letter-spacing: clamp(-0.8px, -0.1vw, -1.6px);
  color: var(--ink);
  margin: 0;
  /* Wrap long words instead of letting them overflow the column. */
  overflow-wrap: break-word;
  word-break: normal;
}
.ital {
  font-family: var(--f-italic);
  font-style: italic;
  font-weight: 400;
}
.ital-accent { color: var(--accent); }
/* Period accent uses the primary Ebony — same color as the italic field
   name and section labels, so the headline's typographic punctuation
   reads as part of the same accent system, not a separate temperature. */
.dot-accent { color: var(--accent); }

.hero-intro {
  font-family: var(--f-display);
  font-size: clamp(17px, 1.6vw, 22px);
  line-height: 1.5;
  color: var(--body);
  margin: 0 auto;            /* center within the hero row instead of left-aligning */
  max-width: 720px;
  width: 100%;
}

/* ─── now strip ─── */
.now {
  max-width: var(--container);
  margin: 0 auto;
  padding: 28px var(--gutter);
  border-top: 1px solid var(--rule);
  border-bottom: 1px solid var(--rule);
  display: grid;
  grid-template-columns: 120px repeat(3, 1fr);
  gap: 36px;
  align-items: baseline;
}
.now-label {
  font-family: var(--f-sans);
  font-size: 14px;
  letter-spacing: 2.4px;
  text-transform: uppercase;
  color: var(--accent);
  font-weight: 600;
  display: flex;
  align-items: center;
  gap: 8px;
}
.now-dot {
  width: 8px;
  height: 8px;
  border-radius: 50%;
  background: var(--accent);
  /* Pulse animation removed: it read as design-toolish for an academic site. */
}
.now-key {
  font-family: var(--f-sans);
  font-size: 13px;
  letter-spacing: 1.5px;
  text-transform: uppercase;
  color: var(--faded);
  margin-bottom: 6px;
}
.now-val {
  font-family: var(--f-display);
  font-size: clamp(18px, 1.7vw, 22px);
  font-weight: 500;
  color: var(--ink);
  line-height: 1.2;
}

/* ─── ticker (full-bleed strip above footer) ─── */
.ticker {
  position: relative;
  width: 100vw;
  left: 50%;
  margin-left: -50vw;
  margin-right: -50vw;
  overflow: hidden;
  padding: 13px 0;
  border-top: 1px solid var(--rule);
  border-bottom: 1px solid var(--rule);
  background: color-mix(in srgb, var(--paper) 78%, var(--paper-deep));
}
.ticker-track {
  display: inline-flex;
  gap: 56px;
  white-space: nowrap;
  font-family: var(--f-sans);
  font-size: clamp(11.5px, 1vw, 12.5px);
  letter-spacing: 0.4px;
  padding-left: 56px;
  /* 60s is a fallback if JS doesn't run; chrome.js overrides animation-
     duration via inline style as a function of viewport width so the
     perceived speed feels consistent across phone and monitor. */
  animation: eo-marquee 60s linear infinite;
}
.ticker-track span.t { color: var(--ink); }
.ticker-track span.s { color: var(--accent); }

/* ─── section headers (shared) ─── */
.section-head {
  display: flex;
  align-items: baseline;
  gap: 20px;
  margin-bottom: 40px;
}
.section-rule { flex: 1; height: 1px; background: var(--rule); }
.section-label {
  font-family: var(--f-sans);
  font-size: 14px;
  letter-spacing: 2px;
  text-transform: uppercase;
  color: var(--accent);
  font-weight: 600;
}
.section-link {
  font-family: var(--f-sans);
  font-size: 12px;
  color: var(--faded);
  text-decoration: none;
}
.section-link:hover { color: var(--accent); }

/* ─── landing featured cards ─── */
.featured {
  max-width: var(--container);
  margin: 0 auto;
  padding: clamp(48px, 6vw, 84px) var(--gutter) clamp(48px, 5vw, 72px);
}
.cards {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: 24px;
}
.card {
  /* Cornsilk surface sits slightly lighter than the cream page, creating
     a subtle "elevation" without needing a heavy shadow. */
  background: var(--paper-card);
  border: 1px solid var(--rule);
  border-radius: 14px;
  padding: 26px 26px 22px;
  display: flex;
  flex-direction: column;
  text-decoration: none;
  color: inherit;
  min-height: 340px;
  transition: transform .35s cubic-bezier(.2,.7,.3,1), box-shadow .35s, border-color .25s;
}
.card:hover {
  transform: translateY(-4px);
  box-shadow: 0 28px 60px -28px rgba(40, 30, 10, .35);
  /* Warm Bronze on hover → temperature shift from the resting sage palette */
  border-color: var(--accent-warm);
}
.card:hover .arrow { transform: translate(4px, -4px); color: var(--accent-warm); }
.card-kicker {
  font-family: var(--f-sans);
  font-size: 13px;
  letter-spacing: 1.6px;
  text-transform: uppercase;
  color: var(--accent);
  font-weight: 600;
  margin-bottom: 22px;
}
.card-title {
  font-family: var(--f-display);
  font-weight: 500;
  font-size: clamp(24px, 2.5vw, 32px);
  letter-spacing: -0.6px;
  line-height: 1.08;
  margin: 0;
  color: var(--ink);
}
.card-sub {
  font-family: var(--f-italic);
  font-style: italic;
  font-size: 16px;
  color: var(--faded);
  margin: 8px 0 18px;
}
.card-blurb {
  font-family: var(--f-display);
  font-size: 15px;
  line-height: 1.55;
  color: var(--body);
  margin: 0;
}
.card-foot {
  margin-top: auto;
  padding-top: 22px;
  display: flex;
  justify-content: space-between;
  align-items: baseline;
  gap: 12px;
}
.card-meta {
  font-family: var(--f-mono);
  font-size: 11px;
  color: var(--faded);
}
.card-read {
  font-family: var(--f-sans);
  font-size: 12.5px;
  font-weight: 600;
  color: var(--ink);
}
.arrow {
  display: inline-block;
  transition: transform .35s, color .25s;
}

/* ─── about teaser ─── */
.about-teaser {
  background: var(--paper-deep);
  border-top: 1px solid var(--rule);
  padding: clamp(48px, 5vw, 72px) 0 clamp(56px, 6vw, 84px);
}
.about-teaser > .section-head,
.about-teaser > .about-grid {
  max-width: var(--container);
  margin-left: auto;
  margin-right: auto;
  padding-left: var(--gutter);
  padding-right: var(--gutter);
}
.about-grid {
  display: grid;
  grid-template-columns: 1fr 1.3fr;
  gap: clamp(28px, 4vw, 56px);
  align-items: start;
}
.about-h {
  font-family: var(--f-display);
  font-weight: 500;
  font-size: clamp(36px, 6vw, 64px);
  letter-spacing: -1.4px;
  line-height: 0.98;
  margin: 0;
  color: var(--ink);
}
.about-p {
  font-family: var(--f-display);
  font-size: clamp(17px, 1.6vw, 21px);
  line-height: 1.55;
  color: var(--body);
  margin: 0 auto;
  max-width: 720px;
  width: 100%;
}
.underline {
  background-image: linear-gradient(var(--accent), var(--accent));
  background-position: 0 100%;
  background-repeat: no-repeat;
  background-size: 0% 1.5px;
  transition: background-size .35s;
  padding-bottom: 1px;
  color: var(--ink);
  text-decoration: none;
  font-weight: 600;
}
.underline:hover { background-size: 100% 1.5px; }

/* ─── footer ─── */
.site-footer {
  border-top: 1px solid var(--rule);
  padding: clamp(36px, 4vw, 48px) var(--gutter) 32px;
  background: var(--paper);
}
.footer-fine {
  max-width: var(--container);
  margin: 0 auto;
  padding-top: 22px;
  border-top: 1px solid var(--rule-2);
  display: flex;
  justify-content: space-between;
  align-items: baseline;
  font-family: var(--f-sans);
  font-size: 12px;
  color: var(--faded);
  gap: 24px;
  flex-wrap: wrap;
}
.footer-fine a { color: var(--faded); text-decoration: none; }
.footer-fine a:hover { color: var(--accent); }
.footer-links { display: flex; gap: 18px; flex-wrap: wrap; }

/* ─── generic page wrapper for non-landing pages ─── */
.page {
  max-width: var(--container);
  margin: 0 auto;
  padding: clamp(48px, 6vw, 84px) var(--gutter) clamp(48px, 5vw, 72px);
}
.page-title {
  font-family: var(--f-display);
  font-weight: 500;
  font-size: clamp(40px, 9vw, 96px);
  letter-spacing: clamp(-1px, -0.15vw, -2px);
  line-height: 0.95;
  color: var(--ink);
  margin: 0 0 24px;
}
/* Per-tab page-title colors. Each subpage's <body> already carries
   data-active="<tab>" (read by chrome.js for nav highlighting), so we
   reuse that hook to assign a palette color per tab. The dot-accent (the
   colored period after every title) follows the same color so the
   punctuation reads as part of the title, not a separate accent. All four
   colors come from color theme.png — Ebony / Charcoal / Light Bronze /
   Deep Tea Green. Landing hero (index.html) is unchanged. */
body[data-active="about"]    .page-title,
body[data-active="about"]    .page-title .dot-accent  { color: var(--accent); }
body[data-active="research"] .page-title,
body[data-active="research"] .page-title .dot-accent  { color: var(--ink-soft); }
body[data-active="news"]     .page-title,
body[data-active="news"]     .page-title .dot-accent  { color: var(--accent-warm); }
body[data-active="contact"]  .page-title,
body[data-active="contact"]  .page-title .dot-accent,
body[data-active="contact"]  .page-title .ital-accent { color: var(--accent-deep); }
.page-lede {
  font-family: var(--f-display);
  font-size: clamp(17px, 1.6vw, 22px);
  line-height: 1.5;
  color: var(--body);
  margin: 0 auto 56px;
  max-width: 760px;
}

/* ─── research page: paper cards ─── */
.papers {
  display: flex;
  flex-direction: column;
  gap: 48px;
  margin-bottom: 96px;
}
.paper {
  display: grid;
  grid-template-columns: 1.05fr 1fr;
  gap: clamp(24px, 3vw, 40px);
  /* Center the media (fixed 16:9) against the text column so it doesn't
     leave an awkward gap below the video when the title/authors/takeaway
     column is taller. Inert in single-column mode (≤960px). */
  align-items: center;
  padding: clamp(16px, 2vw, 24px);
  background: var(--paper-card);
  border: 1px solid var(--rule);
  border-radius: 14px;
  transition: border-color .25s, box-shadow .35s;
}
.paper:hover { border-color: var(--accent-warm); box-shadow: 0 28px 60px -28px rgba(40, 30, 10, .35); }

.paper-media {
  position: relative;
  width: 100%;
  aspect-ratio: 16 / 9;
  border-radius: 8px;
  overflow: hidden;
  background: var(--paper-deep);
}
.paper-media > video,
.paper-media > img {
  display: block;
  width: 100%;
  height: 100%;
  object-fit: cover;
}
/* Per-paper override (set via tools/add_media.py `--fit contain`) for media
   whose native aspect ratio isn't 16:9 — letterboxes instead of cropping.
   The box's own background (--paper-deep) makes the letterbox bars read as
   intentional rather than a layout bug. */
.paper-media.fit-contain > video,
.paper-media.fit-contain > img {
  object-fit: contain;
}
.paper-media-placeholder {
  position: absolute; inset: 0;
  background-image: repeating-linear-gradient(135deg, rgba(0,0,0,0.05) 0 1px, rgba(0,0,0,0.02) 1px 12px);
  background-color: rgba(0,0,0,0.025);
  display: flex;
  align-items: flex-end;
  font-family: var(--f-mono);
  font-size: 11px;
  letter-spacing: 0.4px;
  color: rgba(20,20,20,0.45);
  padding: 10px 12px;
  text-transform: lowercase;
}
/* Copy deterrent overlay (see research.js mediaGuard). Sits above the
   media, below the venue badge; the © mark rides along in screenshots. */
.media-guard {
  position: absolute;
  inset: 0;
  z-index: 1;
  -webkit-user-select: none;
  user-select: none;
  -webkit-touch-callout: none;
}
.media-guard span {
  position: absolute;
  right: 12px;
  bottom: 10px;
  font-family: var(--f-mono);
  font-size: 10.5px;
  letter-spacing: 1px;
  color: color-mix(in srgb, var(--paper) 75%, transparent);
  text-shadow: 0 1px 2px rgba(22, 19, 17, 0.45);
  pointer-events: none;
}
.paper-venue {
  position: absolute;
  z-index: 2;
  top: 12px;
  left: 12px;
  font-family: var(--f-mono);
  font-size: 10.5px;
  letter-spacing: 1.4px;
  text-transform: uppercase;
  color: var(--paper);
  background: rgba(22, 19, 17, 0.78);
  padding: 5px 9px;
  border-radius: 4px;
  backdrop-filter: blur(4px);
}

.paper-body { padding-top: 4px; }
.paper-title {
  font-family: var(--f-display);
  font-weight: 500;
  font-size: clamp(22px, 2.8vw, 34px);
  letter-spacing: -0.6px;
  line-height: 1.1;
  color: var(--ink);
  margin: 0 0 14px;
}
.paper-authors {
  font-family: var(--f-sans);
  font-size: 14px;
  line-height: 1.55;
  color: var(--body);
  margin: 0 0 6px;
}
.paper-authors .me { font-weight: 700; color: var(--ink); }
.paper-author-note {
  font-family: var(--f-italic);
  font-style: italic;
  font-size: 12.5px;
  color: var(--faded);
  margin: 0 0 14px;
}
.paper-takeaway {
  font-family: var(--f-italic);
  font-style: italic;
  font-size: clamp(16px, 1.4vw, 18px);
  line-height: 1.45;
  color: var(--faded);
  margin: 0 0 22px;
}
.paper-links {
  display: flex; flex-wrap: wrap; gap: 8px;
}
.paper-link {
  font-family: var(--f-sans);
  font-size: 12px;
  font-weight: 500;
  letter-spacing: 0.6px;
  text-transform: uppercase;
  padding: 8px 14px;
  border-radius: 999px;
  background: transparent;
  color: var(--ink);
  border: 1px solid var(--rule);
  text-decoration: none;
  transition: transform .25s, background .25s, border-color .25s, color .25s;
  display: inline-flex;
  align-items: center;
  gap: 6px;
}
.paper-link:hover {
  transform: translateY(-2px);
  border-color: var(--accent-warm);
  color: var(--accent-warm);
}

/* ─── research page: CV block ─── */
.cv-block {
  padding-top: 32px;
}
/* Only holds the Safari-only download button now; collapses when empty. */
.cv-row {
  display: flex;
  align-items: center;
  justify-content: flex-end;
  gap: 20px;
  margin-bottom: 24px;
  flex-wrap: wrap;
}
.cv-row:has(#cv-button-slot:empty) { display: none; }
.cv-h {
  font-family: var(--f-display);
  font-weight: 500;
  font-size: clamp(32px, 4.5vw, 48px);
  letter-spacing: -1px;
  line-height: 1;
  margin: 0;          /* margin-bottom comes from .cv-row */
  color: var(--ink);
}
.cv-download {
  font-family: var(--f-sans);
  font-size: 13px;
  font-weight: 600;
  letter-spacing: 0.8px;
  text-transform: uppercase;
  padding: 13px 22px;
  border-radius: 999px;
  background: var(--accent);
  color: var(--paper);
  border: 1px solid var(--accent);
  text-decoration: none;
  display: inline-flex;
  align-items: center;
  gap: 8px;
  transition: transform .25s;
}
.cv-download:hover { transform: translateY(-2px); }
.cv-viewer {
  width: 100%;
  height: clamp(600px, 80vh, 1100px);
  border: 1px solid var(--rule);
  border-radius: 10px;
  background: var(--paper);
  overflow: hidden;
}
.cv-viewer object,
.cv-viewer iframe {
  width: 100%;
  height: 100%;
  border: 0;
  display: block;
}
.cv-fallback {
  display: flex;
  align-items: center;
  justify-content: center;
  height: 100%;
  padding: 32px;
  text-align: center;
  font-family: var(--f-display);
  font-size: 18px;
  color: var(--faded);
}
.cv-fallback a { color: var(--accent); }

/* ─── contact page: form ─── */
.contact-form {
  margin: 8px auto 64px;
  max-width: 760px;
  display: flex;
  flex-direction: column;
  gap: 18px;
}
.form-row {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 18px;
}
.form-field {
  display: flex;
  flex-direction: column;
  gap: 6px;
}
.form-label {
  font-family: var(--f-sans);
  font-size: 13px;
  letter-spacing: 1.6px;
  text-transform: uppercase;
  color: var(--faded);
  font-weight: 600;
}
.form-field input,
.form-field textarea {
  font-family: var(--f-display);
  font-size: 17px;
  line-height: 1.4;
  color: var(--ink);
  background: var(--paper);
  border: 1px solid var(--rule);
  border-radius: 8px;
  padding: 12px 14px;
  width: 100%;
  resize: vertical;
  transition: border-color .25s, box-shadow .25s;
}
.form-field input:focus,
.form-field textarea:focus {
  outline: none;
  border-color: var(--accent);
  box-shadow: 0 0 0 3px color-mix(in srgb, var(--accent) 18%, transparent);
}
.form-field textarea { min-height: 140px; }

.form-turnstile {
  margin-top: 4px;
  min-height: 65px; /* reserves space so the page doesn't jump when the widget loads */
}

.form-foot {
  display: flex;
  align-items: center;
  gap: 18px;
  flex-wrap: wrap;
  margin-top: 6px;
}
.form-submit {
  font-family: var(--f-sans);
  font-size: 13px;
  font-weight: 600;
  letter-spacing: 0.8px;
  text-transform: uppercase;
  padding: 13px 22px;
  border-radius: 999px;
  background: var(--accent);
  color: var(--paper);
  border: 1px solid var(--accent);
  cursor: pointer;
  display: inline-flex;
  align-items: center;
  gap: 8px;
  transition: transform .25s, opacity .25s;
}
.form-submit:hover { transform: translateY(-2px); }
.form-submit:disabled { opacity: 0.55; cursor: progress; transform: none; }

.form-status {
  font-family: var(--f-sans);
  font-size: 13px;
  color: var(--faded);
  flex: 1;
  min-height: 1.2em;
}
.form-status.is-sending { color: var(--faded); font-style: italic; }
.form-status.is-ok      { color: var(--accent); font-weight: 600; }
.form-status.is-error   { color: #b03a3a; font-weight: 500; }

/* Tighten on phones */
@media (max-width: 640px) {
  .form-row { grid-template-columns: 1fr; }
}

.contact-direct { margin-top: 24px; }

/* ─── contact page rows ─── */
.contact-list {
  display: flex;
  flex-direction: column;
  margin-top: 24px;
}
.contact-row {
  display: flex; justify-content: space-between; align-items: baseline;
  gap: 16px;
  padding: 18px 0;
  border-bottom: 1px solid var(--rule);
  text-decoration: none;
  color: var(--ink);
  font-family: var(--f-sans);
  transition: color .25s;
}
.contact-row:hover { color: var(--accent); }
.contact-row .k {
  font-size: 13px; letter-spacing: 1.6px; text-transform: uppercase;
  color: var(--faded); font-weight: 600;
  white-space: nowrap;
}
.contact-row.primary .k { color: var(--accent); }
.contact-row .v {
  font-family: var(--f-display);
  font-size: clamp(17px, 1.8vw, 22px);
  font-weight: 500;
  text-align: right;
  word-break: break-word;
}

/* ─── news page: dated entries ─── */
.news-list {
  display: flex;
  flex-direction: column;
}
.news-row {
  display: grid;
  grid-template-columns: 120px 1fr;
  gap: 28px;
  align-items: baseline;
  padding: 22px 0;
  border-bottom: 1px solid var(--rule);
}
.news-row:first-child { padding-top: 0; }
.news-row:last-child  { border-bottom: 0; }
.news-date {
  font-family: var(--f-mono);
  font-size: 12px;
  letter-spacing: 1.4px;
  color: var(--faded);
  white-space: nowrap;
}
.news-body {
  font-family: var(--f-display);
  font-size: clamp(17px, 1.5vw, 20px);
  line-height: 1.5;
  color: var(--ink);
}
.news-body strong { font-weight: 500; }
.news-body em     { font-family: var(--f-italic); font-style: italic; font-weight: 400; }
.news-body a      { color: var(--ink); }

/* ─── about page blocks ─── */
.about-body {
  display: flex;
  flex-direction: column;
  gap: 48px;
}
/* Each block (label + prose) caps as a single 760px reading column and
   centers within the page so children stay vertically aligned to each
   other instead of drifting left while the page itself is centered. */
.about-block {
  max-width: 760px;
  width: 100%;
  margin-inline: auto;
}
.about-block h3 {
  font-family: var(--f-sans);
  font-size: 14px;
  letter-spacing: 1.8px;
  text-transform: uppercase;
  color: var(--accent);
  font-weight: 600;
  margin: 0 0 16px;
}
.about-block p {
  font-family: var(--f-display);
  font-size: clamp(17px, 1.5vw, 19px);
  line-height: 1.6;
  color: var(--body);
  margin: 0 0 14px;
}
.about-block p:last-child { margin-bottom: 0; }
.about-block em {
  font-family: var(--f-italic);
  font-style: italic;
  font-weight: 400;
}
.about-block ul {
  font-family: var(--f-display);
  font-size: clamp(16px, 1.4vw, 18px);
  line-height: 1.55;
  color: var(--body);
  margin: 0;
  padding-left: 20px;
}
.about-block ul li { margin-bottom: 6px; }
.about-block ul li::marker { color: var(--accent); }

.education {
  margin: 0;
  display: grid;
  grid-template-columns: auto 1fr;
  gap: 8px 28px;
}
.education dt {
  font-family: var(--f-display);
  font-size: clamp(17px, 1.5vw, 20px);
  font-weight: 500;
  color: var(--ink);
  align-self: baseline;
}
.education dd {
  font-family: var(--f-display);
  font-size: clamp(15px, 1.3vw, 17px);
  line-height: 1.5;
  color: var(--body);
  margin: 0 0 12px;
}
.about-note {
  font-family: var(--f-italic);
  font-style: italic;
  font-size: 14px !important;
  color: var(--faded) !important;
  margin-top: 16px !important;
}

/* (.education phone-tier rules moved up into the 720px compact-mode block
   so the collapse and centering happen together.) */

/* ─── (legacy) writing placeholder block — kept for any future reuse ─── */
.todo-block {
  padding: 28px;
  border: 1px dashed var(--rule);
  border-radius: 10px;
  background: var(--paper-card);
  margin-bottom: 24px;
}
.todo-block h3 {
  font-family: var(--f-sans);
  font-size: 11px; letter-spacing: 1.6px; text-transform: uppercase;
  color: var(--accent); font-weight: 600;
  margin: 0 0 10px;
}
.todo-block p {
  font-family: var(--f-display);
  font-size: 17px; line-height: 1.55; color: var(--body);
  margin: 0;
}

/* ─── animations ─── */
@keyframes eo-marquee { from { transform: translateX(0); }  to { transform: translateX(-50%); } }
@keyframes eo-fade-up { from { opacity: 0; transform: translateY(14px); } to { opacity: 1; transform: translateY(0); } }

.enter      { animation: eo-fade-up .9s cubic-bezier(.2,.7,.3,1) both; }
.enter-d1   { animation-delay: .15s; }
.enter-d2   { animation-delay: .30s; }
.enter-d3   { animation-delay: .45s; }

/* ─── responsive ────────────────────────────────────────────────────────
   Reflow happens at three tiers (only where layout shape changes).
   In between, the fluid clamp() rules above handle the scaling. */

/* Tablet & narrow laptop — cards / now strip / about-grid reflow.
   (Hero stacking is deferred to its own narrower breakpoint below so the
   title + portrait stay side-by-side as long as the title still fits.) */
@media (max-width: 960px) {
  .cards { grid-template-columns: 1fr 1fr; }
  .paper { grid-template-columns: 1fr; }
  .now { grid-template-columns: 100px repeat(3, 1fr); gap: 24px; }
  .about-grid { grid-template-columns: 1fr; }
}

/* Narrow tablet & phones — "compact mode". Hero stacks (portrait above
   title), and every major block flips from left-aligned editorial layout
   to a centered phone-app vertical stack — portrait centers, headings +
   prose center text and block, news/contact rows collapse. Lists keep
   left-aligned text (centered bullets look broken). Above 720px the
   wide editorial layout from iterations 1 & 2 stays intact. */
@media (max-width: 720px) {
  /* hero: stack and center */
  .hero {
    grid-template-columns: 1fr;
    grid-template-rows: auto auto auto;
    row-gap: 24px;
  }
  .portrait-wrap {
    grid-column: 1; grid-row: 1;
    width: min(260px, 56vw);
    max-width: 100%;
    justify-self: center;     /* grid centering keeps the explicit width
                                  intact — margin: 0 auto would collapse a
                                  width-less grid item to 0 here. */
  }
  .hero-title    { grid-column: 1; grid-row: 2; text-align: center; }
  .hero-intro    { grid-column: 1; grid-row: 3; text-align: center; }

  /* subpages: titles + lede + about prose center */
  .page-title { text-align: center; }
  .page-lede  { text-align: center; }
  .about-block { text-align: center; }
  /* Lists: keep text left-aligned inside the bullet column, but center the
     whole list block as an inline-block so it sits in the middle of the
     centered .about-block. */
  .about-block ul {
    text-align: left;
    display: inline-block;
    margin-left: auto;
    margin-right: auto;
  }
  .education {
    grid-template-columns: 1fr;
    text-align: center;
    gap: 4px;
  }
  .education dd { margin-bottom: 16px; }

  /* news entries: stack date + body and center both */
  .news-row {
    grid-template-columns: 1fr;
    gap: 6px;
    text-align: center;
  }

  /* contact rows: label above url, both centered */
  .contact-row {
    flex-direction: column;
    align-items: center;
    gap: 4px;
    text-align: center;
  }
  .contact-row .v { text-align: center; }

  /* now strip: center the label + each key/val pair */
  .now-label { justify-content: center; }
  .now-key, .now-val { text-align: center; }

  /* section heads + about teaser heading: center the label between rules,
     center the stacked "Materials. Robots. Hydrogels." heading */
  .section-head { justify-content: center; }
  .about-h { text-align: center; }

  /* featured cards: center copy + footer when stacked single column */
  .card { text-align: center; }
  .card-foot { justify-content: center; }

  /* footer fine print: centered chrome */
  .footer-fine { justify-content: center; text-align: center; }
  .footer-links { justify-content: center; }
}

/* Header drawer mode — at this width the top nav row stops fitting cleanly
   alongside the social icons + EN/KR toggle, so hide it and reveal the
   hamburger that pulls a right-side drawer. Wide-screen markup is
   untouched; the drawer + backdrop sit above everything via fixed
   positioning.
   Also collapses the header grid from `1fr auto 1fr` to `auto 1fr` so the
   right-side cluster (icons + EN/KR + hamburger) sits flush against the
   right edge — no empty middle column creating a visual gap. */
@media (max-width: 840px) {
  .site-header {
    grid-template-columns: auto 1fr;
    gap: 12px;
  }
  .site-header .nav    { display: none; }
  .site-header .nav-toggle { display: inline-flex; }
  .header-right {
    gap: clamp(8px, 1.6vw, 18px);
  }
  .nav-drawer    { display: block; }
  .nav-backdrop  { display: block; pointer-events: none; }
  .nav-backdrop.is-open { pointer-events: auto; }
}

/* Phone — single-column cards, NOW strip collapses to 2 cols. Header
   already reflowed at 840px (auto/1fr grid) and the compact-mode 720px
   block centered the prose; this tier just tightens spacing for phones. */
@media (max-width: 640px) {
  .site-header {
    padding-left: 20px;
    padding-right: 20px;
  }

  .cards { grid-template-columns: 1fr; }
  .now { grid-template-columns: 1fr 1fr; gap: 20px; padding-top: 22px; padding-bottom: 22px; }
  .now-label { grid-column: 1 / -1; margin-bottom: 4px; }

  .footer-fine { gap: 14px; }
}

/* Small phone — tighter still. At ≤420px (down to 320px iPhone SE) the
   viewport is too narrow for default gutter + header gaps, so squeeze
   everything in without horizontal scroll. */
@media (max-width: 420px) {
  :root {
    --gutter: 18px;
  }
  .site-header {
    padding-left: 14px;
    padding-right: 14px;
  }
  /* Header sheds social icons at this width — they re-appear inside the
     drawer (see .drawer-social-icons rule below). Without this, the 4
     icons + EN/KR + hamburger overflow on iPhone SE (320px) and wrap. */
  .site-header .social-icons { display: none; }
  .lang-toggle     { font-size: 11px; }
  .lang-toggle button { padding: 4px 4px; }
  .nav-toggle      { width: 32px; height: 32px; }
  .nav-toggle svg  { width: 20px; height: 20px; }

  /* Drawer picks up the social icons — laid out as a horizontal row
     below the stacked nav tabs, separated by a thin divider. */
  .drawer-social-icons {
    display: flex;
    gap: 16px;
    justify-content: flex-start;
    margin-top: 28px;
    padding-top: 22px;
    border-top: 1px solid var(--rule-2);
  }
  .drawer-social-icons a {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 40px; height: 40px;
    color: var(--ink);
    text-decoration: none;
    transition: color .25s, transform .25s;
  }
  .drawer-social-icons a:hover { color: var(--accent); transform: translateY(-1px); }
  .drawer-social-icons svg { width: 22px; height: 22px; display: block; }

  .hero {
    padding-top: 32px;
    padding-bottom: 28px;
  }
  h1.hero-title {
    font-size: clamp(28px, 8vw, 40px);
  }
  .portrait-wrap { width: min(220px, 62vw); }

  .now             { grid-template-columns: 1fr; }
  .nav-drawer      { width: min(300px, 92vw); }
  .card            { padding: 22px 20px 18px; min-height: 0; }
}

/* Landscape phones (rotate to short height) — flatten hero so it fits */
@media (orientation: landscape) and (max-height: 480px) {
  .hero { padding-top: 24px; padding-bottom: 24px; row-gap: 16px; }
  h1.hero-title { font-size: clamp(36px, 6.5vw, 64px); }
  .portrait-wrap { max-width: 140px; }
}

/* Touch devices — bigger tap targets so fingers can hit links */
@media (hover: none) and (pointer: coarse) {
  .paper-link    { min-height: 40px; padding: 10px 14px; }
  .nav a         { padding: 10px 4px; }
  .contact-row   { padding: 22px 0; }
  .social-icons a { width: 44px; height: 44px; }
  .drawer-social-icons a { width: 44px; height: 44px; }
  .nav-toggle    { width: 44px; height: 44px; }
  .nav-drawer-close { width: 44px; height: 44px; top: 14px; right: 14px; }
  .nav-stacked a { padding: 14px 0; }
  /* Hover lifts feel awkward on touch — undo them. */
  .card:hover, .paper-link:hover, .social-icons a:hover,
  .drawer-social-icons a:hover,
  .nav-toggle:hover, .nav-drawer-close:hover { transform: none; }
}

/* Reduced motion — pause the marquee and entry animations, and make the
   drawer open/close instantly instead of sliding. */
@media (prefers-reduced-motion: reduce) {
  .ticker-track, .enter { animation: none !important; }
  .nav-drawer, .nav-backdrop, .nav-toggle, .nav-drawer-close { transition: none !important; }
}
