/**
 * components.css
 * ─────────────────────────────────────────────────────────────────
 * PRD Section 2.7: Micro-interaction Standards
 * PRD Section 4.5: LinkedIn accessibility requirements
 * PRD Section 0:   Anti-AI-Design principles
 *
 * Contents:
 *   1.  LinkedIn Link (PRD 4.5 exact spec)
 *   2.  Buttons — clip-path reveal + border-crawl (NO scale)
 *   3.  Underline Links (grows left → right)
 *   4.  Tags / Badges
 *   5.  Project Cards
 *   6.  Skeleton Loaders
 *   7.  Scroll Progress Bar (PRD 5.5)
 *   8.  Back-to-Top Button
 *   9.  Navigation
 *  10.  Theme Swatch Selector (PRD 4.4)
 *  11.  Language Toggle (PRD 4.3)
 *  12.  Section Hero Wrapper
 *  13.  Scroll Progress Indicator
 *  14.  Utility: Accent Divider
 *
 * ❌ transition: all  — NEVER used here
 * ❌ transform: scale — NEVER used for hover
 * ✅ cubic-bezier(0.16, 1, 0.3, 1) on every transition
 * ✅ Touch targets ≥ 44px on all interactive elements
 */

/* ═══════════════════════════════════════════════
   1. LINKEDIN LINK  (PRD Section 4.5)
   Exact HTML pattern:
     <a href="..." target="_blank" rel="noopener noreferrer"
        aria-label="Jihoon Kim LinkedIn 프로필 보기 (새 탭에서 열림)"
        class="linkedin-link" data-track="linkedin_click">
       <svg aria-hidden="true" focusable="false">...</svg>
       <span class="link-text">LinkedIn</span>
       <svg aria-hidden="true" focusable="false" class="icon-external">...</svg>
     </a>

   Requirements:
   • Touch target: ≥ 44×44px
   • Never icon-only — .link-text always visible
   • Contrast: ≥ 4.5:1 on all 4 themes (Polypane verified)
   • Focus ring: 2px solid accent, 3px offset
   ═══════════════════════════════════════════════ */

.linkedin-link {
  display: inline-flex;
  align-items: center;
  gap: var(--sp-2);
  min-height: var(--touch-min);     /* PRD: ≥ 44px touch target */
  min-width:  var(--touch-min);
  padding: var(--sp-2) var(--sp-5);
  border: 1px solid rgba(var(--accent-rgb), 0.3);
  border-radius: var(--radius-md);
  color: var(--text-primary);
  font-family: var(--font-mono);
  font-size: var(--text-small);
  font-weight: 500;
  letter-spacing: var(--tracking-wide);
  position: relative;
  overflow: hidden;

  /* Clip-path fill — same technique as .btn-primary */
  transition:
    color        var(--dur-normal) var(--ease-out-expo),
    border-color var(--dur-normal) var(--ease-out-expo);
}

/* Hover fill layer */
.linkedin-link::before {
  content: "";
  position: absolute;
  inset: 0;
  background: var(--accent);
  clip-path: inset(0 100% 0 0);
  z-index: 0;
  transition: clip-path var(--dur-normal) var(--ease-out-expo);
}

.linkedin-link > * {
  position: relative;
  z-index: 1;
}

.linkedin-link:hover {
  color: var(--bg-base);
  border-color: var(--accent);
}

.linkedin-link:hover::before {
  clip-path: inset(0 0% 0 0);
}

/* LinkedIn SVG icon — sized for readability */
.linkedin-link svg:first-child {
  width: 18px;
  height: 18px;
  flex-shrink: 0;
  fill: currentColor;
}

/* External link indicator — small ↗ icon */
.linkedin-link .icon-external {
  width: 12px;
  height: 12px;
  flex-shrink: 0;
  fill: currentColor;
  opacity: 0.6;
  transition: opacity var(--dur-fast) var(--ease-out-expo);
}

.linkedin-link:hover .icon-external {
  opacity: 1;
}

/* .link-text — NEVER hidden (PRD 4.5: never icon-only) */
.linkedin-link .link-text {
  white-space: nowrap;
}

/* Focus ring override (PRD 4.5: 2px accent, 3px offset) */
.linkedin-link:focus-visible {
  outline: var(--focus-ring);
  outline-offset: var(--focus-ring-offset);
}

/* ═══════════════════════════════════════════════
   2. BUTTONS (PRD 2.7: clip-path + border-crawl)
   ❌ NO scale(1.05)
   ❌ NO transition: all
   ❌ NO ease
   ✅ cubic-bezier(0.16, 1, 0.3, 1) on all properties
   ═══════════════════════════════════════════════ */

/* ── Base button reset ── */
.btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: var(--sp-2);
  min-height: var(--touch-min);      /* PRD 4.6: ≥ 44px touch target */
  padding: var(--sp-3) var(--sp-7);
  font-family: var(--font-mono);
  font-size: var(--text-small);
  font-weight: 500;
  letter-spacing: var(--tracking-label);
  text-transform: uppercase;
  white-space: nowrap;
  cursor: pointer;
  position: relative;
  overflow: hidden;
  border-radius: var(--radius-sm);   /* barely rounded — technical aesthetic */
  text-decoration: none;
  border: none;

  /* Focus ring */
  transition:
    color        var(--dur-normal) var(--ease-out-expo),
    border-color var(--dur-normal) var(--ease-out-expo),
    box-shadow   var(--dur-normal) var(--ease-out-expo);
}

.btn:focus-visible {
  outline: var(--focus-ring);
  outline-offset: var(--focus-ring-offset);
}

/* All child elements sit above fill/bg layers */
.btn > * {
  position: relative;
  z-index: 1;
}

/* ── Button A: Primary — clip-path fill reveal (left → right) ── */
/*
 * Technique: ::before pseudo-element holds the fill color.
 * clip-path: inset(0 100% 0 0) = fully clipped (invisible, right-side clamp)
 * clip-path: inset(0 0% 0 0)   = fully visible (no clipping)
 * Effect: fill sweeps in from left on hover.
 */
.btn-primary {
  border: 1px solid var(--accent);
  color: var(--accent);
  background: transparent;
}

.btn-primary::before {
  content: "";
  position: absolute;
  inset: 0;
  background: var(--accent);
  clip-path: inset(0 100% 0 0);
  z-index: 0;
  transition: clip-path var(--dur-normal) var(--ease-out-expo);
}

.btn-primary:hover {
  color: var(--bg-base);
  box-shadow: var(--accent-glow);
}

.btn-primary:hover::before {
  clip-path: inset(0 0% 0 0);
}

.btn-primary:active::before {
  clip-path: inset(0 0% 0 0);
  opacity: 0.85;
}

/* ── Button B: Outline — border-crawl animation ── */
/*
 * Technique: 4 linear-gradient background layers placed on each edge.
 * Initial: background-size 0 (invisible on all 4 sides).
 * Hover: all 4 sides simultaneously expand to full dimension.
 * No existing border — the background IS the border.
 * Effect: border appears to draw itself on hover.
 */
.btn-outline {
  color: var(--text-secondary);
  background-color: transparent;
  background-image:
    linear-gradient(to right, var(--accent), var(--accent)),   /* top */
    linear-gradient(to bottom, var(--accent), var(--accent)),  /* right */
    linear-gradient(to left, var(--accent), var(--accent)),    /* bottom */
    linear-gradient(to top, var(--accent), var(--accent));     /* left */
  background-repeat: no-repeat;
  background-position:
    top    left,   /* top edge: starts top-left, grows right */
    top    right,  /* right edge: starts top-right, grows down */
    bottom right,  /* bottom edge: starts bottom-right, grows left */
    bottom left;   /* left edge: starts bottom-left, grows up */
  background-size:
    0   1px,   /* top: full width, 1px height, initially 0 wide */
    1px 0,     /* right: 1px width, full height, initially 0 tall */
    0   1px,   /* bottom: full width, 1px height, initially 0 wide */
    1px 0;     /* left: 1px width, full height, initially 0 tall */
  transition:
    background-size var(--dur-slow)   var(--ease-out-expo),
    color           var(--dur-normal) var(--ease-out-expo);
}

.btn-outline:hover {
  color: var(--text-primary);
  background-size:
    100% 1px,
    1px  100%,
    100% 1px,
    1px  100%;
}

/* ── Button C: Ghost — minimal, text + accent on hover ── */
.btn-ghost {
  color: var(--text-tertiary);
  background: transparent;
  padding-left: var(--sp-2);
  padding-right: var(--sp-2);
  transition:
    color var(--dur-normal) var(--ease-out-expo);
}

.btn-ghost:hover {
  color: var(--accent);
}

/* ── Button D: Accent Fill — solid background ── */
.btn-accent {
  color: var(--bg-base);
  background: var(--accent);
  border: 1px solid var(--accent);
  transition:
    opacity   var(--dur-normal) var(--ease-out-expo),
    box-shadow var(--dur-normal) var(--ease-out-expo);
}

.btn-accent:hover {
  opacity: 0.88;
  box-shadow: var(--accent-glow);
}

/* ── Button icon alignment ── */
.btn svg {
  width: 16px;
  height: 16px;
  fill: currentColor;
  flex-shrink: 0;
}

/* ── Button size modifiers ── */
.btn-sm {
  min-height: 36px;
  padding: var(--sp-2) var(--sp-4);
  font-size: var(--text-meta);
}

.btn-lg {
  min-height: 56px;
  padding: var(--sp-4) var(--sp-10);
  font-size: var(--text-body);
  letter-spacing: var(--tracking-wide);
}

/* ── Disabled state ── */
.btn:disabled,
.btn[aria-disabled="true"] {
  opacity: 0.35;
  cursor: not-allowed;
  pointer-events: none;
}

/* ── Loading state — spinner inside button ── */
.btn.is-loading {
  pointer-events: none;
  color: transparent;
}

.btn.is-loading::after {
  content: "";
  position: absolute;
  width: 16px;
  height: 16px;
  border: 2px solid transparent;
  border-top-color: currentColor;
  border-radius: 50%;
  animation: btn-spin 0.7s linear infinite;
}

@keyframes btn-spin {
  to { transform: rotate(360deg); }
}

/* ═══════════════════════════════════════════════
   3. UNDERLINE LINKS (PRD 2.7: grows left → right)
   scaleX: 0→1, transform-origin: left
   ═══════════════════════════════════════════════ */

.link-underline {
  position: relative;
  display: inline-block;
  color: var(--text-secondary);
  transition: color var(--dur-normal) var(--ease-out-expo);
}

/* The underline is a ::after pseudo-element */
.link-underline::after {
  content: "";
  position: absolute;
  bottom: -1px;
  left: 0;
  width: 100%;
  height: 1px;
  background: var(--accent);
  transform: scaleX(0);
  transform-origin: left center;           /* grows from left (PRD 2.7) */
  transition: transform var(--dur-normal) var(--ease-out-expo);
}

.link-underline:hover {
  color: var(--text-primary);
}

.link-underline:hover::after {
  transform: scaleX(1);
}

/* Accent variant — already colored  */
.link-underline.is-accent {
  color: var(--accent);
}

/* ═══════════════════════════════════════════════
   4. TAGS / BADGES (PRD 2.1: Accent Label spec)
   Space Grotesk Medium 500
   Uppercase, letter-spacing: 0.15em
   ═══════════════════════════════════════════════ */

.tag {
  display: inline-flex;
  align-items: center;
  gap: var(--sp-1);
  padding: var(--sp-1) var(--sp-3);
  font-family: var(--font-display);
  font-size: var(--text-label);
  font-weight: 500;
  text-transform: uppercase;
  letter-spacing: var(--tracking-label);
  color: var(--text-secondary);
  background: var(--tag-bg, rgba(0, 212, 255, 0.08));
  border: 1px solid var(--border, rgba(0, 212, 255, 0.15));
  border-radius: var(--radius-sm);      /* flat, technical — not pill */
  white-space: nowrap;
  line-height: 1;
  transition:
    color        var(--dur-fast) var(--ease-out-expo),
    border-color var(--dur-fast) var(--ease-out-expo),
    background   var(--dur-fast) var(--ease-out-expo);
}

/* Accent variant — for primary/featured tags */
.tag.is-accent {
  color: var(--accent);
  border-color: rgba(var(--accent-rgb), 0.4);
  background: rgba(var(--accent-rgb), 0.07);
}

/* Alt accent variant */
.tag.is-alt {
  color: var(--accent-alt);
  border-color: rgba(var(--accent-alt-rgb), 0.35);
  background: rgba(var(--accent-alt-rgb), 0.07);
}

/* Clickable tag hover */
.tag[role="button"],
button.tag {
  cursor: pointer;
}

.tag[role="button"]:hover,
button.tag:hover {
  color: var(--accent);
  border-color: rgba(var(--accent-rgb), 0.5);
  background: rgba(var(--accent-rgb), 0.1);
}

/* Tag group layout */
.tag-group {
  display: flex;
  flex-wrap: wrap;
  gap: var(--sp-2);
  align-items: center;
}

/* ═══════════════════════════════════════════════
   5. PROJECT CARDS (PRD 2.7 + 4.1.4)
   • box-shadow: 0 0 0 1px var(--accent) on hover
   • Inner content shifts up 4px
   • NO scale
   • Full-bleed image, title overlay (PRD 6.1)
   ═══════════════════════════════════════════════ */

.card {
  position: relative;
  background: var(--bg-surface);
  border: var(--card-border);
  border-radius: var(--radius-md);
  overflow: hidden;
  display: flex;
  flex-direction: column;

  /* Only specify exact properties (PRD 2.7: no transition: all) */
  transition:
    box-shadow var(--dur-normal) var(--ease-out-expo),
    border-color var(--dur-normal) var(--ease-out-expo);
}

.card:hover {
  box-shadow: var(--card-border-hover);  /* 0 0 0 1px var(--accent) */
}

.card.card--external-link {
  cursor: pointer;
}

.card.card--external-link:focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: 2px;
}

/* Card inner — shifts up 4px on card hover (PRD 2.7) */
.card-body {
  padding: var(--card-pad);
  flex: 1;
  display: flex;
  flex-direction: column;
  transition: transform var(--dur-normal) var(--ease-out-expo);
}

.card:hover .card-body {
  transform: translateY(-4px);
}

/* Card image — full-bleed */
.card-image {
  position: relative;
  aspect-ratio: 16 / 9;
  overflow: hidden;
  background: var(--bg-overlay);
}

.card-media-slot {
  width: 100%;
  height: 100%;
}

.card-image img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  display: block;
  transition: transform var(--dur-slow) var(--ease-out-expo);
}

.card-image video,
.card-video {
  width: 100%;
  height: 100%;
  object-fit: cover;   /* fills card like an image; poster thumbnail fills area too */
  display: block;
  background: var(--bg-overlay);
}

/* Play button overlay — shown when video hasn’t started playing yet */
.card-image:has(.card-video)::after {
  content: '';
  position: absolute;
  inset: 0;
  pointer-events: none;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 64 64'%3E%3Ccircle cx='32' cy='32' r='28' fill='rgba(0,0,0,0.55)'/%3E%3Cpolygon points='26,20 26,44 48,32' fill='rgba(255,255,255,0.9)'/%3E%3C/svg%3E");
  background-repeat: no-repeat;
  background-position: center;
  background-size: 52px 52px;
  opacity: 1;
  transition: opacity var(--dur-fast) var(--ease-out-expo);
  z-index: 1;
}

/* Hide play icon once the video has started (JS adds .is-playing) */
.card-image.is-playing::after,
.card:hover .card-image:has(.card-video)::after {
  opacity: 0;
}

.card-image iframe,
.card-video-embed {
  width: 100%;
  height: 100%;
  border: 0;
  display: block;
  background: var(--bg-overlay);
}

.card:hover .card-image img {
  transform: scale(1.03);    /* EXCEPTION: very subtle on the image only, not the card */
}

img[data-zoomable-image="true"] {
  cursor: zoom-in;
  transition: transform var(--dur-slow) var(--ease-out-expo);
}

[data-zoom-frame="true"] {
  transform-origin: center;
  transition: transform var(--dur-slow) var(--ease-out-expo), box-shadow var(--dur-normal) var(--ease-out-expo);
  will-change: transform;
}

[data-zoom-frame="true"][data-zoom-kind="card"] {
  overflow: hidden;
}

[data-zoom-frame="true"][data-zoom-kind="card"]:hover {
  transform: scale(1.12);
  z-index: 20;
}

/* Zoom-scale hover on experience images — desktop only (touch has no hover) */
@media (hover: hover) and (min-width: 1024px) {
  [data-zoom-frame="true"][data-zoom-kind="exp"] {
    overflow: visible;
  }

  [data-zoom-frame="true"][data-zoom-kind="exp"]:hover {
    transform: scale(1.18);
    z-index: 20;
  }
}

.card.card--zoomable {
  overflow: visible;
}

.card:hover .card-image[data-zoom-kind="card"] img[data-zoomable-image="true"] {
  transform: none;
}

/* Allow overflow only on desktop for zoom hover effect */
@media (hover: hover) and (min-width: 1024px) {
  .exp-item--open .exp-detail {
    overflow: visible;
  }
}

.card:hover .card-image[data-zoom-frame="true"] img[data-zoomable-image="true"] {
  transform: none;
}

.image-zoom-modal {
  position: fixed;
  inset: 0;
  z-index: 9999;
  display: grid;
  place-items: center;
}

.image-zoom-modal[hidden] {
  display: none;
}

.image-zoom-modal__backdrop {
  position: absolute;
  inset: 0;
  background: rgba(0, 0, 0, 0.78);
}

.image-zoom-modal__content {
  position: relative;
  margin: 0;
  width: min(94vw, 1200px);
  max-height: 90vh;
  z-index: 1;
}

.image-zoom-modal__img {
  display: block;
  width: 100%;
  max-height: 90vh;
  object-fit: contain;
  border-radius: var(--radius-md);
}

.image-zoom-modal__close {
  position: absolute;
  top: var(--sp-4);
  right: var(--sp-4);
  z-index: 2;
  width: 44px;
  height: 44px;
  border: 1px solid var(--border);
  border-radius: 999px;
  background: var(--bg-surface);
  color: var(--text-primary);
  font-size: 1.5rem;
  line-height: 1;
  cursor: pointer;
}

/* Title overlay on full-bleed image (PRD 6.1: "title overlay") */
.card-image-overlay {
  position: absolute;
  inset: 0;
  background: linear-gradient(
    to top,
    rgba(var(--bg-base, 6, 8, 16), 0.92) 0%,
    transparent 55%
  );
  display: flex;
  flex-direction: column;
  justify-content: flex-end;
  padding: var(--card-pad);
  transition: opacity var(--dur-normal) var(--ease-out-expo);
}

/* Card heading */
.card-title {
  font-family: var(--font-display);
  font-size: var(--text-h3);
  font-weight: 700;
  letter-spacing: var(--tracking-tight);
  color: var(--text-primary);
  line-height: 1.2;
  margin-bottom: var(--sp-2);
}

/* Card excerpt */
.card-excerpt {
  font-size: var(--text-small);
  color: var(--text-secondary);
  line-height: 1.6;
  display: -webkit-box;
  -webkit-line-clamp: 3;
  -webkit-box-orient: vertical;
  overflow: hidden;
  max-width: none;                        /* override p max-width from base.css */
}

/* Card footer — links, tags */
.card-footer {
  display: flex;
  align-items: center;
  justify-content: space-between;
  flex-wrap: wrap;
  gap: var(--sp-3);
  padding: var(--sp-4) var(--card-pad);
  border-top: 1px solid rgba(255, 255, 255, 0.05);
  transition: transform var(--dur-normal) var(--ease-out-expo);
}

.card:hover .card-footer {
  transform: translateY(-4px);
}

/* Featured project card — larger, asymmetric */
.card.is-featured {
  grid-column: span 2;
}

.card.is-featured .card-image {
  aspect-ratio: 21 / 9;
}

/* ═══════════════════════════════════════════════
   6. SKELETON LOADERS (PRD 4.2: prevent layout shift)
   Animated shimmer — matches card dimensions.
   ═══════════════════════════════════════════════ */

.skeleton {
  background: linear-gradient(
    90deg,
    var(--bg-surface)   0%,
    var(--bg-overlay)  40%,
    var(--bg-overlay)  60%,
    var(--bg-surface) 100%
  );
  background-size: 300% 100%;
  animation: skeleton-shimmer 1.8s ease-in-out infinite;
  border-radius: var(--radius-md);
}

@keyframes skeleton-shimmer {
  0%   { background-position: 100% 50%; }
  100% { background-position: -100% 50%; }
}

/* Skeleton variants */
.skeleton-text {
  height: 1em;
  border-radius: var(--radius-sm);
  margin-bottom: var(--sp-2);
}

.skeleton-text.is-h1 { height: 2.5em; width: 65%; }
.skeleton-text.is-h2 { height: 1.8em; width: 45%; }
.skeleton-text.is-sm { height: 0.8em; width: 80%; }

.skeleton-image {
  aspect-ratio: 16 / 9;
  width: 100%;
}

.skeleton-card {
  background: var(--bg-surface);
  border: var(--card-border);
  border-radius: var(--radius-md);
  overflow: hidden;
  padding: var(--card-pad);
}

/* Freeze shimmer for reduced-motion users */
@media (prefers-reduced-motion: reduce) {
  .skeleton {
    animation: none;
    background: var(--bg-overlay);
    background-size: 100% 100%;
  }
}

/* ═══════════════════════════════════════════════
   7. SCROLL PROGRESS BAR (PRD 5.5)
   Thin accent line at top of viewport.
   Width driven by JS: document.documentElement.style.setProperty(...)
   ═══════════════════════════════════════════════ */

.scroll-progress {
  position: fixed;
  top: 0;
  left: 0;
  height: 2px;
  width: 0%;
  background: var(--accent);
  z-index: calc(var(--z-nav) + 5);
  transform-origin: left;
  pointer-events: none;

  /* Subtle glow below the bar */
  box-shadow: 0 0 8px rgba(var(--accent-rgb), 0.5);

  transition: width 80ms linear;   /* width is set by JS every scroll frame */
}

/* ═══════════════════════════════════════════════
   8. BACK-TO-TOP BUTTON (PRD 5.5: after 400px scroll)
   Hidden until JS adds .is-visible
   ═══════════════════════════════════════════════ */

.back-to-top {
  position: fixed;
  bottom: var(--sp-8);
  right: var(--sp-6);
  z-index: var(--z-sticky);

  display: flex;
  align-items: center;
  justify-content: center;
  width: var(--touch-min);
  height: var(--touch-min);

  background: var(--bg-overlay);
  border: 1px solid var(--text-tertiary);
  color: var(--text-secondary);
  border-radius: var(--radius-sm);
  font-family: var(--font-mono);
  font-size: 11px;

  /* Hidden initially */
  opacity: 0;
  pointer-events: none;
  transform: translateY(var(--sp-4));

  transition:
    opacity   var(--dur-normal) var(--ease-out-expo),
    transform var(--dur-normal) var(--ease-out-expo),
    color     var(--dur-fast)   var(--ease-out-expo),
    border-color var(--dur-fast) var(--ease-out-expo);
}

.back-to-top.is-visible {
  opacity: 1;
  pointer-events: auto;
  transform: translateY(0);
}

.back-to-top:hover {
  color: var(--accent);
  border-color: rgba(var(--accent-rgb), 0.5);
}

.back-to-top svg {
  width: 16px;
  height: 16px;
  fill: currentColor;
}

/* ═══════════════════════════════════════════════
   9. NAVIGATION (PRD 6.1)
   Sticky, transparent → bg on scroll (via JS class)
   ═══════════════════════════════════════════════ */

/* ── iOS notch / Dynamic Island safe-area cover ────────────────
   Pure CSS element that fills env(safe-area-inset-top) at the very
   top of the screen. Uses var(--bg-base) so it auto-updates on every
   theme change with zero JS. z-index sits ABOVE .nav so it's always
   visible even through backdrop-filter / transition states.
   ──────────────────────────────────────────────────────────────── */
.safe-area-top {
  position: fixed;
  top: 0;
  left: 0;
  right: 0;
  height: env(safe-area-inset-top, 0px);
  background: var(--bg-base);
  z-index: calc(var(--z-nav) + 10);
  pointer-events: none;
  /* Instantly reflect theme changes — never animate this fill */
  transition: none !important;
}

.nav {
  position: fixed;
  top: 0;
  left: 0;
  right: 0;
  z-index: var(--z-nav);
  display: grid;
  grid-template-columns: auto 1fr auto;
  align-items: center;
  gap: var(--sp-8);

  /* Nav bar height = content height + notch safe area */
  height: calc(var(--nav-height) + env(safe-area-inset-top, 0px));
  /* Push content below the notch/Dynamic Island, sides respect safe area */
  padding: env(safe-area-inset-top, 0px) max(var(--section-pad-h), env(safe-area-inset-right, 0px)) 0 max(var(--section-pad-h), env(safe-area-inset-left, 0px));

  /* Background covers entire area including notch zone */
  background: var(--bg-base, #07090f);
  border-bottom: 1px solid transparent;

  transition:
    background    var(--dur-normal) var(--ease-out-expo),
    border-color  var(--dur-normal) var(--ease-out-expo),
    backdrop-filter var(--dur-slow) var(--ease-out-expo);
}

.nav.is-scrolled {
  background: var(--bg-base, #07090f);
  border-bottom-color: var(--border, rgba(0, 212, 255, 0.15));
  backdrop-filter: blur(16px) saturate(140%);
  -webkit-backdrop-filter: blur(16px) saturate(140%);
}

/* Light Minimal overrides — lighter nav + card shadows */
[data-theme="light-minimal"] .nav {
  background: var(--bg-base, #f7f8fc);
}

[data-theme="light-minimal"] .nav.is-scrolled {
  background: var(--bg-base, #f7f8fc);
  border-bottom-color: rgba(0, 85, 204, 0.15);
}

[data-theme="light-minimal"] .card {
  box-shadow: 0 1px 6px rgba(0, 0, 0, 0.06);
}

[data-theme="light-minimal"] .card:hover {
  box-shadow: 0 4px 20px rgba(0, 85, 204, 0.15);
}

/* Nav logo — custom SVG monogram (PRD 6.1: NOT text) */
.nav-logo {
  display: flex;
  align-items: center;
  min-height: var(--touch-min);
  min-width: var(--touch-min);
}

.nav-logo svg {
  width: 36px;
  height: 36px;
  fill: var(--text-primary);
  transition: fill var(--dur-fast) var(--ease-out-expo);
}

.nav-logo:hover svg {
  fill: var(--accent);
}

/* Nav anchor links */
.nav-links {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: var(--sp-8);
  list-style: none;
}

.nav-links li {
  line-height: 1;
}

.nav-links a {
  position: relative;
  font-family: var(--font-mono);
  font-size: var(--text-meta);
  font-weight: 500;
  text-transform: uppercase;
  letter-spacing: var(--tracking-label);
  color: var(--text-tertiary);
  padding: var(--sp-2) 0;
  min-height: var(--touch-min);
  display: flex;
  align-items: center;
  transition: color var(--dur-fast) var(--ease-out-expo);
}

/* Underline grow from left */
.nav-links a::after {
  content: "";
  position: absolute;
  bottom: 0;
  left: 0;
  width: 100%;
  height: 1px;
  background: var(--accent);
  transform: scaleX(0);
  transform-origin: left center;
  transition: transform var(--dur-normal) var(--ease-out-expo);
}

.nav-links a:hover,
.nav-links a[aria-current="page"] {
  color: var(--text-primary);
}

.nav-links a:hover::after,
.nav-links a[aria-current="page"]::after {
  transform: scaleX(1);
}

/* Nav right cluster — language + theme + LinkedIn */
.nav-right {
  display: flex;
  align-items: center;
  gap: var(--sp-4);
}

/* Mobile hamburger — visible below md breakpoint */
.nav-hamburger {
  display: none;
  flex-direction: column;
  justify-content: center;
  gap: 5px;
  width: var(--touch-min);
  height: var(--touch-min);
  padding: var(--sp-3);
  background: none;
  border: none;
  cursor: pointer;
}

.nav-hamburger span {
  display: block;
  width: 22px;
  height: 1px;
  background: var(--text-primary);
  border-radius: var(--radius-pill);
  transform-origin: center;
  transition:
    transform  var(--dur-normal) var(--ease-out-expo),
    opacity    var(--dur-fast)   var(--ease-out-expo),
    background var(--dur-fast)   var(--ease-out-expo);
}

/* Open state — X icon */
.nav-hamburger.is-open span:nth-child(1) {
  transform: translateY(6px) rotate(45deg);
}

.nav-hamburger.is-open span:nth-child(2) {
  opacity: 0;
  transform: scaleX(0);
}

.nav-hamburger.is-open span:nth-child(3) {
  transform: translateY(-6px) rotate(-45deg);
}

/* Mobile full-screen overlay (PRD 4.6) */
.nav-overlay {
  position: fixed;
  inset: 0;
  z-index: calc(var(--z-nav) - 1);
  background: var(--bg-base);
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: var(--sp-8);

  opacity: 0;
  pointer-events: none;
  clip-path: inset(0 0 100% 0);

  transition:
    opacity    var(--dur-normal) var(--ease-out-expo),
    clip-path  var(--dur-normal) var(--ease-out-expo);
}

.nav-overlay.is-open {
  opacity: 1;
  pointer-events: auto;
  clip-path: inset(0 0 0% 0);
}

.nav-overlay a {
  font-family: var(--font-display);
  font-size: var(--text-h2);
  font-weight: 700;
  color: var(--text-secondary);
  letter-spacing: var(--tracking-tight);
  transition: color var(--dur-fast) var(--ease-out-expo);
}

.nav-overlay a:hover {
  color: var(--accent);
}

@media (max-width: 1023px) {
  .nav-links { display: none; }
  .nav-hamburger { display: flex; }

  /* Switch from grid to flex for simpler mobile layout */
  .nav {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 0;
    padding: env(safe-area-inset-top, 0px) var(--section-pad-h) 0 var(--section-pad-h);
  }

  .nav-right {
    display: flex;
    align-items: center;
    gap: var(--sp-3);
    margin-left: auto;
  }

  .nav-right .linkedin-link {
    flex: 0 0 auto;
    padding: var(--sp-1) var(--sp-2);
    min-width: unset;
  }

  .nav-right .linkedin-link .link-text,
  .nav-right .linkedin-link .icon-external {
    display: none;
  }
}

/* ═══════════════════════════════════════════════
   10. THEME SWATCH SELECTOR (PRD 4.4)
   4 color dots in nav — NOT dropdown
   ═══════════════════════════════════════════════ */

.theme-selector {
  display: flex;
  align-items: center;
  gap: var(--sp-2);
  list-style: none;
}

.theme-swatch {
  width: 14px;
  height: 14px;
  border-radius: 50%;
  border: 1px solid transparent;
  cursor: pointer;
  transition:
    transform     var(--dur-fast) var(--ease-out-expo),
    border-color  var(--dur-fast) var(--ease-out-expo),
    box-shadow    var(--dur-fast) var(--ease-out-expo);
  position: relative;
}

/* Swatch colors — exact accent values per theme */
.theme-swatch[data-theme-target="dark-cyber"]     { background: #00c8ff; }
.theme-swatch[data-theme-target="terminal-green"] { background: #7fff00; }
.theme-swatch[data-theme-target="midnight-purple"]{ background: #a855f7; }
.theme-swatch[data-theme-target="light-minimal"]  { background: #f5f4f0; border-color: #b0aa9e; }

.theme-swatch:hover {
  transform: translateY(-2px);
  box-shadow: 0 0 8px currentColor;
}

/* Active state */
.theme-swatch.is-active {
  border-color: var(--text-primary);
  box-shadow: 0 0 0 2px var(--bg-base), 0 0 0 3px var(--text-primary);
}

/* Screen reader label */
.theme-swatch span {
  position: absolute;
  width: 1px;
  height: 1px;
  padding: 0;
  margin: -1px;
  overflow: hidden;
  clip: rect(0, 0, 0, 0);
  white-space: nowrap;
  border-width: 0;
}

/* ═══════════════════════════════════════════════
   11. LANGUAGE TOGGLE (PRD 4.3)
   Segmented button, NOT dropdown.
   [KO] [EN] — clearly labelled, keyboard accessible.
   ═══════════════════════════════════════════════ */

.lang-toggle {
  display: flex;
  align-items: center;
  border: 1px solid var(--text-tertiary);
  border-radius: var(--radius-sm);
  overflow: hidden;
  list-style: none;
}

.lang-btn {
  display: flex;
  align-items: center;
  justify-content: center;
  padding: var(--sp-1) var(--sp-3);
  min-height: 36px;                    /* slightly smaller than 44px — in nav bar */
  min-width: 40px;
  font-family: var(--font-mono);
  font-size: var(--text-meta);
  font-weight: 500;
  letter-spacing: var(--tracking-wide);
  text-transform: uppercase;
  color: var(--text-tertiary);
  background: transparent;
  border: none;
  border-right: 1px solid var(--text-tertiary);
  cursor: pointer;
  transition:
    color      var(--dur-fast) var(--ease-out-expo),
    background var(--dur-fast) var(--ease-out-expo);
}

.lang-btn:last-child {
  border-right: none;
}

.lang-btn:hover {
  color: var(--text-primary);
}

.lang-btn[aria-pressed="true"],
.lang-btn.is-active {
  color: var(--bg-base);
  background: var(--accent);
}

/* ═══════════════════════════════════════════════
   12. SECTION WRAPPER DEFAULTS (PRD 2.3)
   100dvh sections, full-bleed bg, centered content
   ═══════════════════════════════════════════════ */

/* NOTE: padding-block for .section lives in layout.css so the
   @media (max-width:1024px) override there takes effect correctly.
   Do NOT add padding-top/bottom here (would override the tablet reduction). */
.section {
  position: relative;
  width: 100%;
}

/* Full-viewport sections (hero, etc.) */
.section.is-full {
  min-height: 100dvh;
  display: grid;
  place-items: center;
}

/* Inner content constrained to max-width */
.section-inner {
  width: 100%;
  max-width: var(--content-max);
  margin-inline: auto;
  padding-inline: var(--section-pad-h);
}

/* Alternate surface background */
.section.is-surface {
  background: var(--bg-surface);
}

/* Overlay background (darkest) */
.section.is-overlay {
  background: var(--bg-overlay);
}

/* ═══════════════════════════════════════════════
   13. ACCENT DIVIDER
   Small accent line — replaces generic <hr>
   ═══════════════════════════════════════════════ */

.accent-divider {
  display: block;
  width: 40px;
  height: 2px;
  background: var(--accent);
  margin-bottom: var(--sp-6);
  box-shadow: 0 0 8px rgba(var(--accent-rgb), 0.4);
}

/* Wide variant */
.accent-divider.is-wide {
  width: 120px;
}

/* ═══════════════════════════════════════════════
   14. FILTER STRIP (PRD 6.1: Project filter)
   [ALL] [Edge AI] [Computer Vision] [Hardware] [Research]
   ═══════════════════════════════════════════════ */

.filter-strip {
  display: flex;
  flex-wrap: wrap;
  gap: var(--sp-2);
  align-items: center;
  margin-bottom: var(--sp-10);
}

.filter-btn {
  display: inline-flex;
  align-items: center;
  min-height: var(--touch-min);
  padding: var(--sp-2) var(--sp-5);
  font-family: var(--font-mono);
  font-size: var(--text-meta);
  font-weight: 500;
  text-transform: uppercase;
  letter-spacing: var(--tracking-label);
  color: var(--text-tertiary);
  background: transparent;
  border: 1px solid var(--text-tertiary);
  border-radius: var(--radius-sm);
  cursor: pointer;
  transition:
    color        var(--dur-fast) var(--ease-out-expo),
    border-color var(--dur-fast) var(--ease-out-expo),
    background   var(--dur-fast) var(--ease-out-expo);
}

.filter-btn:hover {
  color: var(--text-primary);
  border-color: var(--accent);
}

.filter-btn.is-active {
  color: var(--bg-base);
  background: var(--accent);
  border-color: var(--accent);
}

/* ═══════════════════════════════════════════════
   15. INLINE ALERT / TOAST
   Form validation messages (PRD 4.12)
   ═══════════════════════════════════════════════ */

.field-error {
  font-family: var(--font-mono);
  font-size: var(--text-meta);
  color: var(--accent-alt);
  margin-top: var(--sp-1);
  letter-spacing: var(--tracking-wide);

  /* Reveal animation */
  animation: field-error-in var(--dur-normal) var(--ease-out-expo);
}

@keyframes field-error-in {
  from { opacity: 0; transform: translateY(-4px); }
  to   { opacity: 1; transform: translateY(0); }
}

.field-success {
  font-family: var(--font-mono);
  font-size: var(--text-meta);
  color: var(--accent);
  margin-top: var(--sp-1);
  letter-spacing: var(--tracking-wide);
}

/* ═══════════════════════════════════════════════
   16. RESPONSIVE ADJUSTMENTS
   Mobile-first per PRD 4.6: 375px → 768 → 1024 → 1280 → 1920
   ═══════════════════════════════════════════════ */

/* Below tablet — single-column card grid */
@media (max-width: 767px) {
  .card.is-featured {
    grid-column: span 1;
  }

  .filter-strip {
    gap: var(--sp-1);
  }

  /* Nav: tighter gaps */
  .nav {
    padding: env(safe-area-inset-top, 0px) var(--sp-4) 0 var(--sp-4);
  }

  .nav-right {
    gap: var(--sp-3);
  }

  .lang-btn {
    min-width: 44px;
    min-height: 44px;
    padding: var(--sp-1) var(--sp-3);
    font-size: 0.85rem;
  }

  .theme-selector {
    gap: 6px;
  }

  .theme-swatch {
    width: 28px;
    height: 28px;
  }
}

/* Very small mobile — extra compact nav, hide LinkedIn */
@media (max-width: 479px) {
  .nav {
    padding: env(safe-area-inset-top, 0px) var(--sp-3) 0 var(--sp-3);
  }

  .nav-right {
    gap: 10px;
  }

  .nav-right .linkedin-link {
    display: none;
  }

  .theme-selector {
    gap: 5px;
  }

  .theme-swatch {
    width: 26px;
    height: 26px;
  }

  .lang-btn {
    min-width: 42px;
    min-height: 42px;
    padding: var(--sp-1) var(--sp-3);
    font-size: 0.85rem;
  }

  .nav-hamburger {
    width: 40px;
    height: 40px;
    padding: 10px;
  }
}

/* Desktop — restore grid nav, show links, hide hamburger */
@media (min-width: 1024px) {
  .nav {
    display: grid;
    grid-template-columns: auto 1fr auto;
    gap: var(--sp-8);
  }

  .nav-hamburger { display: none; }
  .nav-links { display: flex; }

  .nav-right {
    gap: var(--sp-4);
  }

  .nav-right .linkedin-link {
    padding: var(--sp-2) var(--sp-5);
    min-width: var(--touch-min);
  }

  .nav-right .linkedin-link .link-text,
  .nav-right .linkedin-link .icon-external {
    display: inline;
  }
}

/* Wide screens — generous spacing */
@media (min-width: 1440px) {
  .nav {
    padding-inline: var(--sp-16);
  }
}

/* ═══════════════════════════════════════════════
   17. TERMINAL OVERLAY (PRD 5.4)
   Full-screen terminal easter egg — dark, immersive
   ═══════════════════════════════════════════════ */

.terminal-overlay {
  position: fixed;
  inset: 0;
  z-index: var(--z-modal);
  display: flex;
  align-items: center;
  justify-content: center;
  background: rgba(0, 0, 0, 0.92);
  backdrop-filter: blur(8px);
  -webkit-backdrop-filter: blur(8px);
}

.terminal-overlay[hidden] {
  display: none;
}

.terminal-window {
  width: min(680px, 92vw);
  max-height: 80vh;
  display: flex;
  flex-direction: column;
  background: var(--bg-base);
  border: 1px solid var(--text-tertiary);
  border-radius: var(--radius-lg);
  overflow: hidden;
  box-shadow:
    0 0 0 1px rgba(255, 255, 255, 0.04),
    0 24px 80px rgba(0, 0, 0, 0.6);
}

/* Title bar — macOS style dots */
.terminal-titlebar {
  display: flex;
  align-items: center;
  gap: 6px;
  padding: 10px 14px;
  background: var(--bg-surface);
  border-bottom: 1px solid var(--text-tertiary);
  user-select: none;
}

.terminal-dot {
  width: 12px;
  height: 12px;
  border-radius: 50%;
}

.terminal-dot--close { background: #ff5f57; }
.terminal-dot--min   { background: #ffbd2e; }
.terminal-dot--max   { background: #28c840; }

.terminal-title {
  margin-inline-start: 8px;
  font-size: 11px;
  color: var(--text-secondary);
  letter-spacing: 0.04em;
}

/* Output area */
.terminal-output {
  flex: 1 1 auto;
  overflow-y: auto;
  padding: var(--sp-4);
  font-family: var(--font-mono);
  font-size: clamp(0.75rem, 1.6vw, 0.875rem);
  line-height: 1.7;
  color: var(--text-primary);
  scrollbar-width: thin;
  scrollbar-color: var(--text-tertiary) transparent;
}

.terminal-line {
  white-space: pre-wrap;
  word-break: break-word;
}

.terminal-line--command {
  color: var(--accent);
  font-weight: 600;
}

.terminal-line--boot {
  color: var(--text-secondary);
}

/* Input row */
.terminal-input-row {
  display: flex;
  align-items: center;
  padding: var(--sp-2) var(--sp-4) var(--sp-3);
  border-top: 1px solid var(--text-tertiary);
  background: var(--bg-surface);
}

.terminal-prompt {
  color: var(--accent);
  font-size: clamp(0.75rem, 1.6vw, 0.875rem);
  flex-shrink: 0;
}

.terminal-input {
  flex: 1;
  background: transparent;
  border: none;
  outline: none;
  color: var(--text-primary);
  font-family: var(--font-mono);
  font-size: clamp(0.75rem, 1.6vw, 0.875rem);
  caret-color: var(--accent);
  line-height: 1.4;
}

.terminal-input::placeholder {
  color: var(--text-tertiary);
}

/* ═══════════════════════════════════════════════
   18. COMMAND PALETTE (PRD 5.6)
   Spotlight / Cmd-K overlay — minimal, fast
   ═══════════════════════════════════════════════ */

.command-palette {
  position: fixed;
  inset: 0;
  z-index: var(--z-modal);
  display: flex;
  align-items: flex-start;
  justify-content: center;
  padding-top: clamp(10vh, 18vh, 24vh);
}

.command-palette[hidden] {
  display: none;
}

.command-palette-backdrop {
  position: absolute;
  inset: 0;
  background: rgba(0, 0, 0, 0.65);
  backdrop-filter: blur(6px);
  -webkit-backdrop-filter: blur(6px);
}

.command-palette-panel {
  position: relative;
  width: min(520px, 90vw);
  max-height: 420px;
  display: flex;
  flex-direction: column;
  background: var(--bg-surface);
  border: 1px solid var(--text-tertiary);
  border-radius: var(--radius-lg);
  overflow: hidden;
  box-shadow:
    0 0 0 1px rgba(255, 255, 255, 0.04),
    0 16px 64px rgba(0, 0, 0, 0.55);
  animation: palette-enter var(--dur-normal) var(--ease-out-expo);
}

@keyframes palette-enter {
  from { opacity: 0; transform: translateY(-12px) scale(0.97); }
  to   { opacity: 1; transform: translateY(0) scale(1); }
}

/* Search input row */
.command-palette-input-wrapper {
  display: flex;
  align-items: center;
  gap: var(--sp-2);
  padding: var(--sp-3) var(--sp-4);
  border-bottom: 1px solid var(--text-tertiary);
}

.command-palette-input-wrapper svg {
  flex-shrink: 0;
  color: var(--text-secondary);
}

.command-input {
  flex: 1;
  background: transparent;
  border: none;
  outline: none;
  color: var(--text-primary);
  font-family: var(--font-mono);
  font-size: 0.9rem;
  caret-color: var(--accent);
  line-height: 1.4;
}

.command-input::placeholder {
  color: var(--text-tertiary);
}

/* Results list */
.command-results {
  flex: 1 1 auto;
  overflow-y: auto;
  padding: var(--sp-1) 0;
  list-style: none;
  margin: 0;
  scrollbar-width: thin;
  scrollbar-color: var(--text-tertiary) transparent;
}

.command-result {
  padding: var(--sp-2) var(--sp-4);
  font-size: 0.85rem;
  color: var(--text-secondary);
  cursor: pointer;
  transition: background-color var(--dur-fast) var(--ease-out-expo),
              color var(--dur-fast) var(--ease-out-expo);
}

.command-result:hover,
.command-result.is-active {
  background: var(--accent-glow-bg, rgba(0, 212, 255, 0.08));
  color: var(--text-primary);
}

.command-result.is-active::before {
  content: "›";
  margin-inline-end: var(--sp-2);
  color: var(--accent);
  font-weight: 700;
}

.command-result--empty {
  color: var(--text-tertiary);
  font-style: italic;
  pointer-events: none;
}

/* ═══════════════════════════════════════════════
   19. COOKIE BANNER (PRD 4.11)
   ═══════════════════════════════════════════════ */

.cookie-banner {
  position: fixed;
  bottom: 0;
  left: 0;
  right: 0;
  z-index: var(--z-overlay);
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--sp-4);
  padding: var(--sp-3) var(--sp-6);
  background: var(--bg-overlay);
  border-top: 1px solid var(--text-tertiary);
  backdrop-filter: blur(8px);
  -webkit-backdrop-filter: blur(8px);
}

.cookie-banner[hidden] {
  display: none;
}

.cookie-text {
  flex: 1;
  margin: 0;
}

.cookie-actions {
  display: flex;
  align-items: center;
  gap: var(--sp-3);
  flex-shrink: 0;
}

/* ═══════════════════════════════════════════════
   20. LOADING SCREEN (PRD 5.7)
   ═══════════════════════════════════════════════ */

.loading-screen {
  position: fixed;
  inset: 0;
  z-index: calc(var(--z-grain) + 1);
  display: flex;
  align-items: center;
  justify-content: center;
  background: var(--bg-base);
  transition: opacity 0.4s var(--ease-out-expo);
}

.loading-screen.is-hidden {
  opacity: 0;
  pointer-events: none;
}

.loading-content {
  text-align: center;
}

.loading-ascii {
  font-size: clamp(0.5rem, 2vw, 1rem);
  color: var(--accent);
  line-height: 1.2;
  margin-bottom: var(--sp-4);
}

.loading-boot {
  min-height: 4.5em;
  text-align: left;
  max-width: 40ch;
  margin: 0 auto var(--sp-4);
  color: var(--text-secondary);
  font-size: 0.75rem;
  line-height: 1.7;
}

.loading-bar-wrapper {
  width: 200px;
  height: 2px;
  margin: 0 auto;
  background: var(--text-tertiary);
  border-radius: 1px;
  overflow: hidden;
}

.loading-bar {
  height: 100%;
  width: 0;
  background: var(--accent);
  transition: width 0.3s var(--ease-out-expo);
}

/* ═══════════════════════════════════════════════
   21. PRINT (PRD 5.9 — emergency scope)
   Full rules in print.css
   ═══════════════════════════════════════════════ */

@media print {
  .nav,
  .safe-area-top,
  .nav-overlay,
  .back-to-top,
  .scroll-progress,
  .filter-strip,
  .theme-selector {
    display: none !important;
  }
}

/* ═══════════════════════════════════════════════
   22. TECH STACK GRID (Fix 2 — PRD 6.1)
   2×2 grid of category cards, proficiency bars,
   fallback icon badge when image is broken.
   ❌ border-radius > 2px  ❌ scale on hover
   ═══════════════════════════════════════════════ */

#tech-stack-grid {
  display: grid;
  grid-template-columns: repeat(2, 1fr);
  gap: 1px;  /* 1px gap — grid-line feel (Anti-AI) */
  background: var(--border, rgba(0, 212, 255, 0.15)); /* seam colour */
}

@media (max-width: 768px) {
  #tech-stack-grid {
    grid-template-columns: 1fr;
  }
}

.tech-category-card {
  background: var(--bg-surface);
  border: none;  /* seam provided by grid gap + background */
  padding: var(--sp-6) var(--sp-6) var(--sp-5);
  display: flex;
  flex-direction: column;
  gap: var(--sp-4);
  transition:
    border-color var(--dur-normal) var(--ease-out-expo),
    box-shadow   var(--dur-normal) var(--ease-out-expo);
}

.tech-category-card:hover {
  box-shadow: inset 0 0 0 1px var(--accent);
}

.tech-category-header {
  display: flex;
  flex-direction: column;
  gap: var(--sp-2);
}

.tech-category-header span {
  color: var(--text-secondary);
}

.tech-category-line {
  width: 100%;
  height: 1px;
  background: var(--border, rgba(0, 212, 255, 0.15));
}

/* Item row: icon + name + proficiency bar */
.tech-items {
  list-style: none;
  display: flex;
  flex-direction: column;
  gap: var(--sp-3);
}

.tech-item-row {
  display: grid;
  grid-template-columns: 28px 1fr auto;
  align-items: center;
  gap: var(--sp-3);
}

.tech-icon {
  position: relative;
  width: 24px;
  height: 24px;
  display: flex;
  align-items: center;
  justify-content: center;
  flex-shrink: 0;
}

.tech-icon img {
  width: 24px;
  height: 24px;
  object-fit: contain;
}

/* Fallback badge — shown when image is missing or broken */
.tech-icon-badge {
  display: flex;
  align-items: center;
  justify-content: center;
  width: 24px;
  height: 24px;
  border-radius: 50%;
  background: var(--tag-bg, rgba(0, 212, 255, 0.08));
  color: var(--accent);
  font-family: var(--font-mono);
  font-size: 8px;
  font-weight: 700;
  letter-spacing: -0.03em;
  flex-shrink: 0;
}

.tech-item-name {
  color: var(--text-primary);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

/* Proficiency bar — 2px line, accent fill */
.tech-proficiency {
  display: block;
  width: 60px;
  height: 2px;
  background: var(--border, rgba(0, 212, 255, 0.15));
  flex-shrink: 0;
}

.tech-proficiency-bar {
  display: block;
  height: 100%;
  background: var(--accent);
  transition: width var(--dur-normal) var(--ease-out-expo);
}

/* ═══════════════════════════════════════════════
   23. PROJECT FILTER BUTTONS (Fix 3)
   Dynamically generated from project tags.
   Active: accent fill. Inactive: ghost outline.
   ═══════════════════════════════════════════════ */

/* Issue 6: wrapping filter tags — replace wrap with horizontal scroll.
   Each tag has flex-shrink:0 so it never squashes. */
#projects-filter {
  display: flex;
  flex-wrap: nowrap;                    /* never wrap — scroll instead */
  overflow-x: auto;                     /* horizontal scroll on mobile */
  -webkit-overflow-scrolling: touch;
  gap: var(--sp-2);
  margin-bottom: var(--sp-8);
  padding-bottom: var(--sp-2);          /* reserve space so last tag isn't clipped */
  scrollbar-width: none;                /* Firefox */
  -ms-overflow-style: none;
}

#projects-filter::-webkit-scrollbar {
  display: none;                        /* Chrome / Safari */
}

.filter-btn {
  display: inline-flex;
  align-items: center;
  flex-shrink: 0;                       /* prevent shrinking inside scroll container */
  min-height: 36px;
  padding: var(--sp-1) var(--sp-4);
  font-family: var(--font-mono);
  font-size: var(--text-meta);
  font-weight: 500;
  text-transform: uppercase;
  letter-spacing: var(--tracking-label);
  color: var(--text-secondary);
  background: transparent;
  border: 1px solid var(--border, rgba(0, 212, 255, 0.15));
  border-radius: var(--radius-sm);  /* 2px — flat, Anti-AI */
  cursor: pointer;
  transition:
    color            var(--dur-fast) var(--ease-out-expo),
    background       var(--dur-fast) var(--ease-out-expo),
    border-color     var(--dur-fast) var(--ease-out-expo),
    box-shadow       var(--dur-fast) var(--ease-out-expo);
}

.filter-btn:hover {
  color: var(--accent);
  border-color: var(--accent);
}

.filter-btn.is-active {
  color: var(--bg-base);
  background: var(--accent);
  border-color: var(--accent);
  box-shadow: var(--accent-glow);
}

.filter-btn:focus-visible {
  outline: var(--focus-ring);
  outline-offset: var(--focus-ring-offset);
}

/* ═══════════════════════════════════════════════
   24. IMAGE PLACEHOLDERS (Fix 4)
   .no-thumb — project card thumbnail absent/broken.
   .about-photo-placeholder — JK initials badge.
   ═══════════════════════════════════════════════ */

/* Project card thumbnail fallback */
.card-image.no-thumb {
  background: var(--bg-elevated, var(--bg-overlay));
  display: flex;
  align-items: center;
  justify-content: center;
}

.card-image.no-thumb::before {
  content: attr(data-tag);
  font-family: var(--font-mono);
  font-size: clamp(1.5rem, 5vw, 3rem);
  font-weight: 700;
  letter-spacing: var(--tracking-tight);
  text-transform: uppercase;
  color: var(--border, rgba(0, 212, 255, 0.15));
  pointer-events: none;
  user-select: none;
}

/* ── Project card carousel dots ── */
.card-carousel-dots {
  position: absolute;
  bottom: var(--sp-2);
  left: 50%;
  transform: translateX(-50%);
  display: flex;
  gap: 5px;
  opacity: 0.85;
  transition: opacity 0.2s;
  pointer-events: auto;
}

.card-image:hover .card-carousel-dots {
  opacity: 1;
}

.card-carousel-btn {
  position: absolute;
  top: 50%;
  transform: translateY(-50%);
  width: 34px;
  height: 34px;
  border: 1px solid var(--border);
  border-radius: 999px;
  background: rgba(0, 0, 0, 0.45);
  color: var(--text-primary);
  line-height: 1;
  cursor: pointer;
  display: grid;
  place-items: center;
  z-index: 2;
  opacity: 0;
  transition: opacity var(--dur-fast) var(--ease-out-expo), background var(--dur-fast) var(--ease-out-expo);
}

.card-carousel-btn--prev {
  left: var(--sp-2);
}

.card-carousel-btn--next {
  right: var(--sp-2);
}

.card-image:hover .card-carousel-btn,
.card-carousel-btn:focus-visible {
  opacity: 1;
}

.card-carousel-btn:hover {
  background: rgba(0, 0, 0, 0.62);
}

.carousel-dot {
  border: 0;
  padding: 0;
  width: 6px;
  height: 6px;
  border-radius: 50%;
  background: rgba(255, 255, 255, 0.45);
  transition: background 0.15s;
  cursor: pointer;
}

.carousel-dot.is-active {
  background: var(--accent, #00d4ff);
}

/* ── Project card links ── */
.card-links {
  display: flex;
  flex-wrap: wrap;
  gap: var(--sp-2);
  margin-top: var(--sp-2);
}

.card-link-btn {
  display: inline-flex;
  align-items: center;
  padding: 0.2em 0.65em;
  border: 1px solid rgba(var(--accent-rgb, 0, 212, 255), 0.45);
  border-radius: 3px;
  font-family: var(--font-mono);
  font-size: var(--text-xs, 0.7rem);
  letter-spacing: 0.04em;
  color: var(--accent, #00d4ff);
  text-decoration: none;
  transition: background var(--dur-fast, 0.12s), color var(--dur-fast, 0.12s), border-color var(--dur-fast, 0.12s);
  white-space: nowrap;
}

.card-link-btn:hover {
  background: rgba(var(--accent-rgb, 0, 212, 255), 0.12);
  border-color: var(--accent, #00d4ff);
}

.project-link-hint {
  display: block;
  margin-top: var(--sp-2);
  opacity: 0.8;
}

/* Card footer flex wrap fix for links */
.card-footer:has(.card-links) {
  flex-direction: column;
  align-items: flex-start;
}

/* About photo placeholder — SVG initials badge */
.about-photo-placeholder {
  width: 100%;
  aspect-ratio: 4 / 5;
  display: flex;
  align-items: center;
  justify-content: center;
  background: var(--bg-elevated, var(--bg-overlay));
  border: 1px solid var(--border, rgba(0, 212, 255, 0.15));
}

.about-photo-placeholder svg {
  width: 120px;
  height: 120px;
}
