/* Owned by the "layout" workstream (openspec tasks 2.1-2.8).

   Design system (chosen here since design.md left specifics open):
   - Typography: system font stack (no web-font request -> fast, and reads
     as "simple" rather than styled).
   - Color: neutral off-white surface, near-black text, a single blue
     accent used sparingly for links/interactive affordances. Light theme
     is primary; a dark variant follows prefers-color-scheme until the visitor
     uses the theme switch, which sets data-theme on <html>.
   - Spacing: an 8px-based scale via custom properties.
   - Motion: short (150-260ms) ease transitions only; respects
     prefers-reduced-motion.
*/

:root {
  --color-bg: #dadada;
  --color-surface: #ffffff;
  --color-border: #919191;
  --color-text: #1c1d1f;
  --color-text-muted: #5b5e63;
  --color-accent: #2f5fd1;
  --color-accent-dark: #21449e;
  --color-accent-soft: #ecf1fc;
  /* Icon colour on an accent-filled surface (the theme switch's "on" track). */
  --color-on-accent: #ffffff;
  --color-badge-bg: #fff3e0;
  --color-badge-text: #8a5a00;
  /* Skill pills: a neutral stone, deliberately not the link blue, so they
     don't read as clickable. */
  --color-skill-bg: #eeece5;
  --color-skill-text: #3a3d42;
  --color-skill-border: #dedbd2;

  --color-status-live: #1f8a4c;
  --color-status-live-soft: rgba(31, 138, 76, 0.18);
  --color-status-stable: #2f5fd1;
  --color-status-wip: #c07a00;

  --font-sans: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;

  --radius-sm: 6px;
  --radius-md: 12px;

  --shadow-sm: 0 1px 2px rgba(20, 20, 20, 0.05), 0 1px 1px rgba(20, 20, 20, 0.03);
  --shadow-md: 0 6px 20px rgba(20, 20, 20, 0.08);

  --space-1: 0.25rem;
  --space-2: 0.5rem;
  --space-3: 0.75rem;
  --space-4: 1rem;
  --space-5: 1.5rem;
  --space-6: 2rem;
  --space-7: 3rem;
  --space-8: 4.5rem;

  --content-width: 780px;
  --max-width: 1140px;

  --transition-fast: 150ms ease;
  --transition-base: 240ms ease;

  color-scheme: light;
}

/* Dark theme. `data-theme` on <html> is the visitor's choice (set by the page's
   theme switch); with no choice the system decides. The two blocks below must
   carry identical declarations: tests/es-demo/run_checks.py fails if they differ. */
@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) {
    --color-bg: #111214;
    --color-surface: #1a1b1e;
    --color-border: #2c2e33;
    --color-text: #eef0f2;
    --color-text-muted: #a5a9b0;
    --color-accent: #86a3ff;
    --color-accent-dark: #b7c6ff;
    --color-accent-soft: #1c2438;
    --color-on-accent: #111214;
    --color-badge-bg: #3a2c12;
    --color-badge-text: #e6b866;
    --color-skill-bg: #26282c;
    --color-skill-text: #cfd2d7;
    --color-skill-border: #34363b;
    --color-status-live: #4ad080;
    --color-status-live-soft: rgba(74, 208, 128, 0.22);
    --color-status-stable: #86a3ff;
    --color-status-wip: #e6b866;
    --shadow-sm: 0 1px 2px rgba(0, 0, 0, 0.4);
    --shadow-md: 0 6px 22px rgba(0, 0, 0, 0.5);
    color-scheme: dark;
  }
}

:root[data-theme="dark"] {
  --color-bg: #111214;
  --color-surface: #1a1b1e;
  --color-border: #2c2e33;
  --color-text: #eef0f2;
  --color-text-muted: #a5a9b0;
  --color-accent: #86a3ff;
  --color-accent-dark: #b7c6ff;
  --color-accent-soft: #1c2438;
  --color-on-accent: #111214;
  --color-badge-bg: #3a2c12;
  --color-badge-text: #e6b866;
  --color-skill-bg: #26282c;
  --color-skill-text: #cfd2d7;
  --color-skill-border: #34363b;
  --color-status-live: #4ad080;
  --color-status-live-soft: rgba(74, 208, 128, 0.22);
  --color-status-stable: #86a3ff;
  --color-status-wip: #e6b866;
  --shadow-sm: 0 1px 2px rgba(0, 0, 0, 0.4);
  --shadow-md: 0 6px 22px rgba(0, 0, 0, 0.5);
  color-scheme: dark;
}

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

/* ---- Base ------------------------------------------------------------ */

*, *::before, *::after {
  box-sizing: border-box;
}

html {
  scroll-behavior: smooth;
}

body {
  margin: 0;
  background: var(--color-bg);
  color: var(--color-text);
  font-family: var(--font-sans);
  line-height: 1.55;
  -webkit-font-smoothing: antialiased;
  overflow-x: hidden;
}

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

a {
  color: var(--color-accent);
  text-decoration: none;
}

a:hover {
  text-decoration: underline;
}

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

h1, h2, h3 {
  line-height: 1.2;
  margin: 0;
}

p {
  margin: 0;
}

.section-inner {
  max-width: var(--content-width);
  margin: 0 auto;
  padding-inline: clamp(1.25rem, 4vw, 2rem);
}

.section-heading {
  font-size: 1.5rem;
  margin-bottom: var(--space-4);
}

main > section {
  padding-block: var(--space-7);
}

main > section + section {
  border-top: 1px solid var(--color-border);
}

.noscript-notice {
  max-width: var(--content-width);
  margin: var(--space-5) auto;
  padding: var(--space-4) clamp(1.25rem, 4vw, 2rem);
  text-align: center;
  color: var(--color-text-muted);
}

/* ---- Hero -------------------------------------------------------------- */

.hero {
  position: relative; /* anchors the theme switch */
  max-width: var(--max-width);
  margin: 0 auto;
  padding: var(--space-7) clamp(1.25rem, 4vw, 2.5rem);
  display: flex;
  flex-direction: column;
  gap: var(--space-6);
}

.hero-text {
  max-width: 640px;
}

.eyebrow {
  font-size: 0.85rem;
  font-weight: 600;
  letter-spacing: 0.04em;
  text-transform: uppercase;
  color: var(--color-accent);
  margin-bottom: var(--space-2);
}

.hero-name {
  font-size: clamp(2rem, 5vw, 2.75rem);
  font-weight: 700;
  letter-spacing: -0.01em;
}

.hero-title {
  margin-top: var(--space-2);
  font-size: 1.15rem;
  color: var(--color-text-muted);
  font-weight: 500;
}

.hero-oneliner {
  margin-top: var(--space-4);
  font-size: 1.05rem;
  max-width: 56ch;
}

#es-demo-container {
  width: 100%;
  min-height: 280px;
  background: var(--color-surface);
  border: 1px solid var(--color-border);
  border-radius: var(--radius-md);
  box-shadow: var(--shadow-sm);
}

/* Page-wide light/dark switch: icon only (its name, "Dark mode", is an aria-label),
   top left of the hero, its track aligned with the left edge of the hero text. The
   button is a 44px-tall hit area around a 48x26 track. It is built already showing
   the theme in effect, and .ready (added after the first frames) is what allows it
   to animate, so it never slides into place on load. */
.theme-switch {
  position: absolute;
  top: 0.25rem;
  left: calc(clamp(1.25rem, 4vw, 2.5rem) - 6px);
  z-index: 2;
  padding: 9px 6px;
  border: 0;
  background: transparent;
  cursor: pointer;
}

.theme-switch .track {
  position: relative;
  display: block;
  width: 48px;
  height: 26px;
  border-radius: 999px;
  background: var(--color-border);
  /* The off track alone is only ~1.2:1 against the page; the edge carries the 3:1. */
  border: 1px solid var(--color-text-muted);
}

.theme-switch .track svg {
  position: absolute;
  top: 5px;
  width: 14px;
  height: 14px;
  color: var(--color-text-muted);
}

.theme-switch .track .sun {
  left: 6px;
}

.theme-switch .track .moon {
  right: 6px;
}

.theme-switch .thumb {
  position: absolute;
  top: 2px;
  left: 2px;
  width: 20px;
  height: 20px;
  border-radius: 50%;
  background: var(--color-surface);
  box-shadow: 0 1px 3px rgba(0, 0, 0, 0.3);
}

.theme-switch.ready .track {
  transition: background 0.2s;
}

.theme-switch.ready .thumb {
  transition: transform 0.2s;
}

.theme-switch[aria-checked="true"] .track {
  background: var(--color-accent);
  border-color: var(--color-accent);
}

.theme-switch[aria-checked="true"] .thumb {
  transform: translateX(22px);
}

.theme-switch[aria-checked="true"] .track svg {
  color: var(--color-on-accent);
}

/* Ring the track, not the larger hit area around it. */
.theme-switch:focus-visible {
  outline: none;
}

.theme-switch:focus-visible .track {
  outline: 2px solid var(--color-accent);
  outline-offset: 2px;
}

@media (prefers-reduced-motion: reduce) {
  .theme-switch.ready .track,
  .theme-switch.ready .thumb {
    transition: none;
  }
}

@media (min-width: 800px) {
  .hero {
    flex-direction: row;
    align-items: center;
    gap: var(--space-7);
  }

  .hero-text {
    flex: 1 1 45%;
  }

  #es-demo-container {
    flex: 1 1 50%;
    min-height: 360px;
  }
}

/* ---- Bridge ------------------------------------------------------------ */

.bridge {
  background: var(--color-surface);
  border-top: 1px solid var(--color-border);
  border-bottom: 1px solid var(--color-border);
}

.bridge-inner {
  max-width: var(--content-width);
  margin: 0 auto;
  padding-inline: clamp(1.25rem, 4vw, 2rem);
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: var(--space-4);
}

.bridge-aside {
  flex: none;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: var(--space-4);
}

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

.contact-link {
  display: inline-flex;
  color: var(--color-text-muted);
  transition: color var(--transition-fast);
}

.contact-link:hover {
  color: var(--color-accent);
}

.contact-link svg {
  width: 26px;
  height: 26px;
}

.bridge-photo {
  flex: none;
  width: 132px;
  height: 132px;
  border-radius: 50%;
  box-shadow: 0 0 0 4px var(--color-bg), 0 0 0 7px var(--color-accent), var(--shadow-md);
  object-fit: cover;
  object-position: 50% 50%;
}

@media (min-width: 800px) {
  .bridge-inner {
    flex-direction: row;
    align-items: center;
    gap: var(--space-6);
  }

  .bridge-photo {
    width: 168px;
    height: 168px;
  }
}

.bridge-statement {
  font-size: 1.2rem;
  font-weight: 500;
  max-width: 60ch;
}

.bridge-statement--placeholder {
  color: var(--color-text-muted);
  font-style: italic;
  font-weight: 400;
}

.bridge-supporting {
  margin-top: var(--space-3);
  font-size: 0.85rem;
  color: var(--color-text-muted);
}

.bridge-supporting a {
  font-size: 0.85rem;
}

.bridge-supporting-note {
  color: var(--color-text-muted);
}

/* ---- Projects (visual priority section) -------------------------------- */

/* Deck geometry, shared by the tiles, the deck, and the pointer that links the
   open tile to the detail panel. */
.projects {
  --tile-h: 336px;
  --deck-top: 8px;    /* room above the tiles */
  --deck-slack: 28px; /* room below them, for shadow and the pointer */
  --caret-h: 14px;    /* how far the pointer reaches down from the tile's bottom edge */
  padding-block: var(--space-8);
}

/* Narrow tiles wrap "View live" / "View repo" onto two rows; the extra height
   keeps the second link from being clipped by the tile's fixed size. */
@media (max-width: 480px) {
  .projects {
    --tile-h: 372px;
  }
}

.projects .section-inner {
  max-width: var(--max-width);
}

.projects .section-heading {
  font-size: 1.85rem;
  margin-bottom: var(--space-4);
}

.projects-intro {
  margin-bottom: var(--space-6);
  color: var(--color-text-muted);
  font-size: 0.95rem;
}

.projects-empty {
  padding-block: var(--space-7);
  text-align: center;
  color: var(--color-text-muted);
  font-style: italic;
}

/* ---- Project deck ----------------------------------------------------- */

/* Tiles are absolutely positioned and moved by script.js, so the deck's height
   is fixed: tile height plus the room above and below. The wrapper sits above
   the detail panel so the pointer paints over the panel's top border. */
.project-deck-wrap {
  position: relative;
  z-index: 2;
}

.project-deck {
  position: relative;
  height: calc(var(--deck-top) + var(--tile-h) + var(--deck-slack));
  overflow: hidden;
  touch-action: pan-y;
  cursor: grab;
  border-radius: var(--radius-md);
}

/* Keyboard focus lands on the deck as a whole (arrow keys move through it), but
   a ring around the deck's box would run into the detail panel, so it marks the
   front tile instead, offset clear of the active ring. */
.project-deck:focus-visible {
  outline: none;
}

.project-deck:focus-visible .project-tile--front {
  outline: 2px solid var(--color-accent);
  outline-offset: 4px;
}

.project-deck.is-dragging {
  cursor: grabbing;
  user-select: none;
  -webkit-user-select: none;
}

/* Fixed, uniform tile height regardless of selection state: the active
   marker below is an overlay ring, never border-width or padding, so it never
   changes layout. */
.project-tile {
  position: absolute;
  top: var(--deck-top);
  left: 50%;
  display: flex;
  flex-direction: column;
  width: var(--tile-w, 360px);
  height: var(--tile-h);
  margin-left: calc(var(--tile-w, 360px) / -2);
  background: var(--color-surface);
  border: 1px solid var(--color-border);
  border-radius: var(--radius-md);
  box-shadow: var(--shadow-sm);
  transition: transform 450ms cubic-bezier(0.2, 0.8, 0.2, 1),
    opacity var(--transition-base),
    translate var(--transition-fast),
    box-shadow var(--transition-base);
}

.project-deck--settling .project-tile {
  transition: none;
}

.project-tile--front {
  box-shadow: var(--shadow-md);
}

.project-tile--far {
  opacity: 0;
  pointer-events: none;
}

.project-tile--left {
  --dir: -1;
}

.project-tile--right {
  --dir: 1;
}

.project-tile--side:hover {
  translate: calc(var(--dir) * 10px) 0;
}

.project-tile--placeholder {
  border-style: dashed;
}

.project-tile-content {
  flex: 1 1 auto;
  min-height: 0;
  display: flex;
  flex-direction: column;
  transition: opacity 200ms ease;
}

/* Behind the front tile only the strip (veil + peek) shows, not the content. */
.project-tile--side .project-tile-content {
  opacity: 0;
}

.project-tile-veil {
  position: absolute;
  inset: 0;
  border-radius: inherit;
  background: var(--color-bg);
  opacity: var(--veil, 0);
  pointer-events: none;
  transition: opacity var(--transition-base);
}

/* The strip that peeks out from behind the front tile: status dot, name reading
   top to bottom, then tech icons pinned to the bottom. */
.project-peek {
  position: absolute;
  top: 0;
  bottom: 0;
  width: var(--peek-width, 44px);
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 0.6rem;
  padding: 0.85rem 0;
  background: transparent;
  border: none;
  font: inherit;
  color: var(--color-text);
  cursor: pointer;
  opacity: 0;
  pointer-events: none;
  transition: opacity var(--transition-base);
}

.project-tile--side .project-peek {
  opacity: 1;
  pointer-events: auto;
}

.project-tile--right .project-peek {
  right: 0;
}

.project-tile--left .project-peek {
  left: 0;
}

.project-peek-name {
  flex: none;
  writing-mode: vertical-rl;
  font-size: calc(1.05rem * var(--peek-scale, 1));
  font-weight: 700;
  white-space: nowrap;
  transition: color var(--transition-fast);
}

.project-peek-icons {
  margin-top: auto;
  min-height: 0;
  overflow: hidden;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 4px;
  color: var(--color-text-muted);
  transition: color var(--transition-fast);
}

.project-peek-icons .tech-icon {
  width: var(--peek-icon-size, 15px);
  height: var(--peek-icon-size, 15px);
}

.project-peek:hover .project-peek-name,
.project-peek:hover .project-peek-icons {
  color: var(--color-accent);
}

/* Page-edge lines, like the edge of a book's pages: lit at whichever end still
   has tiles beyond the layers that are drawn. */
.project-deck-edge {
  position: absolute;
  top: var(--deck-top);
  height: var(--tile-h);
  width: var(--edge-gutter, 28px);
  display: flex;
  align-items: center;
  gap: 3px;
  pointer-events: none;
  z-index: 1;
}

.project-deck-edge--left {
  left: 0;
  flex-direction: row-reverse;
  padding-right: 4px;
}

.project-deck-edge--right {
  right: 0;
  padding-left: 4px;
}

.project-deck-edge i {
  flex: none;
  width: 2px;
  height: calc(var(--edge-height, 260px) * (1 - var(--line) * 0.07));
  border-radius: 1px;
  background: var(--color-text-muted);
  opacity: 0;
  transition: opacity var(--transition-base);
}

.project-deck-edge i.is-lit {
  opacity: calc(0.75 - var(--line) * 0.1);
}

/* ---- Deck navigation --------------------------------------------------- */

.project-deck-bar {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: center;
  gap: var(--space-3);
  margin-bottom: var(--space-3);
}

.project-deck-nav {
  display: grid;
  place-items: center;
  width: 2.25rem;
  height: 2.25rem;
  padding: 0 0 0.15rem;
  background: var(--color-surface);
  border: 1px solid var(--color-border);
  border-radius: 50%;
  font: inherit;
  font-size: 1.3rem;
  line-height: 1;
  color: var(--color-text);
  cursor: pointer;
  transition: color var(--transition-fast), border-color var(--transition-fast);
}

.project-deck-nav:hover:not(:disabled) {
  color: var(--color-accent);
  border-color: var(--color-accent);
}

.project-deck-nav:disabled {
  opacity: 0.35;
  cursor: default;
}

.project-deck-position {
  display: flex;
  align-items: center;
  gap: 0.4rem;
  min-width: 0;
}

.project-deck-dot {
  width: 10px;
  height: 10px;
  padding: 0;
  background: var(--color-border);
  border: none;
  border-radius: 50%;
  cursor: pointer;
  transition: width var(--transition-base), background-color var(--transition-fast);
}

.project-deck-dot:hover {
  background: var(--color-text-muted);
}

.project-deck-dot.is-on {
  width: 24px;
  border-radius: 5px;
  background: var(--color-accent);
}

.project-deck-scrub {
  width: min(280px, 50vw);
  accent-color: var(--color-accent);
}

.project-deck-count {
  flex-basis: 100%;
  text-align: center;
  font-size: 0.85rem;
  color: var(--color-text-muted);
  font-variant-numeric: tabular-nums;
}

.project-deck-count strong {
  color: var(--color-text);
}

/* ---- Pointer from the open tile to the detail panel -------------------- */

/* Zero-height overlay whose top edge is the front tile's bottom edge. The
   caret's 17px box is drawn to match --caret-h and the panel's 2px border. */
.project-connector {
  position: absolute;
  left: 0;
  right: 0;
  top: calc(var(--deck-top) + var(--tile-h));
  height: 0;
  opacity: 0;
  pointer-events: none;
  transition: opacity var(--transition-fast);
}

.project-deck-wrap.is-linked .project-connector {
  opacity: 1;
}

.project-connector-caret {
  position: absolute;
  left: 50%;
  top: 0;
  margin-left: -16px;
}

.project-connector-fill {
  fill: var(--color-accent-soft);
}

.project-connector-line {
  fill: none;
  stroke: var(--color-accent);
  stroke-width: 2;
  stroke-linejoin: round;
  stroke-linecap: round;
}

.project-tile-body {
  flex: 1 1 auto;
  min-height: 0;
  overflow: hidden;
  display: flex;
  flex-direction: column;
  gap: var(--space-2);
  padding: var(--space-5);
}

.project-tile-header {
  flex: none;
  display: flex;
  align-items: center;
}

/* Title and tagline get a deterministic height matching their line-clamp
   (line-height * clamped lines) and flex-shrink: 0, so the flex column
   never squeezes them shorter than that — which previously cut the
   clamped text off mid-line instead of at a clean line boundary. */
.project-title {
  flex: none;
  font-size: 1.15rem;
  line-height: 1.3;
  height: calc(1.3em * 2);
  overflow: hidden;
  display: -webkit-box;
  -webkit-box-orient: vertical;
  -webkit-line-clamp: 2;
}

.project-tagline {
  flex: none;
  font-size: 0.92rem;
  color: var(--color-text-muted);
  line-height: 1.4;
  height: calc(1.4em * 2);
  display: -webkit-box;
  -webkit-box-orient: vertical;
  -webkit-line-clamp: 2;
  overflow: hidden;
}

.project-line {
  font-size: 0.92rem;
  color: var(--color-text-muted);
}

.project-label {
  display: block;
  font-size: 0.7rem;
  font-weight: 700;
  letter-spacing: 0.04em;
  text-transform: uppercase;
  color: var(--color-text);
  margin-bottom: 0.15rem;
}

/* One row of links ("View live", "View repo"). Pinned to the bottom of the
   tile's flex column; wraps rather than overflows on narrow tiles. */
.project-links {
  flex: none;
  margin-top: auto;
  padding-top: var(--space-3);
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-1) var(--space-4);
}

.project-link {
  font-weight: 600;
  font-size: 0.92rem;
}

.project-link--disabled {
  color: var(--color-text-muted);
  cursor: default;
}

/* ---- Project status indicator (distinct per value, not text alone) --- */

/* One .project-status per status a project holds ("Stable · Live"). Stacked
   variant is the dots-only column on a peek strip. */
.project-statuses {
  flex: none;
  display: inline-flex;
  align-items: center;
  gap: 0.4rem;
  white-space: nowrap;
}

.project-statuses--stack {
  flex-direction: column;
  gap: 0.35rem;
}

.project-status-sep {
  color: var(--color-text-muted);
  font-size: 0.7rem;
  font-weight: 700;
}

.project-status {
  display: inline-flex;
  align-items: center;
  gap: 0.4rem;
  font-size: 0.7rem;
  font-weight: 700;
  letter-spacing: 0.04em;
  text-transform: uppercase;
  color: var(--color-text-muted);
}

.project-status-dot {
  width: 9px;
  height: 9px;
  border-radius: 50%;
  flex: none;
}

.project-status--live .project-status-dot {
  background: var(--color-status-live);
  box-shadow: 0 0 0 3px var(--color-status-live-soft);
}

.project-status--live {
  color: var(--color-status-live);
}

.project-status--stable .project-status-dot {
  background: var(--color-status-stable);
}

.project-status--stable {
  color: var(--color-status-stable);
}

.project-status--wip .project-status-dot {
  background: transparent;
  border: 2px solid var(--color-status-wip);
}

.project-status--wip {
  color: var(--color-status-wip);
}

.project-status--placeholder .project-status-dot {
  background: transparent;
  border: 2px dashed var(--color-text-muted);
}

/* ---- Details strip (bottom of tile) ----------------------------------- */

.project-details-toggle {
  flex: none;
  display: flex;
  align-items: center;
  justify-content: space-between;
  width: 100%;
  background: transparent;
  border: none;
  border-top: 1px solid var(--color-border);
  padding: var(--space-3) var(--space-5);
  font: inherit;
  font-weight: 600;
  font-size: 0.85rem;
  color: var(--color-text-muted);
  cursor: pointer;
  border-radius: 0 0 var(--radius-md) var(--radius-md);
  transition: color var(--transition-fast), background-color var(--transition-fast);
}

.project-details-toggle:hover {
  color: var(--color-accent);
}

.project-details-toggle .accordion-chevron {
  font-size: 1.1rem;
}

.project-details-toggle[aria-expanded="true"] .accordion-chevron {
  transform: rotate(180deg);
}

/* An overlay rather than an inset box-shadow: a box-shadow paints beneath the
   tile's content, so the Details strip's background would cover the ring. */
.project-tile--active::after {
  content: "";
  position: absolute;
  inset: 0;
  border-radius: inherit;
  box-shadow: inset 0 0 0 2px var(--color-accent);
  pointer-events: none;
}

.project-tile--active .project-details-toggle {
  background: var(--color-accent-soft);
  color: var(--color-accent-dark);
  font-weight: 700;
}

/* ---- Shared detail panel ------------------------------------------------ */

.project-detail-panel {
  display: grid;
  grid-template-rows: 0fr;
  transition: grid-template-rows var(--transition-base), margin-top var(--transition-base);
}

/* Open, the panel tucks up into the deck's bottom slack so its top edge sits
   --caret-h below the tile: exactly where the pointer's base lands. */
.project-detail-panel.is-open {
  grid-template-rows: 1fr;
  margin-top: calc(var(--caret-h) - var(--deck-slack));
}

.project-detail-panel-inner {
  overflow: hidden;
  min-height: 0;
}

.project-detail-panel.is-open .project-detail-panel-inner {
  background: var(--color-surface);
  border: 2px solid var(--color-accent);
  border-radius: var(--radius-md);
  box-shadow: var(--shadow-sm);
  padding: var(--space-5);
}

/* Tinted band across the panel's top, in the same accent as the pointer. */
.project-detail-header {
  display: flex;
  flex-wrap: wrap; /* a long title drops below the status labels on narrow screens */
  align-items: center;
  gap: var(--space-2) var(--space-3);
  margin: calc(-1 * var(--space-5)) calc(-1 * var(--space-5)) var(--space-3);
  padding: var(--space-3) var(--space-5);
  background: var(--color-accent-soft);
}

.project-detail-title {
  font-size: 1.3rem;
}

.project-tech-list {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-2);
  list-style: none;
  margin: var(--space-3) 0 0;
  padding: 0;
}

/* Compact chip row on the tile: icon-only, so it sits tight under the tagline. */
.project-tech-list--tile {
  flex: none;
  margin: var(--space-1) 0 0;
  gap: var(--space-1);
}

/* Icon chips and text chips share one fixed height so mixed rows stay on a
   single baseline. Rounded rectangle (not a pill) so a bare glyph still reads
   as a delimited container. */
.project-tech-tag {
  display: inline-flex;
  align-items: center;
  gap: 0.35rem;
  height: 1.5rem;
  background: var(--color-accent-soft);
  color: var(--color-accent-dark);
  font-size: 0.78rem;
  font-weight: 600;
  line-height: 1;
  padding: 0 0.6rem;
  border-radius: var(--radius-sm);
}

/* Monochrome via currentColor: brand hexes fail 3:1 in one theme or the
   other for six of the ten icons (see add-tech-stack-icons design.md). */
.project-tech-tag--icon {
  padding: 0 0.5rem;
}

.project-tech-tag--icon-only {
  position: relative;
  padding: 0 0.4rem;
}

/* Mouse-over tooltip for icon-only chips: reveals the existing visually-hidden
   label above the chip, so there is one source of text (no title attribute,
   no duplicate announcement) and chips stay out of the tab order. */
.project-tech-tag--icon-only:hover .visually-hidden {
  bottom: calc(100% + 6px);
  left: 50%;
  width: auto;
  height: auto;
  margin: 0;
  padding: 0.2rem 0.4rem;
  overflow: visible;
  clip: auto;
  transform: translateX(-50%);
  background: var(--color-text);
  color: var(--color-bg);
  font-size: 0.72rem;
  font-weight: 600;
  line-height: 1.2;
  border-radius: var(--radius-sm);
  box-shadow: var(--shadow-md);
  pointer-events: none;
  z-index: 2;
}

.tech-icon {
  flex: none;
  width: 1.15rem;
  height: 1.15rem;
  fill: currentColor;
}

/* Clip-rect pattern, not display: none, so the text stays in the a11y tree. */
.visually-hidden {
  position: absolute;
  width: 1px;
  height: 1px;
  margin: -1px;
  padding: 0;
  overflow: hidden;
  clip: rect(0, 0, 0, 0);
  white-space: nowrap;
  border: 0;
}

/* ---- Accordion sections (Teaching / Publications / Thesis) ------------- */

.accordion-section {
  padding-block: var(--space-5);
}

.accordion-section .accordion-heading,
.accordion-section .accordion-summary,
.accordion-section .accordion-panel {
  max-width: var(--content-width);
  margin-inline: auto;
  padding-inline: clamp(1.25rem, 4vw, 2rem);
}

.accordion-heading {
  font-size: 1rem;
}

.accordion-header {
  width: 100%;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-3);
  background: transparent;
  border: none;
  padding: var(--space-2) 0;
  font: inherit;
  font-weight: 700;
  font-size: 1.15rem;
  color: var(--color-text);
  cursor: pointer;
  text-align: left;
  border-radius: var(--radius-sm);
  transition: color var(--transition-fast);
}

.accordion-header:hover {
  color: var(--color-accent);
}

.accordion-chevron {
  flex: none;
  display: inline-block;
  font-size: 1.1rem;
  color: var(--color-text-muted);
  transition: transform var(--transition-base);
}

.accordion-header[aria-expanded="true"] .accordion-chevron {
  transform: rotate(180deg);
}

.accordion-summary {
  margin-top: var(--space-1);
  font-size: 0.92rem;
  color: var(--color-text-muted);
  max-width: 62ch;
}

.accordion-panel {
  display: grid;
  grid-template-rows: 0fr;
  transition: grid-template-rows var(--transition-base);
}

.accordion-panel.is-open {
  grid-template-rows: 1fr;
}

.accordion-panel-inner {
  overflow: hidden;
  min-height: 0;
}

.accordion-panel.is-open .accordion-panel-inner {
  padding-top: var(--space-4);
}

.accordion-detail-text {
  font-size: 0.95rem;
  color: var(--color-text-muted);
}

.publications-list {
  list-style: none;
  margin: 0;
  padding: 0;
  display: flex;
  flex-direction: column;
  gap: var(--space-3);
}

.publications-item {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  gap: var(--space-2);
  font-size: 0.92rem;
  padding-bottom: var(--space-3);
  border-bottom: 1px solid var(--color-border);
}

.publications-item:last-child {
  border-bottom: none;
  padding-bottom: 0;
}

.pub-year {
  font-weight: 700;
  color: var(--color-accent);
  flex: none;
}

.pub-title {
  font-weight: 600;
  color: var(--color-text);
}

a.pub-title:hover {
  color: var(--color-accent);
}

.pub-venue {
  color: var(--color-text-muted);
  font-size: 0.85rem;
}

/* Teaching list. Mobile-first: each entry stacks as
     year   term
     code   title
     institution
   Every field is pinned to an explicit grid cell so a missing field leaves a
   gap instead of pulling its neighbours into the wrong column. */
.teaching-list {
  list-style: none;
  margin: 0;
  padding: 0;
  display: flex;
  flex-direction: column;
  gap: var(--space-3);
}

.teaching-item {
  display: grid;
  /* Floor on the first column keeps titles at one x across rows even though
     course codes differ slightly in width. */
  grid-template-columns: minmax(5.5rem, auto) minmax(0, 1fr);
  column-gap: var(--space-2);
  row-gap: var(--space-1);
  align-items: baseline;
  font-size: 0.92rem;
  padding-bottom: var(--space-3);
  border-bottom: 1px solid var(--color-border);
}

.teaching-item:last-child {
  border-bottom: none;
  padding-bottom: 0;
}

.teaching-year {
  grid-column: 1;
  grid-row: 1;
  font-weight: 700;
  color: var(--color-accent);
}

.teaching-term {
  grid-column: 2;
  grid-row: 1;
  color: var(--color-text-muted);
}

.teaching-code {
  grid-column: 1;
  grid-row: 2;
  color: var(--color-text-muted);
  font-size: 0.85rem;
  white-space: nowrap;
}

.teaching-title {
  grid-column: 2;
  grid-row: 2;
  font-weight: 600;
  color: var(--color-text);
}

.teaching-institution {
  grid-column: 1 / -1;
  grid-row: 3;
  color: var(--color-text-muted);
  font-size: 0.85rem;
}

/* Wide: one row per course, columns aligned across rows via subgrid (so
   long institution names don't need hard-coded widths). A year that repeats
   the row above is hidden visually but stays in the a11y tree. */
@media (min-width: 800px) {
  .teaching-list {
    display: grid;
    grid-template-columns: auto auto auto minmax(0, 1fr) auto;
    column-gap: var(--space-4);
    row-gap: var(--space-3);
  }

  .teaching-item {
    grid-template-columns: subgrid;
    grid-column: 1 / -1;
    column-gap: inherit;
  }

  .teaching-year,
  .teaching-term,
  .teaching-code,
  .teaching-title,
  .teaching-institution {
    grid-row: 1;
  }

  .teaching-year { grid-column: 1; }
  .teaching-term { grid-column: 2; }
  .teaching-code { grid-column: 3; }
  .teaching-title { grid-column: 4; }
  .teaching-institution {
    grid-column: 5;
    text-align: right;
  }

  .teaching-year--repeat {
    position: absolute;
    width: 1px;
    height: 1px;
    margin: -1px;
    padding: 0;
    overflow: hidden;
    clip: rect(0, 0, 0, 0);
    white-space: nowrap;
    border: 0;
  }
}

.thesis-title {
  font-weight: 600;
  color: var(--color-text);
}

.thesis-link,
.pub-title {
  transition: color var(--transition-fast);
}

.thesis-link--disabled {
  color: var(--color-text-muted);
}

.thesis-abstract {
  margin-top: var(--space-3);
  max-width: 70ch;
}

/* ---- Skills -------------------------------------------------------------- */

.skills-empty {
  color: var(--color-text-muted);
  font-style: italic;
}

.skills-groups {
  display: flex;
  flex-direction: column;
  gap: var(--space-4);
}

.skills-group-name {
  font-size: 0.95rem;
  color: var(--color-text-muted);
  margin-bottom: var(--space-2);
}

.skills-tags {
  list-style: none;
  margin: 0;
  padding: 0;
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-2);
}

.skills-tag {
  background: var(--color-skill-bg);
  color: var(--color-skill-text);
  border: 1px solid var(--color-skill-border);
  font-size: 0.85rem;
  padding: 0.3rem 0.7rem;
  border-radius: 999px;
}

