/* ===== Motion =====
   Everything here that hides or moves content is gated on `html.motion`, which
   the inline script in layout.html adds only when the visitor has not asked
   for reduced motion. No class, no motion: the page is complete without it.
   Elements only start hidden once motion.js has tagged them [data-reveal], so
   a script that fails to load never leaves content invisible. */

:root {
  --ease: cubic-bezier(.2, .7, .2, 1);
  --spring: cubic-bezier(.34, 1.56, .64, 1);
}

/* Cross-document view transitions: a soft cross-fade between pages in
   browsers that support it, an ordinary navigation everywhere else. */
@media (prefers-reduced-motion: no-preference) {
  @view-transition { navigation: auto; }
}

/* ---------- Scroll progress ---------- */
.scroll-progress {
  position: fixed; inset: 0 0 auto 0; height: 3px; z-index: 60;
  transform-origin: 0 50%; transform: scaleX(var(--p, 0));
  background: linear-gradient(90deg, var(--accent-brand), var(--accent-interactive));
  pointer-events: none;
}

/* ---------- Ambient background: two slow-drifting colour washes ---------- */
.motion body::before,
.motion body::after {
  content: ""; position: fixed; z-index: -1; pointer-events: none;
  width: 60vmax; height: 60vmax; border-radius: 50%;
  filter: blur(90px); opacity: .10; will-change: transform;
}
.motion body::before {
  top: -25vmax; left: -15vmax;
  background: var(--accent-brand);
  animation: drift-a 38s ease-in-out infinite alternate;
}
.motion body::after {
  bottom: -30vmax; right: -20vmax;
  background: var(--accent-interactive);
  animation: drift-b 46s ease-in-out infinite alternate;
}
:root[data-theme="light"].motion body::before,
:root[data-theme="light"].motion body::after { opacity: .07; }
@keyframes drift-a { to { transform: translate(24vmax, 14vmax) scale(1.15); } }
@keyframes drift-b { to { transform: translate(-22vmax, -12vmax) scale(1.1); } }

/* ---------- Reveal on scroll ---------- */
.motion [data-reveal] { opacity: 0; translate: 0 22px; }
.motion [data-reveal].in {
  opacity: 1; translate: 0 0;
  transition: opacity .7s ease var(--d, 0s), translate .8s var(--ease) var(--d, 0s);
}
/* Cards also animate transform (tilt) and shadow; keep those quick and
   undelayed so hover never feels laggy after the entrance has finished. */
.motion .project-card[data-reveal].in {
  transition: opacity .7s ease var(--d, 0s), translate .8s var(--ease) var(--d, 0s),
              transform .25s var(--ease), box-shadow .25s ease, border-color .2s ease;
}

/* ---------- Headline: words rise into place ---------- */
.motion .w {
  display: inline-block; overflow: hidden;
  vertical-align: bottom; padding-bottom: .12em; margin-bottom: -.12em;
}
.motion .w > span {
  display: inline-block; transform: translateY(115%);
  animation: rise .85s var(--ease) forwards;
  animation-delay: calc(var(--i, 0) * 70ms + 120ms);
}
@keyframes rise { to { transform: none; } }

/* ---------- Nav, brand, theme toggle ---------- */
.site-head nav a { position: relative; }
.site-head nav a::after {
  content: ""; position: absolute; left: 0; right: 0; bottom: -.2rem; height: 2px;
  background: var(--accent-brand); border-radius: 2px;
  transform: scaleX(0); transform-origin: left; transition: transform .3s var(--ease);
}
.site-head nav a:hover::after, .site-head nav a[aria-current="page"]::after { transform: scaleX(1); }

.caret {
  display: inline-block; width: .55ch; height: .95em; margin-left: .25ch;
  vertical-align: -.12em; background: var(--accent-brand);
}
.motion .caret { animation: blink 1.1s steps(1) infinite; }
@keyframes blink { 50% { opacity: 0; } }

#theme-toggle { transition: transform .2s var(--spring), background-color .2s ease; }
#theme-toggle:hover { transform: rotate(-12deg) scale(1.08); }
#theme-toggle.spin { animation: spin .55s var(--ease); }
@keyframes spin { from { transform: rotate(-200deg) scale(.6); } to { transform: none; } }

/* ---------- Buttons ---------- */
.btn { position: relative; overflow: hidden; transition: transform .2s var(--spring), background-color .2s ease, filter .2s ease; }
.btn:active { transform: scale(.96); }
.motion .btn.primary::after {
  content: ""; position: absolute; inset: 0 auto 0 -60%; width: 40%;
  background: linear-gradient(100deg, transparent, rgba(255, 255, 255, .35), transparent);
  transform: skewX(-20deg); animation: shine 4.5s ease-in-out 1.5s infinite;
}
@keyframes shine { 0%, 55% { left: -60%; } 100% { left: 130%; } }

/* ---------- Tags and chips ---------- */
.tag { display: inline-block; transition: transform .25s var(--spring), background-color .2s ease; }
.tag:hover { transform: translateY(-2px) rotate(-2deg); background: color-mix(in srgb, var(--accent-brand) 16%, transparent); }

.stack-chips {
  display: flex; flex-wrap: wrap; gap: .5rem; list-style: none; padding: 0; margin: 0 0 1.1rem;
}
.stack-chips li {
  font: .8rem var(--font-mono); padding: .3rem .7rem; border-radius: 999px;
  border: 1px solid var(--border-subtle); background: var(--surface-card);
  transition: transform .25s var(--spring), border-color .2s ease, background-color .2s ease;
}
.stack-chips li:hover {
  transform: translateY(-3px) rotate(-1.5deg);
  border-color: var(--accent-brand);
  background: color-mix(in srgb, var(--accent-brand) 12%, var(--surface-card));
}
.motion .stack-chips li { animation: pop .55s var(--spring) both; animation-delay: calc(var(--i, 0) * 60ms + 500ms); }
@keyframes pop { from { opacity: 0; transform: scale(.6) translateY(10px); } }

/* ---------- Section headings: an accent tick that draws itself ---------- */
.section-head::before {
  content: ""; display: inline-block; width: 1.6rem; height: 2px; margin-right: .7rem;
  vertical-align: middle; background: var(--accent-brand); border-radius: 2px;
  transform-origin: left;
}
.motion .section-head[data-reveal]::before { transform: scaleX(0); }
.motion .section-head[data-reveal].in::before { transform: scaleX(1); transition: transform .7s var(--ease) calc(var(--d, 0s) + .25s); }

/* ---------- Live dot on the stats line ---------- */
.stats-line::before {
  content: ""; display: inline-block; width: .5rem; height: .5rem; margin-right: .65rem;
  border-radius: 50%; background: var(--status-ok); vertical-align: .05em;
}
.motion .stats-line::before { animation: pulse 2.2s ease-out infinite; }
@keyframes pulse {
  0% { box-shadow: 0 0 0 0 color-mix(in srgb, var(--status-ok) 60%, transparent); }
  70%, 100% { box-shadow: 0 0 0 .55rem transparent; }
}

/* ---------- Project cards: lift, spotlight, tilt ---------- */
.project-card { position: relative; }
.project-card::after {
  content: ""; position: absolute; inset: 0; pointer-events: none; opacity: 0;
  background: radial-gradient(420px circle at var(--mx, 50%) var(--my, 50%),
              color-mix(in srgb, var(--accent-brand) 18%, transparent), transparent 62%);
  transition: opacity .3s ease;
}
.project-card__media img { transition: transform .7s var(--ease); }
@media (hover: hover) and (pointer: fine) {
  .project-card {
    transform: perspective(900px) rotateX(var(--rx, 0deg)) rotateY(var(--ry, 0deg));
    transition: transform .25s var(--ease), box-shadow .25s ease, border-color .2s ease;
  }
  .project-card:hover {
    border-color: color-mix(in srgb, var(--accent-brand) 60%, var(--border-subtle));
    box-shadow: 0 22px 44px -22px rgba(0, 0, 0, .55);
  }
  .project-card:hover::after { opacity: 1; }
  .project-card:hover .project-card__media img { transform: scale(1.045); }
}

/* ---------- Copy-to-clipboard ---------- */
.copy-btn {
  font: 600 .72rem var(--font-mono); padding: .15rem .55rem; margin-left: .25rem;
  border: 1px solid var(--border-subtle); border-radius: 999px;
  background: var(--surface-card); color: var(--text-primary); cursor: pointer;
  transition: transform .2s var(--spring), border-color .2s ease, color .2s ease;
}
.copy-btn:hover { border-color: var(--accent-brand); transform: translateY(-1px); }
.copy-btn.copied { border-color: var(--status-ok); color: var(--status-ok); animation: pop .4s var(--spring); }

/* ---------- Typewriter line ---------- */
.now-line { font: .95rem var(--font-mono); margin: 0 0 1rem; min-height: 1.6em; }
.now-line__label { color: var(--text-muted); }
.now-line__text { color: var(--accent-primary); font-weight: 600; }
.motion .now-line__text[data-typing]::after {
  content: ""; display: inline-block; width: .55ch; height: 1em; margin-left: 2px;
  vertical-align: -.12em; background: var(--accent-primary); animation: blink 1s steps(1) infinite;
}

/* ---------- Confetti (konami code, copied email) ---------- */
.confetti { position: fixed; inset: 0; z-index: 200; pointer-events: none; }

/* ---------- Toast ---------- */
.toast {
  position: fixed; left: 50%; bottom: 1.5rem; z-index: 210;
  translate: -50% 0; padding: .6rem 1rem; border-radius: 999px;
  background: var(--surface-card); color: var(--text-primary);
  border: 1px solid var(--accent-brand); font: .85rem var(--font-mono);
  box-shadow: 0 10px 30px -10px rgba(0, 0, 0, .5);
  animation: toast-in .45s var(--spring);
}
.toast.out { opacity: 0; translate: -50% 12px; transition: opacity .3s ease, translate .3s ease; }
@keyframes toast-in { from { opacity: 0; translate: -50% 18px; } }

/* ---------- 404 ---------- */
.err-code {
  margin: 0 0 .25rem; font: 700 clamp(4rem, 14vw, 8rem)/1 var(--font-mono);
  letter-spacing: -.04em; color: var(--text-muted);
}
.err-code__orb { display: inline-block; color: var(--accent-brand); }
.motion .err-code__orb { animation: bob 2.4s ease-in-out infinite; }
@keyframes bob { 50% { transform: translateY(-.14em) rotate(8deg); } }

/* ---------- Back to top, with the scroll progress as a ring ---------- */
.to-top {
  position: fixed; right: 1.1rem; bottom: 1.1rem; z-index: 55;
  width: 3rem; height: 3rem; padding: 0; border-radius: 50%; cursor: pointer;
  display: grid; place-items: center;
  border: 1px solid var(--border-subtle); background: var(--surface-card); color: var(--text-primary);
  box-shadow: 0 10px 26px -12px rgba(0, 0, 0, .6);
  opacity: 0; translate: 0 12px; pointer-events: none;
  transition: opacity .3s ease, translate .3s var(--ease), transform .2s var(--spring);
}
.to-top.show { opacity: 1; translate: 0 0; pointer-events: auto; }
.to-top:hover { transform: translateY(-3px); border-color: var(--accent-brand); }
.to-top .ring { position: absolute; inset: -1px; width: calc(100% + 2px); height: calc(100% + 2px); transform: rotate(-90deg); }
.to-top .icon { width: 1.3rem; height: 1.3rem; stroke-width: 2.25; transition: transform .25s var(--ease); }
.to-top:hover .icon { transform: translateY(-2px); }
#theme-toggle { display: inline-flex; align-items: center; justify-content: center; }
#theme-toggle .icon { width: 1.15rem; height: 1.15rem; vertical-align: 0; }
.copy-btn .icon { width: 1em; height: 1em; vertical-align: -.15em; }
.to-top circle { fill: none; stroke: var(--accent-brand); stroke-width: 2.5; stroke-linecap: round;
  stroke-dasharray: 100; stroke-dashoffset: calc(100 - var(--p, 0) * 100); }
