/* Subset Iosevka, recruiter-only: the bare login shell declares this family
   pointing at ~11MB Nerd-Font TTFs (thousands of icon glyphs). This page renders
   only Latin + a handful of symbols at weights 500/700, so we re-declare the
   SAME family/weights here pointing at Latin-subset woff2 (~60KB each, built by
   api/scripts/subset_cv_fonts.py). recruiter.css loads after the shell's inline
   @font-face, so for these two descriptors the later definition wins and the big
   TTFs are never fetched — every inherited reference resolves to the small font.
   No other weight/italic is used on the page, so none of those faces download. */
@font-face {
  font-family: 'Iosevka Mayukai Monolite';
  src: url('/fonts/iosevka-cv-500.woff2?v=1') format('woff2');
  font-weight: 500;
  font-style: normal;
  font-display: swap;
}

@font-face {
  font-family: 'Iosevka Mayukai Monolite';
  src: url('/fonts/iosevka-cv-700.woff2?v=1') format('woff2');
  font-weight: 700;
  font-style: normal;
  font-display: swap;
}

/* /recruiter — light-mode résumé for recruiters.
   Brand identity preserved by HUE, not by token: the chrome.css palette is
   tuned for a black page (neon green 135/100/50, cyan 188/100/50) and is
   unreadable on a light background — so this page derives deepened, legible
   shades of the SAME brand hues (135 green, 188 cyan) and keeps everything
   else neutral. The light background is the one deliberate departure the
   palette doesn't cover (requested). No other hues introduced. */
:root {
  --cv-bg:        hsl(140deg 14% 95%);   /* page: soft off-white, faint green cast */
  --cv-surface:   #fff;            /* the résumé card */
  --cv-ink:       hsl(150deg 14% 15%);   /* body text: green-tinted near-black */
  --cv-ink-soft:  hsl(150deg 8% 42%);    /* dates / secondary meta */
  --cv-green:     hsl(135deg 60% 26%);   /* brand green 135, deepened for light */
  --cv-green-dim: hsl(135deg 40% 44%);   /* lighter green — bullets, hairline ticks */
  --cv-cyan:      hsl(188deg 82% 29%);   /* brand cyan 188, deepened → teal link */
  --cv-cyan-dk:   hsl(188deg 82% 23%);   /* CTA hover */
  --cv-pink:      hsl(340deg 71% 50%);   /* [Lizzie's] pink 340, deepened for light */
  --cv-pink-dk:   hsl(340deg 71% 42%);   /* pink CTA hover */
  --cv-rule:      hsl(140deg 18% 88%);   /* hairline dividers */
  --cv-chip:      hsl(135deg 42% 94%);   /* skill chip fill */
}

/* dark mode: flip the page-local tokens to a green-on-black terminal, the same
   brand hues the rest of the site runs on. The CRT scanline overlay
   (.cyber-bg/.cyber-scan from chrome.css) is added by recruiter.js when dark,
   matching the tarot page. Toggled via html.cv-dark. */
html.cv-dark {
  --cv-bg:        hsl(135deg 18% 5%);    /* near-black, faint green cast */
  --cv-surface:   hsl(135deg 22% 7%);    /* the résumé card */
  --cv-ink:       hsl(135deg 72% 72%);   /* body text: legible terminal green */
  --cv-ink-soft:  hsl(135deg 24% 52%);   /* dates / secondary meta */
  --cv-green:     hsl(135deg 95% 62%);   /* brand green, brightened for black */
  --cv-green-dim: hsl(135deg 70% 52%);   /* bullets, hairline ticks */
  --cv-cyan:      hsl(188deg 90% 62%);   /* brand cyan link */
  --cv-cyan-dk:   hsl(188deg 90% 72%);   /* CTA hover */
  --cv-pink:      hsl(340deg 85% 68%);   /* [Lizzie's] pink, brightened for black */
  --cv-pink-dk:   hsl(340deg 85% 78%);   /* pink CTA hover */
  --cv-rule:      hsl(135deg 30% 16%);   /* hairline dividers */
  --cv-chip:      hsl(135deg 40% 11%);   /* skill chip fill */
}

html.cv-dark .cv {
  box-shadow: 0 0 0 1px hsl(135deg 100% 50% / 0.06), 0 0 50px hsl(135deg 100% 50% / 0.05);
  text-shadow: 0 0 2px hsl(135deg 100% 50% / 0.12);
}

/* unwind the bare login-shell's black, centered, overflow-hidden body and
   chrome.css's black html background */
html { background: var(--cv-bg); scrollbar-width: none; }
html::-webkit-scrollbar { display: none; }

/* overflow:visible (NOT overflow-y:auto) is deliberate: this is a plain
   document-scroll page. With overflow-y:auto, iOS Safari makes BODY the
   scroll container and uses body.scrollHeight as the max — which EXCLUDES the
   last child's collapsed bottom margin, so the trailing CTA gets clipped and
   you can't scroll the final ~100px. overflow:visible keeps the documentElement
   (html) as the scroller, whose scrollHeight includes that margin. The tail gap
   is also moved into body padding-bottom (below) so it's inside the scroll box
   either way. */
body {
  display: block;
  height: auto;
  min-height: 100vh;
  overflow: visible;
  padding-bottom: var(--space-10);
  background: var(--cv-bg);
  scrollbar-width: none;
}
body::-webkit-scrollbar { display: none; }

.cv {
  width: min(780px, 92vw);
  margin: var(--space-10) auto var(--space-8);
  position: relative;
  z-index: var(--z-raised);
  background: var(--cv-surface);
  border: 1px solid var(--cv-rule);
  border-radius: var(--radius-4);
  box-shadow: 0 1px 2px hsl(150deg 14% 15% / 0.05), 0 10px 34px hsl(150deg 14% 15% / 0.07);
  padding: 52px clamp(28px, 6vw, 60px) 44px;
  color: var(--cv-ink);
  font-family: var(--font-mono);
  font-weight: var(--fw-normal);
  font-size: var(--fs-xl);
  line-height: var(--lh-relaxed);
}

/* dark-mode toggle, pinned top-right of the card; tokens make it adapt to both
   themes */
.cv-theme {
  position: absolute;
  top: 16px;
  right: 16px;
  display: inline-flex;
  align-items: center;
  background: transparent;
  border: 1px solid var(--cv-rule);
  color: var(--cv-ink-soft);
  font-family: inherit;
  font-size: var(--fs-sm);
  line-height: var(--lh-none);
  padding: var(--space-1-5) var(--space-3);
  border-radius: var(--radius-2);
  cursor: pointer;
  transition: color 0.18s, border-color 0.18s;
  z-index: 3;
}
.cv-theme:hover { color: var(--cv-green); border-color: var(--cv-green-dim); }

/* the moon/sun glyph itself, 3x the frame symbols around it */
.cv-theme .cv-glyph { font-size: 3em; line-height: var(--lh-none); }

.cv a { color: var(--cv-cyan); text-decoration: none; }
.cv a:hover { text-decoration: underline; }
.cv strong { color: var(--cv-green); font-weight: var(--fw-bold); }

/* ── header ───────────────────────────────────────────────────────────── */
.cv-head {
  padding-bottom: 0;
}

.cv h1 {
  font-size: var(--fs-3xl);
  font-weight: var(--fw-bold);
  letter-spacing: var(--tracking-tight);
  color: var(--cv-ink);
  margin-bottom: var(--space-1-5);
}

@keyframes cv-blink { 0%, 50% { opacity: 1; } 50.01%, 100% { opacity: 0; } }

/* whimsy, by design not words: the résumé types itself out on load (recruiter.js)
   behind this terminal caret, which travels with the text and then rests after
   the name — a quiet nod to the monospace/terminal brand the rest of the site
   runs on. A real element, not ::after, so it can move. */
.cv-caret {
  display: inline-block;
  width: 0.5em;
  height: 1.05em;
  margin-left: 0.04em;
  vertical-align: -0.16em;
  background: var(--cv-green);
  animation: cv-blink 1.15s steps(1) infinite;
}

@media (prefers-reduced-motion: reduce) {
  .cv-caret { animation: none; opacity: 0.55; }
}

.cv-title {
  color: var(--cv-green);
  font-size: var(--fs-xl);
  font-weight: var(--fw-bold);
  letter-spacing: var(--tracking-wide);
  margin-bottom: var(--space-3);
}

.cv-contact {
  list-style: none;
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-2) var(--space-6);
  font-size: var(--fs-sm);
  color: var(--cv-ink-soft);
  margin-bottom: var(--space-4);
}

/* download CTA, now in its own .cv-download block below the card (centered).
   Bottom gap lives in body padding-bottom (see above), not a trailing margin
   here — a collapsed trailing margin gets clipped from iOS's body scroll. */
.cv-download {
  width: min(780px, 92vw);
  margin: 0 auto;
  display: flex;
  justify-content: center;
  flex-wrap: wrap;
  gap: var(--space-5);
}

.cv-resume {
  display: inline-block;
  background: var(--cv-cyan);
  color: var(--cv-bg);
  padding: var(--space-3) var(--space-8);
  border-radius: var(--radius-3);
  font-size: var(--fs-sm);
  font-weight: var(--fw-bold);
  letter-spacing: var(--tracking-wide);
  text-transform: uppercase;
  text-decoration: none;
  transition: background 0.18s;
}

.cv-resume:hover {
  background: var(--cv-cyan-dk);
  text-decoration: none;
}

/* wai-lau.net CTA — same pill as the download button, in Lizzie's pink */
.cv-site { background: var(--cv-pink); }
.cv-site:hover { background: var(--cv-pink-dk); }

/* ── summary ──────────────────────────────────────────────────────────── */
.cv-summary {
  margin: var(--space-4) 0 var(--space-1-5);
  color: var(--cv-ink);
  font-size: var(--fs-xl);
  line-height: var(--lh-loose);
}

/* during the type-out the real blurb is hidden but stays in flow, reserving the
   final height so nothing below it bounces; recruiter.js paints the animated
   copy in an absolute overlay on top */
.cv-summary.cv-typing { visibility: hidden; }

.cv-type-overlay {
  position: absolute;
  inset: 0;
  visibility: visible;
}

/* ⏩ skip (inline before the blurb) and ⟳ replay (inline after it): matching
   little button controls, injected by recruiter.js during/after the type-out */
.cv-skip,
.cv-loop {
  display: inline-block;
  background: none;
  border: 1px solid var(--cv-rule);
  border-radius: var(--radius-2);
  padding: var(--space-px) var(--space-2);
  font-family: inherit;
  font-size: var(--fs-sm);
  line-height: var(--lh-snug);
  color: var(--cv-ink-soft);
  opacity: 0.75;
  cursor: pointer;
  vertical-align: middle;
  transition: opacity 0.15s, color 0.15s, border-color 0.15s;
}
.cv-skip { margin: 0 0.5em 0 0; }
.cv-loop { margin: 0 0 0 0.5em; }

.cv-skip:hover,
.cv-loop:hover { opacity: 1; color: var(--cv-green); border-color: var(--cv-green-dim); }

/* ── section headers ──────────────────────────────────────────────────── */
.cv h2 {
  font-size: var(--fs-sm);
  font-weight: var(--fw-bold);
  text-transform: uppercase;
  letter-spacing: var(--tracking-mega);
  color: var(--cv-green);
  margin: var(--space-10) 0 var(--space-7);
  padding-bottom: var(--space-2-5);
  border-bottom: 1px solid var(--cv-rule);
}

/* ── experience ───────────────────────────────────────────────────────── */
.cv-job { margin-bottom: var(--space-10); }

.cv-job-head {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  gap: var(--space-1-5) var(--space-4);
  margin-bottom: var(--space-1-5);
}
.cv-co { color: var(--cv-ink); font-weight: var(--fw-bold); font-size: var(--fs-xl); }
.cv-role { color: var(--cv-green); font-size: var(--fs-xl); font-weight: var(--fw-bold); }

.cv-dates {
  margin-left: auto;
  color: var(--cv-ink-soft);
  font-size: var(--fs-sm);
  white-space: nowrap;
}

.cv-group {
  color: var(--cv-green-dim);
  font-size: var(--fs-sm);
  text-transform: uppercase;
  letter-spacing: var(--tracking-caps);
  margin: var(--space-6) 0 var(--space-2-5);
}
.cv ul { list-style: none; }

.cv-job li {
  position: relative;
  padding-left: var(--space-7);
  margin-bottom: var(--space-2-5);
  color: var(--cv-ink);
  font-size: var(--fs-sm);
  line-height: var(--lh-relaxed);
}

.cv-job li::before {
  content: "▸";
  position: absolute;
  left: 0;
  color: var(--cv-green-dim);
}

/* ── skills (chips) ───────────────────────────────────────────────────── */
.cv-skills { display: flex; flex-direction: column; gap: var(--space-5); }

.cv-skillrow { display: grid; grid-template-columns: 130px 1fr; gap: var(--space-5); align-items: start; }

.cv-skillrow dt {
  color: var(--cv-green);
  font-size: var(--fs-sm);
  text-transform: uppercase;
  letter-spacing: var(--tracking-x);
  padding-top: var(--space-2);
}
.cv-skillrow dd { display: flex; flex-wrap: wrap; gap: var(--space-2); }

.cv-chip {
  background: var(--cv-chip);
  color: var(--cv-green);
  font-size: var(--fs-sm);
  padding: var(--space-1-5) var(--space-3);
  border-radius: var(--radius-2);
  white-space: nowrap;
}

/* ── footer ───────────────────────────────────────────────────────────── */

@media (width <= 560px) {
  .cv h1 { font-size: var(--fs-3xl); }
  .cv-dates { margin-left: 0; width: 100%; }
  .cv-skillrow { grid-template-columns: 1fr; gap: var(--space-2); }
  .cv-skillrow dt { padding-top: 0; }
}
