/* ============================================================================
   COMPONENTS  —  the reusable building blocks. Each is a self-contained UI piece:
     .hero            -> big hero headline blocks
     .accordion       -> stacked expandable rows
     .flip-grid       -> grid of cards that flip on hover
     .tile-grid       -> multi-container tile layout
     .reveal-list     -> click-to-expand list (multi-open)
     .card / .chip / .gallery / .scroller -> supporting pieces
   Variables only — no hardcoded colors or fonts.
   ============================================================================ */

/* ------------------------------------------------------------------- HERO -- */
.hero {
  position: relative;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  text-align: center;
  min-height: clamp(440px, 78vh, 760px);
  padding-block: var(--space-2xl);
  gap: var(--space-md);
}
.hero__eyebrow { color: var(--eyebrow-color); width: 100%; text-align: center; }
.hero__title { font-size: var(--fs-hero); max-width: 16ch; margin-inline: auto; text-align: center; }
.hero__subtitle {
  font-size: var(--fs-lead);
  color: var(--color-text-soft);
  max-width: 52ch;
  margin-inline: auto;
  text-align: center;
  text-wrap: balance;
}
.hero .chip-row { width: 100%; justify-content: center; margin-top: var(--space-md); }
.hero__actions { display: flex; flex-wrap: wrap; gap: var(--space-sm); justify-content: center; align-items: center; margin-top: var(--space-sm); }
/* Last row below the two CTAs: icon-only ghost buttons (same --btn-ghost colour). */
.hero__socials {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-xs);
  justify-content: center;
  align-items: center;
  width: 100%;
  margin-top: var(--space-sm);
}
.hero__socials .btn--icon {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 2.65rem;
  height: 2.65rem;
  padding: 0;
  border-radius: var(--radius-pill);
}
.hero__socials .btn--icon svg {
  width: 1.15rem;
  height: 1.15rem;
  display: block;
  position: relative;
  z-index: 2;
}
/* Hero socials — dark: ghost buttons + brand icon colors; light: brand fills + white/black icons. */
[data-theme="dark"] .hero__socials .hero-social--x svg path,
[data-theme="dark"] .hero__socials .hero-social--github svg path { fill: #ffffff; }
[data-theme="dark"] .hero__socials .hero-social--discord svg path { fill: #5865f2; }
[data-theme="dark"] .hero__socials .hero-social--huggingface svg path { fill: #ffd21e; }
[data-theme="dark"] .hero__socials .hero-social--email svg path { fill: #ea4335; }
[data-theme="dark"] .hero__socials .hero-social--linkedin svg path { fill: #0a66c2; }
[data-theme="dark"] .hero__socials .hero-social--scholar svg path { fill: #38bdf8; }
[data-theme="dark"] .hero__socials .hero-social--instagram svg path { fill: #e1306c; }

[data-theme="light"] .hero__socials .btn--icon {
  border: none;
  background-color: var(--hero-social-bg, #1d1d1f);
}
[data-theme="light"] .hero__socials .btn--icon::before {
  background: conic-gradient(
    from var(--btn-angle),
    transparent 0deg, transparent 60deg,
    rgba(255, 255, 255, 0.88) 90deg,
    transparent 120deg, transparent 360deg);
}
[data-theme="light"] .hero__socials .hero-social--x { --hero-social-bg: #000000; }
[data-theme="light"] .hero__socials .hero-social--x svg path { fill: #ffffff; }
[data-theme="light"] .hero__socials .hero-social--discord { --hero-social-bg: #5865f2; }
[data-theme="light"] .hero__socials .hero-social--discord svg path { fill: #ffffff; }
[data-theme="light"] .hero__socials .hero-social--email { --hero-social-bg: #ea4335; }
[data-theme="light"] .hero__socials .hero-social--email svg path { fill: #ffffff; }
[data-theme="light"] .hero__socials .hero-social--github { --hero-social-bg: #24292f; }
[data-theme="light"] .hero__socials .hero-social--github svg path { fill: #ffffff; }
[data-theme="light"] .hero__socials .hero-social--huggingface { --hero-social-bg: #ffd21e; }
[data-theme="light"] .hero__socials .hero-social--huggingface svg path { fill: #1d1d1f; }
[data-theme="light"] .hero__socials .hero-social--linkedin { --hero-social-bg: #0a66c2; }
[data-theme="light"] .hero__socials .hero-social--linkedin svg path { fill: #ffffff; }
[data-theme="light"] .hero__socials .hero-social--scholar { --hero-social-bg: #38bdf8; }
[data-theme="light"] .hero__socials .hero-social--scholar svg path { fill: #ffffff; }
[data-theme="light"] .hero__socials .hero-social--instagram {
  --hero-social-bg: transparent;
  background: linear-gradient(135deg, #f58529 0%, #dd2a7b 52%, #8134af 100%);
}
[data-theme="light"] .hero__socials .hero-social--instagram svg path { fill: #ffffff; }
[data-theme="light"] .hero__socials .btn--icon:hover { filter: brightness(0.92); }
.hero__media { margin-top: var(--space-lg); width: 100%; max-width: 720px; }
.hero__media img { width: 100%; border-radius: var(--radius-lg); box-shadow: var(--shadow-lg); }

/* Generic media frame (placeholder images / gifs slot here).
   ON-HOVER SWAP: add a second <img class="media-frame__hover"> inside and it
   fades in when the parent card/tile/flip-card (or the frame) is hovered.
   No hardcoding — just set the two `src`s (the hover one can be a .gif). */
.media-frame {
  position: relative;
  width: 100%;
  border: var(--thumb-edge-width) solid var(--thumb-edge-color);
  border-radius: var(--radius-md);   /* fixed px = rounded rectangle, never a circle on phones */
  overflow: hidden;
  background-color: var(--color-surface-2);
  aspect-ratio: 16 / 10;
  transition: var(--theme-transition);
}
.media-frame img {
  width: 100%; height: 100%; object-fit: cover;
  transition: transform var(--speed-slow) var(--ease-out),
              opacity var(--speed-normal) var(--ease-out);
}
.media-frame__hover {
  position: absolute; inset: 0;
  opacity: 0;
}
/* Reveal the hover image when the surrounding card/tile/face — or the frame
   itself on touch-less hover — is hovered. */
.card:hover .media-frame__hover,
.tile:hover .media-frame__hover,
.flip-card__face:hover .media-frame__hover,
.reveal:hover .media-frame__hover,
.media-frame:hover .media-frame__hover { opacity: 1; }
/* Subtle zoom of the base image on hover. */
.card:hover .media-frame > img:first-of-type,
.tile:hover .media-frame > img:first-of-type,
.flip-card__face:hover .media-frame > img:first-of-type,
.media-frame:hover > img:first-of-type { transform: scale(1.06); }
/* For theme-variant frames the visible variant isn't always first-of-type,
   so zoom whichever one is currently shown. */
.card:hover .media-frame > .asset-light,
.card:hover .media-frame > .asset-dark,
.pub-card:hover .media-frame > .asset-light,
.pub-card:hover .media-frame > .asset-dark,
.flip-card__face:hover .media-frame > .asset-light,
.flip-card__face:hover .media-frame > .asset-dark,
.media-frame:hover > .asset-light,
.media-frame:hover > .asset-dark { transform: scale(1.06); }
.media-frame--round { border-radius: var(--radius-circle); aspect-ratio: 1; max-width: 180px; }
.media-frame--tall { aspect-ratio: 3 / 4; }

/* Mouse-tracking 3D tilt (set by interactions.js). The short transition makes
   the motion glide and snap back smoothly without lagging the pointer. */
/* Cursor-follow tilt. No preserve-3d here on purpose: the tilt transform carries
   its own perspective() function, and forcing preserve-3d would hijack the flip
   cards' inner 3D context (they rely on the `perspective` PROPERTY for the flip). */
.tilt-3d {
  transition: transform 0.18s var(--ease-out);
  will-change: transform;
}

/* Accent edge + hover glow + a slow "shine" sweep so the image is never static.
   Uses the frame's own --accent/--glow (set via a k-* class on the wrapper). */
.media-frame--shine {
  border-color: var(--accent, var(--color-border));
  transition: box-shadow var(--speed-normal) var(--ease-out),
              border-color var(--speed-normal) var(--ease-out),
              var(--theme-transition);
}
.media-frame--shine::after {
  content: "";
  position: absolute; inset: 0; z-index: 2; pointer-events: none;
  background: linear-gradient(115deg,
    transparent 38%,
    var(--media-sheen) 50%,
    transparent 62%);
  transform: translateX(-130%);
  animation: media-shine 5.5s ease-in-out infinite;
}
.media-frame--shine:hover {
  box-shadow: 0 24px 70px -6px var(--glow, transparent),
              0 10px 30px -6px var(--glow, transparent);
}
@keyframes media-shine {
  0%        { transform: translateX(-130%); }
  55%, 100% { transform: translateX(130%); }
}
@media (prefers-reduced-motion: reduce) {
  .media-frame--shine::after { animation: none; opacity: 0; }
}

/* LIGHT / DARK ASSET SWAP (opt-in, pure CSS — reacts to the same data-theme
   attribute as the theme toggle, so it switches in place with no reload).
   • Common image  -> keep a single <img> as usual (no class needed).
   • Dual versions -> put two imgs in the frame and tag them:
       <img class="asset-light" src="…-light.png" alt="…">
       <img class="asset-dark"  src="…-dark.png"  alt="…">
   Works for thumbnails, gifs, etc. (the hover swap still applies on top). */
.asset-dark { display: none; }
[data-theme="dark"] .asset-light { display: none; }
[data-theme="dark"] .asset-dark  { display: block; }

/* --------------------------------------------------------------- CARDS ---- */
.card {
  background-color: var(--color-surface);
  border: var(--card-edge-width) solid var(--card-edge-color);
  border-radius: var(--radius-lg);
  padding: var(--space-lg);
  transition: transform var(--speed-normal) var(--ease-out),
              box-shadow var(--speed-normal) var(--ease-out);
}
.card:hover {
  transform: translateY(-5px);
  box-shadow:
    0 24px 70px -6px var(--glow, transparent),   /* wide ambient underglow */
    0 10px 30px -6px var(--glow, transparent),   /* tighter core glow      */
    0 0 0 1px var(--glow, transparent),          /* faint colored rim      */
    var(--shadow-sm);
}
.card__title { font-size: var(--fs-heading); margin-bottom: var(--space-2xs); }
.card__meta { font-size: var(--fs-small); color: var(--color-text-muted); }
.card__body { margin-top: var(--space-sm); }
.card__actions { margin-top: var(--space-md); display: flex; flex-wrap: wrap; gap: var(--space-2xs); }
.card__media { margin: calc(-1 * var(--space-lg)) calc(-1 * var(--space-lg)) var(--space-md); border-radius: 0; }

/* Role cards — same layout rhythm as education / experience (text top, media bottom). */
.card--role {
  display: flex;
  flex-direction: column;
  min-height: 360px;
}
.card--role .card__media {
  margin: var(--space-md) 0 0;
  margin-top: auto;
  border-radius: var(--radius-md);
  background-color: transparent;
  border-color: color-mix(in srgb, var(--accent, var(--color-border)) 35%, var(--thumb-edge-color));
}
.card--role:hover .card__media {
  border-color: color-mix(in srgb, var(--accent, var(--color-border)) 55%, var(--thumb-edge-color));
  box-shadow: 0 12px 40px -8px var(--glow, transparent);
}
.card--role .card__media .mosaic-thumb {
  position: absolute;
  inset: 0;
  display: block;
  width: 100%;
  height: 100%;
  transition: transform var(--speed-slow) var(--ease-out);
}
.card--role:hover .mosaic-thumb { transform: scale(1.03); }
.card--role .card__body { flex: 1; }

.card-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(min(100%, 300px), 1fr));
  gap: var(--space-md);
}
.card-grid .pub-card {
  grid-template-columns: 1fr;
  align-items: stretch;
  height: 100%;
}
.card-grid .pub-card__thumb { width: 100%; max-width: none; }
.card-grid .pub-card__body { flex: 1; }
.card-grid .pub-card__actions { margin-top: auto; padding-top: var(--space-xs); }

[data-visibility-off] { display: none !important; }

/* Blog post prose — theme tokens only; type via .t-lead in markup */
.blog-post__body {
  display: flex;
  flex-direction: column;
  gap: var(--space-xl);
}
.blog-post__section {
  display: flex;
  flex-direction: column;
  gap: var(--space-md);
}
.blog-post__body > *,
.blog-post__section > * { margin: 0; }
.blog-post__list {
  list-style: decimal;
  list-style-position: outside;
  padding-inline-start: 1.25em;
  display: flex;
  flex-direction: column;
  gap: var(--space-sm);
}

/* --------------------------------------------------------------- CHIPS ---- */
.chip {
  display: inline-flex; align-items: center; gap: 0.4em;
  background-color: var(--color-chip-bg);
  color: var(--color-chip-text);
  font-size: var(--fs-small);
  font-weight: var(--weight-medium);
  padding: 0.4em 0.9em;
  border-radius: var(--radius-pill);
}
.chip-row { display: flex; flex-wrap: wrap; gap: var(--space-2xs); }

/* ==========================================================================
   ACCORDION  — stacked rows that expand vertically.
   ========================================================================== */
.accordion { display: flex; flex-direction: column; }
.accordion__item { border-top: 1px solid var(--color-border); }
.accordion__item:last-child { border-bottom: 1px solid var(--color-border); }
.accordion__trigger {
  width: 100%;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-md);
  text-align: left;
  padding: var(--space-md) 0;
  font-family: var(--font-display);
  font-size: var(--fs-heading);
  font-weight: var(--weight-medium);
  color: var(--color-text);
}
.accordion__icon {
  position: relative; flex: 0 0 auto;
  width: 28px; height: 28px; border-radius: var(--radius-circle);
  background-color: var(--accent, var(--color-accent));
  transition: background-color var(--speed-fast) var(--ease-out),
              transform var(--speed-fast) var(--ease-out);
}
.accordion__icon::before,
.accordion__icon::after {
  content: ""; position: absolute; top: 50%; left: 50%;
  width: 12px; height: 2px; background-color: var(--toggle-icon-fg, #1d1d1f);
  transform: translate(-50%, -50%); border-radius: 2px;
  transition: transform var(--speed-normal) var(--ease-out),
              background-color var(--speed-fast) var(--ease-out);
}
.accordion__icon::after { transform: translate(-50%, -50%) rotate(90deg); }
.accordion__trigger:hover .accordion__icon { transform: scale(1.06); }
.accordion__item.is-open .accordion__icon { background-color: var(--accent, var(--color-accent)); }
.accordion__item.is-open .accordion__icon::before,
.accordion__item.is-open .accordion__icon::after { background-color: var(--toggle-icon-fg, #1d1d1f); }
.accordion__item.is-open .accordion__icon::after { transform: translate(-50%, -50%) rotate(0deg); }
.accordion__panel {
  overflow: hidden;
  height: 0;
  transition: height var(--speed-normal) var(--ease-out);
}
.accordion__panel-inner { padding: 0 0 var(--space-lg); display: grid; gap: var(--space-md); }
.accordion__panel-inner.has-media > div:first-child {
  display: flex;
  flex-direction: column;
  gap: var(--space-sm);
}
@media (min-width: 760px) {
  .accordion__panel-inner.has-media { grid-template-columns: 1fr 1fr; align-items: center; }
}

/* ==========================================================================
   FLIP GRID — cards that flip on hover to reveal extended info on the back.
   ========================================================================== */
.flip-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(min(100%, 300px), 1fr));
  gap: var(--space-md);
}
.flip-card { perspective: 1600px; min-height: 360px; }
.flip-card__inner {
  position: relative; width: 100%; height: 100%; min-height: inherit;
  transition: transform var(--speed-slow) var(--ease-in-out);
  transform-style: preserve-3d;
}
.flip-card.is-flipped .flip-card__inner { transform: rotateY(180deg); }
.flip-card__face {
  position: absolute; inset: 0;
  -webkit-backface-visibility: hidden; backface-visibility: hidden;
  display: flex; flex-direction: column;
  background-color: var(--color-surface);
  border: var(--card-edge-width) solid var(--card-edge-color);
  border-radius: var(--radius-lg);
  padding: var(--space-lg);
  box-shadow: var(--shadow-sm);
  overflow: hidden;
}
.flip-card__face--back { transform: rotateY(180deg); justify-content: center; }
.flip-card__eyebrow { color: var(--eyebrow-color); margin-bottom: var(--space-2xs); }
.flip-card__title { font-size: var(--fs-title); margin-bottom: var(--space-xs); }
.flip-card__media { margin-top: auto; }
.k-cert-aws .flip-card__media img,
.k-cert-nvidia .flip-card__media img { object-fit: contain; padding: var(--space-md); box-sizing: border-box; }
.flip-card__toggle {
  position: absolute; bottom: var(--space-md); right: var(--space-md);
  width: 36px; height: 36px; border-radius: var(--radius-circle);
  background-color: var(--accent, var(--color-accent));
  color: var(--toggle-icon-fg, #1d1d1f);
  display: inline-flex; align-items: center; justify-content: center;
  font-size: 1.4rem; line-height: 1;
  transition: transform var(--speed-fast) var(--ease-out), background-color var(--speed-fast);
  z-index: 2;
}
.flip-card__toggle:hover { transform: scale(1.08); }
.flip-card.is-flipped .flip-card__face--front .flip-card__toggle { transform: rotate(45deg); }

/* Flip on HOVER (no click) — back shows different content. */
.flip-card--hover { cursor: default; }
.flip-card--hover:hover .flip-card__inner { transform: rotateY(180deg); }
/* The back face is painted in the card's own set color (set-<hue> on the card).
   --accent / --dark are kept as aliases so existing markup keeps working. */
.flip-card__face--back,
.flip-card__face--accent,
.flip-card__face--dark {
  background: var(--flip-bg);
  color: var(--flip-fg);
  justify-content: center;
}
.flip-card__face--back .flip-card__eyebrow { color: var(--flip-fg); opacity: 0.85; }
.flip-card__face--back .flip-card__title   { color: var(--flip-fg); }
.flip-card__face--back p                    { color: var(--flip-fg); opacity: 0.92; }
.flip-card__actions { margin-top: var(--space-sm); display: flex; flex-wrap: wrap; gap: var(--space-2xs); }

/* ==========================================================================
   MODAL CARDS — a card's "+" opens a large centred dialog while the rest of
   the page blurs. Smooth scale-in; closes on backdrop / × /
   Escape. Backdrop dim + blur are themeable (--modal-backdrop / --modal-blur).
   ========================================================================== */
.modal-card {
  position: relative;
  display: flex;
  flex-direction: column;
  min-height: 360px;
  background-color: var(--color-surface);
  border: var(--card-edge-width) solid var(--card-edge-color);
  border-radius: var(--radius-lg);
  padding: var(--space-lg);
  overflow: hidden;
  cursor: pointer;
  box-shadow: var(--shadow-sm);
  transition: transform var(--speed-normal) var(--ease-out),
              box-shadow var(--speed-normal) var(--ease-out),
              var(--theme-transition);
}
.modal-card { box-shadow: 0 10px 28px -16px var(--glow, transparent), var(--shadow-sm); }  /* faint glow even at rest */
.modal-card:hover {
  transform: translateY(-6px);
  box-shadow:
    0 26px 74px -6px var(--glow, transparent),   /* wide ambient underglow */
    0 12px 32px -6px var(--glow, transparent),   /* tighter core glow      */
    0 0 0 1px var(--glow, transparent),          /* faint colored rim      */
    var(--shadow-sm);
}
.modal-card:focus-visible { outline: 2px solid var(--accent, var(--color-accent)); outline-offset: 3px; }
.modal-card__meta {
  display: flex;
  flex-direction: column;
  gap: var(--space-2xs);
  flex-shrink: 0;
  margin-bottom: var(--space-xs);
}
.modal-card__meta p { margin: 0; }
.modal-card .flip-card__media { margin-top: auto; flex-shrink: 0; width: 100%; }   /* media sits at the bottom, like the old flip front */

/* Full-screen blur + dim layer behind the dialog. */
.modal-overlay {
  position: fixed;
  inset: 0;
  z-index: 200;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: var(--space-lg);
  background-color: var(--modal-backdrop);
  -webkit-backdrop-filter: blur(0);
          backdrop-filter: blur(0);
  opacity: 0;
  visibility: hidden;
  transition: opacity var(--speed-normal) var(--ease-out),
              -webkit-backdrop-filter var(--speed-normal) var(--ease-out),
              backdrop-filter var(--speed-normal) var(--ease-out),
              visibility 0s linear var(--speed-normal);
}
.modal-overlay.is-open {
  opacity: 1;
  visibility: visible;
  -webkit-backdrop-filter: blur(var(--modal-blur));
          backdrop-filter: blur(var(--modal-blur));
  transition: opacity var(--speed-normal) var(--ease-out),
              -webkit-backdrop-filter var(--speed-normal) var(--ease-out),
              backdrop-filter var(--speed-normal) var(--ease-out);
}

/* The dialog: a fixed-height box whose BODY scrolls (so × stays put). */
.modal-dialog {
  position: relative;
  display: flex;
  flex-direction: column;
  width: min(720px, 100%);
  max-height: min(86vh, 920px);
  background-color: var(--color-surface);
  border: var(--card-edge-width) solid var(--card-edge-color);
  border-radius: var(--radius-xl);
  box-shadow: var(--shadow-lg);
  overflow: hidden;
  transform: scale(0.92) translateY(14px);
  opacity: 0;
  transition: transform var(--speed-slow) var(--ease-out),
              opacity var(--speed-normal) var(--ease-out);
}
.modal-overlay.is-open .modal-dialog { transform: none; opacity: 1; }

.modal-dialog__body {
  overflow-y: auto;
  -webkit-overflow-scrolling: touch;
  padding: clamp(var(--space-lg), 4vw, var(--space-2xl));
}

.modal-close {
  position: absolute;
  top: var(--space-md);
  right: var(--space-md);
  z-index: 2;
  width: 38px; height: 38px;
  border-radius: var(--radius-circle);
  background-color: var(--color-surface-3);
  color: var(--color-text);
  font-size: 1.5rem; line-height: 1;
  display: inline-flex; align-items: center; justify-content: center;
  cursor: pointer;
  transition: transform var(--speed-fast) var(--ease-out),
              background-color var(--speed-fast) var(--ease-out),
              color var(--speed-fast) var(--ease-out);
}
.modal-close:hover { transform: scale(1.08); background-color: var(--accent, var(--color-accent)); color: var(--color-accent-contrast); }

/* Dialog content typography. */
.modal-dialog__title { font-size: var(--fs-display); line-height: var(--lh-tight); margin: var(--space-2xs) var(--space-xl) var(--space-sm) 0; color: var(--accent, var(--color-text)); }
.modal-dialog__body .eyebrow { color: var(--eyebrow-color); }
.modal-dialog__body h3 { font-size: var(--fs-heading); margin: var(--space-md) 0 var(--space-3xs); color: var(--color-text); }
.modal-dialog__body p { color: var(--color-text-soft); margin-bottom: var(--space-sm); }
.modal-dialog__body .t-lead { color: var(--color-text); }
.modal-dialog__body ul { margin: 0 0 var(--space-sm) 1.2em; color: var(--color-text-soft); display: flex; flex-direction: column; gap: var(--space-3xs); }

@media (prefers-reduced-motion: reduce) {
  .modal-dialog { transition: opacity var(--speed-normal) var(--ease-out); transform: none; }
}

/* ==========================================================================
   TILE GRID — multi-container collage with varied sizes.
   ========================================================================== */
.tile-grid {
  display: grid;
  grid-template-columns: repeat(2, 1fr);
  gap: var(--space-md);
}
.tile {
  position: relative;
  display: flex;
  flex-direction: column;
  justify-content: flex-end;
  min-height: 320px;
  padding: var(--space-lg);
  border-radius: var(--radius-lg);
  background-color: var(--color-surface);
  border: var(--card-edge-width) solid var(--card-edge-color);
  overflow: hidden;
  transition: transform var(--speed-normal) var(--ease-out), box-shadow var(--speed-normal) var(--ease-out);
}
.tile:hover {
  transform: translateY(-5px);
  box-shadow: 0 16px 48px -12px var(--glow, transparent), var(--shadow-sm);
}
.tile--wide { grid-column: span 2; }
.tile--dark { background-color: var(--color-surface-dark); color: var(--color-text-invert); }
.tile--dark .tile__title { color: var(--color-text-invert); }
.tile--dark .tile__text { color: var(--color-text-invert-soft); }
.tile__eyebrow { color: var(--eyebrow-color); margin-bottom: var(--space-2xs); }
.tile__title { font-size: var(--fs-title); margin-bottom: var(--space-2xs); }
.tile__text { color: var(--color-text-soft); max-width: 46ch; }
.tile__media { margin: 0 calc(-1 * var(--space-lg)) calc(-1 * var(--space-lg)); margin-top: var(--space-md); }
.tile__actions { margin-top: var(--space-md); display: flex; gap: var(--space-sm); flex-wrap: wrap; }
@media (max-width: 720px) {
  .tile-grid { grid-template-columns: 1fr; }
  .tile--wide { grid-column: span 1; }
}

/* ==========================================================================
   REVEAL LIST — "Significant others." Click a row to expand; MANY can be open
   at once, and they push content down (overflow onto others) rather than
   swapping.
   ========================================================================== */
.reveal-list { display: flex; flex-direction: column; gap: var(--space-md); }
.reveal {
  background-color: var(--color-surface);
  border: var(--card-edge-width) solid var(--card-edge-color);
  border-radius: var(--radius-md);
  overflow: hidden;
  transition: box-shadow var(--speed-normal) var(--ease-out);
}
.reveal.is-open { box-shadow: var(--shadow-md); }
.reveal__trigger {
  width: 100%;
  display: flex; align-items: center; gap: var(--space-md);
  text-align: left;
  padding: var(--space-md) var(--space-lg);
}
.reveal__title { flex: 1; font-family: var(--font-display); font-size: var(--fs-heading); font-weight: var(--weight-medium); }
.reveal__plus {
  flex: 0 0 auto; width: 30px; height: 30px; border-radius: var(--radius-circle);
  background-color: var(--accent, var(--color-accent)); position: relative;
  transition: background-color var(--speed-fast) var(--ease-out),
              transform var(--speed-fast) var(--ease-out);
}
.reveal__plus::before, .reveal__plus::after {
  content: ""; position: absolute; top: 50%; left: 50%; width: 13px; height: 2px;
  background-color: var(--toggle-icon-fg, #1d1d1f); transform: translate(-50%, -50%); border-radius: 2px;
  transition: transform var(--speed-normal) var(--ease-out),
              background-color var(--speed-fast) var(--ease-out);
}
.reveal__trigger:hover .reveal__plus { transform: scale(1.06); }
.reveal__plus::after { transform: translate(-50%, -50%) rotate(90deg); }
.reveal.is-open .reveal__plus { background-color: var(--accent, var(--color-accent)); }
.reveal.is-open .reveal__plus::before,
.reveal.is-open .reveal__plus::after { background-color: var(--toggle-icon-fg, #1d1d1f); }
.reveal.is-open .reveal__plus::after { transform: translate(-50%, -50%) rotate(0); }
.reveal__panel { overflow: hidden; height: 0; transition: height var(--speed-normal) var(--ease-out); }
.reveal__panel-inner { padding: 0 var(--space-lg) var(--space-lg); display: grid; gap: var(--space-md); }
@media (min-width: 760px) {
  .reveal__panel-inner.has-media { grid-template-columns: 1.2fr 1fr; align-items: center; }
}

/* ==========================================================================
   HORIZONTAL SCROLLER — swipeable carousels / galleries.
   ========================================================================== */
/* Blog search bar — themeable, accent focus ring. */
.blog-search {
  position: relative;
  max-width: 520px;
  margin-bottom: var(--space-lg);
}
.blog-search__icon {
  position: absolute; left: 16px; top: 50%; transform: translateY(-50%);
  width: 18px; height: 18px; color: var(--color-text-muted); pointer-events: none;
}
.blog-search__input {
  width: 100%;
  padding: var(--space-sm) var(--space-md) var(--space-sm) 44px;
  font: inherit; font-size: var(--fs-body);
  color: var(--color-text);
  background-color: var(--color-surface);
  border: 1px solid var(--color-border);
  border-radius: var(--radius-pill, 999px);
  transition: border-color var(--speed-fast) var(--ease-out),
              box-shadow var(--speed-fast) var(--ease-out);
}
.blog-search__input::placeholder { color: var(--color-text-muted); }
.blog-search__input:focus {
  outline: none;
  border-color: var(--accent, var(--color-accent));
  box-shadow: 0 0 0 3px var(--glow, rgba(0,0,0,0.12));
}
.blog-search__empty { margin-top: var(--space-sm); }

.scroller {
  display: grid;
  grid-auto-flow: column;
  grid-auto-columns: min(80%, 360px);
  gap: var(--space-md);
  overflow-x: auto;
  scroll-snap-type: x mandatory;
  padding-bottom: var(--space-md);
  -webkit-overflow-scrolling: touch;
  scrollbar-width: none;
}
.scroller::-webkit-scrollbar { display: none; }
.scroller > * { scroll-snap-align: start; }

/* Continuous marquee mode (enabled by interactions.js). The track holds two
   copies of the cards and slides left forever; -50% lands on the copy so the
   loop is seamless. Uniform right-margin (incl. the last card) keeps spacing
   consistent across the wrap. */
.scroller.is-marquee {
  display: block;
  overflow: hidden;
  scroll-snap-type: none;
  padding-bottom: var(--space-2xs);
}
.scroller.is-marquee .marquee__track {
  display: flex;
  width: max-content;
  animation: marquee-scroll var(--marquee-duration, 40s) linear infinite;
  will-change: transform;
}
.scroller.is-marquee .marquee__track > * {
  flex: 0 0 min(80vw, 340px);   /* vw (not %) — % flex-basis is unreliable in a max-content track */
  margin-right: var(--space-md);
}
.scroller.is-marquee:hover .marquee__track,
.scroller.is-marquee:focus-within .marquee__track { animation-play-state: paused; }
@keyframes marquee-scroll {
  from { transform: translateX(0); }
  to   { transform: translateX(-50%); }
}
@media (prefers-reduced-motion: reduce) {
  .scroller.is-marquee .marquee__track { animation: none; }
}

/* Carousel wrapper + prev/next arrow buttons (added by interactions.js so any
   .scroller becomes an obviously-clickable carousel). */
.carousel { position: relative; }
.carousel__btn {
  position: absolute; top: 42%; transform: translateY(-50%);
  z-index: 4; width: 46px; height: 46px;
  display: inline-flex; align-items: center; justify-content: center;
  border-radius: var(--radius-circle);
  background-color: var(--color-surface); color: var(--color-text);
  border: 1px solid var(--color-border);
  box-shadow: var(--shadow-md); cursor: pointer;
  transition: transform var(--speed-fast) var(--ease-out),
              background-color var(--speed-fast) var(--ease-out),
              opacity var(--speed-fast) var(--ease-out);
}
.carousel__btn:hover { transform: translateY(-50%) scale(1.09); background-color: var(--color-surface-2); }
.carousel__btn:active { transform: translateY(-50%) scale(0.96); }
.carousel__btn--prev { left: -10px; }
.carousel__btn--next { right: -10px; }
.carousel__btn[disabled] { opacity: 0.3; cursor: default; box-shadow: none; }
.carousel__btn svg { width: 20px; height: 20px; }
@media (max-width: 680px) {
  .carousel__btn--prev { left: 2px; }
  .carousel__btn--next { right: 2px; }
}

/* ==========================================================================
   SPLIT / FEATURE ROWS — alternating text + media.
   ========================================================================== */
.feature {
  display: grid;
  gap: var(--space-lg);
  align-items: center;
}
@media (min-width: 820px) {
  .feature { grid-template-columns: 1fr 1fr; gap: var(--space-2xl); }
  .feature--reverse .feature__media { order: -1; }
  /* Intro row: wider copy, narrower portrait photo. */
  .feature--intro {
    grid-template-columns: minmax(0, 1.5fr) minmax(0, 0.72fr);
    gap: clamp(var(--space-xl), 4vw, var(--space-3xl));
  }
  .feature--intro .feature__media {
    max-width: 300px;
    justify-self: end;
  }
}
.feature__title { font-size: var(--fs-display); margin-bottom: var(--space-sm); }
.feature__text { font-size: var(--fs-lead); color: var(--color-text-soft); max-width: none; }
.feature__actions { margin-top: var(--space-md); display: flex; gap: var(--space-sm); flex-wrap: wrap; }

/* ==========================================================================
   TIMELINE — used on CV / experience / education.
   ========================================================================== */
.timeline { position: relative; display: flex; flex-direction: column; gap: var(--space-lg); }
.timeline::before {
  content: ""; position: absolute; left: 7px; top: 6px; bottom: 6px; width: 2px;
  background-color: var(--color-border);
}
.timeline__item { position: relative; padding-left: var(--space-lg); }
.timeline__item::before {
  content: ""; position: absolute; left: 0; top: 6px; width: 16px; height: 16px;
  border-radius: var(--radius-circle); background-color: var(--accent, var(--color-accent));
  box-shadow: 0 0 0 4px var(--color-bg);
}
.timeline__date { font-size: var(--fs-small); color: var(--color-text-muted); }
.timeline__role { font-size: var(--fs-heading); font-weight: var(--weight-medium); margin-top: 2px; }
.timeline__org { color: var(--accent, var(--color-accent)); font-size: var(--fs-small); font-weight: var(--weight-medium); }
.timeline__desc { margin-top: var(--space-2xs); }

/* ==========================================================================
   PUBLICATION LIST
   ========================================================================== */
.pub-list { display: flex; flex-direction: column; gap: var(--space-md); }
.pub {
  display: flex; flex-wrap: wrap; align-items: baseline; gap: var(--space-sm);
  justify-content: space-between;
  padding: var(--space-md) 0;
  border-bottom: 1px solid var(--color-border-soft);
}
.pub__title { font-size: var(--fs-heading); font-weight: var(--weight-medium); flex: 1 1 60%; }
.pub__venue {
  font-size: var(--fs-small); font-weight: var(--weight-bold);
  letter-spacing: var(--tracking-wide);
  color: var(--accent, var(--color-accent)); flex: 0 0 auto;
}
/* Rank/win badge — loud and proud (these are the wins, make them pop).
   Uses the card's own color so badge + title + glow all match. */
.pub__badge {
  display: inline-flex; align-items: center; gap: 0.35em;
  font-size: var(--fs-small); font-weight: var(--weight-bold);
  color: var(--badge-text);
  background-color: var(--accent, var(--color-accent));
  padding: 0.32em 0.85em; border-radius: var(--radius-pill);
  box-shadow: 0 6px 18px -6px var(--glow, transparent);
}
.pub__badge::before { content: "🏆"; font-size: 0.95em; }

/* Richer publication card with thumbnail + actions (papers-page style). */
.pub-grid { display: flex; flex-direction: column; gap: var(--space-md); }
.pub-card {
  position: relative;
  display: grid;
  grid-template-columns: 200px 1fr;
  gap: var(--space-lg);
  align-items: center;
  background-color: var(--color-surface);
  border: var(--card-edge-width) solid var(--card-edge-color);
  border-radius: var(--radius-lg);
  padding: var(--space-md);
  transition: transform var(--speed-normal) var(--ease-out),
              box-shadow var(--speed-normal) var(--ease-out),
              border-color var(--speed-normal) var(--ease-out),
              var(--theme-transition);
}
.pub-card:hover {
  transform: translateY(-5px);
  box-shadow:
    0 24px 70px -6px var(--glow, transparent),
    0 10px 30px -6px var(--glow, transparent),
    0 0 0 1px var(--glow, transparent),
    var(--shadow-sm);
}
.pub-card__thumb { aspect-ratio: 16 / 10; border-radius: var(--radius-md); }
.pub-card__body { display: flex; flex-direction: column; gap: var(--space-2xs); }
.pub-card__title { font-size: var(--fs-heading); font-weight: var(--weight-medium); line-height: var(--lh-snug); }
.pub-card__meta { display: flex; flex-wrap: wrap; align-items: center; gap: var(--space-2xs) var(--space-sm); }
.pub-card__actions { margin-top: var(--space-xs); display: flex; flex-wrap: wrap; gap: var(--space-2xs); }
.pub-card__actions .btn { padding: 0.55em 1.1em; font-size: var(--fs-small); }
@media (max-width: 680px) {
  .pub-card { grid-template-columns: 1fr; }
  .pub-card__thumb { max-width: 100%; }
}

/* ==========================================================================
   STATS STRIP
   ========================================================================== */
.stats { display: grid; grid-template-columns: repeat(auto-fit, minmax(140px, 1fr)); gap: var(--space-lg); text-align: center; }
.stat__num { font-family: var(--font-display); font-weight: var(--weight-bold); font-size: var(--fs-display); color: var(--accent, var(--color-accent)); line-height: 1; }
.stat__label { font-size: var(--fs-small); color: var(--color-text-muted); margin-top: var(--space-2xs); }

/* ==========================================================================
   SCROLL REVEAL — fade/slide in on enter (toggled by interactions.js)
   ========================================================================== */
.reveal-on-scroll {
  opacity: 0;
  transform: translateY(28px);
  transition: opacity var(--speed-slow) var(--ease-out),
              transform var(--speed-slow) var(--ease-out);
  will-change: opacity, transform;
}
.reveal-on-scroll.is-visible { opacity: 1; transform: none; }

/* ==========================================================================
   MICRO-INTERACTIONS — hover + click polish so it doesn't feel like flat HTML.
   ========================================================================== */
/* Surfaces cross-fade colors on theme switch. */
.card, .tile, .reveal, .flip-card__face, .chip, .pub, .scroller .card {
  transition: transform var(--speed-normal) var(--ease-out),
              box-shadow var(--speed-normal) var(--ease-out),
              var(--theme-transition);
}

/* Chips lift slightly and tint on hover. */
.chip { cursor: default; }
.chip:hover { transform: translateY(-2px); color: var(--accent, var(--color-accent)); }

/* Stat numbers pop on hover of their cell. */
.stat { transition: transform var(--speed-normal) var(--ease-out); }
.stat:hover { transform: translateY(-4px); }
.stat__num { transition: transform var(--speed-normal) var(--ease-out); }
.stat:hover .stat__num { transform: scale(1.08); }

/* Publication rows slide + highlight on hover. */
.pub { padding-inline: var(--space-sm); border-radius: var(--radius-sm); }
.pub:hover { background-color: var(--color-surface-2); transform: translateX(6px); }
.pub:hover .pub__title { color: var(--color-accent); }
.pub__title { transition: color var(--speed-fast) var(--ease-out); }

/* Timeline dots grow on item hover. */
.timeline__item::before { transition: transform var(--speed-normal) var(--ease-out), box-shadow var(--speed-normal); }
.timeline__item:hover::before { transform: scale(1.25); }

/* Buttons: contain the click ripple + lift on hover. */
.btn { position: relative; overflow: hidden; }
.btn--primary:hover, .btn--solid-dark:hover { transform: translateY(-2px); }
.btn .ripple {
  position: absolute; border-radius: var(--radius-circle);
  transform: scale(0); opacity: 0.4; pointer-events: none;
  background-color: currentColor;
  animation: ripple var(--speed-slow) var(--ease-out) forwards;
}
@keyframes ripple { to { transform: scale(2.6); opacity: 0; } }

/* Flip cards float up while idle-hovered (before the flip), with a glow. */
.flip-card { transition: transform var(--speed-normal) var(--ease-out), filter var(--speed-normal) var(--ease-out); }
.flip-card:not(.is-flipped):hover { transform: translateY(-6px); filter: drop-shadow(0 18px 40px var(--glow, transparent)) drop-shadow(0 6px 16px var(--glow, transparent)); }

/* ==========================================================================
   TITLE COLOR + SHIMMER  (read this once — it explains the whole system)
   --------------------------------------------------------------------------
   • PAGE / HERO titles (h1)  -> flowing multi-hue gradient (--title-gradient).
   • EVERY OTHER TITLE        -> painted in ITS OWN component color (--accent),
     right at rest, with a soft sheen that loops slowly and SPEEDS UP on hover.
     This covers: section titles, card titles, flip-card front titles, tile
     titles, accordion/dropdown titles, reveal titles, pub-card titles,
     timeline roles and the stat numbers.
   • The small text ABOVE titles (.eyebrow) -> stays neutral grey, grey sweep.
   Colors are never set here — each title pulls --accent from its own block in
   theme.css, so nothing is hardcoded blue.
   ========================================================================== */

/* PAGE / HERO titles: animated multi-hue gradient so they are never bland. */
main h1 {
  background-image: var(--title-gradient);
  background-size: 220% auto;
  -webkit-background-clip: text;
  background-clip: text;
  -webkit-text-fill-color: transparent;
  color: transparent;
  animation: hue-slide 9s linear infinite;
}
@keyframes hue-slide { to { background-position: 220% center; } }

/* EVERY OTHER TITLE: its own color at rest + a looping sheen in that color. */
.section h2,
.section--dark h2,
.feature__title,
.card__title,
.flip-card__face--front .flip-card__title,
.modal-card .flip-card__title,
.tile__title,
.tile--dark .tile__title,
.accordion__trigger,
.reveal__title,
.pub-card__title,
.blog-post__title,
.timeline__role {
  background-image: linear-gradient(
    100deg,
    var(--accent, #86868b) 0%,
    var(--accent, #86868b) 38%,
    color-mix(in srgb, var(--accent, #86868b) 35%, var(--title-shimmer)) 50%,
    var(--accent, #86868b) 62%,
    var(--accent, #86868b) 100%);
  background-size: 200% auto;
  -webkit-background-clip: text;
  background-clip: text;
  -webkit-text-fill-color: transparent;
  color: transparent;
  animation: text-shimmer 4.5s linear infinite;
}
/* DARK MODE: brighter, slightly wider moving glint so the sheen reads clearly
   against the near-black page, while the resting text stays the full accent. */
[data-theme="dark"] .section h2,
[data-theme="dark"] .section--dark h2,
[data-theme="dark"] .feature__title,
[data-theme="dark"] .card__title,
[data-theme="dark"] .flip-card__face--front .flip-card__title,
[data-theme="dark"] .modal-card .flip-card__title,
[data-theme="dark"] .tile__title,
[data-theme="dark"] .tile--dark .tile__title,
[data-theme="dark"] .accordion__trigger,
[data-theme="dark"] .reveal__title,
[data-theme="dark"] .pub-card__title,
[data-theme="dark"] .blog-post__title,
[data-theme="dark"] .timeline__role {
  background-image: linear-gradient(
    100deg,
    var(--accent, #f5f5f7) 0%,
    var(--accent, #f5f5f7) 34%,
    color-mix(in srgb, var(--accent, #f5f5f7) 12%, var(--title-shimmer)) 50%,
    var(--accent, #f5f5f7) 66%,
    var(--accent, #f5f5f7) 100%);
}
/* Neutral silver research heading — stronger glint (default mix is too faint on grey). */
.section-head.k-home-research h2 {
  background-image: linear-gradient(
    100deg,
    var(--accent) 0%,
    var(--accent) 30%,
    color-mix(in srgb, var(--accent) 18%, var(--title-shimmer)) 50%,
    var(--accent) 70%,
    var(--accent) 100%);
}
[data-theme="dark"] .section-head.k-home-research h2 {
  background-image: linear-gradient(
    100deg,
    var(--accent) 0%,
    var(--accent) 22%,
    var(--title-shimmer) 50%,
    var(--accent) 78%,
    var(--accent) 100%);
}
/* Hover just makes the same colored sheen sweep faster. */
.section h2:hover,
.section--dark h2:hover,
.feature__title:hover,
.card:hover .card__title,
.flip-card__face--front:hover .flip-card__title,
.modal-card:hover .flip-card__title,
.tile:hover .tile__title,
.accordion__trigger:hover,
.reveal__trigger:hover .reveal__title,
.pub-card:hover .pub-card__title,
.blog-post__title:hover,
.timeline__role:hover,
.stat:hover .stat__num,
.link-arrow:hover { animation: text-shimmer 1.1s linear infinite; }

/* The flip-card BACK title sits on a colored panel, so keep it solid. */
.flip-card__face--back .flip-card__title {
  background: none;
  -webkit-text-fill-color: var(--flip-fg);
  color: var(--flip-fg);
  animation: none;
}

/* Cert flip backs: title + tech line — same sheen as front titles. */
.k-cert-aws .flip-card__face--back .flip-card__title,
.k-cert-aws .flip-card__face--back p,
.k-cert-nvidia .flip-card__face--back .flip-card__title,
.k-cert-nvidia .flip-card__face--back p {
  opacity: 1;
  background-image: linear-gradient(
    100deg,
    var(--flip-fg) 0%,
    var(--flip-fg) 38%,
    color-mix(in srgb, var(--flip-fg) 35%, var(--title-shimmer)) 50%,
    var(--flip-fg) 62%,
    var(--flip-fg) 100%);
  background-size: 200% auto;
  -webkit-background-clip: text;
  background-clip: text;
  -webkit-text-fill-color: transparent;
  color: transparent;
  animation: text-shimmer 4.5s linear infinite;
}
[data-theme="dark"] .k-cert-aws .flip-card__face--back .flip-card__title,
[data-theme="dark"] .k-cert-aws .flip-card__face--back p,
[data-theme="dark"] .k-cert-nvidia .flip-card__face--back .flip-card__title,
[data-theme="dark"] .k-cert-nvidia .flip-card__face--back p {
  background-image: linear-gradient(
    100deg,
    var(--flip-fg) 0%,
    var(--flip-fg) 34%,
    color-mix(in srgb, var(--flip-fg) 12%, var(--title-shimmer)) 50%,
    var(--flip-fg) 66%,
    var(--flip-fg) 100%);
}

/* Text ABOVE titles stays neutral grey, with a grey sweep on hover only. */
.eyebrow:hover,
.section-head .eyebrow:hover {
  background-image: linear-gradient(
    100deg, currentColor 0%, currentColor 24%,
    var(--title-shimmer) 42%, var(--title-shimmer) 58%,
    currentColor 76%, currentColor 100%);
  background-size: 200% auto;
  -webkit-background-clip: text;
  background-clip: text;
  -webkit-text-fill-color: transparent;
  animation: text-shimmer 1.1s linear infinite;
}
/* Paragraphs / body copy: NO animation (kept calm on purpose). */

/* ==========================================================================
   ANIMATED METALLIC BUTTON BORDER — a glassy sliver loops around 24/7.
   Color/speed from --btn-border-sheen / --btn-border-speed (themeable).
   ========================================================================== */
@property --btn-angle { syntax: "<angle>"; initial-value: 0deg; inherits: false; }
.btn { isolation: isolate; }
.btn > * { position: relative; z-index: 2; }
.btn::before {
  content: "";
  position: absolute;
  inset: 0;
  border-radius: inherit;
  padding: 1.5px;
  background: conic-gradient(
    from var(--btn-angle),
    transparent 0deg, transparent 60deg,
    var(--btn-border-sheen) 90deg,
    transparent 120deg, transparent 360deg);
  -webkit-mask: linear-gradient(#000 0 0) content-box, linear-gradient(#000 0 0);
  -webkit-mask-composite: xor;
          mask: linear-gradient(#000 0 0) content-box, linear-gradient(#000 0 0);
          mask-composite: exclude;
  animation: btn-border-spin var(--btn-border-speed) linear infinite;
  pointer-events: none;
  z-index: 1;
}
/* Ghost / icon buttons sit on the page bg — need a contrasting sheen (not the
   white default, which vanishes on light backgrounds). */
.btn--ghost::before {
  background: conic-gradient(
    from var(--btn-angle),
    transparent 0deg, transparent 60deg,
    var(--btn-border-sheen-ghost, var(--btn-border-sheen)) 90deg,
    transparent 120deg, transparent 360deg);
}
@keyframes btn-border-spin { to { --btn-angle: 360deg; } }
@media (prefers-reduced-motion: reduce) { .btn::before { animation: none; } }

/* ==========================================================================
   CURSOR FX — animated custom cursor + fog trail + click sparks.
   Only active when interactions.js adds .cursor-fx to <html> (desktop, fine
   pointer, motion allowed). Native cursor is hidden site-wide so the custom
   one is the only pointer; everything is pointer-events:none so clicks pass
   straight through to the page.
   ========================================================================== */
.cursor-fx, .cursor-fx * { cursor: none !important; }
.cursor-fx__canvas {
  position: fixed; inset: 0;
  max-width: none; max-height: none;
  pointer-events: none;
  z-index: 99998;
}
.cursor-fx__icon {
  position: fixed; top: 0; left: 0;
  max-width: none; max-height: none;
  width: 32px; height: 32px;
  pointer-events: none;
  z-index: 99999;
  will-change: transform;
  opacity: 0;
  transition: opacity var(--speed-normal) var(--ease-out);
  image-rendering: pixelated;   /* keep the pixel-art cursor crisp */
}
.cursor-fx__icon.is-active { opacity: 1; }
