/* Motion layer — loaded after styles.css.
   Precision over spectacle: short distances, one easing family, transform/opacity/colour only.
   Hidden states exist only under html.reveal-ready (set by main.js), so content never waits on JS. */

/* ---------- Tokens ---------- */
:root {
  --dur-fast: 150ms;
  --dur-base: 240ms;
  --dur-enter: 600ms;
  --ease-out: cubic-bezier(0.22, 1, 0.36, 1);
  --ease-standard: cubic-bezier(0.4, 0, 0.2, 1);
  --rise: 10px;
  --stagger: 60ms;
}

/* Starts at 0.01 rather than 0 so the browser still counts the hero (h1/photo) as the LCP candidate. */
@keyframes motion-enter {
  from {
    opacity: 0.01;
    transform: translateY(var(--rise));
  }
}

@keyframes motion-panel {
  from {
    opacity: 0;
    transform: translateY(-4px);
  }
}

@keyframes motion-dialog {
  from {
    opacity: 0;
    transform: translateY(8px) scale(0.99);
  }
}

@keyframes motion-fade {
  from {
    opacity: 0;
  }
}

@keyframes motion-menu {
  from {
    opacity: 0;
    transform: translateY(-4px);
  }
}

/* ---------- Navigation underline ---------- */
.nav__link {
  position: relative;
}

.nav__link::after {
  content: '';
  position: absolute;
  left: 0;
  right: 0;
  bottom: 0.25rem;
  height: 1px;
  background: var(--accent);
  transform: scaleX(0);
  transform-origin: left center;
  transition: transform var(--dur-base) var(--ease-standard);
}

.nav__link:hover::after,
.nav__link:focus-visible::after,
.nav__link[aria-current='true']::after {
  transform: scaleX(1);
}

/* Mobile dropdown: block rows with bottom borders — use a 2px left-edge marker at the panel edge instead. */
@media (max-width: 767px) {
  .js .nav__list .nav__link::after {
    top: 0.5rem;
    bottom: 0.5rem;
    left: calc(var(--margin) * -1);
    right: auto;
    width: 2px;
    height: auto;
    transform: scaleY(0);
    transform-origin: center;
  }

  .js .nav__list .nav__link:hover::after,
  .js .nav__list .nav__link:focus-visible::after,
  .js .nav__list .nav__link[aria-current='true']::after {
    transform: scaleY(1);
  }
}

/* ---------- Project rows ---------- */
/* Tint sits on a pseudo-element bleeding slightly past the text column, so the row never looks clipped. */
.project {
  position: relative;
  isolation: isolate;
}

.project::before {
  content: '';
  position: absolute;
  inset: 0 calc(var(--space-sm) * -1);
  z-index: -1;
  border-radius: var(--radius);
  background-color: rgba(245, 245, 244, 0);
  transition: background-color var(--dur-base) var(--ease-standard);
  pointer-events: none;
}

.project:not(.project--featured):hover::before,
.project:not(.project--featured):focus-within::before {
  background-color: rgba(245, 245, 244, 0.6);
}

/* Featured row is a white Level 1 surface: DESIGN.md Level 2 — hairline shifts to accent, no tint. */
.project--featured::before {
  content: none;
}

.project--featured {
  transition: border-color var(--dur-base) var(--ease-standard);
}

.project--featured:hover,
.project--featured:focus-within {
  border-color: var(--accent);
}

.project__title {
  transition: color var(--dur-base) var(--ease-standard);
}

.project:hover .project__title,
.project:focus-within .project__title {
  color: var(--accent-text);
}

/* ---------- Links, buttons, contact ---------- */
.text-link .icon--ne,
.btn--primary .icon {
  transition: transform var(--dur-base) var(--ease-standard);
}

.text-link:hover .icon--ne,
.text-link:focus-visible .icon--ne {
  transform: translate(2px, -2px);
}

.btn--primary:hover .icon:not(.icon--ne),
.btn--primary:focus-visible .icon:not(.icon--ne) {
  transform: translateY(2px);
}

.btn {
  transition: background-color var(--ease), border-color var(--ease), color var(--ease),
    transform var(--dur-fast) var(--ease-standard);
}

.btn:active {
  transform: translateY(1px);
}

.contact-link {
  transition: color var(--dur-fast) var(--ease-standard);
}

.contact-link__icon {
  transition: transform var(--dur-fast) var(--ease-standard);
}

.contact-link:hover .contact-link__icon,
.contact-link:focus-visible .contact-link__icon {
  transform: translateY(-1px);
}

/* ---------- Movement (only when the visitor has not asked for less) ---------- */
@media (prefers-reduced-motion: no-preference) {
  /* Hero entrance — pure CSS, finishes in ~0.85s. */
  [data-enter] {
    animation: motion-enter var(--dur-enter) var(--ease-out) both;
  }
  [data-enter='2'] {
    animation-delay: 60ms;
  }
  [data-enter='3'] {
    animation-delay: 120ms;
  }
  [data-enter='4'] {
    animation-delay: 180ms;
  }
  [data-enter='5'] {
    animation-delay: 240ms;
  }

  /* One-time section reveals. */
  html.reveal-ready [data-reveal]:not(.is-revealed),
  html.reveal-ready [data-reveal-group]:not(.is-revealed) > * {
    opacity: 0;
    transform: translateY(var(--rise));
  }

  html.reveal-ready [data-reveal].is-revealed,
  html.reveal-ready [data-reveal-group].is-revealed > * {
    transition: opacity var(--dur-enter) var(--ease-out), transform var(--dur-enter) var(--ease-out);
  }

  /* Keep the featured border hover transition once revealed (it is the group's first child: no delay). */
  html.reveal-ready [data-reveal-group].is-revealed > .project--featured {
    transition: opacity var(--dur-enter) var(--ease-out), transform var(--dur-enter) var(--ease-out),
      border-color var(--dur-base) var(--ease-standard);
  }

  html.reveal-ready [data-reveal-group].is-revealed > :nth-child(2) {
    transition-delay: var(--stagger);
  }
  html.reveal-ready [data-reveal-group].is-revealed > :nth-child(3) {
    transition-delay: calc(var(--stagger) * 2);
  }
  html.reveal-ready [data-reveal-group].is-revealed > :nth-child(n + 4) {
    transition-delay: calc(var(--stagger) * 3);
  }

  /* Project details: the panel's height eases open/closed where the browser can animate to `auto`
     (interpolate-size + ::details-content); elsewhere it opens instantly. Content fades in everywhere.
     No max-height trick, so long content is never clipped. */
  .project__details {
    interpolate-size: allow-keywords;
  }

  .project__details::details-content {
    block-size: 0;
    overflow-y: clip;
    transition: block-size var(--dur-base) var(--ease-standard), content-visibility var(--dur-base) allow-discrete;
  }

  .project__details[open]::details-content {
    block-size: auto;
  }

  .project__details[open] .project__panel {
    animation: motion-panel var(--dur-enter) var(--ease-out) both;
  }

  .project__chevron {
    transition: transform var(--dur-base) var(--ease-standard);
  }

  .project__toggle {
    transition: border-color var(--dur-fast) linear;
  }

  /* CV viewer opening (closing is instant, so dismissal always feels immediate). */
  .cv-dialog[open] {
    animation: motion-dialog var(--dur-base) var(--ease-out) both;
  }

  .cv-dialog[open]::backdrop {
    animation: motion-fade var(--dur-base) var(--ease-standard) both;
  }

  /* Mobile menu opening. */
  @media (max-width: 767px) {
    .js .nav__list.is-open {
      animation: motion-menu 180ms var(--ease-out) both;
    }
  }
}

/* ---------- Reduced motion: no movement, feedback stays as instant colour/marker changes ---------- */
@media (prefers-reduced-motion: reduce) {
  [data-enter],
  .js .nav__list.is-open {
    animation: none;
  }

  .nav__link::after,
  .text-link .icon--ne,
  .btn--primary .icon,
  .contact-link__icon {
    transition: none;
  }

  .text-link:hover .icon--ne,
  .text-link:focus-visible .icon--ne,
  .btn--primary:hover .icon,
  .btn--primary:focus-visible .icon,
  .btn:active,
  .contact-link:hover .contact-link__icon,
  .contact-link:focus-visible .contact-link__icon {
    transform: none;
  }

  .btn {
    transition: background-color var(--ease), border-color var(--ease), color var(--ease);
  }
}

@media print {
  [data-reveal],
  [data-reveal-group] > * {
    opacity: 1 !important;
    transform: none !important;
  }

  /* Print every project's details, whether or not its panel is open. */
  .project__details::details-content {
    content-visibility: visible;
    block-size: auto;
  }

  .project__toggle {
    display: none;
  }
}
