:root {
  --bg: #f6f4f0;
  --fg: #171717;
  --muted: #6b6660;
  --rule: rgba(23, 23, 23, 0.12);
  --ease: cubic-bezier(0.22, 1, 0.36, 1);
  /* pre-baked spring curves (damped oscillator, sampled as linear()) */
  --spring-hover: linear(0, 0.068, 0.227, 0.421, 0.612, 0.777, 0.905, 0.994, 1.05, 1.077, 1.083, 1.077, 1.063, 1.047, 1.031, 1.018, 1.007, 1, 0.996, 0.994, 0.993, 0.994, 0.995, 0.996, 0.997, 0.999, 0.999, 1, 1, 1.001, 1.001, 1.001, 1);
  --spring-pop: linear(0, 0.104, 0.34, 0.61, 0.849, 1.023, 1.124, 1.162, 1.153, 1.118, 1.075, 1.034, 1.002, 0.983, 0.974, 0.974, 0.979, 0.986, 0.993, 0.999, 1.002, 1.004, 1.004, 1.004, 1.003, 1.001, 1, 1, 0.999, 0.999, 0.999, 1, 1);
  --font-display: "Fraunces", "Shippori Mincho", "Noto Sans JP", "Times New Roman", serif;
  --font-body: "Inter", "Noto Sans JP", -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;
  --theme-speed: 0.6s;
  /* vertical JA name sizing: the full name is a long column (~10 glyphs),
     so it scales down with the viewport to always fit; when it would drop
     below a readable size the short fallback (ケント) takes over and may
     run larger */
  --name-size-v: min(25.5vh, 31vw);
  --name-size-v-full: min(8vh, 31vw);
  --name-min-size: 2.75rem;
}

[data-theme="dark"] {
  --bg: #0f0e0d;
  --fg: #f2efe9;
  --muted: #8d8781;
  --rule: rgba(242, 239, 233, 0.14);
}

* { box-sizing: border-box; margin: 0; padding: 0; }

body {
  background: var(--bg);
  color: var(--fg);
  font-family: var(--font-body);
  font-weight: 300;
  font-size: 16px;
  line-height: 1.6;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
  -webkit-tap-highlight-color: transparent;
  /* Nothing here is meant to be read as text to copy — and rapid clicking
     the toggles must never sweep a selection across the page. */
  -webkit-user-select: none;
  user-select: none;
  overflow: hidden;
  height: 100vh;
  height: 100dvh;
  display: flex;
  flex-direction: column;
  transition: background-color var(--theme-speed) var(--ease),
              color var(--theme-speed) var(--ease);
}

/* Selection reads as ink on paper (inverts with the theme) */
::selection {
  background: var(--fg);
  color: var(--bg);
}

/* Grain overlay: paper tooth over everything.
   Two seamless monochrome SVG tiles, each drawn at its natural size in px so
   the noise is never stretched or resampled — a fine film grain on .grain and
   a slow mottle on ::after, like the cloud in laid paper. Multiply turns the
   speckle into ink on light paper; screen keeps it as light on dark. */
.grain {
  position: fixed;
  inset: 0;
  pointer-events: none;
  z-index: 9998;
  background-image: url("data:image/svg+xml,%3Csvg viewBox='0 0 200 200' width='200' height='200' xmlns='http://www.w3.org/2000/svg'%3E%3Cfilter id='n' x='0' y='0' width='100%25' height='100%25'%3E%3CfeTurbulence type='turbulence' baseFrequency='0.8' numOctaves='1' stitchTiles='stitch'/%3E%3CfeColorMatrix type='saturate' values='0'/%3E%3C/filter%3E%3Crect width='200' height='200' filter='url(%23n)'/%3E%3C/svg%3E");
  background-size: 200px 200px;
  opacity: 0.05;
  mix-blend-mode: multiply;
  transition: opacity var(--theme-speed) var(--ease);
}
.grain::after {
  content: "";
  position: absolute;
  inset: 0;
  background-image: url("data:image/svg+xml,%3Csvg viewBox='0 0 512 512' width='512' height='512' xmlns='http://www.w3.org/2000/svg'%3E%3Cfilter id='m' x='0' y='0' width='100%25' height='100%25'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.07' numOctaves='2' stitchTiles='stitch'/%3E%3CfeColorMatrix type='saturate' values='0'/%3E%3C/filter%3E%3Crect width='512' height='512' filter='url(%23m)'/%3E%3C/svg%3E");
  background-size: 512px 512px;
  opacity: 0.04;
  mix-blend-mode: multiply;
  transition: opacity var(--theme-speed) var(--ease);
}
[data-theme="dark"] .grain {
  opacity: 0.075;
  mix-blend-mode: screen;
}
[data-theme="dark"] .grain::after {
  opacity: 0.055;
  mix-blend-mode: screen;
}

/* Top-right: sound + theme */
.controls-right {
  position: fixed;
  top: 28px;
  right: 28px;
  z-index: 100;
  display: flex;
  align-items: center;
  gap: 12px;
}
.sound-toggle,
.theme-toggle {
  position: relative;
  width: 40px;
  height: 40px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  background: transparent;
  border: 0; /* no ring at rest — the icons stand alone */
  border-radius: 50%; /* rounds the keyboard focus ring below */
  color: var(--fg);
  transition: transform 430ms var(--spring-hover),
              color var(--theme-speed) var(--ease);
}
/* Mouse presses paint nothing; keyboard tab still shows a ring. */
.sound-toggle:focus,
.theme-toggle:focus { outline: none; }
.sound-toggle:focus-visible,
.theme-toggle:focus-visible { outline: 1px solid var(--rule); outline-offset: 3px; }
.sound-toggle svg,
.theme-toggle svg { width: 18px; height: 18px; position: absolute; }

/* icon crossfades */
.sound-toggle .icon-sound-on,
.sound-toggle .icon-sound-off,
.theme-toggle .icon-sun,
.theme-toggle .icon-moon {
  transition: transform 420ms var(--spring-pop), opacity 0.3s var(--ease);
}
.sound-toggle .icon-sound-on { opacity: 1; transform: scale(1); }
.sound-toggle .icon-sound-off { opacity: 0; transform: scale(0.6); }
[data-sound="off"] .sound-toggle .icon-sound-on { opacity: 0; transform: scale(0.6); }
[data-sound="off"] .sound-toggle .icon-sound-off { opacity: 1; transform: scale(1); }

.theme-toggle .icon-sun { opacity: 0; transform: rotate(-90deg) scale(0.5); }
.theme-toggle .icon-moon { opacity: 1; transform: rotate(0) scale(1); }
[data-theme="dark"] .theme-toggle .icon-sun { opacity: 1; transform: rotate(0) scale(1); }
[data-theme="dark"] .theme-toggle .icon-moon { opacity: 0; transform: rotate(90deg) scale(0.5); }

/* Main container */
main {
  flex: 1;
  min-height: 0;
  width: 100%;
  padding: 0 32px;
  text-align: center;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
}

/* Hero name: variable Fraunces. SOFT rounds the serifs a touch,
   WONK switches the italic to its quirkier forms.
   The name is also the language switch. */
.hero-name {
  font-family: var(--font-display);
  font-weight: 300;
  font-size: clamp(2rem, 6.5vw, 5rem);
  line-height: 1;
  letter-spacing: -0.035em;
  white-space: nowrap;
  font-optical-sizing: auto;
  font-variation-settings: "SOFT" 30;
  cursor: pointer;
  /* anchor for morph exit clones */
  position: relative;
}
.hero-name:focus-visible { outline: 1px solid var(--rule); outline-offset: 16px; }
/* Orientation lives on the hero itself so language switches swap it
   together with the text, instead of reflowing the outgoing name. */
.hero-name.is-vertical {
  writing-mode: vertical-rl;
  font-size: min(var(--name-size-v), var(--name-clamp, 100vmax));
  line-height: 1.15;
  letter-spacing: 0.01em;
  font-weight: 500;
}
/* full vertical name gets the tighter column budget */
.hero-name.is-vertical[data-name-v="full"] {
  font-size: min(var(--name-size-v-full), var(--name-clamp, 100vmax));
}
.hero-name em {
  font-style: italic;
  font-weight: inherit;
}
/* Latin name gets Fraunces' wonkier italic; vertical JA runs upright */
.hero-name:not(.is-vertical) em {
  font-variation-settings: "WONK" 1, "SOFT" 30;
}
.hero-name.is-vertical em {
  font-style: normal;
}

/* Per-character animation (spans injected by script.js) */
.hero-name .ch { display: inline-block; }

.hero-name.enter .ch,
.hero-name.flip-out .ch,
.hero-name.flip-in .ch,
.hero-name.seed .ch,
.hero-name.morphing .ch,
.hero-name .ch-exit { will-change: transform, opacity, filter; }

/* Departing glyphs during a morph: clones pinned where they stood (positions
   set inline by script.js) so they can dissolve instead of snapping. */
.hero-name .ch-exit {
  position: absolute;
  display: inline-block;
  pointer-events: none;
}

/* The seed form (kntl) trickles in before it unfolds. */
.hero-name.seed .ch { animation: chIn 430ms var(--ease) calc(var(--i, 0) * 34ms) both; }

@keyframes chIn {
  from { opacity: 0; transform: translateY(0.6em); filter: blur(10px); }
  to { opacity: 1; transform: translateY(0); filter: blur(0); }
}
/* No explicit `from`: the exit starts from whatever the letter currently
   is — mid-morph poses are baked to inline styles before a language flip. */
@keyframes chOut {
  to { opacity: 0; transform: translateY(-0.3em); filter: blur(6px); }
}
.hero-name.enter .ch { animation: chIn 750ms var(--ease) calc(80ms + var(--i, 0) * 42ms) both; }
.hero-name.flip-out .ch { animation: chOut 210ms var(--ease) calc(var(--i, 0) * 14ms) both; }
.hero-name.flip-in .ch { animation: chIn 620ms var(--ease) calc(var(--i, 0) * 26ms) both; }

/* Icons row */
.icons {
  display: flex;
  gap: 28px;
  align-items: center;
  justify-content: center;
}
.icons .icon-link:nth-child(1) { --i: 0; }
.icons .icon-link:nth-child(2) { --i: 1; }
.icons .icon-link:nth-child(3) { --i: 2; }
.icons .icon-link:nth-child(4) { --i: 3; }

@keyframes popUp {
  from { opacity: 0; transform: translateY(14px) scale(0.94); }
  to { opacity: 1; transform: translateY(0) scale(1); }
}
.icons.enter .icon-link { animation: popUp 520ms var(--ease) calc(620ms + var(--i, 0) * 70ms) both; }
.icons.enter .icon-link .dr {
  animation: inkIn 620ms var(--ease) calc(620ms + var(--i, 0) * 70ms + var(--d, 0ms)) both;
}

/* Toggles fade in with the icons; opacity-only so hover transforms stay live */
@keyframes softIn { from { opacity: 0; } }
.controls-right.enter .sound-toggle { animation: softIn 480ms var(--ease) 840ms both; }
.controls-right.enter .theme-toggle { animation: softIn 480ms var(--ease) 910ms both; }

.icon-link {
  position: relative;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 44px;
  height: 44px;
  color: var(--fg);
  opacity: 0.7;
  transition: opacity 0.3s var(--ease),
              transform 430ms var(--spring-hover),
              color var(--theme-speed) var(--ease);
}
.icon-link svg {
  width: 22px;
  height: 22px;
}

/* Animated strokes. Every drawn shape is measured once in script.js (--l),
   so a single dash pattern drives paths, rects and circles alike. The hover
   redraw runs on the Web Animations API (script.js) so it can settle without
   snapping; CSS only carries the load entrance. */
.icon-link svg .dr {
  stroke-dasharray: var(--l, 0);
  stroke-dashoffset: 0;
}
/* draw in, on load */
@keyframes inkIn {
  from { stroke-dashoffset: var(--l, 0); }
}
.icon-link--github .dr-tail {
  transform-box: view-box;
  transform-origin: 9px 18px;
}
/* stroke order within each mark: body first, details after */
.icon-link--mail .dr-flap { --d: 170ms; }
.icon-link--linkedin .dr-bar { --d: 90ms; }
.icon-link--linkedin .dr-dot { --d: 180ms; }
.icon-link--github .dr-tail { --d: 240ms; }
.icon-link--cv .dr-fold { --d: 120ms; }
.icon-link--cv .dr-line1 { --d: 210ms; }
.icon-link--cv .dr-line2 { --d: 300ms; }
.icon-link:active { transform: translateY(-1px) scale(0.9); }

/* Footer */
.site-footer {
  width: 100%;
  display: flex;
  justify-content: center;
  padding: 8px 24px 32px;
}

/* Hover motion only where hovering is real */
@media (hover: hover) and (pointer: fine) {
  .sound-toggle:hover { transform: scale(1.08); }
  .sound-toggle:active,
  .sound-toggle.pressing { transform: scale(0.92); transition-duration: 0.12s; }
  .theme-toggle:hover { transform: rotate(18deg) scale(1.08); }
  .theme-toggle:active,
  .theme-toggle.pressing { transform: rotate(18deg) scale(0.92); transition-duration: 0.12s; }
  .icon-link:hover {
    opacity: 1;
    transform: translateY(-4px);
  }
}

/* ---------- Theme change: circular reveal from the toggle ---------- */
::view-transition-old(root) {
  animation: none;
  z-index: 1;
}
::view-transition-new(root) {
  z-index: 2;
  animation: vt-reveal 680ms cubic-bezier(0.4, 0, 0.2, 1) both;
}
@keyframes vt-reveal {
  from { clip-path: circle(0px at var(--vt-x, 50%) var(--vt-y, 50%)); }
  to { clip-path: circle(150vmax at var(--vt-x, 50%) var(--vt-y, 50%)); }
}
/* while a view transition is running, live colour transitions must not fight the snapshots */
html.vt,
html.vt *,
html.vt *::before,
html.vt *::after { transition: none !important; }

/* Respect reduced motion: keep colour/opacity, drop transform choreography */
@media (prefers-reduced-motion: reduce) {
  .hero-name.enter .ch,
  .hero-name.flip-out .ch,
  .hero-name.flip-in .ch,
  .hero-name.seed .ch,
  .icons.enter .icon-link,
  .icon-link svg .dr,
  .controls-right.enter .sound-toggle,
  .controls-right.enter .theme-toggle { animation: none !important; }

  .icon-link:hover,
  .icon-link:active,
  .theme-toggle:hover,
  .theme-toggle:active,
  .theme-toggle.pressing,
  .sound-toggle:hover,
  .sound-toggle:active,
  .sound-toggle.pressing { transform: none !important; }
}

@media (max-width: 720px) {
  main { padding: 0 20px; }
  .site-footer { padding: 8px 20px 24px; }
  .icons { gap: 20px; }
  .icon-link { width: 40px; height: 40px; }
  .icon-link svg { width: 20px; height: 20px; }
  .controls-right { top: 20px; right: 20px; }
}
