:root {
  --primary: #18181B;
  --secondary: #3F3F46;
  --surface: #FAFAFA;
  --surface-alt: #FFFFFF;
  --border: #E4E4E7;
  --canvas-dot: rgba(24, 24, 27, 0.16);
  --nav-home: #E89B3A;
  --nav-projects: #2E9E5C;
  --nav-about: #5B9BD1;
  --nav-contact: #E85C82;
}

:root[data-theme="dark"] {
  --primary: #FAFAFA;
  --secondary: #A1A1AA;
  --surface: #0B0B0D;
  --surface-alt: #17171A;
  --border: #27272A;
  --canvas-dot: rgba(250, 250, 250, 0.12);
  --nav-home: #F5C878;
  --nav-projects: #6FDB9C;
  --nav-about: #8FC1E8;
  --nav-contact: #F491AC;
}

/* Nav links hover with colors sampled from the name's sunrise gradient */
/* (compound selectors so these reliably beat the base .nav-link::after rule
   regardless of stylesheet load order — same specificity would otherwise
   let whichever loads last win) */
.nav-link.nav-link--home::after { background: var(--nav-home); }
.nav-link--home:hover { color: var(--nav-home); }
.nav-link.nav-link--projects::after { background: var(--nav-projects); }
.nav-link--projects:hover { color: var(--nav-projects); }
.nav-link.nav-link--about::after { background: var(--nav-about); }
.nav-link--about:hover { color: var(--nav-about); }
.nav-link.nav-link--contact::after { background: var(--nav-contact); }
.nav-link--contact:hover { color: var(--nav-contact); }

body,
header,
footer,
section,
a,
button,
span,
p,
div,
svg {
  transition: background-color .5s ease, border-color .5s ease, color .5s ease, box-shadow .5s ease;
}

/* ─── Theme toggle ──────────────────────────────────────────────────────── */
.theme-toggle {
  position: relative;
  display: inline-flex;
  border: none;
  padding: 0;
  cursor: pointer;
  background: none;
  -webkit-tap-highlight-color: transparent;
}
.theme-toggle:focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: 4px;
  border-radius: 999px;
}

/* Pull-cord switch: a small weight hanging off the nav bar's own bottom
   stroke. .pull-switch is a zero-size positioning anchor pinned to nav's
   bottom edge (nav is the positioned ancestor — see the "relative" class
   added to <nav> — not the header, so it stays put even if the mobile menu
   below it expands and grows header's height). Its horizontal position
   (left) is set at runtime by pull-switch.js, which lines it up with the
   color-picker swatches when present, or next to the Contact link on pages
   without one — CSS alone can't reference another element's position.

   It never actually scrolls with the page — the header it lives in is
   fixed, so it's always in the same viewport spot. Instead, past a small
   scroll threshold, pull-switch.js adds .is-docked: the bulb retracts up
   into the nav row itself and the cord disappears, landing next to Contact
   (still inside the fixed header, just repositioned — see left just above).
   That was deliberate: letting it actually travel through the header's own
   space would mean it visibly passing behind the nav's semi-transparent/
   blurred background mid-transition, which looks like a glitch. Docking
   sidesteps that — it never crosses behind anything, it just relocates
   within the same fixed layer.

   The position change itself is a hard snap, not an animated move — an
   attempt at animating top/left directly here looked janky (two elements'
   top independently animating, cord fading in place while the bulb moved
   through it). pull-switch.js masks the snap with a brief opacity
   fade-to-0-and-back instead (see .is-dock-transitioning below), which
   reads as smooth because the position change itself is never seen. */
.pull-switch {
  position: absolute;
  top: 100%;
  left: 0;
  width: 0;
  height: 0;
  transition: opacity 0.18s ease;
}
.pull-switch.is-docked {
  top: 50%;
}
.pull-switch.is-dock-transitioning {
  opacity: 0 !important;
}
.pull-switch__cord {
  position: absolute;
  top: 0;
  left: 50%;
  width: 2px;
  height: 30px;
  background: var(--border);
  transform: translateX(-50%);
  pointer-events: none;
}
.pull-switch.is-docked .pull-switch__cord {
  display: none;
}
.pull-switch__bulb {
  position: absolute;
  top: 30px;
  left: 50%;
  width: 20px;
  height: 20px;
  border-radius: 50%;
  transform: translate(-50%, 0);
  touch-action: none;
  cursor: grab;
  background: radial-gradient(circle at center, #facc15, #fcd34d, #fef9c3);
  box-shadow: 0 0 16px 6px rgba(250, 204, 21, 0.45);
  transition: transform 0.4s cubic-bezier(0.34, 1.56, 0.64, 1), background 0.4s ease, box-shadow 0.4s ease;
}
[data-theme="dark"] .pull-switch__bulb {
  background: radial-gradient(circle at center, #4b5563, #1f2937, #000);
  box-shadow: 0 0 16px 5px rgba(31, 41, 55, 0.6);
}
.pull-switch__bulb:hover {
  box-shadow: 0 0 20px 8px rgba(250, 204, 21, 0.55);
}
[data-theme="dark"] .pull-switch__bulb:hover {
  box-shadow: 0 0 20px 7px rgba(31, 41, 55, 0.7);
}
/* The full glow is meant to catch the eye once, while it's hanging at the
   top — a lit bulb, on purpose. Left at full size/brightness once docked as
   a permanent nav item, though, it keeps pulling focus on every scroll.
   Tried making it a neutral surface-toned dot with a thin colored ring
   instead — that solved the distraction but broke the illusion that it's
   the same bulb: different fill, a border it never had, a different shadow
   type. It read as one object being swapped for a different one, not the
   same one settling down, no matter how the transition between them was
   animated. This keeps the exact same background gradient and still no
   border in both states — just smaller and dimmer once docked — so it's
   unmistakably the same bulb, quieted down. */
.pull-switch.is-docked .pull-switch__bulb {
  width: 12px;
  height: 12px;
  top: -6px; /* re-centers the smaller bulb on the nav row's 50% mark */
  box-shadow: 0 0 5px 1px rgba(250, 204, 21, 0.3);
}
.pull-switch.is-docked .pull-switch__bulb:hover {
  box-shadow: 0 0 7px 2px rgba(250, 204, 21, 0.4);
}
[data-theme="dark"] .pull-switch.is-docked .pull-switch__bulb {
  box-shadow: 0 0 5px 1px rgba(31, 41, 55, 0.45);
}
[data-theme="dark"] .pull-switch.is-docked .pull-switch__bulb:hover {
  box-shadow: 0 0 7px 2px rgba(31, 41, 55, 0.55);
}
.pull-switch.is-dragging .pull-switch__bulb {
  cursor: grabbing;
}

/* Dot patches: small faded pieces of the hero's canvas motif, scattered
   through other sections/pages as a static (no hover, no full-bleed) echo
   of the landing dot grid — just enough to read as "the same canvas",
   never a competing focal point. Host section needs position:relative. */
.dot-patch {
  position: absolute;
  pointer-events: none;
  z-index: 0;
  background-image: radial-gradient(circle, var(--canvas-dot) 1px, transparent 1px);
  background-size: 20px 20px;
  -webkit-mask-image: radial-gradient(ellipse at center, black 0%, black 25%, transparent 70%);
          mask-image: radial-gradient(ellipse at center, black 0%, black 25%, transparent 70%);
}
.dot-patch--sm { width: 140px; height: 140px; }
.dot-patch--md { width: 240px; height: 200px; }
.dot-patch--lg { width: 340px; height: 280px; }
.dot-patch--xl { width: 480px; height: 400px; }
/* For sections with a fixed dark background regardless of site theme
   (e.g. the always-dark contact/footer) — --canvas-dot would otherwise
   resolve to the light-theme (dark-dot) value and vanish against it. */
.dot-patch--on-dark { background-image: radial-gradient(circle, rgba(250, 250, 250, 0.14) 1px, transparent 1px); }
