/* ============================================================
   v3 additions — new styles ONLY (v1 files stay untouched apart
   from explicitly requested edits). Loaded after all v1 CSS.
   ============================================================ */

/* ---------- Custom cursor: arrowhead rounded outline ----------
   User request: no dot+circle cursors. White arrow on dark theme,
   black arrow on light theme. Fine pointers only — touch devices
   and text fields keep native behavior. */
@media (pointer: fine) {
  :root {
    /* user-provided "Arrowhead Rounded Outline" glyph; white on dark themes,
       black on light themes; hotspot at the arrow tip */
    --v3-cursor-dark: url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" width="26" height="26" viewBox="0 0 24 24"><path fill="%23FFF" stroke="%23000" stroke-width="2" d="M5.5 3.21V20.8c0 .45.54.67.85.35l4.86-4.86a.5.5 0 0 1 .35-.15h6.87a.5.5 0 0 0 .35-.85L6.35 2.85a.5.5 0 0 0-.85.35Z"/></svg>') 6 3;
    --v3-cursor-light: url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" width="26" height="26" viewBox="0 0 24 24"><path fill="%230f172a" stroke="%23FFF" stroke-width="2" d="M5.5 3.21V20.8c0 .45.54.67.85.35l4.86-4.86a.5.5 0 0 1 .35-.15h6.87a.5.5 0 0 0 .35-.85L6.35 2.85a.5.5 0 0 0-.85.35Z"/></svg>') 6 3;
  }

  body,
  a, a *,
  button, button *,
  .btn, .filter-btn, .theme-toggle, .hamburger,
  [role="button"], label, select, summary {
    cursor: var(--v3-cursor-dark), auto;
  }

  html[data-theme="light"] body,
  html[data-theme="light"] a, html[data-theme="light"] a *,
  html[data-theme="light"] button, html[data-theme="light"] button *,
  html[data-theme="light"] .btn, html[data-theme="light"] .filter-btn,
  html[data-theme="light"] .theme-toggle, html[data-theme="light"] .hamburger,
  html[data-theme="light"] [role="button"], html[data-theme="light"] label,
  html[data-theme="light"] select, html[data-theme="light"] summary {
    cursor: var(--v3-cursor-light), auto;
  }

  /* Text entry keeps the I-beam */
  input:not([type="button"]):not([type="submit"]):not([type="checkbox"]):not([type="radio"]),
  textarea {
    cursor: text;
  }
}

/* ---------- Terminal relocated into About (user request) ----------
   Replaces the old "Code Samples" showcase; the standalone terminal
   section between Research and Journey is gone. */
.v3-terminal-in-about {
  padding: 0;
  background: transparent;
  margin-top: 60px;
}

.v3-terminal-in-about .container {
  padding: 0;
  max-width: none;
}

/* Fixed height (prevents the loop from shifting the page) but taller than
   v1's 480px — the trailing $ prompt line was getting clipped (user report) */
.terminal-body {
  height: 560px;
}

@media (max-width: 768px) {
  .terminal-body {
    height: 520px;
  }
}

/* ---------- Tech logos in skill chips (user request: real logos) ---------- */
.skill-logo {
  width: 22px;
  height: 22px;
  object-fit: contain;
  flex-shrink: 0;
}

.skill-item:hover .skill-logo {
  /* keep colored logos legible on the primary-colored hover pill */
  filter: drop-shadow(0 0 2px rgba(255, 255, 255, 0.9));
}

/* ---------- Journey spaceship layer (user request, desktop only) ---------- */
/* v1 sets body{overflow-x:hidden}, which makes body a (non-scrolling) scroll
   container and silently disables every position:sticky descendant — including
   the spaceship render window. overflow-x:clip clips identically but creates
   no scroll container, so sticky works. */
body {
  overflow-x: clip !important;
}

.v3-journey-host {
  position: relative;
  /* user request: deep-space backdrop (cards keep their own colors).
     Intro/outro scroll room comes from spacer divs, NOT padding — padding
     would push the sticky canvas below the section top and hide the intro. */
  background: #05070d;
  /* no overflow:hidden — it would break the sticky canvas */
}

/* Sticky viewport-sized render window: rides along while the user scrolls
   through the tall section, so the ship is always in view */
.v3-journey-canvas {
  position: sticky;
  top: 0;
  height: 100vh;
  margin-bottom: -100vh; /* don't add height to the section */
  z-index: 0;
  pointer-events: none;
}

/* keep the timeline content above the ship */
.v3-journey-host .container {
  position: relative;
  z-index: 1;
}

/* heading/subtitle legible on the space backdrop in light themes too
   (html prefix outranks the per-palette solid-heading overrides) */
html .v3-journey-host .section-title,
html[data-palette] .v3-journey-host .section-title {
  background: none;
  -webkit-text-fill-color: currentColor;
  background-clip: border-box;
  color: #e6edf3;
}

.v3-journey-host .section-subtitle {
  color: #9ba8b7;
}

/* intro / outro overlay copy inside the sticky window */
.v3-journey-text {
  position: absolute;
  left: 50%;
  top: 24%;
  transform: translateX(-50%);
  z-index: 1;
  text-align: center;
  opacity: 0;
  pointer-events: none;
  width: min(90%, 640px);
}

.v3-journey-text h3 {
  font-size: clamp(1.8rem, 4vw, 3rem);
  color: #e6edf3;
  margin-bottom: 10px;
}

.v3-journey-text p {
  font-size: clamp(1rem, 2vw, 1.3rem);
  color: #9ba8b7;
}

.v3-journey-text-outro {
  top: 38%;
}

.v3-journey-inner {
  height: 100%;
}

/* heading + subtitle shown ABOVE the ride (user request) */
.v3-journey-header {
  position: relative;
  z-index: 1;
  text-align: center;
  padding-bottom: 10px;
}

/* scroll room for the intro (big ship + copy) and the departure moment.
   Taller than before (user request): slows the ride down and gives the
   outro breathing room so the ship doesn't leave right off the last card. */
.v3-journey-spacer-top {
  height: 100vh;
}

.v3-journey-spacer-bottom {
  height: 150vh;
}

/* wider gaps between timeline events so the ship can weave between them
   (user request) — also stretches the section, slowing the scrub further */
.v3-journey-host .timeline-item {
  margin-bottom: 220px;
}

.v3-journey-audio {
  /* bottom-LEFT: the right edge is occupied by the back-to-top/social
     floating buttons, which hid this control before (user report) */
  position: absolute;
  left: 24px;
  bottom: 24px;
  z-index: 3;
  pointer-events: auto;
  width: 44px;
  height: 44px;
  border-radius: 50%;
  border: 1px solid var(--border-color);
  background: var(--bg-card);
  color: var(--text-secondary);
  font-size: 1rem;
  display: flex;
  align-items: center;
  justify-content: center;
  transition: color 0.2s ease, border-color 0.2s ease, transform 0.2s ease;
}

.v3-journey-audio:hover {
  color: var(--primary-color);
  border-color: var(--primary-color);
  transform: scale(1.06);
}

/* ---------- Spline nexbot showcase (after hero) ---------- */
.v3-spline-section {
  padding: var(--section-padding, 100px 0);
  background: var(--bg-dark);
  overflow: hidden;
}

.v3-spline-grid {
  display: grid;
  grid-template-columns: minmax(0, 5fr) minmax(0, 7fr);
  align-items: center;
  gap: 40px;
}

.v3-spline-text .section-title {
  margin-bottom: 8px;
}

.v3-spline-subtitle {
  margin-bottom: 20px;
}

.v3-spline-text p {
  color: var(--text-secondary);
  line-height: 1.7;
  margin-bottom: 28px;
  max-width: 46ch;
}

.v3-spline-cta {
  display: inline-flex;
  align-items: center;
  gap: 8px;
}

.v3-spline-stage {
  /* user request: slightly larger stage; explicit light backdrop so the
     black robot stays visible in every theme */
  height: 620px;
  border-radius: 20px;
  overflow: hidden;
  position: relative;
  background: #e9e9ef;
}

/* user request: hide the scene's "NEXBOT" backdrop text and the Spline
   badge — the viewer is oversized and recentered so scene edges (text,
   bottom-right badge) fall outside the clipped stage */
/* No more oversize-crop: the "NEXBOT" backdrop lettering and the Spline
   badge are hidden via the viewer runtime instead (SplineShowcase effect),
   so the robot renders whole inside the stage (user report: was cut off). */
.v3-spline-stage spline-viewer {
  position: absolute;
  inset: 0;
  width: 100% !important;
  height: 100% !important;
}

/* v1's mobile-responsive.css ships `img[loading="lazy"] { opacity: 0 }`,
   half of a JS fade-in that v1 never wires up (v1 has zero lazy images, so
   the rule was inert). v3 DOES lazy-load images (project cards, MacBook
   showcase, case cards) and they'd stay invisible — neutralize it. */
img[loading="lazy"] {
  opacity: 1 !important;
}

/* Theme-switch circular reveal (View Transitions API): disable the UA's
   default cross-fade so the scripted expanding-circle clip in animations.js
   (v3ThemeTransition) is the only animation. */
::view-transition-old(root),
::view-transition-new(root) {
  animation: none;
  mix-blend-mode: normal;
}

/* ============================================================
   Theme palettes (user request). Base mode stays on [data-theme]
   (all v1 rules keep working); palettes remap the color variables
   on [data-palette]. Toggle cycles: dark → midnight → light → manuscript.
   ============================================================ */

/* Midnight — near-black blue + teal/indigo (dark mode palette) */
html[data-palette="midnight"] {
  --primary-color: #2dd4bf;
  --primary-dark: #14b8a6;
  --primary-light: #5eead4;
  --secondary-color: #818cf8;
  --accent-color: #818cf8;
  --bg-dark: #0b0f14;
  --bg-light: #121821;
  --bg-card: #1a222e;
  --text-primary: #e6edf3;
  --text-secondary: #9ba8b7;
  --text-muted: #7c8794;
  --border-color: #1f2935;
  --error-color: #ef4444;
  --shadow-glow: 0 0 20px rgba(45, 212, 191, 0.25);
  --glass-bg: rgba(18, 24, 33, 0.65);
  --glass-border: rgba(155, 168, 183, 0.12);
}

/* Manuscript — warm cream + terracotta (light mode palette)
   Reworked per user feedback: brighter terracotta family; gradients stay
   inside the terracotta→light-orange family (never terracotta+green);
   green (#6E7B4A) is a standalone accent only; #AE4A33 reserved for
   solid single-color uses (primary-dark). */
html[data-palette="manuscript"] {
  --primary-color: #c45e3a;
  --primary-dark: #ae4a33;
  --primary-light: #e5a26f;
  --secondary-color: #e5a26f;
  --accent-color: #6e7b4a;
  --bg-dark: #fbf8f1;
  --bg-light: #fffdf7;
  --bg-card: #fffdf7;
  --text-primary: #2b2622;
  --text-secondary: #6e635a;
  --text-muted: #8a7f73;
  --border-color: #e7dece;
  --error-color: #b91c1c;
  --shadow-sm: 0 2px 8px rgba(43, 38, 34, 0.06);
  --shadow-md: 0 4px 16px rgba(43, 38, 34, 0.08);
  --shadow-lg: 0 10px 40px rgba(43, 38, 34, 0.12);
  --shadow-glow: 0 0 20px rgba(174, 74, 51, 0.15);
  --glass-bg: rgba(255, 253, 247, 0.75);
  --glass-border: rgba(231, 222, 206, 0.6);
}

/* Manuscript professionalism (user request): headings are solid, never
   gradient-filled, in this palette only */
html[data-palette="manuscript"] .section-title {
  background: none;
  -webkit-text-fill-color: currentColor;
  background-clip: border-box;
  color: var(--text-primary);
}

html[data-palette="manuscript"] .gradient-text,
html[data-palette="manuscript"] .gpa-value {
  background: none;
  -webkit-text-fill-color: currentColor;
  background-clip: border-box;
  color: var(--primary-dark);
}

/* Neon — cyberpunk: space navy + electric cyan/purple/yellow (user request) */
html[data-palette="neon"] {
  --primary-color: #00e5ff;
  --primary-dark: #00b8cc;
  --primary-light: #66efff;
  --secondary-color: #b026ff;
  --accent-color: #e5ff00;
  --bg-dark: #0a0f1e;
  --bg-light: #121a2e;
  --bg-card: #18233d;
  --text-primary: #ffffff;
  --text-secondary: #a1a1aa;
  --text-muted: #8b8b94;
  --border-color: #243352;
  --error-color: #ef4444;
  --shadow-glow: 0 0 24px rgba(0, 229, 255, 0.35);
  --glass-bg: rgba(18, 26, 46, 0.65);
  --glass-border: rgba(0, 229, 255, 0.15);
}

/* ---------- Footer: large brand-colored social icons (user request) ---------- */

.v3-footer-social {
  display: flex;
  justify-content: center;
  gap: 22px;
  padding: 8px 0 28px;
}

.v3-footer-icon {
  width: 54px;
  height: 54px;
  border-radius: 50%;
  display: flex;
  align-items: center;
  justify-content: center;
  font-size: 1.5rem;
  color: #ffffff;
  transition: transform 0.25s ease, box-shadow 0.25s ease;
}

.v3-footer-icon:hover {
  transform: translateY(-5px) scale(1.08);
  box-shadow: 0 10px 24px rgba(0, 0, 0, 0.35);
}

/* user request: name pops up over each footer icon on hover */
.v3-footer-icon {
  position: relative;
}

.v3-footer-icon::after {
  content: attr(aria-label);
  position: absolute;
  bottom: calc(100% + 10px);
  left: 50%;
  transform: translateX(-50%) translateY(4px);
  background: var(--bg-card);
  color: var(--text-primary);
  border: 1px solid var(--border-color);
  border-radius: 8px;
  padding: 4px 10px;
  font-size: 0.8rem;
  white-space: nowrap;
  opacity: 0;
  pointer-events: none;
  transition: opacity 0.2s ease, transform 0.2s ease;
}

.v3-footer-icon:hover::after {
  opacity: 1;
  transform: translateX(-50%) translateY(0);
}

.v3-footer-linkedin { background: #0a66c2; }
.v3-footer-github { background: #24292e; }
.v3-footer-email { background: #ea4335; }
.v3-footer-youtube { background: #ff0000; }
/* solid teal — user request: no gradient, match solid-brand style of the rest */
.v3-footer-resume { background: #0d9488; }

@media (max-width: 767px) {
  .v3-footer-icon {
    width: 46px;
    height: 46px;
    font-size: 1.3rem;
  }

  .v3-footer-social {
    gap: 16px;
  }
}

/* ---------- Certification issuer logos (user request) ---------- */
.certification-icon.has-logo {
  background: #ffffff;
  border: 1px solid var(--border-color, rgba(148, 163, 184, 0.2));
  /* user request: bigger logo square, thinner white padding around the mark */
  width: 58px;
  height: 58px;
}

.certification-issuer-logo {
  width: 52px;
  height: 52px;
  object-fit: contain;
  border-radius: 10px;
}

/* user request: cert icon tiles lose the gradient in Neon + Manuscript only
   (solid primary instead); Dark/Midnight/Light keep the v1 gradient.
   Logo tiles (.has-logo) stay white everywhere. */
html[data-palette="neon"] .certification-icon:not(.has-logo),
html[data-palette="manuscript"] .certification-icon:not(.has-logo) {
  background: var(--primary-color);
}

/* user request: same for the Professional Development card icons — their
   accent→primary gradient turns green+terracotta in Manuscript and
   yellow+cyan in Neon; solid primary (terracotta / electric cyan) instead.
   Other themes keep the v1 gradient. */
/* Item 28: :not(.has-logo) added. A development entry can now carry a real
   org logo, and a logo tile must stay white in every theme (same rule the
   certification tiles follow) - a terracotta square behind a transparent
   Berkeley wordmark would erase the mark. */
html[data-palette="neon"] .development-icon:not(.has-logo),
html[data-palette="manuscript"] .development-icon:not(.has-logo) {
  background: var(--primary-color);
}

/* user request: Documents & Credentials icons (Resume / Unofficial
   Transcript / LinkedIn Profile) solid in the classic Dark + Light themes
   only — palettes (no [data-palette] attr set) keep their gradient. */
html:not([data-palette]) .document-icon {
  background: var(--primary-color);
}

/* ---------- Contact companion robot (user request) ---------- */
.v3-companion {
  position: relative;
  margin-top: 28px;
}

.v3-companion-stage {
  /* user report: robot was vertically cut off — taller stage, gentler crop */
  height: 440px;
  border-radius: 18px;
  overflow: hidden;
  position: relative;
  background: #e9e9ef; /* light backdrop keeps the robot visible in dark themes */
}

/* badge hidden in JS (hideSplineBadge); only a light framing crop remains */
.v3-companion-stage spline-viewer {
  position: absolute;
  width: 110% !important;
  height: 104% !important;
  left: -5%;
  top: -2%;
}

/* user request: the info column (with the robot) grew taller than the form —
   don't stretch the form to match; give it back its natural height */
#contact .contact-content {
  align-items: start;
}

/* Currently UNUSED (Session 7): the closing note was replaced by the
   NewsletterSignup card. Kept in case the user wants a quote back later. */
.v3-contact-note {
  margin-top: 24px;
  padding: 18px 22px;
  background: var(--bg-card);
  border: 1px solid var(--border-color);
  border-left: 3px solid var(--primary-color);
  border-radius: 12px;
  color: var(--text-secondary);
  font-style: italic;
  line-height: 1.7;
}

.v3-contact-note span {
  display: block;
  margin-top: 8px;
  color: var(--text-primary);
  font-style: normal;
  font-weight: 600;
}

/* ---------- Newsletter signup card (Session 7, user request) ----------
   Sits under the contact form inside .contact-form-wrapper, replacing the old
   closing note. Theme-driven vars keep it correct in all 5 palettes. */
.v3-sr-only {
  position: absolute;
  width: 1px;
  height: 1px;
  padding: 0;
  margin: -1px;
  overflow: hidden;
  clip: rect(0, 0, 0, 0);
  white-space: nowrap;
  border: 0;
}

.v3-newsletter {
  margin-top: 26px;
  padding: 22px 22px 20px;
  background: var(--glass-bg);
  border: 1px solid var(--border-color);
  border-top: 3px solid var(--primary-color);
  border-radius: 15px;
}

.v3-newsletter-head {
  display: flex;
  align-items: center;
  gap: 10px;
  margin-bottom: 14px;
}

.v3-newsletter-icon {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 30px;
  height: 30px;
  flex: 0 0 30px;
  border-radius: 50%;
  background: color-mix(in srgb, var(--primary-color) 18%, transparent);
  color: var(--primary-color);
  font-size: 0.85rem;
}

.v3-newsletter-eyebrow {
  font-size: 0.72rem;
  font-weight: 700;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--text-secondary);
}

.v3-newsletter-headline {
  font-size: 1.18rem;
  line-height: 1.35;
  color: var(--text-primary);
  margin: 0 0 10px;
}

.v3-newsletter-body {
  font-size: 0.95rem;
  line-height: 1.6;
  color: var(--text-secondary);
  margin: 0 0 16px;
}

.v3-newsletter-form {
  display: flex;
  gap: 10px;
  align-items: stretch;
}

.v3-newsletter-input {
  flex: 1 1 auto;
  min-width: 0;
  padding: 12px 14px;
  border-radius: 8px;
  border: 1px solid var(--border-color);
  background: var(--bg-card);
  color: var(--text-primary);
  font-size: 0.95rem;
  transition: border-color 0.2s ease, box-shadow 0.2s ease;
}

.v3-newsletter-input::placeholder {
  color: var(--text-secondary);
  opacity: 0.7;
}

.v3-newsletter-input:focus {
  outline: none;
  border-color: var(--primary-color);
  box-shadow: 0 0 0 3px color-mix(in srgb, var(--primary-color) 25%, transparent);
}

.v3-newsletter-input-bad,
.v3-newsletter-input-bad:focus {
  border-color: var(--error-color, #ef4444);
  box-shadow: 0 0 0 3px color-mix(in srgb, var(--error-color, #ef4444) 22%, transparent);
}

.v3-newsletter-btn {
  flex: 0 0 auto;
  white-space: nowrap;
  padding: 12px 22px;
  border: none;
  border-radius: 8px;
  background: linear-gradient(135deg, var(--primary-color), var(--secondary-color));
  color: #fff;
  font-weight: 600;
  font-size: 0.95rem;
  cursor: pointer;
  transition: transform 0.2s ease, box-shadow 0.2s ease, opacity 0.2s ease;
}

.v3-newsletter-btn:hover:not(:disabled) {
  transform: translateY(-2px);
  box-shadow: 0 8px 22px color-mix(in srgb, var(--primary-color) 40%, transparent);
}

.v3-newsletter-btn:disabled {
  opacity: 0.7;
  cursor: not-allowed;
}

.v3-newsletter-status {
  min-height: 20px;
  margin-top: 8px;
}

.v3-newsletter-error {
  font-size: 0.85rem;
  color: var(--error-color, #ef4444);
}

.v3-newsletter-fine {
  margin: 12px 0 0;
  font-size: 0.78rem;
  color: var(--text-secondary);
  opacity: 0.85;
}

.v3-newsletter-link {
  background: none;
  border: none;
  padding: 0;
  color: var(--primary-color);
  font: inherit;
  cursor: pointer;
  text-decoration: underline;
}

/* success / already-subscribed confirmation */
.v3-newsletter-done {
  padding: 6px 0;
  animation: v3NewsletterIn 0.4s ease both;
}

.v3-newsletter-done-title {
  font-size: 1.05rem;
  font-weight: 700;
  color: var(--text-primary);
  margin: 0 0 6px;
}

.v3-newsletter-done-title .fa-circle-check {
  color: var(--primary-color);
  margin-right: 6px;
}

.v3-newsletter-done-sub {
  font-size: 0.9rem;
  color: var(--text-secondary);
  margin: 0;
}

@keyframes v3NewsletterIn {
  from {
    opacity: 0;
    transform: scale(0.96);
  }
  to {
    opacity: 1;
    transform: scale(1);
  }
}

@media (prefers-reduced-motion: reduce) {
  .v3-newsletter-done {
    animation: none;
  }
  .v3-newsletter-btn:hover:not(:disabled) {
    transform: none;
  }
}

@media (max-width: 480px) {
  .v3-newsletter-form {
    flex-direction: column;
  }
}

.v3-companion-bubble {
  position: absolute;
  top: -14px;
  left: 50%;
  transform: translate(-50%, -100%);
  z-index: 3;
  background: var(--bg-card);
  color: var(--text-primary);
  border: 1px solid var(--border-color);
  border-radius: 16px;
  padding: 10px 16px;
  font-size: 0.95rem;
  white-space: nowrap;
  box-shadow: 0 8px 24px rgba(0, 0, 0, 0.18);
  animation: v3BubbleIn 0.35s ease;
}

.v3-companion-bubble::after {
  content: "";
  position: absolute;
  bottom: -7px;
  left: 50%;
  transform: translateX(-50%) rotate(45deg);
  width: 12px;
  height: 12px;
  background: var(--bg-card);
  border-right: 1px solid var(--border-color);
  border-bottom: 1px solid var(--border-color);
}

@keyframes v3BubbleIn {
  from { opacity: 0; transform: translate(-50%, calc(-100% + 8px)); }
  to   { opacity: 1; transform: translate(-50%, -100%); }
}

/* ---------- Hero (user requests) ---------- */
/* portrait: larger, rounded vertical rectangle; no floating frame */
.hero-image .image-wrapper .image-frame {
  display: none;
}

.hero-image .profile-img {
  width: min(460px, 94%);
  aspect-ratio: 4 / 5;
  object-fit: cover;
  border-radius: 28px;
}

/* particles opt-in toggle, bottom-left of hero */
.v3-particles-toggle {
  position: absolute;
  left: 24px;
  bottom: 24px;
  z-index: 5;
  width: 44px;
  height: 44px;
  border-radius: 50%;
  border: 1px solid var(--border-color);
  background: var(--bg-card);
  color: var(--text-secondary);
  font-size: 1rem;
  display: flex;
  align-items: center;
  justify-content: center;
  cursor: pointer;
  transition: color 0.2s, border-color 0.2s, transform 0.2s;
}

.v3-particles-toggle:hover {
  transform: scale(1.07);
}

.v3-particles-toggle.is-on {
  color: var(--primary-color);
  border-color: var(--primary-color);
}

/* ---------- ITEM 23: navbar theme switch (replaced the FloatingDock) ----------
   The bottom quick-nav dock is removed (the top navbar covers navigation).
   The navbar's theme-cycle circle becomes a segmented control: one segment per
   ENABLED theme, with a single indicator that slides between them.

   Why not the dock any more: magnify-on-hover is a macOS affordance for a long
   row of small targets. At two themes there is nothing to magnify, and the
   effect cost a pointer-move spring, a capsule that scaled on hover, and a
   tooltip layer to name icons that can now simply be labelled.

   Sizing is intentionally driven by content (the labels), not fixed widths, so
   re-enabling a third theme needs no CSS change. */

/* hide the legacy single-cycle button while the switch is mounted (desktop) */
@media (min-width: 1024px) and (pointer: fine) {
  html.v3-theme-switch-on .theme-toggle {
    display: none;
  }
}

/* Fix 1 (Session 7): declutter the navbar at wide widths. v1 caps .nav-container
   at 1200px so at 1440px the logo, links, and switch crowd the middle. Let the
   container use more width with edge padding: logo hugs the left, switch rails
   to the right, links breathe in between. Desktop + fine pointer only - mobile
   hamburger layout is untouched. */
@media (min-width: 1024px) and (pointer: fine) {
  .nav-container {
    max-width: 1400px;
    padding: 0 32px;
  }
  .nav-menu {
    margin-left: auto;
    margin-right: 36px;
  }
  html.v3-theme-switch-on .v3-theme-switch {
    margin-left: auto;
  }
}

/* Fixed height so nothing in the navbar can ever reflow on interaction - the
   old dock's whole complexity budget went on avoiding exactly this. */
.v3-theme-switch {
  display: inline-flex;
  align-items: center;
  height: 40px;
  margin-left: 28px;
}

.v3-switch-track {
  display: inline-flex;
  align-items: center;
  gap: 2px;
  height: 38px;
  padding: 4px;
  border-radius: 19px;
  background: var(--glass-bg);
  border: 1px solid var(--border-color);
  transition: border-color 0.25s ease;
}

.v3-switch-track:hover {
  border-color: var(--primary-color);
}

.v3-switch-seg {
  position: relative;
  display: inline-flex;
  align-items: center;
  gap: 7px;
  height: 30px;
  padding: 0 13px;
  border: 0;
  border-radius: 15px;
  background: transparent;
  color: var(--text-secondary);
  font: inherit;
  font-size: 0.82rem;
  font-weight: 600;
  line-height: 1;
  white-space: nowrap;
  cursor: pointer;
  /* No transform/scale on hover: the sliding thumb is the only motion, so the
     control reads as one moving part rather than three competing ones. */
  transition: color 0.2s ease;
}

.v3-switch-seg:hover {
  color: var(--text-primary);
}

.v3-switch-seg:focus-visible {
  outline: 2px solid var(--primary-color);
  outline-offset: 2px;
}

/* The travelling indicator. Painted BEHIND the icon and label via z-index
   rather than by DOM order, because motion's layoutId animation requires the
   element to be a sibling that mounts/unmounts inside the active segment. */
.v3-switch-thumb {
  position: absolute;
  inset: 0;
  border-radius: 15px;
  background: var(--primary-color);
  z-index: 0;
}

.v3-switch-icon,
.v3-switch-label {
  position: relative;
  z-index: 1;
  display: inline-flex;
  align-items: center;
}

/* White on --primary-color clears 4.5:1 in both palettes (terracotta #c45e3a
   and the dark theme's indigo), so one rule covers both themes. */
.v3-switch-seg-active,
.v3-switch-seg-active:hover {
  color: #ffffff;
}

/* Below the desktop breakpoint the switch never mounts, but guard anyway so a
   pointer-type change mid-session cannot leave a half-styled control. */
@media (max-width: 1023px), (pointer: coarse) {
  .v3-theme-switch {
    display: none;
  }
}

/* Mobile / coarse pointers: nexbot now renders here too (user request). Stack
   the text above a shorter stage; the robot follows touch-drag within its band. */
@media (max-width: 1023px), (pointer: coarse) {
  .v3-spline-grid {
    grid-template-columns: 1fr;
    text-align: center;
  }

  .v3-spline-text p {
    margin-left: auto;
    margin-right: auto;
  }

  .v3-spline-stage {
    height: 360px;
    margin-top: 8px;
  }
}

/* ---------- Technical Skills grid (Session 7, user request) ----------
   Lock the desktop layout to 3 columns and center the lone 4th card
   ("Tools & Frameworks") in the middle column of the second row instead of
   letting it sit flush-left. Below 1024px the v1 auto-fit responsive stacking
   is untouched. */
@media (min-width: 1024px) {
  .skills .skills-container {
    grid-template-columns: repeat(3, 1fr);
  }
  .skills .skills-container > .skill-category:nth-child(4) {
    grid-column: 2;
  }
}

/* ---------- Poster carousel (Future Feature #1) ----------
   [REMOVABLE]: delete this block + the <PosterCarousel /> line in page.tsx to
   remove the feature. A full-bleed black section that heads the Projects area:
   the legacy <h2>Featured Projects</h2> sits above it (shifted left), the
   "Explore my data science…" subtitle drops below it as the grid intro. Spread
   coverflow — one big poster centred, neighbours spaced toward the page edges. */
.v3-poster-mount {
  width: 100%;
}
/* Heading is a thin strip so the carousel gets the rest of the screen. */
#projects.projects {
  padding-top: 22px;
}
/* Featured heading: break full-bleed and left-align near the page edge. */
#projects > .container > .section-title {
  width: 100vw;
  margin-left: calc(50% - 50vw);
  margin-bottom: 6px;
  padding-left: clamp(20px, 4vw, 72px);
  text-align: left;
}
/* Full-bleed: break out of the .container padding so the black section + spaced
   posters use the whole browser width (body has overflow-x: clip). */
.v3-poster-carousel {
  width: 100vw;
  margin-left: calc(50% - 50vw);
  margin-right: calc(50% - 50vw);
  margin-bottom: 34px;
  padding: 8px 0 20px;
  background: #000;
  min-height: calc(100vh - 150px);
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  scroll-snap-align: start;
}
.v3-poster-stage {
  width: 100%;
}

/* Spread coverflow */
.v3-cf {
  width: 100%;
  display: flex;
  flex-direction: column;
  align-items: center;
  outline: none;
}
.v3-cf-stage {
  position: relative;
  width: 100%;
  height: clamp(360px, 56vh, 640px);
  display: flex;
  align-items: center;
  justify-content: center;
}
.v3-cf-card {
  position: absolute;
  height: 100%;
  aspect-ratio: 4 / 5;
  padding: 0;
  border: none;
  border-radius: 18px;
  overflow: hidden;
  background: #05070c;
  cursor: pointer;
  box-shadow: 0 24px 70px rgba(0, 0, 0, 0.6);
  transform-origin: center center;
  transition: transform 0.6s cubic-bezier(0.4, 0, 0.2, 1),
    opacity 0.6s ease, filter 0.6s ease;
  will-change: transform, opacity;
}
/* blurred cover of the same poster fills the letterbox so mixed ratios
   (4:5, 3:4, 9:16) all read as full cards with no black bars */
.v3-cf-blur {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
  transform: scale(1.15);
  filter: blur(26px) brightness(0.5) saturate(1.1);
  user-select: none;
  -webkit-user-drag: none;
}
/* the full poster, always fully visible */
.v3-cf-poster {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: contain;
  user-select: none;
  -webkit-user-drag: none;
}
.v3-cf-card.is-active {
  box-shadow: 0 36px 100px rgba(0, 0, 0, 0.8);
}
/* active-card parallax: shift only the (slightly over-scaled) poster */
.v3-cf-card.is-active .v3-cf-poster {
  transform: translate3d(calc(var(--tx, 0) / 40), calc(var(--ty, 0) / 40), 0) scale(1.04);
  transition: transform 0.18s ease-out;
}

.v3-cf-name {
  margin-top: 22px;
  min-height: 1.4em;
  color: #fff;
  font-family: "Playfair Display", Georgia, serif;
  font-size: clamp(1.4rem, 2.4vw, 2.2rem);
  font-weight: 600;
  letter-spacing: 0.01em;
  text-align: center;
}

.v3-cf-nav {
  margin-top: 20px;
  display: flex;
  justify-content: center;
  gap: 34px; /* space the arrows apart, centered */
}
/* white keys with black arrows for the premium look */
.v3-cf-arrow {
  width: 46px;
  height: 46px;
  display: grid;
  place-items: center;
  border-radius: 999px;
  background: #fff;
  border: none;
  color: #000;
  cursor: pointer;
  box-shadow: 0 6px 18px rgba(0, 0, 0, 0.35);
  transition: transform 0.2s ease, box-shadow 0.2s ease, background 0.2s ease;
}
.v3-cf-arrow:hover {
  transform: translateY(-2px);
  box-shadow: 0 10px 24px rgba(0, 0, 0, 0.45);
  background: #f1f1f1;
}
.v3-cf-arrow:active {
  transform: translateY(0);
}
.v3-cf-arrow svg {
  width: 23px;
  height: 23px;
  stroke-width: 2.2;
}

@media (prefers-reduced-motion: reduce) {
  .v3-cf-card,
  .v3-cf-card.is-active .v3-cf-poster {
    transition: none;
  }
}

/* Gentle scroll-snap so the Featured section lands cleanly when scrolled into.
   proximity (not mandatory) + align only on #projects keeps every other
   section unaffected. */
html {
  scroll-snap-type: y proximity;
}
#projects {
  scroll-snap-align: start;
  scroll-margin-top: 70px;
}

@media (max-width: 640px) {
  .v3-poster-carousel {
    min-height: calc(100vh - 90px);
  }
  .v3-cf-stage {
    height: clamp(300px, 52vh, 460px);
  }
  .v3-cf-arrow {
    width: 46px;
    height: 46px;
  }
}

/* ---------- Tech-stack Spline keyboard (Future Feature #7) ----------
   [REMOVABLE]: delete this block + the <SplineKeyboard /> line in
   SkillsSection.tsx. Desktop-only (component gates on pointer+width). */
.v3-keyboard {
  margin: 84px auto 8px; /* extra breathing room below the last skill card row */
  max-width: 1200px; /* wider frame: room for drag/rotate without edge clipping */
  text-align: center;
}
.v3-keyboard-stage {
  position: relative;
  width: 100%;
  height: 660px; /* taller frame: vertical rotation clearance (user request) */
  overflow: hidden;
  /* frameless: the scene's baked backdrop objects are hidden at runtime and
     the canvas is transparent, so the keyboard floats on the page background
     in both themes. */
  background: transparent;
}
.v3-keyboard-stage spline-viewer {
  display: block;
  position: absolute;
  /* oversize + crop: gives the render extra room when the user drags/rotates
     the keyboard AND pushes the in-canvas Spline watermark (bottom-right
     corner of the canvas) outside the visible frame. */
  width: 140%;
  height: 140%;
  left: -20%;
  top: -20%;
}

/* ---------- Typing keyboard, Contact section ----------
   [REMOVABLE]: delete this block + the <SplineTypingKeyboard /> line in
   page.tsx. Desktop-only (component gates on pointer+width). The scene is a
   white/orange keyboard, so it sits in an intentional dark framed panel that
   reads well in both themes. */
.v3-typekb {
  margin: 56px auto 0;
  max-width: 1150px; /* wider frame so the keyboard shows edge to edge (user request) */
  text-align: center;
}
.v3-typekb-caption {
  margin-bottom: 14px;
  font-size: 0.95rem;
  color: var(--text-secondary, #9aa3b2);
}
.v3-typekb-stage {
  position: relative;
  width: 100%;
  height: 500px; /* slightly larger keyboard (user request) */
  border-radius: 20px;
  overflow: hidden;
  background: #0a0c12;
  border: 1px solid color-mix(in srgb, var(--accent-primary, #6366f1) 25%, transparent);
  box-shadow: 0 20px 50px rgba(0, 0, 0, 0.35);
}
.v3-typekb-stage spline-viewer {
  display: block;
  position: absolute;
  /* oversize + crop pushes the in-canvas Spline watermark (bottom-right
     corner) outside the visible panel */
  width: 118%;
  height: 118%;
  left: -9%;
  top: -9%;
}

/* ---------- AI Concierge (Feature 5) ----------
   [REMOVABLE]: delete this block + the <ConciergeChat /> line in page.tsx +
   ConciergeChat.tsx. Launcher sits bottom-LEFT (the right edge hosts the
   .floating-buttons stack). Drawer is a right-side glass panel; full-screen
   sheet on mobile. */
/* user request 2026-07-18: the bottom-right corner belongs to the concierge
   alone — v1's floating stack (back-to-top / email / LinkedIn, built by
   premium-effects.js initFloatingActions) is retired, and the launcher
   moves from bottom-left to bottom-right. */
.floating-actions {
  display: none !important;
}
.v3-concierge-launcher {
  position: fixed;
  right: 22px;
  bottom: 22px;
  z-index: 1200;
  width: 52px;
  height: 52px;
  border-radius: 50%;
  border: 1px solid color-mix(in srgb, var(--primary-color, #6366f1) 45%, transparent);
  background: color-mix(in srgb, var(--bg-dark, #11151f) 88%, var(--primary-color, #6366f1));
  color: var(--text-primary, #eef1f6);
  font-size: 1.15rem;
  cursor: pointer;
  box-shadow: 0 8px 24px rgba(0, 0, 0, 0.3);
  transition: transform 0.15s ease;
}
.v3-concierge-launcher:hover { transform: scale(1.07); }
.v3-concierge-launcher::after {
  content: "";
  position: absolute;
  inset: -4px;
  border-radius: 50%;
  border: 1px solid color-mix(in srgb, var(--primary-color, #6366f1) 55%, transparent);
  animation: v3-concierge-pulse 2.6s ease-out infinite;
  pointer-events: none;
}
@keyframes v3-concierge-pulse {
  0% { transform: scale(1); opacity: 0.9; }
  70%, 100% { transform: scale(1.55); opacity: 0; }
}
.v3-concierge-drawer {
  position: fixed;
  top: 0;
  right: 0;
  bottom: 0;
  z-index: 1300;
  width: 420px;
  max-width: 100vw;
  display: flex;
  flex-direction: column;
  background: color-mix(in srgb, var(--bg-dark, #0b0f17) 82%, transparent);
  backdrop-filter: blur(18px);
  -webkit-backdrop-filter: blur(18px);
  border-left: 1px solid color-mix(in srgb, var(--primary-color, #6366f1) 22%, transparent);
  box-shadow: -18px 0 50px rgba(0, 0, 0, 0.35);
  animation: v3-concierge-slide 0.28s ease;
}
@keyframes v3-concierge-slide {
  from { transform: translateX(40px); opacity: 0; }
  to { transform: translateX(0); opacity: 1; }
}
.v3-concierge-head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 10px;
  padding: 16px 18px;
  border-bottom: 1px solid color-mix(in srgb, var(--text-primary, #fff) 10%, transparent);
}
.v3-concierge-title {
  display: flex;
  align-items: center;
  gap: 10px;
  color: var(--text-primary, #eef1f6);
  font-weight: 600;
}
.v3-concierge-title i { color: var(--primary-color, #6366f1); }
.v3-concierge-title small {
  display: block;
  font-weight: 400;
  font-size: 0.72rem;
  color: var(--text-secondary, #9aa3b2);
}
.v3-concierge-actions { display: flex; align-items: center; gap: 8px; }
.v3-concierge-clear {
  background: none;
  border: 1px solid color-mix(in srgb, var(--text-primary, #fff) 18%, transparent);
  color: var(--text-secondary, #9aa3b2);
  border-radius: 999px;
  padding: 4px 10px;
  font-size: 0.72rem;
  cursor: pointer;
}
.v3-concierge-clear:hover { color: var(--text-primary, #fff); }
.v3-concierge-close {
  background: none;
  border: none;
  color: var(--text-secondary, #9aa3b2);
  font-size: 1rem;
  cursor: pointer;
  padding: 6px 8px;
}
.v3-concierge-close:hover { color: var(--text-primary, #fff); }
.v3-concierge-list {
  flex: 1;
  overflow-y: auto;
  padding: 16px;
  display: flex;
  flex-direction: column;
  gap: 10px;
}
.v3-concierge-msg {
  max-width: 85%;
  padding: 10px 14px;
  border-radius: 14px;
  font-size: 0.88rem;
  line-height: 1.45;
  color: var(--text-primary, #eef1f6);
  white-space: pre-wrap;
  overflow-wrap: break-word;
}
.v3-concierge-msg--assistant {
  align-self: flex-start;
  background: color-mix(in srgb, var(--text-primary, #fff) 8%, transparent);
  border: 1px solid color-mix(in srgb, var(--text-primary, #fff) 8%, transparent);
  border-bottom-left-radius: 4px;
}
.v3-concierge-msg--user {
  align-self: flex-end;
  background: color-mix(in srgb, var(--primary-color, #6366f1) 26%, transparent);
  border: 1px solid color-mix(in srgb, var(--primary-color, #6366f1) 30%, transparent);
  border-bottom-right-radius: 4px;
}
.v3-concierge-typing { display: flex; gap: 5px; padding: 14px; }
.v3-concierge-typing span {
  width: 7px;
  height: 7px;
  border-radius: 50%;
  background: var(--text-secondary, #9aa3b2);
  animation: v3-concierge-bounce 1.1s infinite;
}
.v3-concierge-typing span:nth-child(2) { animation-delay: 0.18s; }
.v3-concierge-typing span:nth-child(3) { animation-delay: 0.36s; }
@keyframes v3-concierge-bounce {
  0%, 60%, 100% { transform: translateY(0); opacity: 0.5; }
  30% { transform: translateY(-4px); opacity: 1; }
}
.v3-concierge-chips {
  display: flex;
  flex-wrap: wrap;
  gap: 8px;
  padding: 0 16px 12px;
}
.v3-concierge-chips button {
  border: 1px solid color-mix(in srgb, var(--primary-color, #6366f1) 38%, transparent);
  background: color-mix(in srgb, var(--primary-color, #6366f1) 10%, transparent);
  color: var(--text-primary, #eef1f6);
  border-radius: 999px;
  padding: 7px 12px;
  font-size: 0.78rem;
  cursor: pointer;
  transition: background 0.15s ease;
}
.v3-concierge-chips button:hover {
  background: color-mix(in srgb, var(--primary-color, #6366f1) 22%, transparent);
}
.v3-concierge-inputrow {
  display: flex;
  gap: 8px;
  padding: 12px 16px 16px;
  border-top: 1px solid color-mix(in srgb, var(--text-primary, #fff) 10%, transparent);
}
.v3-concierge-inputrow textarea {
  flex: 1;
  resize: none;
  border-radius: 12px;
  border: 1px solid color-mix(in srgb, var(--text-primary, #fff) 16%, transparent);
  background: color-mix(in srgb, var(--text-primary, #fff) 5%, transparent);
  color: var(--text-primary, #eef1f6);
  padding: 10px 12px;
  font-size: 0.88rem;
  font-family: inherit;
  line-height: 1.4;
  max-height: 110px;
}
.v3-concierge-inputrow textarea:focus {
  outline: 2px solid color-mix(in srgb, var(--primary-color, #6366f1) 55%, transparent);
}
.v3-concierge-inputrow button {
  width: 42px;
  border-radius: 12px;
  border: none;
  background: var(--primary-color, #6366f1);
  color: #fff;
  cursor: pointer;
  font-size: 0.95rem;
}
.v3-concierge-inputrow button:disabled { opacity: 0.45; cursor: default; }
.v3-concierge-status {
  padding: 0 16px 12px;
  font-size: 0.75rem;
  color: var(--text-secondary, #9aa3b2);
  display: flex;
  align-items: center;
  gap: 6px;
}
.v3-concierge-status i { color: var(--primary-color, #6366f1); }
@media (max-width: 768px) {
  .v3-concierge-drawer { width: 100vw; border-left: none; }
  .v3-concierge-launcher { right: 16px; bottom: 16px; }
}
@media (prefers-reduced-motion: reduce) {
  .v3-concierge-launcher::after { animation: none; opacity: 0; }
  .v3-concierge-drawer { animation: none; }
  .v3-concierge-typing span { animation: none; opacity: 0.7; }
  .v3-concierge-launcher { transition: none; }
}

/* ---------- Scroll-frame sequence (Future Features 2-4) ----------
   [REMOVABLE]: styles the ScrollFrameSequence component (PcTeardown etc.).
   The component self-disables until frames exist, so this is inert until then. */
.v3-sfs {
  position: relative;
  width: 100%;
  background: var(--bg-primary, #0b0f14);
}
/* v1's section-reveal animation leaves a transform on the <section>, and a
   transformed ancestor silently breaks position:sticky (the canvas scrolled
   away instead of pinning). This section must never carry a transform. */
.v3-sfs.section-reveal {
  transform: none !important;
  opacity: 1 !important;
}
.v3-sfs-sticky {
  position: sticky;
  top: 0;
  height: 100vh;
  /* clear the fixed translucent navbar so frames are never tinted/cut on top */
  padding-top: 88px;
  box-sizing: border-box;
  display: flex;
  align-items: center;
  justify-content: center;
  overflow: hidden;
}
.v3-sfs-canvas {
  display: block;
  width: 100%;
  height: 100%;
}
.v3-sfs-beats {
  position: absolute;
  left: 6%;
  top: 50%;
  transform: translateY(-50%);
  max-width: 380px;
  pointer-events: none;
}
.v3-sfs-beat {
  opacity: 0.22;
  transform: translateY(10px);
  transition: opacity 0.45s ease, transform 0.45s ease;
}
.v3-sfs-beat.is-active {
  opacity: 1;
  transform: translateY(0);
}
.v3-sfs-beat h3 {
  font-size: clamp(1.3rem, 2vw, 1.9rem);
  font-weight: 700;
  color: var(--text-primary, #f5f7fa);
  margin: 0 0 6px;
}
.v3-sfs-beat p {
  font-size: 1.05rem;
  line-height: 1.5;
  color: var(--text-secondary, #aab2c0);
  margin: 0;
}
/* caption overlay: one centered lower-third line at a time */
.v3-sfs-captions {
  position: absolute;
  left: 50%;
  bottom: 7%;
  transform: translateX(-50%);
  width: min(88%, 720px);
  pointer-events: none;
}
.v3-sfs-caption {
  position: absolute;
  left: 0;
  right: 0;
  bottom: 0;
  margin: 0;
  text-align: center;
  font-size: clamp(1.15rem, 1.8vw, 1.6rem);
  font-weight: 600;
  letter-spacing: 0.01em;
  line-height: 1.4;
  color: #f5f7fa;
  text-shadow: 0 1px 2px rgba(0, 0, 0, 0.9), 0 2px 24px rgba(0, 0, 0, 0.75);
  opacity: 0;
  transform: translateY(14px);
  transition: opacity 0.5s ease, transform 0.5s ease;
}
.v3-sfs-caption.is-active {
  opacity: 1;
  transform: translateY(0);
}
@media (prefers-reduced-motion: reduce) {
  .v3-sfs-caption {
    transition: opacity 0.3s ease;
    transform: none;
  }
}
/* reduced-motion / mobile static fallback */
.v3-sfs-lite {
  padding: 48px 20px;
  text-align: center;
}
.v3-sfs-static {
  display: block;
  width: 100%;
  max-width: 900px;
  margin: 0 auto 28px;
  border-radius: 16px;
  object-fit: contain;
}
.v3-sfs-beatlist {
  list-style: none;
  padding: 0;
  margin: 0 auto;
  max-width: 640px;
  display: grid;
  gap: 16px;
  text-align: left;
}
.v3-sfs-beatlist li {
  display: flex;
  flex-direction: column;
  gap: 2px;
}
.v3-sfs-beatlist strong {
  color: var(--text-primary, #f5f7fa);
  font-size: 1.1rem;
}
.v3-sfs-beatlist span {
  color: var(--text-secondary, #aab2c0);
}

/* ---------- Bento "at a glance" (About) [REMOVABLE] ---------- */
.v3-bento {
  display: grid;
  grid-template-columns: repeat(4, 1fr);
  gap: 14px;
  margin-top: 42px;
}
.v3-bento-tile {
  display: flex;
  flex-direction: column;
  gap: 4px;
  padding: 18px 20px;
  border-radius: 16px;
  background: var(--bg-card, #1a222e);
  border: 1px solid var(--border-color, #1f2935);
  transition: transform 0.25s ease, box-shadow 0.25s ease;
}
.v3-bento-tile:hover {
  transform: translateY(-4px);
  box-shadow: var(--shadow-glow, 0 0 20px rgba(45, 212, 191, 0.25));
}
.v3-bento-wide { grid-column: span 2; }
.v3-bento-kicker {
  font-size: 0.72rem;
  text-transform: uppercase;
  letter-spacing: 0.12em;
  color: var(--primary-color, #2dd4bf);
}
.v3-bento-tile strong {
  color: var(--text-primary, #e6edf3);
  font-size: 1.05rem;
}
.v3-bento-tile span:not(.v3-bento-kicker),
.v3-bento-tile li {
  color: var(--text-secondary, #9ba8b7);
  font-size: 0.88rem;
}
.v3-bento-github ul { list-style: none; margin: 2px 0 0; padding: 0; display: grid; gap: 3px; }
.v3-bento-github em {
  font-style: normal;
  color: var(--primary-color, #2dd4bf);
  margin-right: 6px;
}
.v3-bento-github a { color: var(--primary-color, #2dd4bf); }
@media (max-width: 900px) {
  .v3-bento { grid-template-columns: repeat(2, 1fr); }
}
@media (max-width: 520px) {
  .v3-bento { grid-template-columns: 1fr; }
  .v3-bento-wide { grid-column: auto; }
}

/* ---------- MacBook scroll showcase [REMOVABLE] ----------
   Aceternity-faithful rig: 200vh scroll container, lid unfolds + scales
   1.5x + travels down the page. */
.v3-mac {
  min-height: 200vh;
  display: flex;
  flex-direction: column;
  align-items: center;
  overflow: hidden;
  padding-top: 12vh;
}
.v3-mac-rig {
  display: flex;
  flex-direction: column;
  align-items: center;
  perspective: 800px;
  transform: scale(0.9);
}
.v3-mac-title {
  text-align: center;
  font-size: clamp(1.6rem, 3vw, 2.6rem);
  font-weight: 700;
  color: var(--text-primary, #e6edf3);
  margin-bottom: 130px;
  line-height: 1.35;
}
.v3-mac-lidwrap {
  position: relative;
  z-index: 3; /* the traveling screen must layer OVER the base */
  perspective: 800px;
  width: 512px;
  height: 384px;
}
.v3-mac-lidback {
  position: absolute;
  inset: 0;
  border-radius: 16px;
  background: #010101;
  display: flex;
  align-items: center;
  justify-content: center;
  transform: perspective(800px) rotateX(-25deg) translateZ(0);
  transform-origin: bottom;
  transform-style: preserve-3d;
}
.v3-mac-logo {
  color: #7c8794;
  font-weight: 800;
  font-size: 2rem;
  letter-spacing: 0.1em;
}
.v3-mac-screenplate {
  position: absolute;
  inset: 0;
  z-index: 3; /* travels down OVER the base, aceternity-style */
  border-radius: 16px;
  background: #010101;
  padding: 10px;
}
.v3-mac-screen {
  width: 100%;
  height: 100%;
  border-radius: 10px;
  overflow: hidden;
  background: #000;
}
.v3-mac-slides {
  position: relative;
  width: 100%;
  height: 100%;
}
.v3-mac-slides img {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
  opacity: 0;
  transition: opacity 0.9s ease;
}
.v3-mac-slides img.is-active { opacity: 1; }
.v3-mac-base {
  position: relative;
  z-index: 1;
  width: 640px;
  border-radius: 4px 4px 20px 20px;
  background: linear-gradient(180deg, #272729, #17171a);
  padding: 14px 30px 18px;
  margin-top: -2px;
}
.v3-mac-notch {
  position: absolute;
  top: 0;
  left: 50%;
  transform: translateX(-50%);
  width: 120px;
  height: 8px;
  border-radius: 0 0 8px 8px;
  background: #0b0b0d;
}
.v3-mac-keys {
  display: grid;
  grid-template-columns: repeat(12, 1fr);
  gap: 4px;
  margin: 12px 0 10px;
}
.v3-mac-keys span {
  height: 13px;
  border-radius: 3px;
  background: #050506;
  border: 1px solid #2a2a2e;
}
.v3-mac-trackpad {
  width: 40%;
  height: 84px;
  margin: 0 auto;
  border-radius: 10px;
  background: #0b0b0d;
  box-shadow: inset 0 0 2px #1c1c1f;
}
.v3-mac-lite {
  min-height: 0;
  padding: 70px 20px;
}
.v3-mac-caption {
  font-size: 0.95rem;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--text-secondary, #9ba8b7);
  margin: 0 0 30px;
}
.v3-mac-lite-frame {
  width: min(92vw, 720px);
  aspect-ratio: 16 / 10;
  border-radius: 14px;
  overflow: hidden;
  border: 1px solid var(--border-color, #1f2935);
}

/* ---------- Case-study cards (Projects) [REMOVABLE] ---------- */
.v3-cases { margin-top: 64px; }
.v3-cases-title {
  font-size: 1.5rem;
  color: var(--text-primary, #e6edf3);
  margin: 0 0 22px;
}
.v3-cases-row {
  display: flex;
  gap: 18px;
  overflow-x: auto;
  padding: 4px 4px 18px;
  scroll-snap-type: x mandatory;
  scrollbar-width: thin;
}
.v3-cases-card {
  position: relative;
  flex: 0 0 240px;
  aspect-ratio: 3 / 4;
  border-radius: 20px;
  overflow: hidden;
  border: 1px solid var(--border-color, #1f2935);
  padding: 0;
  cursor: pointer;
  scroll-snap-align: start;
  background: var(--bg-card, #1a222e);
}
.v3-cases-card img {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
}
.v3-cases-card-label {
  position: absolute;
  inset: auto 0 0 0;
  display: flex;
  flex-direction: column;
  gap: 2px;
  padding: 40px 16px 14px;
  text-align: left;
  background: linear-gradient(180deg, transparent, rgba(5, 8, 12, 0.88));
}
.v3-cases-card-label em {
  font-style: normal;
  font-size: 0.72rem;
  text-transform: uppercase;
  letter-spacing: 0.1em;
  color: #9adbd2;
}
.v3-cases-card-label strong { color: #fff; font-size: 1.05rem; }
.v3-cases-overlay {
  position: fixed;
  inset: 0;
  z-index: 1400;
  background: rgba(4, 6, 10, 0.72);
  backdrop-filter: blur(6px);
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 4vh 4vw;
}
.v3-cases-sheet {
  position: relative;
  width: min(860px, 94vw);
  max-height: 88vh;
  overflow-y: auto;
  border-radius: 22px;
  background: var(--bg-card, #131a24);
  border: 1px solid var(--border-color, #1f2935);
}
.v3-cases-hero {
  width: 100%;
  aspect-ratio: 21 / 9;
  object-fit: cover;
  display: block;
}
.v3-cases-body { padding: 26px 30px 34px; }
.v3-cases-body h4 {
  margin: 0 0 8px;
  font-size: 1.4rem;
  color: var(--text-primary, #e6edf3);
}
.v3-cases-tech {
  color: var(--primary-color, #2dd4bf);
  font-size: 0.85rem;
  margin: 0 0 14px;
}
.v3-cases-body p { color: var(--text-secondary, #9ba8b7); line-height: 1.65; }
.v3-cases-close {
  position: absolute;
  top: 14px;
  right: 14px;
  z-index: 2;
  width: 36px;
  height: 36px;
  border-radius: 50%;
  border: none;
  cursor: pointer;
  background: rgba(0, 0, 0, 0.55);
  color: #fff;
}

/* ---------- Command palette (Ctrl/Cmd-K) [REMOVABLE] ---------- */
.v3-cmdk {
  position: fixed;
  inset: 0;
  z-index: 1500;
  background: rgba(4, 6, 10, 0.55);
  backdrop-filter: blur(4px);
  display: flex;
  justify-content: center;
  padding-top: 14vh;
}
.v3-cmdk-panel {
  width: min(560px, 92vw);
  max-height: 60vh;
  display: flex;
  flex-direction: column;
  border-radius: 16px;
  overflow: hidden;
  background: color-mix(in srgb, var(--bg-dark, #0b0f17) 88%, transparent);
  border: 1px solid color-mix(in srgb, var(--primary-color, #6366f1) 30%, transparent);
  box-shadow: 0 30px 80px rgba(0, 0, 0, 0.5);
  height: fit-content;
}
.v3-cmdk-input {
  border: none;
  outline: none;
  background: transparent;
  padding: 18px 20px;
  font-size: 1rem;
  color: var(--text-primary, #e6edf3);
  border-bottom: 1px solid var(--border-color, #1f2935);
}
.v3-cmdk-list {
  list-style: none;
  margin: 0;
  padding: 8px;
  overflow-y: auto;
}
.v3-cmdk-list button {
  width: 100%;
  display: flex;
  justify-content: space-between;
  align-items: center;
  padding: 10px 14px;
  border: none;
  border-radius: 10px;
  background: transparent;
  color: var(--text-primary, #e6edf3);
  font-size: 0.95rem;
  cursor: pointer;
  text-align: left;
}
.v3-cmdk-list button.is-selected {
  background: color-mix(in srgb, var(--primary-color, #6366f1) 18%, transparent);
}
.v3-cmdk-list em {
  font-style: normal;
  font-size: 0.75rem;
  color: var(--text-muted, #7c8794);
}
.v3-cmdk-empty { padding: 14px; color: var(--text-muted, #7c8794); }
.v3-cmdk-foot {
  padding: 10px 16px;
  font-size: 0.75rem;
  color: var(--text-muted, #7c8794);
  border-top: 1px solid var(--border-color, #1f2935);
}

/* ---------- Scroll rail [REMOVABLE] ---------- */
.v3-rail {
  position: fixed;
  right: 18px;
  top: 50%;
  transform: translateY(-50%);
  z-index: 1100;
  display: none;
  flex-direction: column;
  gap: 12px;
}
@media (min-width: 1024px) {
  .v3-rail { display: flex; }
}
.v3-rail-dot {
  width: 9px;
  height: 9px;
  border-radius: 50%;
  border: none;
  cursor: pointer;
  padding: 0;
  background: color-mix(in srgb, var(--text-secondary, #9ba8b7) 45%, transparent);
  transition: transform 0.2s ease, background 0.2s ease;
}
.v3-rail-dot:hover { transform: scale(1.4); }
.v3-rail-dot.is-active {
  background: var(--primary-color, #2dd4bf);
  transform: scale(1.5);
  box-shadow: 0 0 10px color-mix(in srgb, var(--primary-color, #2dd4bf) 60%, transparent);
}

/* ============================================================
   2026-07-19 polish batch (user requests)
   ============================================================ */

/* (1) Beyond the Code: "National 1st Place Winner" corner ribbon was
   clipped ("...nal 1st Place Winner"). Slide the band down-right so the
   whole diagonal chord sits inside the card, and tighten the type. */
.national-winner-banner {
  top: 20px;
  right: -42px;
  padding: 7px 46px;
  /* "National 1st Place Winner" physically cannot fit a 45-degree corner
     chord at readable size — swap in the short form via ::after (the DOM
     text is hidden with font-size:0, v1 markup untouched). */
  font-size: 0;
  letter-spacing: 0.6px;
  white-space: nowrap;
}
.national-winner-banner i {
  font-size: 0.72rem;
  margin-right: 4px;
}
.national-winner-banner::after {
  content: "1st in the Nation";
  font-size: 0.72rem;
}

/* (2) Manuscript hero: plain cream — kill the baked indigo radials
   (hardcoded rgba(99,102,241,...) in v1), the terracotta wash and the
   floating blobs. Name renders solid in the button's terracotta. */
html[data-palette="manuscript"] .hero::before,
html[data-palette="manuscript"] .hero::after,
html[data-palette="manuscript"] .hero-blobs {
  display: none !important;
}
html[data-palette="manuscript"] .hero-title .name {
  background: none;
  -webkit-text-fill-color: currentColor;
  background-clip: border-box;
  color: var(--primary-color);
}

/* (3) Hero portrait: larger and vertically centered (all themes). The
   wrapper is the sizing element in v1 (fixed 300px), so grow it. */
.hero .image-wrapper {
  width: clamp(320px, 28vw, 420px) !important;
  height: auto !important;
}
.hero .profile-img {
  width: 100% !important;
  height: auto !important;
}
.hero .hero-content {
  align-items: center;
}
.hero .hero-image {
  display: flex;
  align-items: center;
  justify-content: center;
  align-self: center;
}

/* (4) Light + Manuscript: the About stat cards, achievement badges and
   the Academic Excellence GPA card carry hardcoded dark-slate / indigo-pink
   washes. Replace with flat theme surfaces. */
html[data-theme="light"] .stat-item,
html[data-theme="light"] .badge-item,
html[data-theme="light"] .gpa-card {
  background: var(--bg-card, #fffdf7) !important;
  border: 1px solid var(--border-color, #e7dece);
  box-shadow: 0 4px 14px rgba(43, 38, 34, 0.08) !important;
}

/* (5a) Manuscript courses: Completed-card headers lose the indigo-pink
   wash (terracotta tint instead); enrolled cards keep their green. */
html[data-palette="manuscript"] .course-card:not(.in-progress):not(.upcoming) .course-card-header {
  background: color-mix(in srgb, var(--primary-color) 8%, transparent);
}
html[data-palette="manuscript"] .course-skill-tag {
  background: color-mix(in srgb, var(--primary-color) 12%, transparent);
  color: var(--primary-dark);
}
/* status pills solid, like Completed grades */
html[data-palette="manuscript"] .course-grade.in-progress {
  background: var(--accent-color);
  color: #ffffff;
}
html[data-palette="manuscript"] .course-grade.upcoming {
  background: var(--primary-dark);
  color: #ffffff;
}

/* (5b) Manuscript research tags (CFD, Navier-Stokes...) */
html[data-palette="manuscript"] .research-topic-tag {
  background: color-mix(in srgb, var(--primary-color) 12%, transparent);
  color: var(--primary-dark);
}

/* (5c) Manuscript cert tags: green-accent tint (distinct from terracotta
   course tags, still in palette) */
html[data-palette="manuscript"] .certification-skill {
  background: color-mix(in srgb, var(--accent-color) 16%, transparent);
  color: #4d5531;
}

/* (5d) Light + Manuscript: tags get a dark border so they separate from
   the card surface */
html[data-theme="light"] .course-skill-tag,
html[data-theme="light"] .research-topic-tag,
html[data-theme="light"] .certification-skill {
  border: 1px solid rgba(0, 0, 0, 0.45);
}

/* (5e) Light + Manuscript: card hovers lose the dark effect. Root cause
   found 2026-07-19: v1 swaps the card BACKGROUND to dark slate
   rgba(30,41,59,.8) on hover (a dark-theme-only design), not just the
   shadow. Pin both to theme surfaces on every card family. */
html[data-theme="light"] .skill-category:hover,
html[data-theme="light"] .certification-card:hover,
html[data-theme="light"] .development-card:hover,
html[data-theme="light"] .project-card:hover,
html[data-theme="light"] .course-card:hover,
html[data-theme="light"] .hobby-card:hover,
html[data-theme="light"] .stat-item:hover,
html[data-theme="light"] .badge-item:hover,
html[data-theme="light"] .award-item:hover {
  background: var(--bg-card, #fffdf7) !important;
  box-shadow: 0 12px 30px rgba(43, 38, 34, 0.14) !important;
}

/* (5f) Light + Manuscript: white-background pills need edges to separate from
   light surfaces — Technical Skills pills + achievement/award badges. */
html[data-theme="light"] .skill-item,
html[data-theme="light"] .badge-item,
html[data-theme="light"] .award-badge {
  border: 1px solid rgba(0, 0, 0, 0.35);
}

/* (7b) Light + Manuscript: "Currently Building / What I'm Working On"
   card drops the indigo-pink wash + animated purple border glow */
html[data-theme="light"] .currently-building-card {
  background: var(--bg-card, #fffdf7) !important;
  border: 1px solid var(--border-color, #e7dece) !important;
}
html[data-theme="light"] .currently-building-card::before {
  display: none;
}
html[data-theme="light"] .currently-building-card .currently-title,
html[data-theme="light"] .currently-building-card .currently-header {
  background: none;
}

/* ============================================================================
   2026-09-07 MANUSCRIPT PASS
   ============================================================================
   Context: the Manuscript palette was created by copying the dark theme and
   remapping colour variables. Gradients, glows and glass surfaces that read
   well on near-black were inherited wholesale onto cream, where they read as
   stray purple and mud. This block removes them, section by section, in page
   order.

   SCOPING RULE (non-negotiable): Manuscript and Dark are separate products.
   Anything cosmetic is keyed on html[data-palette="manuscript"] so Dark is
   provably untouched. The handful of rules that are deliberately shared say
   BOTH THEMES in their comment and are keyed on nothing.

   Reference: https://newsletter.parshvpatel.com - cream ground (#f6f1e7),
   ivory cards (#fffdf8), a visible rule between them (#e3d9c6), solid colour
   instead of gradient, and contrast coming from surface + border rather than
   from glow.
   ========================================================================= */

/* ---------------------------------------------------------------------------
   ITEM 2 + 3 + 13 (root fix) - surface contrast.
   The complaint "card background and page background look the same" was not a
   per-card problem: --bg-dark (#fbf8f1) and --bg-card (#fffdf7) differ by ~2%
   luminance, and every card family draws its surface from --glass-bg
   (75% alpha) with a --glass-border at 60% alpha, i.e. a near-invisible edge.
   Fixing the four variables fixes every card at once - About stat cards,
   badges, awards, GPA, courses, certifications, Professional Development,
   projects and hobbies - instead of patching thirty selectors that would then
   drift apart. Ground darkened to the newsletter's cream; cards made fully
   opaque; the rule darkened past the newsletter's because the ask was
   explicitly "more dark".
   ------------------------------------------------------------------------ */
html[data-palette="manuscript"] {
  --bg-dark: #f6f1e7;          /* page ground - was #fbf8f1 (too close to card) */
  --bg-light: #fffdf8;
  --bg-card: #fffdf8;
  --border-color: #d9cdb5;     /* was #e7dece */
  --glass-bg: #fffdf8;         /* was rgba(255,253,247,.75) - no see-through cards */
  --glass-border: #cdbfa4;     /* was rgba(231,222,206,.6) - now actually visible */
}

/* Cards sit on the ground with a real edge, not a glow. The 1px rule does the
   separating work; the shadow only lifts. */
html[data-palette="manuscript"] .stat-item,
html[data-palette="manuscript"] .badge-item,
html[data-palette="manuscript"] .gpa-card,
html[data-palette="manuscript"] .award-card,
html[data-palette="manuscript"] .info-item,
html[data-palette="manuscript"] .course-card,
html[data-palette="manuscript"] .certification-card,
html[data-palette="manuscript"] .development-card,
html[data-palette="manuscript"] .project-card,
html[data-palette="manuscript"] .hobby-card,
html[data-palette="manuscript"] .skill-category {
  /* ITEM 22 (2026-09-07): .terminal-window is deliberately NOT in this list.
     A terminal is a terminal in both themes - the dark shell IS the visual
     metaphor, and re-skinning it cream made it read as just another card.
     style.css already paints it #1a1a2e for every theme, so the correct fix
     was to delete the override rather than add a counter-override. */
  background: var(--bg-card) !important;
  border: 1px solid var(--glass-border) !important;
  box-shadow: 0 2px 10px rgba(43, 38, 34, 0.07) !important;
}

/* ITEM 3 - Resume / Transcript / LinkedIn. These carried a white-on-white
   rgba(255,255,255,.5) border, i.e. no perceptible edge at all. */
html[data-palette="manuscript"] .document-card {
  background: var(--bg-card) !important;
  border: 1px solid var(--glass-border) !important;
}
html[data-palette="manuscript"] .document-card:hover {
  background: var(--bg-card) !important;
  border-color: var(--primary-color) !important;
  box-shadow: 0 8px 22px rgba(43, 38, 34, 0.12) !important;
}

/* ---------------------------------------------------------------------------
   ITEM 1 - hero background.
   .hero::before/::after and .hero-blobs were already suppressed, but the
   purple wash survived because .hero-particles carries its OWN hardcoded
   indigo+pink radial gradient (style.css). That element is also the mount
   point for the opt-in Three.js constellation, so only the painted background
   is removed - the container keeps working.
   ------------------------------------------------------------------------ */
html[data-palette="manuscript"] .hero-particles {
  background: none !important;
}

/* ---------------------------------------------------------------------------
   ITEM 5 - Academic Excellence, GPA card.
   (a) .gpa-card::before is a hardcoded indigo radial glow.
   (b) 4.00 rendered in --primary-dark (#ae4a33), which reads muddy on cream.
       Moved to --primary-color (#c45e3a): same family, more light in it.
   ------------------------------------------------------------------------ */
html[data-palette="manuscript"] .gpa-card::before {
  display: none !important;
}
html[data-palette="manuscript"] .gpa-value {
  color: var(--primary-color);
  -webkit-text-fill-color: var(--primary-color);
}

/* ---------------------------------------------------------------------------
   ITEM 6 - the 4.00 / 9 / 8 / 5 stat numbers were gradient-filled text
   (background-clip:text + transparent fill). Solid terracotta instead; the
   existing manuscript rule covered .gradient-text and .gpa-value but not
   .stat-number, which is why these survived.
   ------------------------------------------------------------------------ */
html[data-palette="manuscript"] .stat-number,
html[data-palette="manuscript"] .academic-stats .stat-number,
html[data-palette="manuscript"] .stat-value {
  background: none !important;
  -webkit-text-fill-color: var(--primary-color) !important;
  background-clip: border-box !important;
  color: var(--primary-color) !important;
}

/* ---------------------------------------------------------------------------
   ITEM 8 - Completed Courses.
   (a) the letter-grade badge was a terracotta gradient; now solid.
   (b) hover painted an indigo border + indigo outer glow. Replaced with a
       dark neutral edge and a plain lift.
   ------------------------------------------------------------------------ */
html[data-palette="manuscript"] .course-grade {
  background: var(--primary-color) !important;
  color: #ffffff;
}
html[data-palette="manuscript"] .course-card:hover,
html[data-palette="manuscript"] .certification-card:hover,
html[data-palette="manuscript"] .project-card:hover,
html[data-palette="manuscript"] .development-card:hover,
html[data-palette="manuscript"] .skill-category:hover,
html[data-palette="manuscript"] .hobby-card:hover {
  background: var(--bg-card) !important;
  border-color: rgba(43, 38, 34, 0.55) !important;
  box-shadow: 0 10px 26px rgba(43, 38, 34, 0.14) !important;
}

/* ---------------------------------------------------------------------------
   ITEM 9 - My Journey timeline.
   Year pills were gradient-filled; dots alternated primary/secondary/accent
   via nth-child; the rail was a three-stop gradient that ended by blending
   terracotta into olive. All made solid: dots terracotta, rail olive.
   ------------------------------------------------------------------------ */
html[data-palette="manuscript"] .timeline-date {
  background: var(--primary-color) !important;
  color: #ffffff !important;
  -webkit-text-fill-color: #ffffff !important;
}
html[data-palette="manuscript"] .timeline-line {
  background: var(--accent-color) !important;
}
html[data-palette="manuscript"] .timeline-item .timeline-dot,
html[data-palette="manuscript"] .timeline-item:nth-child(3n+1) .timeline-dot,
html[data-palette="manuscript"] .timeline-item:nth-child(3n+2) .timeline-dot,
html[data-palette="manuscript"] .timeline-item:nth-child(3n) .timeline-dot,
html[data-palette="manuscript"] .timeline-future .timeline-dot {
  background: var(--primary-color) !important;
}

/* ---------------------------------------------------------------------------
   ITEM 12 - Professional Development status pills.
   The bright #22c55e green belonged to the dark theme. Olive (--accent-color,
   #6e7b4a) is the palette's own "positive" colour and is already used for the
   in-progress course pills, so status reads consistently across sections.
   ------------------------------------------------------------------------ */
html[data-palette="manuscript"] .development-status.completed {
  background: var(--accent-color) !important;
  color: #ffffff !important;
  border: 1px solid rgba(43, 38, 34, 0.25);
}
html[data-palette="manuscript"] .development-status.in-progress {
  background: var(--primary-color) !important;
  color: #ffffff !important;
  border: 1px solid rgba(43, 38, 34, 0.25);
}

/* ---------------------------------------------------------------------------
   ITEM 14 - Featured Projects. BOTH THEMES, deliberately: the ask was to move
   the heading up, tighten the strip above it and give the black band more of
   the page. That is a layout judgement that is correct in Dark too, and
   splitting it per-theme would make the two drift structurally.
   ------------------------------------------------------------------------ */
#projects.projects {
  padding-top: 6px;
}
#projects > .container > .section-title {
  margin-top: 0;
  margin-bottom: 0;
  line-height: 1.1;
}
.v3-poster-carousel {
  margin-top: 6px;
  padding: 4px 0 16px;
  min-height: calc(100vh - 104px);
}

/* ---------------------------------------------------------------------------
   ITEM 15 - project cards.
   (a) tag pills and the GitHub / Report icon buttons were borderless ivory on
       ivory. Given a dark edge, matching the treatment already applied to the
       Technical Skills pills and award badges.
   (b) .project-card::before is a hardcoded indigo wash that fades in on hover
       - the "ugly background effect" on Interlace, 3D Voxel and every other
       card. Suppressed. (Card hover surface + border are handled in ITEM 8.)
   ------------------------------------------------------------------------ */
html[data-palette="manuscript"] .project-tag,
html[data-palette="manuscript"] .project-link {
  border: 1px solid rgba(43, 38, 34, 0.55) !important;
}
html[data-palette="manuscript"] .project-card::before {
  display: none !important;
}

/* ---------------------------------------------------------------------------
   ITEM 18 - Poetry & Creative Writing.
   Gold (#eab308) competed with the gold medal badges and award icons stacked
   inside the very same card. Crimson #a62b37 is lifted from the newsletter
   palette: bright, literary, and unused anywhere else on the page.
   ------------------------------------------------------------------------ */
html[data-palette="manuscript"] .poetry-accent {
  background: #a62b37 !important;
}
html[data-palette="manuscript"] .poetry-icon {
  background: #a62b37 !important;
}

/* ---------------------------------------------------------------------------
   ITEM 19 - remaining gradient fills.
   The Fun Facts pill, the contact form's Send Message button and the
   newsletter's Count me in button all used the terracotta -> light-orange
   gradient. Applied to every .btn-primary rather than just those two, because
   a page with three solid buttons and one gradient one looks like a bug.
   ------------------------------------------------------------------------ */
html[data-palette="manuscript"] .fun-facts-label,
html[data-palette="manuscript"] .btn-primary,
html[data-palette="manuscript"] .v3-newsletter-btn {
  background: var(--primary-color) !important;
  color: #ffffff !important;
}
html[data-palette="manuscript"] .btn-primary:hover,
html[data-palette="manuscript"] .v3-newsletter-btn:hover {
  background: var(--primary-dark) !important;
}
/* The Fun Facts container itself inherited a dark-slate glass panel. */
html[data-palette="manuscript"] .fun-facts-container {
  background: var(--bg-card) !important;
  border: 1px solid var(--glass-border) !important;
}

/* ---------------------------------------------------------------------------
   ITEM 20 - the two Spline robot stages get a defined edge so they read as
   framed objects on cream rather than as floating cut-outs.
   ------------------------------------------------------------------------ */
html[data-palette="manuscript"] .v3-spline-stage,
html[data-palette="manuscript"] .v3-companion-stage {
  border: 1px solid rgba(43, 38, 34, 0.7);
  border-radius: 14px;
  overflow: hidden;
}

/* ---------------------------------------------------------------------------
   RESIDUAL INDIGO / PINK LEAKS
   Found by tests/e2e/theme-isolation.spec.ts "no indigo or pink survives
   anywhere on the page", which sweeps every element's computed style for the
   dark theme's rgb(99,102,241) and rgb(236,72,153). These are all the same
   root cause as the items above - rules copied from the dark theme and never
   re-scoped - they simply sit in sections that were not called out by name.
   ------------------------------------------------------------------------ */

/* ITEM 22 (2026-09-07): the .terminal-prompt override that used to live here is
   GONE on purpose. Its indigo #6366f1 is not a leaked dark-theme colour - it is
   the terminal's own palette, sitting on the terminal's own #1a1a2e shell, which
   both themes share. Recolouring the caret to terracotta made the prompt fight
   the green output text. The indigo sweep in theme-isolation.spec.ts exempts
   `terminal*` for exactly this reason. */

/* Enrolled-course headers and the Professional Development impact strip both
   used a teal -> indigo wash. Flat olive tint keeps them reading as "active"
   without importing a cold gradient. */
html[data-palette="manuscript"] .course-card.in-progress .course-card-header,
html[data-palette="manuscript"] .development-impact {
  background: color-mix(in srgb, var(--accent-color) 12%, transparent) !important;
}

/* Research field badges and their primary buttons take their colour from a
   per-field --field-color set inline by research.js, whose values are the dark
   theme's indigo and pink. Manuscript overrides the variable itself rather
   than each rule, so anything else keyed on --field-color follows along.
   Trade-off accepted: the per-field colour distinction is lost in this
   palette. Those hexes are unreadable here anyway, and field identity is
   already carried by the badge text. */
html[data-palette="manuscript"] .research-btn-primary {
  --field-color: var(--primary-color);
}
/* The badge needs the CONSUMING property overridden, not the variable:
   research.js assigns --field-color as an INLINE custom property
   (fieldBadge.style.setProperty), and an inline variable beats any stylesheet
   rule that tries to redefine it. Overriding `background` with !important wins
   instead, because research.css declares `background: var(--field-color)` as a
   normal declaration. Caught by the indigo/pink sweep in the E2E suite. */
html[data-palette="manuscript"] .research-field-badge {
  background: var(--primary-color) !important;
  color: #ffffff !important;
}
html[data-palette="manuscript"] .research-btn-primary:hover {
  box-shadow: 0 8px 16px rgba(43, 38, 34, 0.2);
}

/* ---------------------------------------------------------------------------
   ITEM 16 - the JARVIS "What I'm working on" showcase.
   The panel was an indigo -> pink wash with a white-alpha border (the "hard
   colour effect on the top margin"), and the project highlight row inside it
   was an indigo tint. Both become plain paper with the standard card edge.
   ------------------------------------------------------------------------ */
html[data-palette="manuscript"] .currently-building-showcase {
  background: var(--bg-card) !important;
  border: 1px solid var(--glass-border) !important;
}
html[data-palette="manuscript"] .project-highlight {
  background: color-mix(in srgb, var(--primary-color) 10%, transparent) !important;
  border-color: color-mix(in srgb, var(--primary-color) 30%, transparent) !important;
}
/* The header strip is the "hard colour effect on the top margin": a
   rgba(15,23,42,.6) dark-slate bar sitting on a cream card. It becomes a
   quiet tinted header separated by the same rule the card uses. */
html[data-palette="manuscript"] .currently-building-header {
  background: color-mix(in srgb, var(--primary-color) 7%, transparent) !important;
  border-bottom: 1px solid var(--glass-border) !important;
}
/* The ACTIVE badge was red-on-dark; on the light strip it read as a smudge.
   Olive matches the "positive / running" meaning used by the Completed and
   in-progress pills elsewhere, so status colour stays consistent site-wide. */
html[data-palette="manuscript"] .active-badge {
  background: color-mix(in srgb, var(--accent-color) 16%, transparent) !important;
  color: #4d5531 !important;
  border: 1px solid rgba(43, 38, 34, 0.2);
}
html[data-palette="manuscript"] .pulse-dot {
  background: var(--accent-color) !important;
}

/* ---------------------------------------------------------------------------
   ITEM 16 (layout) - JARVIS card spec rows.
   BOTH THEMES: this is new markup with no prior styling, so there is nothing
   theme-specific to preserve. Colours are drawn from variables only, which is
   what makes one rule correct in Dark and Manuscript alike. The card's
   Manuscript-only surface fixes live in the leaks block above.

   A definition list rather than divs: "Routing / Services / Infra" are labels
   for their values, which is exactly what <dl> means, and it gives screen
   readers the pairing for free.
   ------------------------------------------------------------------------ */
.project-kicker {
  font-size: 0.78rem;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--text-muted);
  margin-left: auto;
  white-space: nowrap;
}

.project-specs {
  margin: 0 0 16px;
  padding: 0;
  display: grid;
  gap: 8px;
}

.project-spec {
  display: grid;
  /* Fixed label column so the three values start on one optical line. */
  grid-template-columns: 82px 1fr;
  gap: 12px;
  align-items: baseline;
}

.project-spec dt {
  font-size: 0.72rem;
  font-weight: 700;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--text-muted);
}

.project-spec dd {
  margin: 0;
  font-size: 0.86rem;
  line-height: 1.55;
  color: var(--text-secondary);
}

.project-status-note {
  margin: 12px 0 0;
  font-size: 0.78rem;
  color: var(--text-muted);
  font-style: italic;
}

/* Stack the label above its value once the two-column grid would squeeze the
   text to a few words per line. */
@media (max-width: 600px) {
  .project-spec {
    grid-template-columns: 1fr;
    gap: 2px;
  }
  .project-kicker {
    margin-left: 0;
    flex-basis: 100%;
  }
}

/* ==========================================================================
   2026-09-07 follow-up pass (items 22-28)
   ========================================================================== */

/* ---------- ITEM 24 (BOTH THEMES): brand-coloured hero social icons ----------
   The footer already uses solid brand fills (.v3-footer-linkedin etc). The hero
   used a neutral --bg-card circle that then turned terracotta/indigo on hover,
   so the same three destinations looked like two different systems depending on
   where you were on the page. This reuses the footer's exact brand values so
   there is one source of truth for "what colour is LinkedIn".

   Unscoped on purpose: brand colours are brand colours. LinkedIn blue does not
   become a different blue because the page is cream. White glyphs stay legible
   on all three fills in both themes (contrast >= 4.6:1). */
.hero-social .social-link {
  color: #ffffff;
  border-color: transparent;
}

.hero-social .social-linkedin { background: #0a66c2; }
.hero-social .social-github { background: #24292e; }
.hero-social .social-email { background: #ea4335; }

/* Keep the lift, drop the colour change: hovering must not repaint a brand
   mark. Overrides .social-link:hover in style.css, which set the fill to
   var(--primary-color). */
.hero-social .social-linkedin:hover { background: #0a66c2; }
.hero-social .social-github:hover { background: #24292e; }
.hero-social .social-email:hover { background: #ea4335; }

.hero-social .social-link:hover {
  color: #ffffff;
  transform: translateY(-5px) scale(1.06);
  box-shadow: 0 10px 24px rgba(0, 0, 0, 0.28);
}

/* ---------- ITEM 25 (MANUSCRIPT ONLY): solid "PP" wordmark ----------
   style.css paints .nav-logo with a primary->secondary gradient clipped to the
   text. On Manuscript that ran terracotta into a colour with far less contrast
   against cream, so the second P visibly faded. Killing the clip needs BOTH the
   background reset and -webkit-text-fill-color: the fill property wins over
   `color` on its own, so setting only `color` would leave the text transparent.
   Dark keeps its gradient - it reads well on near-black and was not raised. */
html[data-palette="manuscript"] .nav-logo {
  background: none;
  -webkit-background-clip: border-box;
  background-clip: border-box;
  -webkit-text-fill-color: var(--primary-color);
  color: var(--primary-color);
}

/* ---------- ITEM 28 (BOTH THEMES): logo tiles on Professional Development ----
   Mirrors .certification-icon.has-logo so the two sections stay visually
   consistent; a white tile also guarantees the mark is legible whatever the
   palette, since most org logos are dark-on-transparent. */
.development-icon.has-logo {
  background: #ffffff;
  border: 1px solid var(--border-color, rgba(148, 163, 184, 0.2));
  /* Landscape, not the 50x50 square the icon tiles use. Both org marks here are
     WORDMARKS (Berkeley 215x68 = 3.2:1, AI4ALL 408x142 = 2.9:1). object-fit
     contain inside a square would have rendered them 50px wide and ~15px tall,
     which is a grey smudge rather than a logo. Sized so a ~3:1 mark lands at a
     readable ~26px cap height. Icon-only cards are untouched: .has-logo is only
     added when a logo is actually present. */
  width: 84px;
  height: 50px;
  padding: 4px 6px;
  flex-shrink: 0;
}

.development-org-logo {
  width: 100%;
  height: 100%;
  object-fit: contain;
}

/* ==========================================================================
   ITEM 27 (BOTH THEMES): Experience — Amazon SDE internship
   --------------------------------------------------------------------------
   Deliberately built from theme variables only, with ONE exception: the
   smile-arrow keeps Amazon's orange in both palettes, on the same reasoning as
   the brand-coloured social icons in item 24 — a brand mark that changes colour
   per theme stops being a brand mark. Everything else (surface, border, text,
   chips) resolves through --bg-card / --glass-border / --text-*, so the section
   themes itself and needs no html[data-palette] override at all.
   ========================================================================== */

.v3-experience {
  padding: var(--section-padding);
  background: var(--bg-dark);
}

.v3-xp-card {
  max-width: 900px;
  margin: 0 auto;
  padding: 34px 34px 30px;
  background: var(--bg-card);
  border: 1px solid var(--glass-border, var(--border-color));
  border-radius: 16px;
  box-shadow: 0 2px 10px rgba(0, 0, 0, 0.07);
}

.v3-xp-head {
  display: flex;
  align-items: center;
  gap: 20px;
  flex-wrap: wrap;
}

.v3-xp-mark {
  flex: 0 0 96px;
  width: 96px;
}

.v3-xp-mark svg {
  width: 100%;
  height: auto;
  display: block;
  overflow: visible;
}

/* Amazon orange in both themes; see the header note. */
.v3-xp-smile,
.v3-xp-smile-tip {
  stroke: #ff9900;
}

/* The draw. 260 comfortably exceeds the real path length, so the dash gap
   fully hides the stroke at rest; overshooting is safe, undershooting would
   leave a visible stub before the animation starts. */
.v3-xp-smile,
.v3-xp-smile-tip {
  stroke-dasharray: 260;
  stroke-dashoffset: 260;
}

.v3-xp-card.is-visible .v3-xp-smile {
  animation: v3-xp-draw 1.1s ease-out forwards;
}

.v3-xp-card.is-visible .v3-xp-smile-tip {
  /* Arrowhead lands after the curve, so it reads as one continuous stroke
     arriving rather than two shapes appearing together. */
  animation: v3-xp-draw 0.4s ease-out 0.9s forwards;
}

@keyframes v3-xp-draw {
  to {
    stroke-dashoffset: 0;
  }
}

/* Reduced motion: no draw, and the mark is simply present. The JS already
   skips the counters; this is the CSS half of the same promise. */
@media (prefers-reduced-motion: reduce) {
  .v3-xp-smile,
  .v3-xp-smile-tip {
    stroke-dashoffset: 0;
    animation: none !important;
  }
}

.v3-xp-title-group {
  flex: 1 1 320px;
  min-width: 0;
}

.v3-xp-role {
  margin: 0;
  font-size: 1.35rem;
  font-weight: 700;
  color: var(--text-primary);
  line-height: 1.25;
}

.v3-xp-org {
  margin: 4px 0 0;
  font-size: 1rem;
  font-weight: 600;
  color: var(--primary-color);
}

.v3-xp-dot {
  opacity: 0.5;
  margin: 0 6px;
}

.v3-xp-meta {
  margin: 3px 0 0;
  font-size: 0.88rem;
  color: var(--text-secondary);
}

.v3-xp-metrics {
  display: grid;
  grid-template-columns: repeat(4, 1fr);
  gap: 14px;
  margin: 26px 0 22px;
  padding: 20px 0;
  border-top: 1px solid var(--glass-border, var(--border-color));
  border-bottom: 1px solid var(--glass-border, var(--border-color));
}

.v3-xp-metric {
  text-align: center;
  min-width: 0;
}

.v3-xp-metric-value {
  display: block;
  font-size: 1.75rem;
  font-weight: 800;
  line-height: 1.1;
  color: var(--primary-color);
  /* Tabular figures stop the counter jittering horizontally while it counts. */
  font-variant-numeric: tabular-nums;
}

.v3-xp-metric-label {
  display: block;
  margin-top: 5px;
  font-size: 0.78rem;
  letter-spacing: 0.02em;
  color: var(--text-secondary);
}

.v3-xp-points {
  margin: 0;
  padding-left: 20px;
  display: grid;
  gap: 11px;
}

.v3-xp-points li {
  color: var(--text-secondary);
  line-height: 1.65;
  font-size: 0.95rem;
}

.v3-xp-points strong {
  color: var(--text-primary);
}

.v3-xp-stack {
  display: flex;
  flex-wrap: wrap;
  gap: 8px;
  margin: 22px 0 0;
}

.v3-xp-chip {
  padding: 5px 11px;
  border-radius: 999px;
  font-size: 0.78rem;
  font-weight: 600;
  color: var(--text-secondary);
  background: transparent;
  border: 1px solid var(--glass-border, var(--border-color));
}

.v3-xp-note {
  margin: 22px 0 0;
  padding-left: 14px;
  border-left: 3px solid var(--primary-color);
  font-size: 0.92rem;
  font-style: italic;
  line-height: 1.6;
  color: var(--text-secondary);
}

@media (max-width: 760px) {
  .v3-xp-card {
    padding: 24px 20px 22px;
  }
  .v3-xp-metrics {
    grid-template-columns: repeat(2, 1fr);
    gap: 18px 10px;
  }
  .v3-xp-mark {
    flex-basis: 72px;
    width: 72px;
  }
  .v3-xp-role {
    font-size: 1.15rem;
  }
  .v3-xp-metric-value {
    font-size: 1.45rem;
  }
}

/* ---------- Navbar fit at 1024-1279px (found by probe, 2026-09-08) ----------
   The switch mounts at min-width:1024px, but at that width the bar has to hold
   a logo, TEN nav links (item 27 added Experience) and the control. Measured at
   1024px: the menu alone was 953px inside a 960px content box, pushing the
   switch to x=1057 — completely off-screen.

   Two changes, cheapest first:
   1. Below 1280px the switch drops its text labels and becomes icon-only
      (219px -> ~92px). The sliding indicator, the aria-labels and the native
      title tooltips all survive, so nothing but the visible text is lost.
   2. The link gap tightens from 30px to 18px in the same band.

   Raising the mount breakpoint instead was rejected: it would hand 1024-1279px
   users the legacy single-cycle button, which is a worse control, to solve a
   problem that is really just about text width. */
@media (min-width: 1024px) and (max-width: 1279px) and (pointer: fine) {
  .nav-menu {
    gap: 18px;
    margin-right: 18px;
  }

  .nav-container {
    padding: 0 20px;
  }

  .v3-theme-switch {
    margin-left: 14px;
  }

  .v3-switch-label {
    /* Not display:none — the accessible name comes from aria-label on the
       button, and the title attribute still gives a hover tooltip. */
    display: none;
  }

  .v3-switch-seg {
    padding: 0 10px;
  }
}

/* ==========================================================================
   ANIME LAB (BOTH THEMES) — the Anime hobby card, made concrete
   --------------------------------------------------------------------------
   Five 16:9 preview cards under Beyond the Code, each opening a franchise
   collection on anime.parshvpatel.com.

   Built entirely from theme variables, so it needs no per-palette override and
   cannot leak a dark-theme colour onto the cream ground. The one fixed colour
   is the scrim behind the scene-count pill, which sits on top of a photograph
   in both themes and therefore has to be dark either way.
   ========================================================================== */

.v3-anime-lab {
  margin: 56px auto 0;
  max-width: 1100px;
}

.v3-anime-head {
  text-align: center;
  margin-bottom: 26px;
}

.v3-anime-title {
  margin: 0 0 6px;
  font-size: 1.5rem;
  font-weight: 700;
  color: var(--text-primary);
}

.v3-anime-sub {
  margin: 0 auto;
  max-width: 62ch;
  font-size: 0.94rem;
  line-height: 1.6;
  color: var(--text-secondary);
}

.v3-anime-sub a {
  color: var(--primary-color);
  text-decoration: underline;
  text-underline-offset: 3px;
}

.v3-anime-grid {
  display: grid;
  /* 300px, not 250px: at the 1100px container width 250px yields four columns,
     which strands the fifth card alone on its own row. 300px gives 3 + 2,
     which reads as a deliberate arrangement rather than an overflow. */
  grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));
  gap: 18px;
}

.v3-anime-card {
  display: flex;
  flex-direction: column;
  text-decoration: none;
  background: var(--bg-card);
  border: 1px solid var(--glass-border, var(--border-color));
  border-radius: 14px;
  overflow: hidden;
  transition: border-color 0.22s ease, transform 0.22s ease, box-shadow 0.22s ease;
}

.v3-anime-card:hover,
.v3-anime-card:focus-visible {
  border-color: var(--primary-color);
  transform: translateY(-4px);
  box-shadow: 0 12px 28px rgba(0, 0, 0, 0.18);
}

.v3-anime-card:focus-visible {
  outline: 2px solid var(--primary-color);
  outline-offset: 3px;
}

.v3-anime-shot {
  position: relative;
  display: block;
  aspect-ratio: 16 / 9;
  overflow: hidden;
  background: #000;
}

.v3-anime-shot img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  display: block;
  transition: transform 0.4s ease;
}

.v3-anime-card:hover .v3-anime-shot img,
.v3-anime-card:focus-visible .v3-anime-shot img {
  transform: scale(1.05);
}

/* Sits on artwork in both themes, so it carries its own dark scrim rather
   than a theme colour. */
.v3-anime-count {
  position: absolute;
  right: 10px;
  bottom: 10px;
  padding: 3px 9px;
  border-radius: 999px;
  background: rgba(10, 10, 12, 0.72);
  color: #fff;
  font-size: 0.72rem;
  font-weight: 700;
  letter-spacing: 0.02em;
}

.v3-anime-meta {
  display: block;
  padding: 13px 15px 15px;
}

.v3-anime-name {
  display: block;
  font-weight: 700;
  font-size: 1rem;
  color: var(--text-primary);
}

.v3-anime-blurb {
  display: block;
  margin-top: 3px;
  font-size: 0.83rem;
  line-height: 1.5;
  color: var(--text-secondary);
}

.v3-anime-all {
  display: block;
  width: fit-content;
  margin: 24px auto 0;
  padding: 10px 22px;
  border-radius: 999px;
  border: 1px solid var(--primary-color);
  color: var(--primary-color);
  font-weight: 600;
  font-size: 0.9rem;
  text-decoration: none;
  transition: background-color 0.2s ease, color 0.2s ease;
}

.v3-anime-all:hover,
.v3-anime-all:focus-visible {
  background: var(--primary-color);
  color: #fff;
}

@media (prefers-reduced-motion: reduce) {
  .v3-anime-card,
  .v3-anime-shot img {
    transition: none;
  }
  .v3-anime-card:hover,
  .v3-anime-card:focus-visible {
    transform: none;
  }
  .v3-anime-card:hover .v3-anime-shot img {
    transform: none;
  }
}

@media (max-width: 560px) {
  .v3-anime-grid {
    grid-template-columns: 1fr;
  }
}
