* {
  box-sizing: border-box;
}

:root {
  /* Dock glass tokens (Phase 3.1) — shared by both day and night mode,
     since the dock sits at body level rather than inside .desktop--day.
     Kept independent of any day-mode-scoped tokens so the dock's glass
     look never depends on what else exists in day mode. */
  --dock-glass-fill: rgba(20, 20, 30, 0.45);
  --dock-glass-border: rgba(255, 255, 255, 0.18);
  --dock-glass-blur: blur(16px);
  --dock-glass-shadow: 0 8px 24px rgba(0, 0, 0, 0.25);
}

html,
body {
  margin: 0;
  padding: 0;
  height: 100%;
  background: #000;
  cursor: none;
}

/* ---------------- Loading screen ---------------- */

.loader-screen {
  position: fixed;
  inset: 0;
  z-index: 100;
  background: #000;
  display: flex;
  align-items: center;
  justify-content: center;
}

/* From Uiverse.io by dexter-st */
.loader-wrapper {
  position: relative;
  display: flex;
  align-items: center;
  justify-content: center;
  width: 180px;
  height: 180px;
  font-family: "Inter", sans-serif;
  font-size: 1.2em;
  font-weight: 300;
  color: white;
  border-radius: 50%;
  background-color: transparent;
  user-select: none;
}
.loader {
  position: absolute;
  top: 0;
  left: 0;
  width: 100%;
  aspect-ratio: 1 / 1;
  border-radius: 50%;
  background-color: transparent;
  animation: loader-rotate 2s linear infinite;
  z-index: 0;
}
@keyframes loader-rotate {
  0% {
    transform: rotate(90deg);
    box-shadow:
      0 10px 20px 0 #fff inset,
      0 20px 30px 0 #ad5fff inset,
      0 60px 60px 0 #471eec inset;
  }
  50% {
    transform: rotate(270deg);
    box-shadow:
      0 10px 20px 0 #fff inset,
      0 20px 10px 0 #d60a47 inset,
      0 40px 60px 0 #311e80 inset;
  }
  100% {
    transform: rotate(450deg);
    box-shadow:
      0 10px 20px 0 #fff inset,
      0 20px 30px 0 #ad5fff inset,
      0 60px 60px 0 #471eec inset;
  }
}

/* ---------------- Landing page ---------------- */

.landing {
  position: relative;
  width: 100%;
  height: 100vh;
  overflow: hidden;
  --animate-duration: 1.2s;
}

.landing-image {
  width: 100%;
  height: 100%;
  object-fit: cover;
  display: block;
}

/* ---------------- Hover hotspots ---------------- */
/* Each hotspot is invisible and shaped to trace a specific part of
   the photo (via clip-path) — hovering it reveals the info panel. */

.hotspot {
  position: absolute;
  inset: 0;
  cursor: none;
}

.hotspot-works {
  clip-path: polygon(
    10.23% 0%,
    10.52% 9.04%,
    7.63% 13.65%,
    7.53% 18.77%,
    5.6% 25.43%,
    5.98% 29.35%,
    9.75% 29.86%,
    9.85% 40.78%,
    21.24% 40.44%,
    22.2% 42.32%,
    30.12% 43.86%,
    31.37% 40.96%,
    32.05% 30.89%,
    31.47% 0%
  );
}

.hotspot-about {
  clip-path: polygon(
    49.23% 23.2%,
    47.17% 26.97%,
    48.97% 37.03%,
    45.12% 42.63%,
    42.99% 66.06%,
    40.75% 61.6%,
    38.05% 61.6%,
    39.65% 66.29%,
    39.72% 77.03%,
    41.52% 80.46%,
    54.24% 85.03%,
    61.89% 83.77%,
    63.88% 79.54%,
    63.82% 65.83%,
    64.78% 61.83%,
    63.37% 59.54%,
    61.63% 60.69%,
    61.63% 47.66%,
    59.83% 46.06%,
    59.51% 42.4%,
    53.34% 37.14%,
    52.76% 25.71%,
    51.41% 23.09%
  );
}

.hotspot-socials-1 {
  clip-path: polygon(
    47.4% 44.98%,
    42.58% 44.75%,
    42.52% 54.45%,
    43.03% 54.34%,
    43.22% 51.37%,
    43.61% 50.91%,
    44.38% 47.95%,
    45.73% 46.12%,
    46.63% 45.78%,
    46.82% 45.32%,
    47.33% 45.32%
  );
}

.hotspot-socials-2 {
  clip-path: polygon(
    53.76% 44.98%,
    53.82% 45.32%,
    54.34% 45.32%,
    54.4% 45.43%,
    55.11% 45.55%,
    55.17% 45.66%,
    55.17% 46%,
    55.3% 46.23%,
    55.94% 46.58%,
    56.26% 47.03%,
    56.52% 47.03%,
    56.71% 46.46%,
    56.58% 46%,
    56.58% 45.21%,
    56.52% 45.09%,
    55.94% 45.09%,
    55.49% 44.86%,
    54.27% 44.86%,
    54.21% 44.98%
  );
}

.hotspot-more {
  clip-path: polygon(
    100% 22.03%,
    92.94% 22.03%,
    86.91% 25.23%,
    87.93% 67.58%,
    88.7% 68.38%,
    89.47% 80.59%,
    90.05% 80.02%,
    90.12% 76.03%,
    95.12% 75.23%,
    96.15% 72.95%,
    95.12% 68.04%,
    96.02% 67.81%,
    96.21% 66.55%,
    94.74% 58.68%,
    95.31% 58.56%,
    96.79% 62.44%,
    97.5% 62.79%,
    98.01% 56.62%,
    100% 50.57%
  );
}

.hotspot-music {
  clip-path: polygon(
    3.28% 53.88%,
    3.15% 58.9%,
    4.17% 59.59%,
    5.2% 59.47%,
    7.39% 60.05%,
    8.09% 61.99%,
    8.09% 63.47%,
    8.41% 63.93%,
    11.82% 64.27%,
    12.4% 64.73%,
    15.41% 59.7%,
    15.35% 54.79%,
    15.03% 54.34%,
    13.87% 54.45%,
    13.55% 54%,
    3.6% 53.08%
  );
}

.hotspot-open-to-work {
  clip-path: polygon(
    14.26% 72.72%,
    14.26% 73.52%,
    14.45% 73.97%,
    14.45% 77.17%,
    14.9% 78.31%,
    15.41% 78.77%,
    16.83% 78.77%,
    17.08% 78.54%,
    17.6% 77.51%,
    18.3% 77.4%,
    18.82% 76.48%,
    18.88% 74.2%,
    18.63% 73.52%,
    17.85% 73.52%,
    17.73% 72.83%,
    16.51% 72.37%,
    16.31% 72.49%,
    14.58% 72.37%
  );
}

.hotspot-coffee {
  clip-path: polygon(
    18.82% 68.04%,
    18.82% 70.89%,
    18.69% 71%,
    18.69% 71.35%,
    19.01% 71.69%,
    19.01% 72.26%,
    19.14% 72.83%,
    19.46% 73.63%,
    19.91% 74.32%,
    20.62% 74.66%,
    21.77% 74.66%,
    21.9% 74.43%,
    21.9% 73.4%,
    22.67% 72.95%,
    23.12% 72.37%,
    23.44% 71.58%,
    23.44% 69.98%,
    23.31% 69.41%,
    22.86% 68.72%,
    22.35% 68.72%,
    22.29% 68.04%,
    21.77% 67.92%,
    19.78% 67.92%,
    19.72% 68.04%,
    18.88% 67.92%
  );
}

/* ---------------- Hover reveal text (follows the cursor) ---------------- */

.hover-label {
  position: fixed;
  top: 0;
  left: 0;
  z-index: 60;
  pointer-events: none;
  opacity: 0;
  transform: translate(-9999px, -9999px);
  transition: opacity 0.2s ease;
  white-space: nowrap;
}

.hover-label.active {
  opacity: 1;
}

.hover-label-title {
  display: block;
  font-family: Georgia, "Times New Roman", serif;
  font-style: italic;
  font-size: 2.1rem;
  font-weight: 600;
  color: #fff;
  text-shadow: 0 2px 20px rgba(0, 0, 0, 0.7);
}

.hover-label-desc {
  display: block;
  margin-top: 0.3rem;
  font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;
  font-size: 0.85rem;
  color: rgba(255, 255, 255, 0.82);
  text-shadow: 0 1px 10px rgba(0, 0, 0, 0.7);
}

/* ---------------- Custom cursor ---------------- */

.cursor {
  position: fixed;
  top: 0;
  left: 0;
  width: 20px;
  height: 20px;
  border-radius: 50%;
  background: #fff;
  mix-blend-mode: difference;
  pointer-events: none;
  z-index: 9999;
  transform: translate(-50%, -50%);
  will-change: transform;
}

@media (hover: none) {
  .cursor {
    display: none;
  }
  html,
  body {
    cursor: auto;
  }
}

/* ================================================================
   DAY MODE — FOUNDATION (Phase 1A)
   ----------------------------------------------------------------
   Structural scaffolding only: region placement, spacing rhythm, and
   the day/night mode boundary. No final day photography, icon
   artwork, or color palette yet. Every rule below is additive —
   nothing above this point (the existing night styles) is modified.
   ================================================================ */

/* Mode boundary. Defaults to night-visible: .desktop--day only shows
   once [data-mode="day"] is explicitly present, so if the mode script
   ever fails to run, the page falls back to the working night UI
   rather than to a half-built day one. */
.desktop--day {
  display: none;
}

[data-mode="day"] .landing {
  display: none;
}

[data-mode="day"] .desktop--day {
  display: block;
}

/* ---------------- Day desktop canvas ---------------- */
/* Figma-matched color foundation: a warm terracotta → dusty mauve →
   periwinkle gradient, as CSS variables so the palette stays a
   one-place edit. Supersedes the earlier blue-only placeholder. */
.desktop--day {
  /* Gradient stops */
  --day-bg-top: #a94d4f;
  --day-bg-mid: #98658d;
  --day-bg-bottom: #6f8df5;

  position: relative;
  width: 100%;
  height: 100vh;
  overflow: hidden;
  background: linear-gradient(
    160deg,
    var(--day-bg-top) 0%,
    var(--day-bg-mid) 48%,
    var(--day-bg-bottom) 100%
  );
}

/* Widget cluster (weather/date/clock, top-right) removed at the user's
   request — it and its day-glass and day-widget custom-property tokens,
   its .widget rules, and its dedicated responsive tiers all lived only
   here and are gone together, cleanly, rather than left as unused dead
   CSS. */

/* ---------------- Day background shader (js/day-background.js) ---------------- */
/* Full-viewport WebGL canvas — the day desktop's base visual layer,
   replacing the earlier morning-aurora overlay. z-index 0, pointer-events
   disabled, so it never blocks a click. It still paints over the
   .desktop--day gradient below, which is kept in the CSS purely as the
   fallback shown when WebGL is unavailable or prefers-reduced-motion is
   on (js/day-background.js hides this canvas in both cases).
   pointer-events:none keeps it from ever intercepting a click. */
.day-shader-bg {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  z-index: 0;
  pointer-events: none;
}

/* ---------------- Day-mode cursor ---------------- */
/* Day mode now uses the exact same JS-driven circle-dot cursor as night
   mode (see `.cursor` above and js/main.js's mousemove listener, both
   unscoped to any mode). No day-specific cursor rule is needed: the
   base `html, body { cursor: none; }` already hides the native OS
   cursor in day mode too, and `.cursor` is visible by default — there
   used to be a `[data-mode="day"] .cursor { display: none; }` override
   plus a custom image cursor here, both removed so day and night render
   identically. */

/* ================================================================
   DOCK — REAL ICONS + MAGNIFICATION (Phase 3.2, on the Phase 3.1
   structure)
   ----------------------------------------------------------------
   Shared by both day and night mode: the markup lives at body level
   in index.html, not inside #desktop--day, so it renders identically
   regardless of data-mode. js/dock.js drives the cursor-distance
   magnification; everything here is static positioning/sizing that
   JS temporarily overrides via inline width/height on each .dock-slot
   (dividers are a separate class and are never touched by JS).
   ================================================================ */

/* Hover/focus catch zone (Phase 3.3) — a fixed strip spanning the full
   viewport width, from the very bottom edge up past the dock's resting
   position. js/dock.js binds its show/hide listeners to this element,
   not to the dock itself, so that the "hotspot" area below the dock
   and the dock itself are one continuous hoverable region: crossing
   between them never fires a leave/re-enter, which is what keeps the
   reveal from flickering. It sits at the same z-index tier as the
   dock it contains; its height is just tall enough to comfortably
   contain the dock (including its reveal-slide offset) at each
   breakpoint, defined alongside the dock's own breakpoints below. */
.dock-hover-zone {
  position: fixed;
  left: 0;
  bottom: 0;
  width: 100%;
  height: 130px;
  z-index: 40;
}

/* Fixed, horizontally centered at the bottom of the viewport.
   Sits below .hover-label (60), .loader-screen (100, temporary on
   load) and .cursor (9999); above the day shader background (0). */
.dock-wrapper {
  position: fixed;
  left: 50%;
  bottom: 1.5rem;
  transform: translateX(-50%);
  z-index: 40;
  max-width: calc(100vw - 1.5rem);
}

.dock {
  display: flex;
  align-items: flex-end;
  justify-content: center;
  gap: 10px;
  padding: 10px 16px;
  border-radius: 26px;
  background: var(--dock-glass-fill);
  border: 1px solid var(--dock-glass-border);
  backdrop-filter: var(--dock-glass-blur);
  -webkit-backdrop-filter: var(--dock-glass-blur);
  box-shadow: var(--dock-glass-shadow);
  max-width: 100%;
  overflow: visible;
}

/* Each slot is now a real icon (or a link wrapping one) rather than a
   placeholder swatch — the icons are already finished, shadowed,
   rounded-square artwork with transparent backgrounds, so the slot
   itself carries no background/border of its own; it's just a sizing
   box. object-fit: contain on the image means the original 1254x1254
   files are only ever displayed smaller, never cropped or distorted,
   and never modified on disk. transform-origin: bottom keeps growth
   pointing upward, like a real macOS dock, instead of growing from
   the slot's center. Some slots are <a> tags (GitHub/LinkedIn/Gmail);
   this resets their default inline/underline/color behavior so they
   size and center exactly like the non-link slots. */
.dock-slot {
  width: 40px;
  height: 40px;
  flex: 0 0 auto;
  transform-origin: bottom center;
  position: relative;
  display: flex;
  align-items: center;
  justify-content: center;
  text-decoration: none;
  color: inherit;
}

.dock-icon-img {
  width: 100%;
  height: 100%;
  object-fit: contain;
  display: block;
  pointer-events: none; /* hover/click land on .dock-slot, not the image */
  -webkit-user-drag: none;
  user-select: none;
}

/* Group separators — thin static dividers between the PERSONAL /
   PORTFOLIO / CONTACT & SOCIAL / OTHERS groups. Fixed height (not
   percentage/stretch) so a neighboring icon's magnification never
   makes a divider grow or pulse — it only reflects the dock's resting
   proportions at the current breakpoint. Excluded from js/dock.js's
   .dock-slot selector entirely, so it's never a magnification target. */
.dock-divider {
  width: 1px;
  height: 26px;
  align-self: center;
  background: rgba(255, 255, 255, 0.22);
  flex: 0 0 auto;
}

/* Per-icon name tooltip — a small pill above the icon, shown on
   hover/keyboard focus. Pure CSS (content: attr()) rather than
   reusing #hoverLabel (that system is built for the landing page's
   cursor-following title+description hotspots, not single-word dock
   labels), so it can't interfere with dock.js's spring loop. Anchored
   to bottom: 100% of the slot, so it stays glued above the icon as
   dock.js resizes the slot during magnification. */
.dock-slot[data-tooltip] {
  cursor: pointer;
}

.dock-slot[data-tooltip]::before {
  content: attr(data-tooltip);
  position: absolute;
  bottom: calc(100% + 10px);
  left: 50%;
  transform: translateX(-50%) translateY(4px);
  padding: 4px 9px;
  border-radius: 8px;
  background: rgba(20, 20, 30, 0.85);
  border: 1px solid rgba(255, 255, 255, 0.18);
  color: #fff;
  font-family: system-ui, -apple-system, "Segoe UI", sans-serif;
  font-size: 12px;
  line-height: 1.4;
  white-space: nowrap;
  pointer-events: none;
  opacity: 0;
  visibility: hidden;
  transition: opacity 0.15s ease, transform 0.15s ease;
  z-index: 5;
}

.dock-slot[data-tooltip]:hover::before,
.dock-slot[data-tooltip]:focus-visible::before {
  opacity: 1;
  visibility: visible;
  transform: translateX(-50%) translateY(0);
}

/* No hover capability (touch devices) — the magnification effect
   needs a moving cursor to react to, which doesn't exist on touch, so
   js/dock.js skips its mousemove wiring entirely in this case. Also
   hide the hover tooltip there, since touch has no hover state to
   trigger it and it would otherwise stay stuck open after a tap. */
@media (hover: none) {
  .dock-slot {
    transition: none;
  }
  .dock-slot[data-tooltip]::before {
    display: none;
  }
}

/* ---------------- Dock "Switch" button (index.html only) ----------------
   Adapted from a Uiverse.io "Liquid" gradient-button component the user
   supplied verbatim (layered rotating radial-gradient divs blended with
   color-dodge/difference/multiply, a soft pulsing highlight, a text
   label and a text-overlay sitting on top of it for the raised/glossy
   look). The mechanism is kept exactly as given; every dimension that
   was originally a fixed px/rem value (sized for a ~200px pill button)
   is converted to a percentage of this element's own box instead, so
   the whole thing scales down cleanly to the same box a normal dock
   icon uses (40/34/26/20px across the existing breakpoints below)
   rather than being clipped or mis-positioned at icon scale. Click
   behavior (the actual day/night toggle) is wired in index.html's own
   inline script, right next to the mode logic it drives — day.html and
   night.html don't get this slot at all, since they're static,
   single-mode previews with no clock logic to toggle.
   .dock-slot itself supplies the sizing/position/magnify/tooltip
   plumbing already shared by every other icon (js/dock.js is
   completely unchanged); only its contents are custom here. */
.dock-slot--switch {
  isolation: isolate;
}

.dock-slot--switch .btn-wrapper {
  --rad: 34%;
  --color-wrapper-border: rgba(255, 255, 255, 0.85);
  --color-btn-bg: #f00;
  --color-btn-text: #000;
  --color-btn-text-shadow: #fff;
  --color-btn-inset-shadow: #558;
  --color-layer-a: #fff;
  --color-layer-b: #00f;
  --color-overlay-text: #000;
  --color-overlay-glow: #fff;
  --color-overlay-shadow: #0004;
  --color-overlay-highlight: #fff5;

  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  display: flex;
  align-items: center;
  justify-content: center;
  overflow: clip;

  border: 1.5px solid var(--color-wrapper-border);
  border-radius: var(--rad);

  font-family: system-ui, -apple-system, "Segoe UI", sans-serif;
  font-size: 8px;
  font-weight: 600;

  filter: saturate(0.65) brightness(1.8);
}

.dock-slot--switch .gradient-btn {
  position: absolute;
  inset: 0;
  z-index: -1;
  width: 100%;
  height: 100%;
  display: flex;
  align-items: center;
  justify-content: center;

  padding: 0;
  border: none;
  border-radius: var(--rad);

  font-family: inherit;
  font-size: inherit;
  font-weight: inherit;
  letter-spacing: 0.02em;

  color: var(--color-btn-text);
  background-color: var(--color-btn-bg);
  background-size: 200% 200%;
  box-shadow: inset 0 0 4px 3px var(--color-btn-inset-shadow);
  text-shadow: 0 1px 1px var(--color-btn-text-shadow);

  cursor: pointer;
  mix-blend-mode: color-dodge;
  transition:
    color 0.3s ease,
    text-shadow 0.3s ease;
}

.dock-slot--switch .gradient-btn::after {
  content: "";
  position: absolute;
  pointer-events: none;

  left: 0;
  top: 0;
  width: 100%;
  height: 100%;

  border-radius: var(--rad);
  background-size: 200% 200%;
  mix-blend-mode: difference;
  z-index: 1;
}

.dock-slot--switch .gradient-layer {
  position: absolute;
  pointer-events: none;

  left: -80%;
  top: 0;
  width: 500%;
  aspect-ratio: 1;

  background: radial-gradient(
    ellipse at 65% 180%,
    var(--color-layer-a),
    var(--color-layer-b),
    var(--color-layer-a),
    var(--color-layer-b),
    var(--color-layer-a),
    var(--color-layer-b),
    var(--color-layer-a),
    var(--color-layer-b),
    var(--color-layer-a),
    var(--color-layer-b),
    var(--color-layer-a)
  );

  mix-blend-mode: difference;
  animation: dock-switch-rotate 8s linear infinite;
}

.dock-slot--switch .gradient-layer:last-child {
  mix-blend-mode: color-dodge;
}

@keyframes dock-switch-rotate {
  0% {
    transform: rotate(0deg);
  }
  100% {
    transform: rotate(360deg);
  }
}

.dock-slot--switch .text-overlay {
  position: absolute;
  inset: 0;
  pointer-events: none;
  z-index: 2;
  display: flex;
  align-items: center;
  justify-content: center;

  border-radius: var(--rad);

  font-family: inherit;
  font-size: inherit;
  font-weight: inherit;
  letter-spacing: 0.02em;

  color: var(--color-overlay-text);
  text-shadow: 0 0 2px var(--color-overlay-glow);
  box-shadow:
    inset 0 -2px 2px 0 var(--color-overlay-shadow),
    inset 0 2px 2px 0 var(--color-overlay-highlight);

  mix-blend-mode: multiply;
  transition: transform 0.3s ease;
  animation: dock-switch-opacity-pulse 5s ease infinite;
}

.dock-slot--switch:hover .text-overlay,
.dock-slot--switch:focus-within .text-overlay {
  transform: scale(1.08);
}

.dock-slot--switch:hover .gradient-btn,
.dock-slot--switch:focus-within .gradient-btn {
  color: #0000;
  text-shadow: 0 0 0 #0000;
}

.dock-slot--switch:active .text-overlay {
  transform: scale(0.92);
}

.dock-slot--switch .light {
  position: absolute;
  pointer-events: none;
  left: 10%;
  top: 14%;
  z-index: 1;
  border-radius: 50px;
  width: 80%;
  height: 22%;
  background-color: #fff5;
  filter: blur(2px);
  animation: dock-switch-pulse 3s ease-in-out infinite;
}

@keyframes dock-switch-pulse {
  0%,
  100% {
    opacity: 1;
  }
  50% {
    opacity: 0.1;
  }
}

@keyframes dock-switch-opacity-pulse {
  0%,
  100% {
    opacity: 1;
  }
  50% {
    opacity: 0.5;
  }
}

.dock-slot--switch .gradient-btn,
.dock-slot--switch .text-overlay {
  -webkit-tap-highlight-color: transparent;
}

@media (max-width: 900px) {
  .dock-slot--switch .btn-wrapper {
    font-size: 7px;
  }
}

@media (max-width: 640px) {
  .dock-slot--switch .btn-wrapper {
    font-size: 6px;
    border-width: 1px;
  }
}

@media (max-width: 420px) {
  .dock-slot--switch .btn-wrapper {
    font-size: 5px;
    border-width: 1px;
  }
}

@media (prefers-reduced-motion: reduce) {
  .dock-slot--switch .gradient-layer,
  .dock-slot--switch .light,
  .dock-slot--switch .text-overlay {
    animation: none;
  }
}

/* ---------------- Night-mode hidden dock + cinematic reveal (Phase 3.3) ----------------
   Hidden by default, revealed by approaching the bottom edge. Scoped
   two ways so nothing else regresses:
   - @media (hover: hover) — only devices with a real pointer get this
     at all. Touch has no hover state to trigger a reveal with, so a
     hidden-by-default dock there would be permanently unreachable;
     touch devices simply never get this rule and keep the dock
     always visible, same as before.
   - [data-mode="night"] — day mode's dock is completely untouched by
     this block and keeps its original always-visible behavior. Since
     this keys off the live data-mode attribute (not a JS-cached
     value), it also self-corrects automatically at the exact moment
     the site's day/night boundary timer flips data-mode, with no
     extra code needed here.
   Only opacity/transform are animated (GPU-composited, no layout
   thrash) and #dockHoverZone's own box never moves — only its
   .dock-visible class (toggled by js/dock.js's show/hide state
   machine) and :focus-within (a pure-CSS fallback for keyboard
   access, independent of JS) switch between these two states. */
@media (hover: hover) {
  [data-mode="night"] .dock-wrapper {
    opacity: 0;
    transform: translateX(-50%) translateY(24px);
    pointer-events: none;
    transition: opacity 0.35s cubic-bezier(0.22, 1, 0.36, 1),
      transform 0.35s cubic-bezier(0.22, 1, 0.36, 1);
  }

  [data-mode="night"] .dock-hover-zone.dock-visible .dock-wrapper,
  [data-mode="night"] .dock-hover-zone:focus-within .dock-wrapper {
    opacity: 1;
    transform: translateX(-50%) translateY(0);
    pointer-events: auto;
  }

  /* Reduced motion: keep the show/hide behavior, drop the slide/fade
     animation — same convention as the magnification code's own
     prefers-reduced-motion bailout in js/dock.js. */
  @media (prefers-reduced-motion: reduce) {
    [data-mode="night"] .dock-wrapper {
      transition: none;
    }
  }
}

@media (max-width: 900px) {
  .dock-hover-zone {
    height: 110px;
  }
  .dock-wrapper {
    bottom: 1.25rem;
  }
  .dock {
    gap: 8px;
    padding: 8px 13px;
  }
  .dock-slot {
    width: 34px;
    height: 34px;
  }
  .dock-divider {
    height: 22px;
  }
}

@media (max-width: 640px) {
  .dock-hover-zone {
    height: 95px;
  }
  .dock-wrapper {
    bottom: 1rem;
  }
  .dock {
    gap: 6px;
    padding: 6px 10px;
  }
  .dock-slot {
    width: 26px;
    height: 26px;
  }
  .dock-divider {
    height: 18px;
  }
}

@media (max-width: 420px) {
  .dock-hover-zone {
    height: 85px;
  }
  .dock {
    gap: 4px;
    padding: 5px 8px;
  }
  .dock-slot {
    width: 20px;
    height: 20px;
  }
  .dock-divider {
    height: 14px;
    margin: 0 1px;
  }
}

/* ---------------- Resume popover (dock "Resume" slot) ----------------
   Opened by js/dock.js from the Resume dock slot. Styled after a macOS
   menu (Sonoma/Sequoia): dark vibrancy material, hairline border with a
   top inner highlight, 12px outer / 6px row radius, SF-style type, line
   icons on the left, a hairline divider, and the system-blue row
   highlight on hover/focus. Lives inside .dock-wrapper so it inherits
   the night-mode hide/reveal (it is focus-/hover-within the hover zone
   while open); the same look is used in day and night, like the dock.
   Horizontal position is set by js/dock.js (--resume-pop-x) to sit above
   the Resume icon, clamped to the viewport. Icons are CSS masks, so no
   markup or image files are needed and they follow the text colour. */
@font-face {
  /* Self-hosted Inter (already in the repo for the music page) — the
     closest free match to SF Pro, used where SF isn't installed
     (Windows/Android/Linux). Apple devices use the real SF via
     -apple-system first. */
  font-family: "Resume Menu Inter";
  src: url("../assets/music/m2/fonts/inter-400.woff2") format("woff2");
  font-weight: 400;
  font-style: normal;
  font-display: swap;
}

:root {
  --resume-pop-font: -apple-system, BlinkMacSystemFont, "SF Pro Text",
    "Resume Menu Inter", "Segoe UI", system-ui, sans-serif;
  --resume-pop-bg: rgba(36, 36, 40, 0.72);
  --resume-pop-border: rgba(255, 255, 255, 0.14);
  --resume-pop-highlight: rgba(255, 255, 255, 0.08);
  --resume-pop-shadow: 0 0 0 0.5px rgba(0, 0, 0, 0.6),
    0 10px 30px rgba(0, 0, 0, 0.45), 0 2px 8px rgba(0, 0, 0, 0.25);
  --resume-pop-text: rgba(255, 255, 255, 0.92);
  --resume-pop-icon: rgba(255, 255, 255, 0.72);
  --resume-pop-accent: #0a84ff; /* macOS system blue (dark appearance) */
  --resume-pop-divider: rgba(255, 255, 255, 0.12);
  --resume-pop-radius: 12px;
  --resume-pop-row-radius: 6px;
}

.resume-popover {
  position: absolute;
  bottom: calc(100% + 12px);
  left: var(--resume-pop-x, 0px);
  z-index: 6;
  display: flex;
  flex-direction: column;
  min-width: 196px;
  padding: 5px;
  border-radius: var(--resume-pop-radius);
  background: var(--resume-pop-bg);
  border: 0.5px solid var(--resume-pop-border);
  box-shadow: inset 0 0.5px 0 var(--resume-pop-highlight), var(--resume-pop-shadow);
  backdrop-filter: blur(30px) saturate(180%);
  -webkit-backdrop-filter: blur(30px) saturate(180%);
  font-family: var(--resume-pop-font);
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
  transform-origin: bottom center;
  animation: resume-pop-in 0.16s cubic-bezier(0.2, 0.9, 0.3, 1.1);
}

.resume-popover[hidden] {
  display: none;
}

.resume-popover__action {
  display: flex;
  align-items: center;
  gap: 9px;
  padding: 5px 10px 5px 8px;
  border-radius: var(--resume-pop-row-radius);
  color: var(--resume-pop-text);
  font-size: 13px;
  font-weight: 400;
  line-height: 18px;
  letter-spacing: -0.08px;
  text-decoration: none;
  white-space: nowrap;
  cursor: pointer;
  outline: none;
}

/* Hairline divider between the two rows, inset like a macOS menu separator. */
.resume-popover__action + .resume-popover__action {
  position: relative;
  margin-top: 11px;
}
.resume-popover__action + .resume-popover__action::after {
  content: "";
  position: absolute;
  left: 8px;
  right: 8px;
  top: -6px;
  height: 1px;
  background: var(--resume-pop-divider);
  pointer-events: none;
}

/* Line icons: eye for View, tray-arrow for Download. */
.resume-popover__action::before {
  content: "";
  flex: 0 0 auto;
  width: 15px;
  height: 15px;
  background-color: var(--resume-pop-icon);
  -webkit-mask: var(--resume-pop-icon-src) center / contain no-repeat;
  mask: var(--resume-pop-icon-src) center / contain no-repeat;
}
.resume-popover__action[target="_blank"] {
  --resume-pop-icon-src: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='black' stroke-width='1.8' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M1.5 12S5.5 4.5 12 4.5 22.5 12 22.5 12 18.5 19.5 12 19.5 1.5 12 1.5 12Z'/%3E%3Ccircle cx='12' cy='12' r='3.25'/%3E%3C/svg%3E");
}
.resume-popover__action[download] {
  --resume-pop-icon-src: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='black' stroke-width='1.8' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M12 3.5v12M7 11l5 5 5-5'/%3E%3Cpath d='M4.5 15.5v3a2 2 0 0 0 2 2h11a2 2 0 0 0 2-2v-3'/%3E%3C/svg%3E");
}

.resume-popover__action:hover,
.resume-popover__action:focus-visible {
  background: var(--resume-pop-accent);
  color: #fff;
}
.resume-popover__action:hover::before,
.resume-popover__action:focus-visible::before {
  background-color: #fff;
}
.resume-popover__action:active {
  background: #0071e3;
}

/* The slot's own name tooltip would sit on top of the open popover. */
.dock-slot[aria-expanded="true"]::before {
  display: none;
}

@keyframes resume-pop-in {
  from {
    opacity: 0;
    transform: scale(0.96) translateY(4px);
  }
  to {
    opacity: 1;
    transform: none;
  }
}

@media (prefers-reduced-motion: reduce) {
  .resume-popover {
    animation: none;
  }
}

/* Touch: roomier rows (Apple's 44pt tap-target guidance) — iOS-style
   menu proportions, same material. */
@media (hover: none) {
  .resume-popover {
    min-width: 220px;
    padding: 6px;
    border-radius: 14px;
  }
  .resume-popover__action {
    padding: 11px 12px;
    font-size: 15px;
    line-height: 20px;
    gap: 11px;
  }
  .resume-popover__action::before {
    width: 18px;
    height: 18px;
  }
  .resume-popover__action + .resume-popover__action {
    margin-top: 9px;
  }
  .resume-popover__action + .resume-popover__action::after {
    top: -5px;
  }
}
