/* ============================
   1. CSS VARIABLES
   (Inter and Space Mono are loaded via <link> tags in index.html
   <head> for performance; Caveat is loaded below since it's only
   used in one place — see font-family declarations below)
   ============================ */
@import url('https://fonts.googleapis.com/css2?family=Caveat:wght@500;600&display=swap');

:root {
  /* Color */
  --background: #FAF9F6;
  --text-primary: #181818;
  --text-muted: #6B6965;
  --border: #D8D5CF;
  --accent: #7A2638;

  /* Timeline component tokens */
  --bg-raised: #FFFFFF;   /* panel background, one step lighter than --background */
  --line-dim: #EDEAE4;    /* subtler divider inside detail panel */
  --ink-faint: #7D7A73;   /* tertiary text — dates, captions, inactive dots */
  --accent-dim: #A67885;  /* bullet markers — quieter than --accent */

  /* Typography */
  --font-sans: 'Inter', -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;
  --font-mono: 'Space Mono', 'SFMono-Regular', Consolas, monospace;
  --font-caption: 'Caveat', var(--font-sans), cursive;

  /* Type scale */
  --text-xs: 0.75rem;   /* 12px — mono metadata, small labels */
  --text-sm: 0.875rem;  /* 14px — mono labels, secondary text */
  --text-base: 1rem;    /* 16px — body copy */
  --text-md: 1.25rem;   /* 20px — subheadings */
  --text-lg: 1.75rem;   /* 28px — h3 / section titles */
  --text-xl: 2.5rem;    /* 40px — h2 */
  --text-2xl: 3.5rem;   /* 56px — hero name */
  --text-3xl: 4rem;     /* 64px — hero name, large desktop */

  /* Spacing scale (8px base, linear) */
  --space-1: 0.5rem;  /* 8px */
  --space-2: 1rem;    /* 16px */
  --space-3: 1.5rem;  /* 24px */
  --space-4: 2rem;    /* 32px */
  --space-5: 3rem;    /* 48px */
  --space-6: 4rem;    /* 64px */
  --space-7: 6rem;    /* 96px */

  /* Structure */
  --border-width: 1px;
  --border-frame: #4A4A4A;
  --radius: 0;
  --nav-height: 4rem; /* sticky nav bar, excluding its 1px bottom border */
  --container-max: 1200px;
  --frame-margin: 1rem;  /* 4px — space between viewport edge and page border */

  /* Motion */
  --transition-fast: 150ms ease;
  --transition-base: 250ms ease;
}

/* Breakpoints (referenced in @media queries below, not usable as custom properties):
   mobile:  <= 640px
   tablet:  641px - 1024px
   desktop: > 1024px
*/

/* ============================
   2. RESET
   ============================ */
*,
*::before,
*::after {
  box-sizing: border-box;
  margin: 0;
  padding: 0;
}

html {
  -webkit-text-size-adjust: 100%;
  scroll-behavior: smooth;
  /* Anchor jumps (nav links, "Back to top") stop just below the sticky nav. */
  scroll-padding-top: calc(var(--nav-height) + var(--border-width));
}

body {
  background: var(--background);
  color: var(--text-primary);
  font-family: var(--font-sans);
  font-size: var(--text-base);
  line-height: 1.5;
  -webkit-font-smoothing: antialiased;
  padding: var(--frame-margin);
}

.page {
  border: var(--border-width) solid var(--border-frame);
}

img,
picture,
video {
  display: block;
  max-width: 100%;
}

a {
  color: inherit;
  text-decoration: none;
}

button,
input,
textarea {
  font: inherit;
  color: inherit;
  background: none;
  border: none;
}

ul,
ol {
  list-style: none;
}

h1, h2, h3, h4, h5, h6 {
  font-weight: 700;
  line-height: 1.15;
}

@media (prefers-reduced-motion: reduce) {
  html {
    scroll-behavior: auto;
  }
  *,
  *::before,
  *::after {
    animation-duration: 0.01ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 0.01ms !important;
    scroll-behavior: auto !important;
  }
}

/* ============================
   3. TYPOGRAPHY UTILITIES
   ============================ */
.mono-label {
  font-family: var(--font-mono);
  font-size: var(--text-xs);
  font-weight: 700;
  letter-spacing: 0.05em;
  text-transform: uppercase;
  color: var(--text-muted);
}

.mono-label--accent {
  color: var(--accent);
}

/* ============================
   4. LAYOUT / CONTAINER
   ============================ */
.container {
  width: 100%;
  max-width: var(--container-max);
  margin-inline: auto;
  padding-inline: var(--space-3);
}

@media (max-width: 640px) {
  .container {
    padding-inline: var(--space-2);
  }
}

/* ============================
   5. CONNECTED GRID SYSTEM
   ============================
   Modules share borders instead of using gaps + shadows.
   A parent with class `.grid-connected` draws an outer border;
   child cells draw only the interior dividing lines, so adjacent
   cells never produce a doubled border.
*/
.grid-connected {
  display: grid;
  border-top: var(--border-width) solid var(--border);
  border-left: var(--border-width) solid var(--border);
}

.grid-connected > * {
  border-right: var(--border-width) solid var(--border);
  border-bottom: var(--border-width) solid var(--border);
  min-width: 0;
}

.grid-connected--cols-2 {
  grid-template-columns: 1fr 1fr;
}

.grid-connected--cols-3 {
  grid-template-columns: repeat(3, 1fr);
}

.grid-connected--cols-4 {
  grid-template-columns: repeat(4, 1fr);
}

@media (max-width: 1024px) {
  .grid-connected--cols-4 {
    grid-template-columns: repeat(2, 1fr);
  }
}

@media (max-width: 640px) {
  .grid-connected--cols-2,
  .grid-connected--cols-3,
  .grid-connected--cols-4 {
    grid-template-columns: 1fr;
  }
}

.module {
  padding: var(--space-4);
}

@media (max-width: 640px) {
  .module {
    padding: var(--space-3);
  }
}

/* ============================
   6. SKIP LINK
   ============================ */
.skip-link {
  position: absolute;
  top: -100%;
  left: var(--space-2);
  z-index: 100;
  padding: var(--space-1) var(--space-2);
  background: var(--background);
  border: var(--border-width) solid var(--text-primary);
  font-family: var(--font-mono);
  font-size: var(--text-sm);
}

.skip-link:focus {
  top: var(--space-2);
}

/* ============================
   7. NAV
   ============================ */
.nav {
  position: sticky;
  top: 0;
  z-index: 50;
  background: var(--background);
  border-bottom: var(--border-width) solid var(--border-frame);
}

.nav__inner {
  display: flex;
  align-items: center;
  justify-content: space-between;
  height: var(--nav-height);
}

.nav__brand {
  font-size: var(--text-bs);
}

.nav__links {
  display: flex;
  gap: var(--space-4);
}

.nav__links a {
  font-size: var(--text-sm);
  font-weight: 900;
  transition: color var(--transition-fast);
}

.nav__links a:hover,
.nav__links a:focus-visible {
  color: var(--accent);
}

/* Phones: name on its own row above the links, so all five links fit.
   --nav-height grows to match, which keeps anchor jumps clear of the nav. */
@media (max-width: 640px) {
  :root {
    --nav-height: 5.5rem;
  }

  .nav__inner {
    flex-direction: column;
    justify-content: center;
    gap: 0.5rem;
  }

  .nav__links {
    gap: var(--space-2);
  }

  .nav__links a {
    font-size: var(--text-xs);
  }
}

/* ============================
   8. HERO
   ============================ */
.hero {
  position: relative;
  /* Keeps the photos' and drag hint's z-indexes inside the hero, so they
     always pass under the sticky nav when scrolled. */
  isolation: isolate;
  min-height: 640px;
}

.hero__inner {
  position: relative;
  z-index: 1;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  text-align: center;
  min-height: inherit;
  padding-block: var(--space-7);
  /* Its box spans the full hero width even where there's no visible
     text, which would otherwise steal pointer events from any hero
     photo placed underneath at the same z-index. Only the links
     need to stay clickable. */
  pointer-events: none;
}

.hero__link {
  pointer-events: auto;
}

/* ---- Hero photos ----
   Configured in data.js (heroPhotos). Each photo is an absolutely
   positioned figure; --top/--offset/--rotate/--width/--ratio are set
   inline per photo by script.js from that config. */
.hero__photos {
  position: absolute;
  inset: 0;
  pointer-events: none;
}

/* Polaroid-style card shared by the hero photos and the about collage;
   built by createPhotoCard() in script.js. */
.photo-card {
  background: var(--bg-raised);
  border: var(--border-width) solid var(--border-frame);
  padding: var(--space-1) var(--space-1) var(--space-2);
  box-shadow: 5px 5px 0 rgba(24, 18, 14, 0.1);
}

.photo-card img {
  display: block;
  width: 100%;
  aspect-ratio: var(--ratio);
  object-fit: cover;
  -webkit-user-drag: none;
}

.photo-card__caption {
  margin-top: var(--space-1);
  font-family: var(--font-caption);
  font-size: 1.25rem;
  font-weight: 600;
  color: var(--border-frame);
  text-align: center;
}

/* Optional link under a caption (`link` in the photo data), on its own line. */
.photo-card__link {
  display: block;
  text-decoration: underline;
  text-underline-offset: 2px;
}

.hero__photo {
  position: absolute;
  top: var(--top);
  width: var(--width);
  transform: rotate(var(--rotate));
  transform-origin: 50% 0; /* swing (script.js) pivots from a pin at the top */
  pointer-events: auto;
  touch-action: none;
  cursor: grab;
  transition: scale 120ms ease;
  user-select: none;
  -webkit-user-select: none;
}

.hero__photo--left {
  left: var(--offset);
}

.hero__photo--right {
  right: var(--offset);
}

.hero__photo--dragging {
  cursor: grabbing;
  scale: 1.05;
}

/* "drag me!" note, shown briefly by initDragHint() in script.js. Lives
   inside .hero__photos so it hides along with the photos on small screens. */
.hero__drag-hint {
  position: absolute;
  inset: 0;
  z-index: 1000; /* above photos, whose z-index climbs as they're dragged (contained by .hero) */
  pointer-events: none;
  opacity: 0;
  transition: opacity 400ms ease;
}

.hero__drag-hint--visible {
  opacity: 1;
}

.hero__drag-hint svg {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  overflow: visible;
}

.hero__drag-hint-label {
  position: absolute;
  font-family: var(--font-caption);
  font-size: 1.35rem;
  font-weight: 600;
  line-height: 1.2;
  color: var(--text-muted);
  white-space: nowrap;
}

@media (max-width: 900px) {
  .hero__photos {
    display: none;
  }
}

.hero__name {
  font-size: var(--text-3xl);
  font-family: var(--font-sans);
}

.hero__subtitle {
  margin-top: var(--space-2);
  font-family: var(--font-mono);
  font-size: var(--text-md);
  font-weight: 400;
  color: var(--text-muted);
}

.hero__tagline {
  margin-top: var(--space-3);
  max-width: 40ch;
  font-family: var(--font-sans);
  font-size: var(--text-md);
  font-weight: 400;
  color: var(--text-muted);
}

.hero__links {
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  gap: var(--space-2);
  margin-top: var(--space-4);
}

.hero__link {
  display: inline-flex;
  align-items: center;
  gap: var(--space-1);
  padding: var(--space-1) var(--space-3);
  border: var(--border-width) solid var(--text-primary);
  font-family: var(--font-sans);
  font-size: var(--text-sm);
  font-weight: 500;
  transition: background-color var(--transition-fast), color var(--transition-fast), border-color var(--transition-fast);
}

.hero__link:hover,
.hero__link:focus-visible {
  background-color: var(--accent);
  color: var(--background);
}

@media (max-width: 640px) {
  .hero__inner {
    padding-block: var(--space-5);
  }

  .hero__name {
    font-size: var(--text-2xl);
  }
}

/* ============================
   9. EXPERIENCE TIMELINE
   ============================ */
.experience {
  padding-block: var(--space-6);
}

.section-head {
  border-top: var(--border-width) solid var(--border-frame);
  border-bottom: var(--border-width) solid var(--border-frame);
  margin-bottom: var(--space-4);
}

.section-head__inner {
  padding-block: var(--space-2);
}

.section-title {
  font-family: var(--font-mono);
  font-size: var(--text-bs);
  font-weight: 600;
  letter-spacing: 0.02em;
  color: var(--border-frame);
}

.panelframe {
  border: var(--border-width) solid var(--border);
}

.grid {
  display: grid;
  grid-template-columns: 1fr;
}

.rail {
  display: flex;
  flex-direction: column;
  padding: var(--space-4);
  border-bottom: var(--border-width) solid var(--border);
}

.rail-label {
  display: block;
  margin-bottom: var(--space-3);
}

.nodes {
  display: flex;
  flex-direction: row;
  align-items: flex-start;
  width: 100%;
}

.node {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: var(--space-2);
  flex: 1 1 0;
  min-width: 0;
  padding: 0;
  text-align: center;
  background: none;
  cursor: pointer;
}

.dotcol {
  position: relative;
  width: 100%;
  height: 1rem;
  display: flex;
  align-items: center;
  justify-content: center;
}

.stem {
  position: absolute;
  top: 50%;
  height: 1px;
  width: 50%;
  background: var(--border);
  transform: translateY(-50%);
}

.stem.up {
  left: 0;
}

.stem.down {
  right: 0;
}

.node:first-child .stem.up {
  display: none;
}

.node:last-child .stem.down {
  display: none;
}

.dot {
  position: relative;
  z-index: 1;
  width: 0.625rem;
  height: 0.625rem;
  border-radius: 50%;
  border: var(--border-width) solid var(--ink-faint);
  background: var(--bg-raised);
}

.node.active .dot {
  width: 0.875rem;
  height: 0.875rem;
  border-color: var(--accent);
  background: var(--accent);
}

.co {
  padding-inline: var(--space-1);
  font-family: var(--font-mono);
  font-size: var(--text-sm);
  color: var(--ink-faint);
  transition: color var(--transition-fast);
}

.node.active .co {
  color: var(--text-primary);
  font-weight: 700;
}

.node:hover .co {
  color: var(--text-primary);
}

.node:focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: 2px;
}

.detail {
  padding: var(--space-4);
  overflow: hidden;
}

/* Slide content in when switching jobs; direction follows the rail. */
.detail.slide-next > *,
.detail.slide-prev > * {
  animation: detail-slide 0.4s cubic-bezier(0.22, 1, 0.36, 1) both;
}

.detail.slide-next > * { --slide-from: 2rem; }
.detail.slide-prev > * { --slide-from: -2rem; }

.detail > :nth-child(2) { animation-delay: 0.04s; }
.detail > :nth-child(3) { animation-delay: 0.08s; }
.detail > :nth-child(4) { animation-delay: 0.1s; }
.detail > :nth-child(n + 5) { animation-delay: 0.12s; }

@keyframes detail-slide {
  from {
    opacity: 0;
    transform: translateX(var(--slide-from));
  }
  to {
    opacity: 1;
    transform: translateX(0);
  }
}

.detail-head {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  font-family: var(--font-mono);
  font-size: var(--text-xs);
  gap: var(--space-3);
}

.comment,
.period {
  color: var(--ink-faint);
}

.company {
  margin-top: var(--space-2);
  font-family: var(--font-sans);
  font-size: var(--text-lg);
  text-transform: uppercase;
}

.role {
  margin-top: var(--space-1);
  font-family: var(--font-sans);
  font-size: var(--text-md);
  font-weight: 500;
  color: var(--accent);
}

.rule {
  margin-block: var(--space-3);
  border: none;
  border-top: var(--border-width) solid var(--line-dim);
}

.bullets {
  display: flex;
  flex-direction: column;
  gap: var(--space-3);
}

.bullets li {
  position: relative;
  padding-left: var(--space-3);
  color: var(--text-muted);
  line-height: 1.6;
}

.bullets li::before {
  content: "\203A";
  position: absolute;
  left: 0;
  font-weight: 700;
  color: var(--accent-dim);
}

.placeholder-note {
  display: inline-block;
  margin-top: var(--space-3);
  font-family: var(--font-mono);
  font-size: var(--text-xs);
  color: var(--ink-faint);
}

/* ============================
   10. PROJECTS
   ============================ */
.projects {
  padding-block: var(--space-6);
}

.projects__header {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
}

.projects__grid {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: var(--space-3);
}

@media (max-width: 1024px) {
  .projects__grid {
    grid-template-columns: repeat(2, 1fr);
  }
}

@media (max-width: 640px) {
  .projects__grid {
    grid-template-columns: 1fr;
  }
}

.project-card {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  padding: var(--space-4);
  background: var(--bg-raised);
  border: var(--border-width) solid var(--border);
  min-width: 0;
  transition: transform var(--transition-fast), box-shadow var(--transition-fast), border-color var(--transition-fast);
}

/* Floats up on hover (or while one of its links has keyboard focus), with
   the same offset shadow as the photo cards. */
.project-card:hover,
.project-card:focus-within {
  transform: translateY(-4px);
  border-color: var(--border-frame);
  box-shadow: 5px 5px 0 rgba(24, 18, 14, 0.1);
}

@media (prefers-reduced-motion: reduce) {
  .project-card:hover,
  .project-card:focus-within {
    transform: none;
  }
}

.project-card__label {
  color: var(--ink-faint);
}

.project-card__name {
  margin-top: var(--space-2);
  font-family: var(--font-sans);
  font-size: var(--text-lg);
  text-transform: uppercase;
}

.project-card__status {
  margin-top: var(--space-1);
  color: var(--text-muted);
}

.project-card__status[data-status="DEPLOYED"] {
  color: var(--accent);
  text-decoration: underline;
}

/* In-progress projects: italic status, faded card background. */
.project-card[data-status="CURRENTLY BUILDING"] {
  background: color-mix(in srgb, var(--bg-raised) 40%, transparent);
}

.project-card__status[data-status="CURRENTLY BUILDING"] {
  font-style: italic;
}

.project-card__summary {
  margin-top: var(--space-3);
  font-family: var(--font-sans);
  font-size: var(--text-sm);
  color: var(--text-muted);
  line-height: 1.6;
}

.project-card__highlights {
  margin-top: var(--space-2);
  margin-bottom: var(--space-4); /* minimum gap above the links */
  padding-left: 1.1em;
  list-style: disc; /* the reset strips list markers */
  font-family: var(--font-sans);
  font-size: var(--text-sm);
  color: var(--text-muted);
  line-height: 1.6;
}

.project-card__highlights li::marker {
  color: var(--accent-dim);
}

.project-card__stack {
  margin-top: var(--space-2);
  font-family: var(--font-mono);
  font-size: var(--text-xs);
  letter-spacing: 0.02em;
  color: var(--ink-faint);
}

/* Live site / GitHub / demo buttons, pinned to the card bottom so they
   line up across a row. */
.project-card__links {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-1);
  margin-top: auto;
}

.project-card__link {
  padding: var(--space-1) var(--space-2);
  border: var(--border-width) solid var(--text-primary);
  font-family: var(--font-mono);
  font-size: var(--text-xs);
  font-weight: 700;
  transition: background-color var(--transition-fast), color var(--transition-fast);
}

.project-card__link:hover,
.project-card__link:focus-visible {
  background-color: var(--accent);
  color: var(--background);
}

button.project-card__link {
  background: none;
  color: inherit;
  cursor: pointer;
}

.demo-modal {
  /* As large as the window allows at 16:9, since YouTube picks its
     quality from the player size. */
  width: min(92vw, 80rem, calc((90vh - 4rem) * 16 / 9));
  max-height: 90vh;
  margin: auto;
  padding: 0;
  border: var(--border-width) solid var(--border-frame);
  background: var(--bg-raised);
  color: var(--text-primary);
  box-shadow: 8px 8px 0 rgba(24, 18, 14, 0.15);
}

.demo-modal::backdrop {
  background: rgba(24, 18, 14, 0.6);
}

.demo-modal__header {
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: var(--space-1) var(--space-2);
  border-bottom: var(--border-width) solid var(--border);
}

.demo-modal__close {
  padding: 0 var(--space-1);
  border: none;
  background: none;
  color: var(--text-primary);
  font-size: var(--text-xl);
  line-height: 1;
  cursor: pointer;
}

.demo-modal__close:hover,
.demo-modal__close:focus-visible {
  color: var(--accent);
}

.demo-modal__media iframe {
  display: block;
  width: 100%;
  aspect-ratio: 16 / 9;
  max-height: calc(90vh - 4rem);
  border: 0;
  background: #000;
}

.demo-modal__media video,
.demo-modal__media img {
  display: block;
  width: 100%;
  max-height: calc(90vh - 4rem);
  object-fit: contain;
  background: #000;
}

/* ============================
   11. ABOUT
   ============================ */
.about {
  padding-block: var(--space-6);
}

.about__container {
  max-width: 72rem; /* 1152px */
  margin-inline: auto;
}

/* Clears the floated text so the section wraps the whole collage. */
.about__container::after {
  content: "";
  display: block;
  clear: both;
}

/* Floated so the collage's cards flow beside it, then on below it. */
.about__text {
  float: left;
  width: 50%;
  margin: 0 var(--space-4) var(--space-2) 0;
}

/* Photo collage: inline cards wrapping around the text. Every photo is the
   same height (scaled to the window height so the section fits on screen);
   `ratio` sets each card's width, `drop` and `rotate` (aboutPhotos in
   data.js) loosen the rows. Cards never overlap. */
.about__collage {
  --photo-h: clamp(90px, 14vh, 130px);
  text-align: center;
}

.about__photo {
  display: inline-block;
  vertical-align: top;
  margin: var(--drop) var(--space-1) var(--space-2);
  transform: rotate(var(--rotate));
  text-align: center;
}

.about__photo img {
  width: calc(var(--photo-h) * var(--ratio));
  height: var(--photo-h);
}

.about__photo .photo-card__caption {
  margin-top: 0.25rem;
  font-size: 1rem;
}

@media (max-width: 640px) {
  .about__text {
    float: none;
    width: 100%;
    margin-right: 0;
  }

  .about__collage {
    --photo-h: 90px;
    margin-top: var(--space-3);
  }
}

.about__heading {
  margin: 0 0 var(--space-2) 0;
  font-family: var(--font-sans);
  font-size: var(--text-md);
  font-weight: 500;
  color: var(--text-primary);
}

.about__intro {
  margin: 0;
  font-family: var(--font-sans);
  font-size: var(--text-sm);
  color: var(--text-muted);
  line-height: 1.6;
}

.about__intro p {
  margin: 0;
}

.about__intro p + p {
  margin-top: 1em;
}

.about__intro a {
  color: var(--text-primary);
  text-decoration: underline;
  text-underline-offset: 2px;
}

/* ============================
   12. SKILLS
   ============================
   Mind map rendered by skills.js from skillsData / strengthTiers in
   data.js. Wide: a fixed ring of categories around the hub, each opening
   its skills beside it on hover, focus or tap. Narrow (container query on
   .skills__layout): a stacked list. Colours arrive per skill as --c, size
   factor as --s. */
.skills {
  padding-top: var(--space-6);
  padding-bottom: var(--space-1);
}

.skills .section-head {
  margin-bottom: calc(var(--space-4) + var(--space-1));
}

.skills__header {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
}

.skills__count {
  font-weight: 400;
}

.skills__layout {
  position: relative;
  container: skills / inline-size;
}

/* ---- Corner: hint + key ----
   The always-on hint and the key (one sample entry showing how to read
   the map) share a row: the top-right corner of the ring layout, or above
   the stacked layout. */
.skills__corner {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: flex-end;
  gap: var(--space-1) var(--space-3);
  margin-bottom: var(--space-2);
}

@container skills (min-width: 980px) {
  .skills__corner {
    position: absolute;
    top: 0;
    right: 0;
    z-index: 6;
    margin: 0;
  }
}

.skills__key {
  position: relative;
  z-index: 2;
  display: flex;
  align-items: baseline;
  gap: 0.6rem;
  width: max-content;
  padding: 0.5rem 0.75rem;
  border: var(--border-width) solid var(--border-frame);
  background: var(--background);
}


.skills__key-title {
  font-family: var(--font-sans);
  font-size: 1.1rem;
  font-weight: 700;
  line-height: 1;
  color: var(--text-primary);
}

.skills__key-sample .skills__leaf {
  padding: 0;
  font-size: 1.25rem;
}

.skills__key-sample .skills__leaf sup {
  font-size: 0.8rem;
}

/* ---- Map ---- */
.skills__map {
  position: relative;
  --skills-scale: 0.85;
  --skills-line: var(--text-primary);
}

.skills__svg {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  overflow: visible;
  pointer-events: none;
}

.skills__line {
  fill: none;
  stroke-linecap: round;
}

.skills__line--trunk {
  stroke: var(--skills-line);
  stroke-width: 1.4;
}

.skills__hub,
.skills__leaf {
  position: relative;
  z-index: 1;
}

.skills__hub {
  display: inline-block;
  padding: 0.45em 1.2em;
  border-radius: 999px;
  background: var(--text-primary);
  color: var(--background);
  font-family: var(--font-sans);
  font-size: calc(var(--text-md) * var(--skills-scale, 1));
  font-weight: 700;
  line-height: 1.2;
}

.skills__cat-label {
  display: block;
  width: max-content;
  max-width: 11.5rem;
  padding: 0.4em 0.95em;
  border: 1.6px solid var(--accent);
  border-radius: 30px;
  background: var(--accent);
  font-family: var(--font-sans);
  font-size: calc(var(--text-base) * var(--skills-scale, 1));
  font-weight: 600;
  line-height: 1.2;
  text-align: center;
  color: var(--background);
  cursor: pointer;
}

.skills__leaf {
  padding: 0.05em 0.25em;
  background: var(--background);
  color: var(--c, var(--text-primary));
  font-family: var(--font-sans);
  font-size: calc((0.95rem + var(--s, 0) * 0.7rem) * var(--skills-scale, 1));
  font-weight: 600;
  line-height: 1.15;
  white-space: nowrap;
}

.skills__leaf--top {
  font-weight: 700;
}

/* Hover: accent colour and a small zoom, lifted above its neighbours. */
.skills__map .skills__leaf {
  display: inline-block;
  cursor: default;
  transition: color var(--transition-fast), transform var(--transition-fast);
}

.skills__map .skills__leaf:hover {
  z-index: 3;
  color: var(--accent);
  transform: scale(1.12);
}

.skills__leaf sup {
  margin-left: 0.15em;
  font-family: var(--font-sans);
  font-size: 0.62rem;
  font-weight: 400;
  line-height: 0;
  vertical-align: super;
}

/* ---- Skill lists ----
   Each category's skills form a small tree: a stub from the pill to a
   vertical spine, and a tick from the spine to each skill. The spine is
   drawn per item (::after) and the tick per item (::before). */
.skills__pop {
  width: max-content;
}

.skills__pop-item {
  position: relative;
  padding: 3px 0 3px 10px;
}

.skills__pop-item::before,
.skills__pop-item::after {
  content: "";
  position: absolute;
  left: 0;
}

.skills__pop-item::before {
  top: 50%;
  width: 10px;
  border-top: 1px solid var(--skills-line);
}

.skills__pop-item::after {
  top: 0;
  bottom: 0;
  border-left: 1px solid var(--skills-line);
}

.skills__pop-item:first-child::after {
  top: 50%;
}

.skills__pop-item:last-child::after {
  bottom: 50%;
}

/* ---- Hint ---- */
.skills__hint {
  font-family: var(--font-caption);
  font-size: 1.35rem;
  font-weight: 600;
  line-height: 1.2;
  color: var(--text-muted);
}

/* Hint and arrow are temporary: skills.js adds --hint to the layout while
   they should show (on entering view, until timeout or first interaction). */
.skills__hint,
.skills__hint-arrow {
  opacity: 0;
  transition: opacity 400ms ease;
}

.skills__layout--hint .skills__hint,
.skills__layout--hint .skills__hint-arrow {
  opacity: 1;
}

.skills__hint-arrow,
.hero__drag-hint-arrow {
  fill: none;
  stroke: var(--text-muted);
  stroke-width: 1.4;
  stroke-linecap: round;
  stroke-linejoin: round;
}

@keyframes skills-pulse {
  0%, 100% { box-shadow: 0 0 0 0 rgba(122, 38, 56, 0); }
  50% { box-shadow: 0 0 0 6px rgba(122, 38, 56, 0.18); }
}

.skills__map--intro .skills__cat:not(.open) .skills__cat-label {
  animation: skills-pulse 1.8s ease-in-out 0.8s 1;
}

/* ---- Ring layout (wide) ---- */
@container skills (min-width: 980px) {
  /* Nav + section header + stage take about 83% of the screen height;
     --skills-chrome (nav + header) is measured by skills.js. */
  .skills__map {
    height: clamp(380px, calc(83vh - var(--skills-chrome, 160px)), 600px);
    --ring-dx: clamp(190px, 18cqw, 210px);
  }

  .skills__hub {
    position: absolute;
    left: 50%;
    top: 50%;
    transform: translate(-50%, -50%);
  }

  .skills__cat {
    position: absolute;
    left: calc(50% + var(--dx) * var(--ring-dx));
    top: var(--y);
    z-index: 2;
    transform: translate(-50%, -50%);
  }

  .skills__cat:hover,
  .skills__cat:focus-within,
  .skills__cat.open {
    z-index: 5;
  }

  /* Keeps the pill above its list's hover wedge (below). */
  .skills__cat-heading {
    position: relative;
    z-index: 1;
  }

  /* Opens on the outer side, vertically centred on the pill and flush
     against it (the stub sits in the list's own padding), so moving the
     pointer from pill to list never leaves the node. */
  .skills__pop {
    position: absolute;
    top: 50%;
    left: 100%;
    z-index: 0;
    padding-left: 22px;
    opacity: 0;
    pointer-events: none;
    transform: translate(6px, calc(-50% + var(--pop-shift, 0px)));
    transition: opacity 180ms ease, transform 180ms ease;
  }

  /* The stub stays level with the pill even when the list is shifted. */
  .skills__pop::before {
    content: "";
    position: absolute;
    left: 0;
    top: calc(50% - var(--pop-shift, 0px));
    width: 22px;
    border-top: 1px solid var(--skills-line);
  }

  /* Invisible wedge from the pill out to the full height of an open list,
     so a diagonal move from the pill to the first or last skill stays
     inside the node. It sits under the pill, so the pill still gets
     clicks. */
  .skills__pop::after {
    content: "";
    position: absolute;
    top: 0;
    bottom: 0;
    right: 100%;
    z-index: -1;
    width: 5.75rem;
    clip-path: polygon(0 calc(50% - var(--pop-shift, 0px) - 1.5rem), 100% 0, 100% 100%, 0 calc(50% - var(--pop-shift, 0px) + 1.5rem));
  }

  .skills__cat:hover .skills__pop,
  .skills__cat:focus-within .skills__pop,
  .skills__cat.open .skills__pop {
    opacity: 1;
    pointer-events: auto;
    transform: translate(0, calc(-50% + var(--pop-shift, 0px)));
  }

  .skills__cat--right .skills__leaf {
    transform-origin: left center;
  }

  /* Left-hand categories: everything mirrored, text right-aligned. */
  .skills__cat--left .skills__pop {
    left: auto;
    right: 100%;
    padding-left: 0;
    padding-right: 22px;
    text-align: right;
    transform: translate(-6px, calc(-50% + var(--pop-shift, 0px)));
  }

  .skills__cat--left .skills__pop::before {
    left: auto;
    right: 0;
  }

  .skills__cat--left .skills__pop::after {
    right: auto;
    left: 100%;
    clip-path: polygon(100% calc(50% - var(--pop-shift, 0px) - 1.5rem), 0 0, 0 100%, 100% calc(50% - var(--pop-shift, 0px) + 1.5rem));
  }

  .skills__cat--left .skills__pop-item {
    padding: 3px 10px 3px 0;
  }

  .skills__cat--left .skills__pop-item::before,
  .skills__cat--left .skills__pop-item::after {
    left: auto;
    right: 0;
  }

  .skills__cat--left .skills__leaf {
    transform-origin: right center;
  }
}

/* ---- Stacked layout (narrow) ---- */
@container skills (max-width: 979.98px) {
  .skills__map {
    display: flex;
    flex-direction: column;
    align-items: flex-start;
    gap: var(--space-2);
  }

  .skills__svg {
    display: none;
  }

  .skills__corner {
    justify-content: space-between;
  }

  .skills__cats {
    align-self: stretch;
    display: flex;
    flex-direction: column;
    gap: var(--space-1);
  }

  .skills__cat {
    max-width: 26rem;
  }

  .skills__cat-label {
    width: 100%;
    max-width: none;
    text-align: left;
  }

  .skills__pop {
    padding: var(--space-1) 0 var(--space-1) 1.4rem;
  }

  /* The spine runs up to the pill above. */
  .skills__pop-item:first-child::after {
    top: calc(-1 * var(--space-1));
  }

  /* Closed lists are clipped, not removed, so screen readers keep them. */
  .skills__cat:not(.open) .skills__pop {
    position: absolute;
    width: 1px;
    height: 1px;
    padding: 0;
    overflow: hidden;
    clip-path: inset(50%);
    white-space: nowrap;
  }
}

@media (prefers-reduced-motion: reduce) {
  .skills__pop {
    transition: none;
  }

  .skills__map .skills__cat-label {
    animation: none !important;
  }

  .skills__map .skills__leaf:hover {
    transform: none;
  }
}

/* ============================
   13. FOCUS STATES
   ============================ */
a:focus-visible,
button:focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: 2px;
}

/* ============================
   14. FOOTER
   ============================
   Name, subtitle and contact links come from `hero` in data.js
   (rendered by renderFooter() in script.js). */
.footer {
  border-top: var(--border-width) solid var(--border-frame);
}

.footer__inner {
  padding-block: var(--space-5) var(--space-3);
}

.footer__id {
  display: flex;
  flex-direction: row;
  gap: var(--space-2);
}

.footer__top {
  display: flex;
  flex-wrap: wrap;
  align-items: flex-end;
  justify-content: space-between;
  gap: var(--space-3);
}

.footer__name {
  margin: 0;
  font-family: var(--font-sans);
  font-size: var(--text-md);
  font-weight: 500;
  color: var(--text-primary);
}

.footer__subtitle {
  margin: var(--space-1) 0 0;
}

.footer__links {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-1) var(--space-3);
}

.footer__link {
  display: inline-flex;
  align-items: center;
  gap: var(--space-1);
  font-family: var(--font-sans);
  font-size: var(--text-sm);
  color: var(--text-primary);
  transition: color var(--transition-fast);
}

.footer__link:hover,
.footer__link:focus-visible,
.footer__top-link:hover,
.footer__top-link:focus-visible {
  color: var(--accent);
}

.footer__bottom {
  display: flex;
  flex-wrap: wrap;
  justify-content: space-between;
  gap: var(--space-1);
  margin-top: var(--space-4);
  padding-top: var(--space-2);
  border-top: var(--border-width) solid var(--border);
}

.footer__top-link {
  transition: color var(--transition-fast);
}

