/* ==========================================================
   Retro pastel
   Cream #FFF8EC · Amethyst #8B42B8 · Lavender #CDB8FF
   Butter #FFE17A · Mint #B6F0D2 · Ink #1D1528

   Neo-brutalist: thick outlines, hard offset shadows, bold caps.
   Each band sets --bg and --accent-text; everything else comes
   from the shared --t-* surface tokens, which flip for dark mode.
   ========================================================== */

:root {
  --amethyst: #8b42b8;
  --amethyst-deep: #5b2380;
  --cream: #fff8ec;
  --paper: #fffdf7;
  --lavender: #cdb8ff;
  --butter: #ffe17a;
  --mint: #b6f0d2;
  --ink: #1d1528;

  /* Shared surface tokens */
  --t-fg: var(--ink);
  --t-muted: rgba(29, 21, 40, 0.74);
  --t-card: var(--paper);
  --t-line: var(--ink);
  --t-shadow: var(--ink);
  --t-grid: rgba(29, 21, 40, 0.06);
  --t-dot: rgba(29, 21, 40, 0.04);
  --t-hatch: rgba(29, 21, 40, 0.018);
  --t-rule: rgba(29, 21, 40, 0.015);
  --t-soft-rule: rgba(29, 21, 40, 0.16);
  --focus: var(--amethyst);

  /* Paper grain laid over the whole page: ink specks in light mode */
  --grain: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='180' height='180'%3E%3Cfilter id='g' color-interpolation-filters='sRGB'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='.85' numOctaves='2' stitchTiles='stitch'/%3E%3CfeColorMatrix values='0 0 0 0 .11 0 0 0 0 .08 0 0 0 0 .16 2.2 0 0 0 -1.1'/%3E%3C/filter%3E%3Crect width='100%25' height='100%25' filter='url(%23g)'/%3E%3C/svg%3E");
  --grain-opacity: 0.055;

  /* Band backgrounds and the accent text colour that stays legible on each */
  --c-cream: var(--cream);
  --a-cream: var(--amethyst);
  --c-lavender: var(--lavender);
  --a-lavender: var(--amethyst-deep);
  --c-butter: var(--butter);
  --a-butter: var(--amethyst-deep);
  --c-mint: var(--mint);
  --a-mint: var(--amethyst-deep);
  --c-footer: var(--ink);

  /* Fixed-colour elements (terminal, window bars) */
  --bar-bg: var(--ink);
  --term-bg: var(--ink);

  --contrib-0: #efe7ff;
  --contrib-1: #cdb8ff;
  --contrib-2: #a97fd8;
  --contrib-3: #8b42b8;
  --contrib-4: #1d1528;

  --font-head: 'Archivo', 'Arial Black', system-ui, sans-serif;
  --font-body: 'DM Sans', system-ui, -apple-system, 'Segoe UI', sans-serif;
  --font-mono: 'Space Mono', ui-monospace, Consolas, monospace;
  --font-hand: 'Caveat', 'Segoe Print', 'Comic Sans MS', cursive;

  /* Hand-drawn pointers (see Doodles) */
  --doodle: var(--amethyst);

  --bw: 2.5px;
  --radius: 8px;
  --gutter: clamp(16px, 4vw, 40px);
  --maxw: 1180px;

  color-scheme: light;
}

:root[data-theme='dark'] {
  --t-fg: var(--cream);
  --t-muted: rgba(255, 248, 236, 0.74);
  --t-card: #251b33;
  --t-line: #0d0913;
  --t-shadow: var(--lavender);
  --t-grid: rgba(205, 184, 255, 0.07);
  --t-dot: rgba(205, 184, 255, 0.04);
  --t-hatch: rgba(205, 184, 255, 0.018);
  --t-rule: rgba(205, 184, 255, 0.018);
  --t-soft-rule: rgba(255, 248, 236, 0.14);
  --focus: var(--butter);

  /* Cream specks instead of ink ones */
  --grain: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='180' height='180'%3E%3Cfilter id='g' color-interpolation-filters='sRGB'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='.85' numOctaves='2' stitchTiles='stitch'/%3E%3CfeColorMatrix values='0 0 0 0 1 0 0 0 0 .97 0 0 0 0 .93 2.2 0 0 0 -1.1'/%3E%3C/filter%3E%3Crect width='100%25' height='100%25' filter='url(%23g)'/%3E%3C/svg%3E");
  --grain-opacity: 0.04;

  --c-cream: var(--ink);
  --a-cream: var(--lavender);
  --c-lavender: #2a1f3a;
  --a-lavender: var(--lavender);
  --c-butter: #231a30;
  --a-butter: var(--butter);
  --c-mint: #140e1c;
  --a-mint: var(--mint);
  --c-footer: #140e1c;

  --bar-bg: #140e1c;
  --term-bg: #140e1c;

  --contrib-0: #2a1f3a;
  --contrib-1: #4a3566;
  --contrib-2: #8b42b8;
  --contrib-3: #cdb8ff;
  --contrib-4: #fff8ec;

  --doodle: var(--lavender);

  color-scheme: dark;
}

/* Surface tokens for every band and floating surface */
.band,
.interlude,
.site-header,
.case,
.hc-card,
.player,
.chatbot {
  --fg: var(--t-fg);
  --muted: var(--t-muted);
  --card: var(--t-card);
  --card-fg: var(--t-fg);
  --card-muted: var(--t-muted);
  --line: var(--t-line);
  --shadow: var(--t-shadow);
}

.band-cream,
.site-header,
.case,
.hc-card,
.player,
.chatbot {
  --bg: var(--c-cream);
  --accent-text: var(--a-cream);
}

.band-lavender {
  --bg: var(--c-lavender);
  --accent-text: var(--a-lavender);
}

.band-butter {
  --bg: var(--c-butter);
  --accent-text: var(--a-butter);
}

.band-mint {
  --bg: var(--c-mint);
  --accent-text: var(--a-mint);
}

/* Print texture for each band colour. Tiles are whole pixels so a band
   (anchored at its bottom) runs straight on into the matching interlude
   below it (anchored at its top) without a seam. */
.band-cream {
  --texture:
    linear-gradient(var(--t-grid) 1px, transparent 1px),
    linear-gradient(90deg, var(--t-grid) 1px, transparent 1px);
  --texture-size: 44px 44px;
}

.band-lavender {
  --texture: radial-gradient(circle at 50% 50%, var(--t-dot) 1.8px, transparent 2.2px);
  --texture-size: 22px 22px;
}

.band-butter {
  --texture: linear-gradient(45deg, transparent 45%, var(--t-hatch) 45% 55%, transparent 55%);
  --texture-size: 18px 18px;
}

.band-mint {
  --texture: linear-gradient(transparent 11px, var(--t-rule) 11px);
  --texture-size: 100% 12px;
}

/* ---------- Base ---------- */

*,
*::before,
*::after {
  box-sizing: border-box;
}

html {
  scroll-behavior: smooth;
  scroll-padding-top: 76px;
  -webkit-text-size-adjust: 100%;
}

body {
  margin: 0;
  background: var(--c-cream);
  color: var(--t-fg);
  font-family: var(--font-body);
  font-size: 16.5px;
  line-height: 1.6;
  -webkit-font-smoothing: antialiased;
  overflow-x: hidden;
}

/* Lenis smooth wheel scrolling (script.js) */
html.lenis,
html.lenis body {
  height: auto;
}

.lenis.lenis-stopped {
  overflow: clip;
}

img,
svg {
  display: block;
  max-width: 100%;
}

h1,
h2,
h3,
h4 {
  margin: 0;
  font-family: var(--font-head);
  line-height: 1.05;
  letter-spacing: -0.01em;
  text-wrap: balance;
}

p {
  margin: 0;
}

a {
  color: inherit;
}

code {
  font-family: var(--font-mono);
  font-size: 0.85em;
}

:focus-visible {
  outline: 3px solid var(--focus);
  outline-offset: 3px;
}

::selection {
  background: var(--amethyst);
  color: var(--cream);
}

.container {
  width: 100%;
  max-width: var(--maxw);
  margin: 0 auto;
  padding: 0 var(--gutter);
}

.skip-link {
  position: absolute;
  left: 16px;
  top: -60px;
  z-index: 100;
  padding: 10px 16px;
  border: var(--bw) solid var(--ink);
  background: var(--amethyst);
  color: var(--cream);
  font-weight: 700;
  text-decoration: none;
}

.skip-link:focus {
  top: 16px;
}

/* Fine paper grain over everything, including the intro */
body::after {
  content: '';
  position: fixed;
  inset: 0;
  z-index: 1100;
  pointer-events: none;
  background: var(--grain);
  opacity: var(--grain-opacity);
}

.band {
  position: relative;
  background: var(--bg);
  background-image: var(--texture, none);
  background-size: var(--texture-size, auto);
  background-position: 0 100%;
  color: var(--fg);
  padding: clamp(72px, 10vw, 120px) 0;
  border-top: var(--bw) solid var(--line);
}

.hero {
  border-top: 0;
}

/* ---------- Type helpers ---------- */

.label {
  font-family: var(--font-mono);
  font-size: 0.78rem;
  font-weight: 700;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--accent-text);
}

.mark {
  display: inline-block;
  padding: 0.02em 0.16em 0.06em;
  margin-top: 0.08em;
  background: var(--amethyst);
  color: var(--cream);
  border: 3px solid var(--line);
  box-shadow: 6px 6px 0 var(--butter);
  transform: rotate(-1deg);
}

.hl {
  color: var(--accent-text);
}

/* Keeps a phrase together when its line wraps */
.nobr {
  white-space: nowrap;
}

.stack {
  font-family: var(--font-mono);
  font-size: 0.76rem;
  line-height: 1.7;
  color: var(--accent-text);
}

/* ---------- Buttons ---------- */

.btn {
  --btn-bg: var(--card);
  --btn-fg: var(--card-fg);
  --btn-shadow: var(--shadow);
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 8px;
  min-height: 46px;
  padding: 0 20px;
  border: var(--bw) solid var(--line);
  border-radius: 6px;
  background: var(--btn-bg);
  color: var(--btn-fg);
  box-shadow: 4px 4px 0 var(--btn-shadow);
  font: 800 0.82rem/1 var(--font-head);
  letter-spacing: 0.04em;
  text-transform: uppercase;
  text-decoration: none;
  cursor: pointer;
  transition: transform 0.12s ease, box-shadow 0.12s ease, background-color 0.2s ease;
}

.btn:hover {
  transform: translate(-2px, -2px);
  box-shadow: 6px 6px 0 var(--btn-shadow);
}

.btn:active {
  transform: translate(3px, 3px);
  box-shadow: 1px 1px 0 var(--btn-shadow);
}

.btn svg {
  width: 17px;
  height: 17px;
  fill: none;
  stroke: currentColor;
  stroke-width: 2;
  stroke-linejoin: round;
}

.btn svg.fill,
.social svg.fill {
  fill: currentColor;
  stroke: none;
}

.btn-accent {
  --btn-bg: var(--amethyst);
  --btn-fg: var(--cream);
}

.btn-sm {
  min-height: 38px;
  padding: 0 14px;
  font-size: 0.74rem;
}

.btn-lg {
  min-height: 58px;
  padding: 0 26px;
  font-size: 0.95rem;
}

.text-link {
  font-weight: 700;
  font-size: 0.92rem;
  color: var(--card-fg);
  text-decoration: underline;
  text-decoration-thickness: 2px;
  text-decoration-color: var(--amethyst);
  text-underline-offset: 4px;
}

.text-link:hover {
  color: var(--accent-text);
}

/* ---------- Header ---------- */

.site-header {
  position: sticky;
  top: 0;
  z-index: 50;
  background: var(--bg);
  color: var(--fg);
  border-bottom: var(--bw) solid var(--line);
}

.header-inner {
  display: flex;
  align-items: center;
  gap: 24px;
  height: 66px;
}

.brand {
  display: inline-flex;
  align-items: center;
  gap: 10px;
  color: inherit;
  text-decoration: none;
  font: 900 1.05rem var(--font-head);
  text-transform: uppercase;
  letter-spacing: 0.02em;
  white-space: nowrap;
}

.brand-dim {
  color: var(--amethyst);
}

:root[data-theme='dark'] .brand-dim {
  color: var(--lavender);
}

.nav {
  display: flex;
  gap: 6px;
  margin: 0 auto;
}

.nav a {
  position: relative;
  padding: 8px 12px;
  white-space: nowrap;
  font: 700 0.76rem var(--font-head);
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--muted);
  text-decoration: none;
}

.nav a::after {
  content: '';
  position: absolute;
  left: 12px;
  right: 12px;
  bottom: 2px;
  height: 3px;
  background: var(--amethyst);
  transform: scaleX(0);
  transform-origin: left;
  transition: transform 0.2s ease;
}

.nav a:hover,
.nav a.is-active {
  color: var(--fg);
}

.nav a:hover::after,
.nav a.is-active::after {
  transform: scaleX(1);
}

.header-actions {
  display: flex;
  flex-shrink: 0;
  align-items: center;
  gap: 14px;
}

/* Translations run longer than English: keep header buttons on one line and
   let the greeting give way instead. */
.header-actions .btn {
  white-space: nowrap;
}

.brand {
  min-width: 0;
}

.brand-name {
  overflow: hidden;
  text-overflow: ellipsis;
}

.theme-toggle,
.sound-toggle {
  display: grid;
  place-items: center;
  width: 40px;
  height: 40px;
  border: var(--bw) solid var(--line);
  border-radius: 6px;
  background: var(--butter);
  color: var(--ink);
  box-shadow: 3px 3px 0 var(--shadow);
  cursor: pointer;
  transition: transform 0.12s ease, box-shadow 0.12s ease;
}

.theme-toggle:hover,
.sound-toggle:hover {
  transform: translate(-1px, -1px);
  box-shadow: 4px 4px 0 var(--shadow);
}

.theme-toggle:active,
.sound-toggle:active {
  transform: translate(2px, 2px);
  box-shadow: 1px 1px 0 var(--shadow);
}

.theme-toggle svg,
.sound-toggle svg {
  width: 19px;
  height: 19px;
  fill: none;
  stroke: currentColor;
  stroke-width: 2;
  stroke-linecap: round;
  stroke-linejoin: round;
  grid-area: 1 / 1;
  transition: transform 0.35s cubic-bezier(0.16, 1, 0.3, 1), opacity 0.25s ease;
}

:root[data-theme='light'] .icon-moon,
:root[data-theme='dark'] .icon-sun {
  opacity: 0;
  transform: rotate(-90deg) scale(0.5);
}

.sound-toggle {
  background: var(--mint);
}

.icon-sound-on,
.icon-sound-off {
  transition: opacity 0.2s ease;
}

[data-sound-toggle].is-on .icon-sound-off,
[data-sound-toggle]:not(.is-on) .icon-sound-on {
  opacity: 0;
}

/* ---------- Language picker ---------- */

.lang-picker {
  position: relative;
}

.lang-toggle {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  height: 40px;
  padding: 0 10px;
  border: var(--bw) solid var(--line);
  border-radius: 6px;
  background: var(--lavender);
  color: var(--ink);
  box-shadow: 3px 3px 0 var(--shadow);
  font: 700 0.78rem var(--font-mono);
  letter-spacing: 0.04em;
  cursor: pointer;
  transition: transform 0.12s ease, box-shadow 0.12s ease;
}

.lang-toggle:hover {
  transform: translate(-1px, -1px);
  box-shadow: 4px 4px 0 var(--shadow);
}

.lang-toggle:active,
.lang-toggle[aria-expanded='true'] {
  transform: translate(2px, 2px);
  box-shadow: 1px 1px 0 var(--shadow);
}

.lang-toggle svg {
  width: 18px;
  height: 18px;
  fill: none;
  stroke: currentColor;
  stroke-width: 2;
  stroke-linecap: round;
  stroke-linejoin: round;
  flex-shrink: 0;
}

.lang-code {
  min-width: 2ch;
}

.lang-menu {
  position: absolute;
  top: calc(100% + 12px);
  right: 0;
  z-index: 60;
  width: 248px;
  max-width: calc(100vw - 2 * var(--gutter));
  animation: lang-pop 0.18s cubic-bezier(0.16, 1, 0.3, 1);
}

.lang-menu[hidden] {
  display: none;
}

@keyframes lang-pop {
  from {
    opacity: 0;
    transform: translateY(-6px);
  }
}

.lang-menu .window-title {
  font: 700 0.7rem var(--font-mono);
}

.lang-list {
  list-style: none;
  margin: 0;
  padding: 6px;
  max-height: min(58vh, 420px);
  overflow-y: auto;
  overscroll-behavior: contain;
}

.lang-list button,
.lang-list a {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
  width: 100%;
  padding: 8px 10px;
  border: 2px solid transparent;
  border-radius: 5px;
  background: none;
  color: inherit;
  font: 500 0.92rem var(--font-body);
  text-align: left;
  text-decoration: none;
  cursor: pointer;
}

.lang-list button small,
.lang-list a small {
  font: 700 0.68rem var(--font-mono);
  letter-spacing: 0.06em;
  color: var(--t-muted);
}

.lang-list button:hover,
.lang-list button:focus-visible,
.lang-list a:hover,
.lang-list a:focus-visible {
  border-color: var(--t-line);
  background: var(--mint);
  color: var(--ink);
  outline: none;
}

.lang-list button[aria-current='true'] {
  border-color: var(--t-line);
  background: var(--butter);
  color: var(--ink);
  font-weight: 700;
}

.lang-list button:hover small,
.lang-list button:focus-visible small,
.lang-list a:hover small,
.lang-list a:focus-visible small,
.lang-list button[aria-current='true'] small {
  color: var(--ink);
}

.lang-sep {
  margin: 8px 4px 4px;
  padding-top: 10px;
  border-top: 2px dashed var(--t-soft-rule);
  font: 700 0.66rem var(--font-mono);
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--t-muted);
}

.lang-note {
  margin: 0;
  padding: 9px 14px;
  border-top: var(--bw) solid var(--t-line);
  font: 400 0.68rem var(--font-mono);
  color: var(--t-muted);
}

/* ---------- CV picker ---------- */

/* Borrows the language menu's window and list */
.cv-picker {
  position: relative;
}

.cv-toggle[aria-expanded='true'] {
  transform: translate(3px, 3px);
  box-shadow: 1px 1px 0 var(--btn-shadow);
}

.cv-menu {
  width: 236px;
}

/* ---------- Settings (language, theme, sound) ---------- */

/* On wide screens the wrappers drop out of the layout and the three buttons
   sit in the header row on their own; phones get the cog (see Responsive). */
.settings,
.settings-panel,
.settings-row {
  display: contents;
}

.settings-toggle,
.settings .settings-bar,
.settings-label {
  display: none;
}

.settings-toggle {
  place-items: center;
  width: 36px;
  height: 36px;
  border: var(--bw) solid var(--line);
  border-radius: 6px;
  background: var(--butter);
  color: var(--ink);
  box-shadow: 3px 3px 0 var(--shadow);
  cursor: pointer;
  transition: transform 0.12s ease, box-shadow 0.12s ease;
}

.settings-toggle:hover {
  transform: translate(-1px, -1px);
  box-shadow: 4px 4px 0 var(--shadow);
}

.settings-toggle:active,
.settings-toggle[aria-expanded='true'] {
  transform: translate(2px, 2px);
  box-shadow: 1px 1px 0 var(--shadow);
}

.settings-toggle svg {
  width: 19px;
  height: 19px;
  fill: none;
  stroke: currentColor;
  stroke-width: 2;
  stroke-linecap: round;
  stroke-linejoin: round;
  transition: transform 0.35s cubic-bezier(0.16, 1, 0.3, 1);
}

.settings-toggle[aria-expanded='true'] svg {
  transform: rotate(90deg);
}

/* While a saved hand translation is swapped in on load (see the head script) */
.i18n-wait body {
  visibility: hidden;
}

/* Japanese headings break between phrases rather than mid-word, and German's
   long compounds hyphenate instead of overflowing. */
html[lang='ja'] :is(h1, h2, h3, h4, .intro-title, .tbox-name) {
  word-break: auto-phrase;
  overflow-wrap: anywhere;
}

html[lang='de'] :is(h1, h2, h3, h4, .intro-title, .tbox-name) {
  -webkit-hyphens: auto;
  hyphens: auto;
  overflow-wrap: anywhere;
}

/* German section titles hold single long words ("Werkzeugkasten?"): shrink
   them on phones so the word fits whole instead of being split. */
html[lang='de'] .section-head h2 {
  font-size: min(clamp(2rem, 5.2vw, 3.4rem), 7.6vw);
}

/* German nav labels run longest; give them a little more room */
@media (min-width: 1321px) {
  html[lang='de'] .nav a {
    padding-inline: 10px;
  }

  html[lang='de'] .nav a::after {
    left: 10px;
    right: 10px;
  }
}

/* Their hero lines run longer than the English ones */
html:is([lang='id'], [lang='de']) .hero-title {
  font-size: min(clamp(2.6rem, 5.4vw, 4.4rem), 12vw);
}


/* Hide Google Translate's own banner, tooltips and highlights; the picker
   above replaces its UI. */
.goog-te-banner-frame,
.skiptranslate > iframe,
#goog-gt-tt,
.goog-te-balloon-frame,
.goog-tooltip,
.VIpgJd-ZVi9od-ORHb-OEVmcd,
.VIpgJd-ZVi9od-aZ2wEe-wOHMyf,
.VIpgJd-ZVi9od-l4eHX-hSRGPd,
#google_translate_element {
  display: none !important;
}

body {
  top: 0 !important;
}

.goog-text-highlight,
.VIpgJd-yAWNEb-VIpgJd-fmcmS-sn54Q {
  background: none !important;
  box-shadow: none !important;
}

/* Circular theme wipe (View Transitions API) */
::view-transition-old(root),
::view-transition-new(root) {
  animation: none;
  mix-blend-mode: normal;
}

::view-transition-old(root) {
  z-index: 1;
}

::view-transition-new(root) {
  z-index: 2;
}

/* Language switch: a frosted veil blurs the page out and back in around the swap */
.lang-veil {
  position: fixed;
  inset: 0;
  z-index: 2000;
  background: rgb(255 255 255 / 0.1);
  opacity: 0;
  visibility: hidden;
  pointer-events: none;
  -webkit-backdrop-filter: blur(0);
  backdrop-filter: blur(0);
  transition:
    opacity 0.3s ease-in-out,
    -webkit-backdrop-filter 0.3s ease-in-out,
    backdrop-filter 0.3s ease-in-out,
    visibility 0s 0.3s;
}

.lang-veil.is-on {
  opacity: 1;
  visibility: visible;
  pointer-events: auto;
  -webkit-backdrop-filter: blur(20px);
  backdrop-filter: blur(20px);
  transition-delay: 0s;
}

[data-theme='dark'] .lang-veil {
  background: rgb(0 0 0 / 0.1);
}

/* ---------- Windows (retro title bars) ---------- */

.window {
  border: var(--bw) solid var(--line);
  border-radius: var(--radius);
  background: var(--card);
  color: var(--card-fg);
  box-shadow: 6px 6px 0 var(--shadow);
  overflow: hidden;
}

.window-bar {
  display: flex;
  align-items: center;
  gap: 12px;
  min-height: 38px;
  padding: 0 14px;
  background: var(--bar-bg);
  color: var(--cream);
  border-bottom: var(--bw) solid var(--line);
}

.dots {
  display: inline-flex;
  gap: 6px;
  flex-shrink: 0;
}

.dots i {
  width: 11px;
  height: 11px;
  border-radius: 50%;
  border: 2px solid var(--ink);
  background: var(--amethyst);
}

.dots i:nth-child(2) {
  background: var(--butter);
}

.dots i:nth-child(3) {
  background: var(--mint);
}

.window-title {
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  font-family: var(--font-mono);
  font-size: 0.74rem;
  font-weight: 700;
  letter-spacing: 0.04em;
  text-transform: uppercase;
}

/* ---------- Cards ---------- */

.card {
  border: var(--bw) solid var(--line);
  border-radius: var(--radius);
  background: var(--card);
  color: var(--card-fg);
  box-shadow: 6px 6px 0 var(--shadow);
}

.icon-box {
  display: grid;
  place-items: center;
  width: 46px;
  height: 46px;
  border: var(--bw) solid var(--line);
  border-radius: 6px;
  box-shadow: 3px 3px 0 var(--line);
  color: var(--ink);
}

.icon-box svg {
  width: 22px;
  height: 22px;
  fill: none;
  stroke: currentColor;
  stroke-width: 2;
  stroke-linecap: round;
  stroke-linejoin: round;
}

.ib-lavender { background: var(--lavender); }
.ib-amethyst { background: var(--amethyst); color: var(--cream); }
.ib-mint { background: var(--mint); }
.ib-butter { background: var(--butter); }

/* ---------- Hero ---------- */

.hero {
  padding: clamp(64px, 9vw, 110px) 0 clamp(80px, 10vw, 120px);
  overflow: hidden;
}

.hero-grid {
  display: grid;
  grid-template-columns: minmax(0, 1.1fr) minmax(0, 1fr);
  gap: clamp(40px, 6vw, 80px);
  align-items: center;
}

.hero-title {
  margin-top: 20px;
  font-weight: 900;
  /* 13vw only wins below ~350px, where "Membangun" would overflow */
  font-size: min(clamp(2.9rem, 7.2vw, 5.4rem), 13vw);
  line-height: 0.98;
  letter-spacing: -0.02em;
  text-transform: uppercase;
}

.hero-body {
  margin-top: 28px;
  max-width: 50ch;
  color: var(--muted);
  font-size: 1.05rem;
}

.hero-facts {
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: 12px;
  margin: 30px 0 0;
  max-width: 520px;
}

.hero-facts div {
  padding: 10px 12px;
  border: var(--bw) solid var(--line);
  border-radius: 6px;
  background: var(--card);
  box-shadow: 3px 3px 0 var(--shadow);
}

.hero-facts dt {
  font-family: var(--font-mono);
  font-size: 0.66rem;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--accent-text);
}

.hero-facts dd {
  margin: 2px 0 0;
  font-family: var(--font-head);
  font-weight: 800;
  font-size: 0.95rem;
  line-height: 1.25;
}

.hero-facts dd span {
  display: block;
  font-family: var(--font-body);
  font-weight: 400;
  font-size: 0.78rem;
  color: var(--muted);
}

.hero-actions {
  display: flex;
  flex-wrap: wrap;
  gap: 14px;
  margin-top: 30px;
}

.hero-visual {
  position: relative;
  padding: 44px 18px 50px 0;
}

.terminal {
  --card: var(--term-bg);
  --card-fg: var(--cream);
  position: relative;
  z-index: 1;
  box-shadow: 10px 10px 0 var(--amethyst);
}

.terminal .window-bar {
  background: var(--butter);
  color: var(--ink);
}

.terminal-body {
  padding: 20px 20px 24px;
  font-family: var(--font-mono);
  font-size: 0.86rem;
  line-height: 1.75;
}

.terminal-body .prompt {
  color: var(--butter);
  font-weight: 700;
  margin-right: 4px;
}

.terminal-body .out {
  color: var(--lavender);
}

.terminal-body .row {
  margin: 8px 0;
  padding: 5px 12px;
  border: 2px solid #0d0913;
  border-radius: 4px;
  background: var(--amethyst);
  color: var(--cream);
  box-shadow: 3px 3px 0 #0d0913;
  font-weight: 700;
}

.terminal-body .cols {
  display: flex;
  flex-wrap: wrap;
  gap: 2px 18px;
  color: var(--mint);
}

.cursor {
  display: inline-block;
  width: 9px;
  height: 1.05em;
  vertical-align: -0.15em;
  background: var(--cream);
  animation: blink 1.05s steps(1) infinite;
}

@keyframes blink {
  50% {
    opacity: 0;
  }
}

.float-card {
  position: absolute;
  z-index: 2;
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 10px 14px;
  border: var(--bw) solid var(--line);
  border-radius: 6px;
  box-shadow: 4px 4px 0 var(--shadow);
  color: var(--ink);
}

.float-card > span:not(.live-dot) {
  display: flex;
  flex-direction: column;
}

.float-label {
  font-family: var(--font-mono);
  font-size: 0.64rem;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  opacity: 0.8;
}

.float-value {
  font-family: var(--font-head);
  font-weight: 900;
  font-size: 1rem;
  text-transform: uppercase;
  line-height: 1.1;
}

.float-status {
  top: 0;
  right: 0;
  /* Long translations wrap instead of covering the terminal's title bar */
  max-width: min(62%, 250px);
  flex-direction: column;
  align-items: flex-start;
  gap: 0;
  background: var(--butter);
  transform: rotate(3deg);
}

.float-live {
  left: -18px;
  bottom: 0;
  background: var(--mint);
  transform: rotate(-2deg);
}

.live-dot {
  position: relative;
  width: 12px;
  height: 12px;
  border-radius: 50%;
  border: 2px solid var(--ink);
  background: var(--amethyst);
  flex-shrink: 0;
}

.live-dot::after {
  content: '';
  position: absolute;
  inset: -2px;
  border-radius: 50%;
  border: 2px solid var(--amethyst);
  animation: ping 1.8s ease-out infinite;
}

@keyframes ping {
  to {
    transform: scale(2.4);
    opacity: 0;
  }
}

/* ---------- Section heads ---------- */

.section-head {
  display: flex;
  flex-direction: column;
  align-items: center;
  text-align: center;
  margin-bottom: clamp(40px, 6vw, 64px);
}

.section-head h2 {
  margin-top: 14px;
  font-weight: 900;
  font-size: clamp(2rem, 5.2vw, 3.4rem);
  line-height: 1.1;
  text-transform: uppercase;
  letter-spacing: -0.015em;
}

.section-sub {
  margin-top: 18px;
  max-width: 62ch;
  color: var(--muted);
}

/* ---------- Projects ---------- */

.projects {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: 36px 32px;
}

.project {
  display: flex;
  flex-direction: column;
  overflow: hidden;
  transition: transform 0.15s ease, box-shadow 0.15s ease;
}

.project:hover {
  transform: translate(-3px, -3px);
  box-shadow: 9px 9px 0 var(--shadow);
}

/* The screenshot rises out of the pastel block and runs off its bottom edge.
   Padding percentages follow the width, so the crop holds at every size. */
.project-visual {
  position: relative;
  aspect-ratio: 2 / 1;
  padding: 7% 8% 0;
  overflow: hidden;
  border-bottom: var(--bw) solid var(--line);
  color: var(--ink);
  isolation: isolate;
}

.project-visual::before {
  content: '';
  position: absolute;
  inset: 0;
  z-index: -1;
  background-image: radial-gradient(currentColor 1px, transparent 1.2px);
  background-size: 16px 16px;
  opacity: 0.14;
}

/* The pastel blocks keep their colours in both themes */
.v-butter { background: var(--butter); }
.v-mint { background: var(--mint); }
.v-lavender { background: var(--lavender); }
.v-amethyst { background: var(--amethyst); color: var(--cream); }

.project-shot {
  display: block;
  width: 100%;
  height: auto;
  aspect-ratio: 16 / 10;
  object-fit: cover;
  object-position: top;
  border: var(--bw) solid var(--ink);
  border-radius: 6px 6px 0 0;
  background: var(--paper);
  box-shadow: 6px 6px 0 var(--ink);
  transition: transform 0.3s cubic-bezier(0.16, 1, 0.3, 1);
}

.project:hover .project-shot {
  transform: translateY(-8px);
}

.project-body {
  display: flex;
  flex-direction: column;
  gap: 12px;
  flex: 1;
  padding: 22px 24px 24px;
}

.project-meta {
  font-family: var(--font-mono);
  font-size: 0.72rem;
  font-weight: 700;
  letter-spacing: 0.04em;
  text-transform: uppercase;
  color: var(--a-cream);
}

.project .stack {
  color: var(--a-cream);
}

.project h3 {
  font-weight: 900;
  font-size: 1.7rem;
  text-transform: uppercase;
}

.project-summary {
  color: var(--card-muted);
  font-size: 0.97rem;
}

.project-actions {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 12px 22px;
  margin-top: auto;
  padding-top: 8px;
}

/* ---------- More work ---------- */

.more-work {
  margin-top: 56px;
}

.more-list {
  list-style: none;
  margin: 0;
  padding: 0;
}

.more-list li {
  display: grid;
  grid-template-columns: minmax(0, 1fr) auto;
  gap: 8px 32px;
  padding: 20px 24px;
}

.more-list li + li {
  border-top: 2px dashed var(--t-soft-rule);
}

.more-name {
  font-family: var(--font-head);
  font-weight: 900;
  font-size: 1.1rem;
  text-transform: uppercase;
  text-decoration: none;
}

a.more-name:hover {
  color: var(--a-cream);
}

.more-list p {
  margin-top: 4px;
  max-width: 72ch;
  color: var(--card-muted);
  font-size: 0.94rem;
}

.more-meta {
  font-family: var(--font-mono);
  font-size: 0.72rem;
  font-weight: 700;
  text-align: right;
  text-transform: uppercase;
  color: var(--a-cream);
  white-space: nowrap;
  padding-top: 3px;
}

/* Shown on phones, where the year and role share one line */
.more-sep {
  display: none;
}

/* ---------- About ----------
   The capability cards are held as a fanned hand. When the hand scrolls
   into view they are dealt in from the right, left card first; hovering a
   card straightens and lifts it and nudges its neighbours aside. Each card
   rotates around its own centre so straightening never moves it out from
   under the pointer. */

.capabilities {
  --cw: clamp(236px, 24vw, 290px);
  display: flex;
  justify-content: center;
  padding: 40px 0 24px;
  counter-reset: capability;
}

.js .capabilities.reveal {
  opacity: 1;
  translate: none;
}

.capability {
  --x: 0px;
  --y: 0px;
  --r: 0deg;
  position: relative;
  flex: none;
  width: var(--cw);
  min-height: 360px;
  padding: 30px 28px 60px;
  counter-increment: capability;
  translate: var(--x) var(--y);
  rotate: var(--r);
  transition: translate 0.35s cubic-bezier(0.2, 0.8, 0.2, 1), rotate 0.35s cubic-bezier(0.2, 0.8, 0.2, 1),
    scale 0.35s cubic-bezier(0.2, 0.8, 0.2, 1), box-shadow 0.25s ease, filter 0.25s ease;
}

.capability + .capability {
  margin-left: calc(var(--cw) * -0.2);
}

.capability:nth-child(1) { --r: -8deg; --y: 34px; }
.capability:nth-child(2) { --r: -2.5deg; --y: 6px; }
.capability:nth-child(3) { --r: 2.5deg; --y: 6px; }
.capability:nth-child(4) { --r: 8deg; --y: 34px; }

.capability::after {
  content: counter(capability, decimal-leading-zero);
  position: absolute;
  right: 24px;
  bottom: 20px;
  font-family: var(--font-mono);
  font-size: 0.8rem;
  font-weight: 700;
  letter-spacing: 0.1em;
  color: var(--card-muted);
}

.capability .icon-box {
  width: 56px;
  height: 56px;
}

.capability .icon-box svg {
  width: 26px;
  height: 26px;
}

.capability h3 {
  margin-top: 24px;
  font-weight: 900;
  font-size: 1.3rem;
  text-transform: uppercase;
}

.capability p {
  margin-top: 12px;
  font-size: 1rem;
  color: var(--card-muted);
  line-height: 1.6;
}

/* Deal: each card flies in from the right, landing on top of the one before */

.js .capabilities.reveal:not(.is-visible) .capability {
  opacity: 0;
}

.capabilities.is-visible .capability {
  animation: deal-in 0.8s cubic-bezier(0.2, 0.9, 0.25, 1) backwards;
}

.capabilities.is-visible .capability:nth-child(2) { animation-delay: 0.14s; }
.capabilities.is-visible .capability:nth-child(3) { animation-delay: 0.28s; }
.capabilities.is-visible .capability:nth-child(4) { animation-delay: 0.42s; }

@keyframes deal-in {
  from {
    opacity: 0;
    translate: 70vw -60px;
    rotate: 24deg;
  }
  15% {
    opacity: 1;
  }
}

@media (hover: hover) and (min-width: 821px) {
  .capability:hover {
    --r: 0deg;
    --y: -26px;
    z-index: 2;
    scale: 1.06;
    box-shadow: 12px 12px 0 var(--shadow);
  }

  .capability:hover ~ .capability {
    --x: 56px;
  }

  .capability:has(~ .capability:hover) {
    --x: -56px;
  }

  /* Dim with a filter, not opacity, so the cards stay solid where they overlap */
  .capabilities:has(.capability:hover) .capability:not(:hover) {
    filter: saturate(0.6) brightness(0.9);
  }
}

/* ---------- Toolbox ----------
   Four closed toolboxes. Hovering one cracks the lid, swings the latch and
   lets a few tool handles peek out, then (script.js) the box morphs into an
   open toolbox sitting right over it, a non-modal <dialog> whose tools pop
   out of the tray. When it packs away it lands with a thump that jolts the
   rest of the row. */

.tb-lavender { --box: var(--lavender); --box-fg: var(--ink); }
.tb-butter { --box: var(--butter); --box-fg: var(--ink); }
.tb-amethyst { --box: var(--amethyst); --box-fg: var(--cream); }
.tb-paper { --box: var(--paper); --box-fg: var(--ink); }

.ib-paper { background: var(--paper); }

.toolboxes {
  display: grid;
  grid-template-columns: repeat(4, minmax(0, 1fr));
  gap: clamp(20px, 3vw, 36px);
  padding-top: 12px;
}

.tbox {
  position: relative;
  display: block;
  width: 100%;
  padding: 0;
  border: 0;
  background: none;
  color: var(--box-fg);
  font: inherit;
  text-align: left;
  cursor: pointer;
  filter: drop-shadow(6px 6px 0 var(--shadow));
  transform-origin: 50% 100%;
  transition: filter 0.25s ease;
  -webkit-tap-highlight-color: transparent;
}

.toolboxes.has-open .tbox:not(.is-out) {
  filter: drop-shadow(6px 6px 0 var(--shadow)) saturate(0.6) brightness(0.92);
}

.tbox.is-out {
  visibility: hidden;
}

.tbox-lid {
  position: relative;
  z-index: 2;
  display: block;
  height: 42px;
  margin-top: 30px;
  border: var(--bw) solid var(--ink);
  border-radius: 12px 12px 0 0;
  background: var(--box);
  transform-origin: 6% 100%;
  transition: transform 0.35s cubic-bezier(0.3, 1.6, 0.5, 1);
}

/* Ridge along the lid */
.tbox-lid::after {
  content: '';
  position: absolute;
  left: 10px;
  right: 10px;
  top: 12px;
  border-top: 2px solid var(--ink);
  opacity: 0.35;
}

.tbox-handle {
  position: absolute;
  left: 50%;
  bottom: 100%;
  width: 38%;
  height: 26px;
  margin-bottom: calc(var(--bw) * -1 + 1px);
  translate: -50% 0;
  border: 5px solid var(--ink);
  border-bottom: 0;
  border-radius: 14px 14px 0 0;
}

.tbox-latch {
  position: absolute;
  left: 50%;
  top: 100%;
  width: 24px;
  height: 18px;
  margin: -6px 0 0 -12px;
  border: var(--bw) solid var(--ink);
  border-radius: 3px;
  background: var(--paper);
  transform-origin: 50% 0;
  transition: rotate 0.3s cubic-bezier(0.3, 1.6, 0.5, 1);
}

.tbox-body {
  position: relative;
  z-index: 1;
  display: grid;
  gap: 14px;
  min-height: 190px;
  margin-top: calc(var(--bw) * -1);
  padding: 30px 20px 18px;
  border: var(--bw) solid var(--ink);
  border-radius: 0 0 12px 12px;
  background: var(--box);
  align-content: start;
}

.tbox-name {
  font-family: var(--font-head);
  font-size: 1.2rem;
  font-weight: 900;
  line-height: 1.1;
  text-transform: uppercase;
}

/* Two boxes side by side on a phone leave ~90px for the name */
@media (max-width: 560px) {
  .tbox-body {
    padding-inline: 12px;
  }

  .tbox-name {
    font-size: clamp(0.82rem, 4.1vw, 1.2rem);
  }
}

.tbox-meta {
  display: flex;
  flex-wrap: wrap;
  justify-content: space-between;
  align-items: center;
  gap: 8px;
  font: 700 0.72rem var(--font-mono);
  letter-spacing: 0.08em;
  text-transform: uppercase;
}

.tbox-meta > span {
  white-space: nowrap;
}

.tbox-hint {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  padding: 3px 8px;
  border: 2px solid var(--ink);
  border-radius: 4px;
  background: var(--paper);
  color: var(--ink);
  transition: background 0.2s ease, color 0.2s ease;
}

.tbox-hint::after {
  content: '\2197';
  transition: translate 0.25s cubic-bezier(0.3, 1.6, 0.5, 1);
}

/* Tool handles tucked under the lid, only seen when it lifts */
.tbox-peek {
  position: absolute;
  z-index: 0;
  left: 0;
  right: 0;
  top: 44px;
  height: 40px;
}

.tbox-peek i {
  position: absolute;
  bottom: 0;
  width: 13px;
  height: 36px;
  border: var(--bw) solid var(--ink);
  border-radius: 5px 5px 2px 2px;
  transition: translate 0.35s cubic-bezier(0.3, 1.6, 0.5, 1);
}

.tbox-peek i:nth-child(1) { left: 34%; background: var(--mint); }
.tbox-peek i:nth-child(2) { left: 54%; background: var(--paper); height: 30px; transition-delay: 0.04s; }
.tbox-peek i:nth-child(3) { left: 74%; background: var(--butter); width: 18px; transition-delay: 0.08s; }
.tb-butter .tbox-peek i:nth-child(3),
.tb-paper .tbox-peek i:nth-child(2) { background: var(--lavender); }

@media (hover: hover) {
  .tbox:hover .tbox-lid,
  .tbox:focus-visible .tbox-lid {
    transform: translateY(-12px) rotate(-5deg);
  }

  .tbox:hover .tbox-latch,
  .tbox:focus-visible .tbox-latch {
    rotate: 35deg;
  }

  .tbox:hover .tbox-peek i,
  .tbox:focus-visible .tbox-peek i {
    translate: 0 -20px;
  }

  .tbox:hover .tbox-hint,
  .tbox:focus-visible .tbox-hint {
    background: var(--ink);
    color: var(--cream);
  }

  .tbox:hover .tbox-hint::after,
  .tbox:focus-visible .tbox-hint::after {
    translate: 2px -2px;
  }

  .tbox:hover {
    animation: tbox-rattle 0.45s ease-in-out;
  }
}

.tbox:focus-visible {
  outline: none;
}

.tbox:focus-visible .tbox-body {
  outline: 3px solid var(--focus);
  outline-offset: 4px;
}

/* After the hover rules so a hover rattle never cancels a landing */
.tbox.is-landing {
  animation: tbox-land 0.55s cubic-bezier(0.3, 1.2, 0.5, 1);
}

.tbox.is-bumped {
  animation: tbox-bump 0.42s ease-out var(--bump-delay, 0.14s);
}

/* Opened toolbox */

.tbox-dialog {
  position: absolute;
  inset: auto;
  z-index: 20;
  width: min(600px, calc(100vw - 32px));
  max-width: none;
  max-height: none;
  margin: 0;
  padding: 0;
  border: 0;
  background: none;
  color: var(--box-fg);
  overflow: visible;
}

.tbox-open {
  display: flex;
  flex-direction: column;
  max-height: calc(100dvh - 48px);
  padding-top: 30px;
  transform-origin: 0 0;
  filter: drop-shadow(10px 10px 0 var(--amethyst));
}

.tb-amethyst .tbox-open {
  filter: drop-shadow(10px 10px 0 var(--butter));
}

.tbox-open-lid {
  position: relative;
  flex: none;
  height: 46px;
  border: var(--bw) solid var(--ink);
  border-radius: 14px 14px 0 0;
  background: var(--box);
  transform-origin: 50% 100%;
  transform: perspective(700px) rotateX(58deg);
}

.tbox-open-lid .tbox-handle {
  width: 26%;
}

.tbox-dialog[open] .tbox-open-lid {
  animation: tbox-lid-open 0.7s cubic-bezier(0.3, 1.5, 0.5, 1) 0.15s backwards;
}

.tbox-open-body {
  display: flex;
  flex-direction: column;
  min-height: 0;
  margin-top: calc(var(--bw) * -1);
  border: var(--bw) solid var(--ink);
  border-radius: 0 0 14px 14px;
  background: var(--box);
}

.tbox-open-head {
  display: grid;
  grid-template-columns: auto minmax(0, 1fr) auto;
  align-items: start;
  gap: 16px;
  padding: 24px clamp(18px, 4vw, 28px) 20px;
}

.tbox-open-head h3 {
  font-weight: 900;
  font-size: clamp(1.4rem, 3vw, 1.8rem);
  text-transform: uppercase;
}

.tbox-open-head p {
  margin-top: 6px;
  font-size: 0.95rem;
  font-weight: 500;
  line-height: 1.45;
}

.tbox-close {
  display: grid;
  place-items: center;
  width: 36px;
  height: 36px;
  border: var(--bw) solid var(--ink);
  border-radius: 6px;
  background: var(--paper);
  color: var(--ink);
  box-shadow: 3px 3px 0 var(--ink);
  font-size: 0.95rem;
  font-weight: 700;
  cursor: pointer;
  transition: translate 0.15s ease, box-shadow 0.15s ease;
}

.tbox-close:hover {
  translate: -1px -1px;
  box-shadow: 4px 4px 0 var(--ink);
  background: var(--butter);
}

/* The tray: inside of the box, with each tool popping up out of it */
.tbox-tray {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(210px, 1fr));
  gap: 16px;
  min-height: 0;
  margin: 0 clamp(12px, 3vw, 20px) clamp(12px, 3vw, 20px);
  padding: 20px;
  overflow-y: auto;
  list-style: none;
  counter-reset: tool;
  border: var(--bw) solid var(--ink);
  border-radius: 8px;
  background: color-mix(in srgb, var(--box) 72%, var(--ink));
  box-shadow: inset 0 8px 0 rgba(29, 21, 40, 0.25);
}

/* While the tools pop in they rise from below the tray's edge */
.tbox-dialog.is-opening .tbox-tray {
  overflow: hidden;
}

.tbox-tray li {
  display: grid;
  grid-template-columns: 2.2em minmax(0, 1fr);
  column-gap: 6px;
  padding: 14px 16px;
  border: var(--bw) solid var(--ink);
  border-radius: 6px;
  background: var(--paper);
  color: var(--ink);
  box-shadow: 4px 4px 0 var(--ink);
  counter-increment: tool;
  rotate: -1deg;
}

.tbox-tray li:nth-child(even) { rotate: 1.2deg; }
.tbox-tray li:nth-child(3n) { rotate: -0.4deg; }

.tbox-tray li::before {
  content: counter(tool, decimal-leading-zero);
  grid-row: 1 / span 2;
  padding-top: 0.2em;
  font: 700 0.72rem var(--font-mono);
  color: var(--amethyst);
}

.tbox-tray strong {
  font-family: var(--font-head);
  font-weight: 800;
  font-size: 1rem;
  line-height: 1.2;
  text-transform: uppercase;
}

.tbox-tray span {
  margin-top: 4px;
  font-family: var(--font-mono);
  font-size: 0.74rem;
  color: rgba(29, 21, 40, 0.74);
}

/* Brand logos above a tool's name, when it has one */
.tbox-tray li {
  align-content: start;
}

.tbox-tray li > * {
  grid-column: 2;
}

.tbox-tray li:has(.tool-logos)::before {
  grid-row: 1 / span 3;
}

.tbox-tray .tool-logos {
  display: flex;
  flex-wrap: wrap;
  gap: 7px;
  margin: 0 0 10px;
}

.tool-logos i {
  display: grid;
  place-items: center;
  width: 30px;
  height: 30px;
  border: 2px solid var(--ink);
  border-radius: 5px;
  background: #fff;
  color: var(--c);
  box-shadow: 2px 2px 0 var(--ink);
}

.tool-logos svg {
  width: 17px;
  height: 17px;
  fill: currentColor;
}

.tbox-dialog[open] .tbox-tray li {
  animation: tbox-tool-pop 0.5s cubic-bezier(0.3, 1.6, 0.5, 1) backwards;
}

.tbox-dialog[open] .tbox-tray li:nth-child(1) { animation-delay: 0.3s; }
.tbox-dialog[open] .tbox-tray li:nth-child(2) { animation-delay: 0.36s; }
.tbox-dialog[open] .tbox-tray li:nth-child(3) { animation-delay: 0.42s; }
.tbox-dialog[open] .tbox-tray li:nth-child(4) { animation-delay: 0.48s; }
.tbox-dialog[open] .tbox-tray li:nth-child(5) { animation-delay: 0.54s; }

@keyframes tbox-rattle {
  25% { rotate: -1.5deg; }
  50% { rotate: 1.2deg; }
  75% { rotate: -0.6deg; }
}

/* Drops back into its spot, squashes on impact and springs up */
@keyframes tbox-land {
  0% { translate: 0 -18px; scale: 1; }
  40% { translate: 0 0; scale: 1.06 0.9; }
  65% { scale: 0.98 1.03; }
  100% { translate: 0 0; scale: 1; }
}

/* The rest of the row jolts from the impact, leaning away from it */
@keyframes tbox-bump {
  30% { translate: 0 -7px; rotate: calc(var(--bump-dir, 1) * 1.5deg); }
  60% { translate: 0 1px; rotate: calc(var(--bump-dir, 1) * -0.5deg); }
}

@keyframes tbox-lid-open {
  from { transform: perspective(700px) rotateX(0deg); }
}

@keyframes tbox-tool-pop {
  from {
    opacity: 0;
    translate: 0 60px;
    scale: 0.7;
    rotate: 8deg;
  }
}

/* ---------- Experience ---------- */

/* Education runs down the left as a tall column; languages and the
   hackathons stack on the right */
.exp-grid {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: 30px;
  max-width: 880px;
  margin: 0 auto;
}

.exp-edu {
  grid-row: span 2;
  display: flex;
  flex-direction: column;
}

.exp {
  padding: 26px 24px 28px;
}

/* Graduation photo. --photo-ratio (e.g. 1 / 1, 3 / 4) sets its minimum size;
   in the tall desktop column it grows to fill the spare height */
.exp-photo {
  --photo-ratio: 4 / 5;
  flex: 1 0 auto;
  aspect-ratio: var(--photo-ratio);
  margin: 0 0 22px;
  overflow: hidden;
  border: var(--bw) solid var(--line);
  border-radius: 6px;
  box-shadow: 4px 4px 0 var(--line);
}

/* Portrait shot: keep the crop on the cap and gown, not the paving below */
.exp-photo img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  object-position: 50% 40%;
}

.exp.sh-amethyst { box-shadow: 7px 7px 0 var(--amethyst); }
.exp.sh-lavender { box-shadow: 7px 7px 0 var(--lavender); }
.exp.sh-ink { box-shadow: 7px 7px 0 var(--shadow); }

:root[data-theme='dark'] .exp.sh-ink {
  box-shadow: 7px 7px 0 var(--mint);
}

.exp-when {
  margin-top: 20px;
  font-family: var(--font-mono);
  font-size: 0.72rem;
  font-weight: 700;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--accent-text);
}

.exp h3 {
  margin-top: 8px;
  font-weight: 900;
  font-size: 1.2rem;
  text-transform: uppercase;
  line-height: 1.15;
}

.exp-where {
  margin-top: 6px;
  font-size: 0.9rem;
  color: var(--card-muted);
}

.exp ul {
  margin: 16px 0 0;
  padding: 16px 0 0 1.1em;
  border-top: 2px dashed var(--t-soft-rule);
  font-size: 0.92rem;
  color: var(--card-muted);
}

.exp li + li {
  margin-top: 6px;
}

/* Greetings in each language pop out of the card's corner one after another
   and drift up, like speech bubbles */

.exp-lang {
  position: relative;
}

.lang-bubbles {
  position: absolute;
  z-index: 1;
  top: 18px;
  right: 22px;
}

.lang-bubble {
  position: absolute;
  right: var(--x);
  bottom: 0;
  padding: 7px 12px 8px;
  border: var(--bw) solid var(--ink);
  border-radius: 10px;
  background: var(--bubble);
  color: var(--ink);
  box-shadow: 3px 3px 0 var(--ink);
  font-family: var(--font-head), 'Yu Gothic', 'Hiragino Sans', 'Noto Sans JP', sans-serif;
  font-size: 0.95rem;
  font-weight: 800;
  line-height: 1;
  white-space: nowrap;
  opacity: 0;
  transform-origin: 20% 100%;
  animation: lang-bubble 6s cubic-bezier(0.3, 1.3, 0.5, 1) var(--delay) infinite;
}

/* Tail pointing back down into the card */
.lang-bubble::after {
  content: '';
  position: absolute;
  left: 14px;
  bottom: -7px;
  width: 11px;
  height: 11px;
  border-right: var(--bw) solid var(--ink);
  border-bottom: var(--bw) solid var(--ink);
  background: var(--bubble);
  rotate: 45deg;
}

/* Each greeting gets its own lane so overlapping ones never collide */
.lb-id { --bubble: var(--butter); --x: 0px; --dx: -8px; --tilt: -4deg; --delay: 0s; }
.lb-en { --bubble: var(--mint); --x: 96px; --dx: -6px; --tilt: 3deg; --delay: 2s; }
.lb-ja { --bubble: var(--lavender); --x: 190px; --dx: -10px; --tilt: -2deg; --delay: 4s; }

@keyframes lang-bubble {
  0% { opacity: 0; translate: 0 26px; scale: 0.3; rotate: 0deg; }
  10% { opacity: 1; translate: 0 -8px; scale: 1; rotate: var(--tilt); }
  62% { opacity: 1; translate: var(--dx) -46px; scale: 1; rotate: var(--tilt); }
  74%, 100% { opacity: 0; translate: var(--dx) -60px; scale: 0.9; rotate: var(--tilt); }
}

.exp li::marker {
  content: '▪  ';
  color: var(--amethyst);
}

/* ---------- Contact ---------- */

.contact {
  padding-bottom: clamp(96px, 12vw, 150px);
}

.contact-inner {
  display: flex;
  flex-direction: column;
  align-items: center;
  text-align: center;
}

.contact h2 {
  margin-top: 18px;
  font-weight: 900;
  font-size: clamp(2.3rem, 6.5vw, 4.6rem);
  line-height: 1.02;
  text-transform: uppercase;
}

.contact .mark {
  margin-top: 0.18em;
}

.contact-sub {
  margin-top: 30px;
  max-width: 48ch;
  color: var(--muted);
}

.email-row {
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  gap: 16px;
  margin-top: 36px;
}

.email-btn {
  text-transform: none;
  letter-spacing: 0;
  font-weight: 800;
  overflow-wrap: anywhere;
}

.copy-btn.is-copied {
  --btn-bg: var(--mint);
  --btn-fg: var(--ink);
}

.socials {
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  gap: 16px;
  margin-top: 48px;
}

.social {
  display: inline-flex;
  align-items: center;
  gap: 10px;
  min-height: 48px;
  padding: 0 18px;
  border: var(--bw) solid var(--line);
  border-radius: 6px;
  background: var(--card);
  color: var(--card-fg);
  box-shadow: 4px 4px 0 var(--shadow);
  font: 800 0.8rem var(--font-head);
  letter-spacing: 0.06em;
  text-transform: uppercase;
  text-decoration: none;
  transition: transform 0.12s ease, box-shadow 0.12s ease;
}

.social:hover {
  transform: translate(-2px, -2px);
  box-shadow: 6px 6px 0 var(--shadow);
}

.social svg {
  width: 18px;
  height: 18px;
}

/* ---------- Hover cards (GitHub / LinkedIn) ---------- */

.hc {
  position: relative;
  display: inline-block;
  perspective: 1000px;
}

.hc-card {
  position: absolute;
  bottom: calc(100% + 18px);
  left: 50%;
  z-index: 60;
  display: flex;
  flex-direction: column;
  width: 310px;
  padding: 18px;
  border: var(--bw) solid var(--line);
  border-radius: var(--radius);
  background: var(--card);
  color: var(--card-fg);
  box-shadow: 6px 6px 0 var(--amethyst);
  text-align: left;
  line-height: 1.4;
  opacity: 0;
  pointer-events: none;
  transform-origin: bottom center;
  transform-style: preserve-3d;
  transform: translateX(-50%) translateY(8px) scale(0.97)
    rotateX(var(--rx, 0deg)) rotateY(var(--ry, 0deg));
  transition: opacity 0.14s ease-in, transform 0.14s ease-in;
}

.hc-card::after {
  content: '';
  position: absolute;
  top: 100%;
  left: 0;
  width: 100%;
  height: 20px;
}

.hc.is-open .hc-card {
  opacity: 1;
  pointer-events: auto;
  transform: translateX(-50%) translateY(0) scale(1)
    rotateX(var(--rx, 0deg)) rotateY(var(--ry, 0deg));
  transition:
    opacity 0.2s cubic-bezier(0.16, 1, 0.3, 1),
    transform 0.24s cubic-bezier(0.16, 1, 0.3, 1);
}

.hc-head {
  display: flex;
  align-items: center;
  gap: 12px;
  margin-bottom: 14px;
}

.hc-avatar {
  width: 48px;
  height: 48px;
  border: var(--bw) solid var(--line);
  border-radius: 6px;
  object-fit: cover;
  background: var(--lavender);
}

.hc-id {
  display: flex;
  flex-direction: column;
  min-width: 0;
}

.hc-name {
  font-family: var(--font-head);
  font-weight: 900;
  font-size: 1rem;
  text-transform: uppercase;
}

.hc-handle {
  font-family: var(--font-mono);
  font-size: 0.78rem;
  color: var(--card-muted);
}

.contrib-grid {
  display: grid;
  grid-template-rows: repeat(7, 11px);
  grid-auto-flow: column;
  grid-auto-columns: 11px;
  gap: 4px;
  justify-content: center;
  padding: 10px;
  border: 2px solid var(--line);
  border-radius: 6px;
}

.contrib-grid[hidden] {
  display: none;
}

.contrib-grid i {
  display: block;
  border-radius: 2px;
  background: var(--contrib-0);
}

.contrib-grid i[data-l='1'] { background: var(--contrib-1); }
.contrib-grid i[data-l='2'] { background: var(--contrib-2); }
.contrib-grid i[data-l='3'] { background: var(--contrib-3); }
.contrib-grid i[data-l='4'] { background: var(--contrib-4); }

.hc-foot {
  margin-top: 12px;
  font-family: var(--font-mono);
  font-size: 0.7rem;
  color: var(--card-muted);
}

/* Each card's site logo on a white badge in its top-right corner */
.hc-logo {
  position: absolute;
  top: 12px;
  right: 12px;
  display: grid;
  place-items: center;
  width: 26px;
  height: 26px;
  border: 2px solid var(--ink);
  border-radius: 4px;
  background: #fff;
  color: #000;
  font: 900 0.95rem/1 var(--font-head);
  letter-spacing: -0.04em;
}

.hc-logo svg {
  width: 15px;
  height: 15px;
  fill: currentColor;
}

/* GitHub has no banner: keep the name clear of the badge */
.hc-github .hc-head {
  padding-right: 30px;
}

/* Profile cards (LinkedIn, X, Discord): a banner with the avatar overlapping it */
.hc-profile {
  padding-top: 0;
  overflow: hidden;
}

.li-banner {
  position: relative;
  display: block;
  height: 70px;
  margin: 0 -18px;
  border-bottom: var(--bw) solid var(--line);
  background-color: var(--amethyst);
  background-image: radial-gradient(rgba(255, 248, 236, 0.35) 1.2px, transparent 1.4px);
  background-size: 12px 12px;
}

.li-avatar {
  position: relative;
  width: 70px;
  height: 70px;
  margin-top: -35px;
  border: var(--bw) solid var(--line);
  border-radius: 6px;
  background: var(--lavender);
  object-fit: cover;
  box-shadow: 3px 3px 0 var(--line);
}

.hc-profile .hc-name {
  margin-top: 12px;
}

.li-headline {
  margin-top: 4px;
  font-size: 0.86rem;
  color: var(--card-muted);
}

/* LinkedIn: brand-blue banner with the "in" mark, round photo */
.hc-linkedin .li-banner {
  background-color: #0a66c2;
}

.hc-linkedin .hc-logo {
  color: #0a66c2;
}

.hc-linkedin .li-avatar {
  border-radius: 50%;
  box-shadow: 3px 3px 0 var(--amethyst);
}

/* Discord: the LinkedIn layout in blurple, with a round avatar */
.hc-discord .li-banner {
  background-color: #5865f2;
}

.hc-discord .hc-logo {
  color: #5865f2;
}

.hc-discord .li-avatar {
  border-radius: 50%;
  box-shadow: 3px 3px 0 var(--amethyst);
}

.hc-discord .hc-name {
  text-transform: none;
  font-size: 1.15rem;
}

/* X: black banner, round avatar, handle under the name */
.hc-x .li-banner {
  background-color: #0f0f10;
  background-image: radial-gradient(rgba(255, 248, 236, 0.22) 1.2px, transparent 1.4px);
}

.hc-x .li-avatar {
  border-radius: 50%;
  box-shadow: 3px 3px 0 var(--amethyst);
}

.hc-x .hc-handle {
  margin-top: 2px;
}

.hc-x .li-headline {
  margin-top: 10px;
}

/* ---------- Footer (ink in both themes) ---------- */

.site-footer {
  background: var(--c-footer);
  color: var(--cream);
  border-top: var(--bw) solid var(--t-line);
  padding-top: 64px;
}

.site-footer .brand-dim {
  color: var(--lavender);
}

.footer-grid {
  display: grid;
  grid-template-columns: 2fr 1fr 1fr 1fr;
  gap: 40px;
}

.footer-brand p {
  margin-top: 16px;
  max-width: 34ch;
  font-size: 0.92rem;
  color: rgba(255, 248, 236, 0.72);
}

.site-footer h3 {
  font-family: var(--font-mono);
  font-size: 0.76rem;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--butter);
}

.site-footer ul {
  list-style: none;
  margin: 16px 0 0;
  padding: 0;
  display: grid;
  gap: 8px;
}

.site-footer ul a {
  font-size: 0.92rem;
  color: rgba(255, 248, 236, 0.72);
  text-decoration: none;
}

.site-footer ul a:hover {
  color: var(--cream);
  text-decoration: underline;
  text-decoration-color: var(--butter);
  text-decoration-thickness: 2px;
  text-underline-offset: 4px;
}

.footer-bottom {
  margin-top: 56px;
  padding: 22px var(--gutter);
  border-top: 1px solid rgba(255, 248, 236, 0.14);
  text-align: center;
  font-family: var(--font-mono);
  font-size: 0.7rem;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: rgba(255, 248, 236, 0.6);
}

/* ---------- Music player ---------- */

/* A small window pinned to the bottom left; it takes the page theme through
   the shared surface tokens. */
.player {
  position: fixed;
  bottom: max(16px, env(safe-area-inset-bottom));
  left: max(16px, env(safe-area-inset-left));
  z-index: 45;
  width: 288px;
  box-shadow: 5px 5px 0 var(--shadow);
}

.player .window-bar {
  min-height: 30px;
  padding: 0 10px;
  gap: 10px;
}

.player .dots i {
  width: 9px;
  height: 9px;
}

.player-count {
  margin-left: auto;
  font-family: var(--font-mono);
  font-size: 0.66rem;
  font-weight: 700;
  letter-spacing: 0.08em;
  color: rgba(255, 248, 236, 0.65);
}

.player-body {
  display: flex;
  align-items: center;
  gap: 12px;
  padding: 10px 12px;
}

.player-btn {
  display: grid;
  flex-shrink: 0;
  place-items: center;
  width: 36px;
  height: 36px;
  padding: 0;
  border: var(--bw) solid var(--line);
  border-radius: 6px;
  background: var(--card);
  color: var(--fg);
  box-shadow: 2px 2px 0 var(--shadow);
  cursor: pointer;
  transition: transform 0.12s ease, box-shadow 0.12s ease;
}

.player-btn:hover {
  transform: translate(-1px, -1px);
  box-shadow: 3px 3px 0 var(--shadow);
}

.player-btn:active {
  transform: translate(2px, 2px);
  box-shadow: 0 0 0 var(--shadow);
}

.player-btn svg {
  grid-area: 1 / 1;
  width: 16px;
  height: 16px;
  fill: currentColor;
}

.player-play {
  width: 40px;
  height: 40px;
  background: var(--butter);
  color: var(--ink);
}

.player-play svg {
  width: 18px;
  height: 18px;
}

.player .icon-pause,
.player.is-playing .icon-play {
  display: none;
}

.player.is-playing .icon-pause {
  display: block;
}

/* Equaliser: bars bounce while music plays, rest low when paused */
.player-eq {
  display: flex;
  flex-shrink: 0;
  align-items: flex-end;
  gap: 3px;
  height: 22px;
}

.player-eq i {
  width: 4px;
  height: 100%;
  border-radius: 1px;
  background: var(--accent-text);
  transform: scaleY(0.2);
  transform-origin: bottom;
  transition: transform 0.3s ease;
}

.player.is-playing .player-eq i {
  animation: player-eq 0.9s ease-in-out infinite alternate;
}

.player.is-playing .player-eq i:nth-child(2) { animation-duration: 0.7s; animation-delay: -0.3s; }
.player.is-playing .player-eq i:nth-child(3) { animation-duration: 1.1s; animation-delay: -0.5s; }
.player.is-playing .player-eq i:nth-child(4) { animation-duration: 0.8s; animation-delay: -0.2s; }

.player-info {
  display: grid;
  flex: 1;
  min-width: 0;
  line-height: 1.15;
}

.player-info small {
  font-family: var(--font-mono);
  font-size: 0.62rem;
  font-weight: 700;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--muted);
}

.player-info strong {
  overflow: hidden;
  font-family: var(--font-head);
  font-size: 0.92rem;
  font-weight: 800;
  text-overflow: ellipsis;
  text-transform: uppercase;
  white-space: nowrap;
}

.player-progress {
  display: block;
  height: 4px;
  background: var(--t-soft-rule);
}

.player-progress i {
  display: block;
  height: 100%;
  background: var(--accent-text);
  transform: scaleX(var(--progress, 0));
  transform-origin: left;
  transition: transform 0.25s linear;
}

/* Out of sight under the intro, then it pops up with the page */
.intro-play .player {
  visibility: hidden;
}

.intro-reveal .player {
  animation: player-in 0.6s cubic-bezier(0.34, 1.4, 0.64, 1) 0.6s both;
}

/* Keep the last footer line clear of it */
.footer-bottom {
  padding-bottom: 116px;
}

@keyframes player-eq {
  from { transform: scaleY(0.25); }
  to { transform: scaleY(1); }
}

@keyframes player-in {
  from { opacity: 0; transform: translateY(40px); }
}

/* Phones: just the controls */
@media (max-width: 560px) {
  .player {
    bottom: max(12px, env(safe-area-inset-bottom));
    left: max(12px, env(safe-area-inset-left));
    width: auto;
  }

  .player .window-bar,
  .player-info {
    display: none;
  }

  .player-body {
    gap: 10px;
    padding: 8px 10px;
  }

  .footer-bottom {
    padding-bottom: 96px;
  }
}

@media print {
  .player {
    display: none;
  }
}

/* ---------- Project bot ---------- */

/* A little robot pinned to the bottom right. Clicking it opens the chat
   window out of the corner it was standing in, and the bot jumps up to
   perch on the window's top edge; closing drops it back down with a squash.
   The box itself is only bot-sized; the window hangs off it. */
.chatbot {
  --bot: 68px;
  --chat-w: min(390px, calc(100vw - 32px));
  --chat-h: min(540px, calc(100dvh - 200px));
  --lift: calc(var(--chat-h) * -1 + 16px);
  --bot-hot: #FF6B6B;
  --bot-tear: #9BD7FF;
  position: fixed;
  right: max(18px, env(safe-area-inset-right));
  bottom: max(18px, env(safe-area-inset-bottom));
  z-index: 48;
  width: var(--bot);
  height: var(--bot);
}

/* The bot */

.chat-bot {
  position: absolute;
  right: 0;
  bottom: 0;
  z-index: 2;
  width: var(--bot);
  height: var(--bot);
  padding: 0;
  border: 0;
  background: none;
  cursor: pointer;
  -webkit-tap-highlight-color: transparent;
}

.chat-bot:focus-visible {
  outline: 3px solid var(--focus);
  outline-offset: 6px;
  border-radius: 14px;
}

.bot-float {
  position: absolute;
  inset: 0;
  animation: bot-bob 3.2s ease-in-out infinite;
}

.bot-svg {
  display: block;
  width: 100%;
  height: 100%;
  overflow: visible;
  filter: drop-shadow(4px 4px 0 var(--shadow));
  transform-origin: 50% 90%;
  transition: transform 0.35s cubic-bezier(0.34, 1.56, 0.64, 1), filter 0.2s ease;
}

.bot-svg * {
  stroke: var(--ink);
  stroke-width: 2.5;
  stroke-linecap: round;
  stroke-linejoin: round;
}

.bot-head {
  fill: var(--cream);
}

.bot-ear {
  fill: var(--amethyst);
}

.bot-screen {
  fill: var(--ink);
  transition: fill 0.3s ease;
}

/* Everything inside the svg, so a mood can move the whole robot */
.bot-body,
.bot-face {
  transform-box: view-box;
  transform-origin: 32px 56px;
}

.bot-antenna {
  transform-box: view-box;
  transform-origin: 32px 15px;
  fill: none;
  transition: transform 0.5s cubic-bezier(0.34, 1.56, 0.64, 1);
}

.bot-bulb {
  fill: var(--butter);
  transition: fill 0.3s ease;
}

.bot-eyes {
  transform-box: fill-box;
  transform-origin: center;
  animation: bot-blink 4.6s infinite;
}

.bot-eyes-open circle {
  fill: var(--mint);
  stroke: none;
  transform-box: fill-box;
  transform-origin: center;
  transition: fill 0.2s ease, transform 0.3s cubic-bezier(0.34, 1.56, 0.64, 1);
}

.bot-eyes-open,
.bot-eyes-happy {
  transform-box: fill-box;
  transform-origin: center;
  transition: opacity 0.2s ease, transform 0.3s cubic-bezier(0.34, 1.56, 0.64, 1);
}

.bot-eyes-happy {
  opacity: 0;
  transform: scale(0.4);
}

.bot-eyes-happy path,
.bot-mouth,
.bot-brow {
  fill: none;
  stroke: var(--mint);
}

/* The parts only some moods use start hidden; the plain smile is the default */
.bot-mouth {
  opacity: 0;
  transition: opacity 0.15s ease, stroke 0.2s ease;
}

.bot-mouth-smile {
  opacity: 1;
}

.bot-mouth-grin {
  fill: var(--mint);
}

.bot-brow {
  opacity: 0;
  transform-box: fill-box;
  transform-origin: center;
  transition: opacity 0.2s ease, stroke 0.2s ease, transform 0.3s cubic-bezier(0.34, 1.56, 0.64, 1);
}

.bot-tear,
.bot-vein,
.bot-spark,
.bot-huh {
  opacity: 0;
  transform-box: fill-box;
  transform-origin: center;
  transition: opacity 0.2s ease;
}

.bot-tear {
  fill: var(--bot-tear);
  stroke-width: 1.2;
}

.bot-vein {
  fill: none;
  stroke: var(--bot-hot);
  stroke-width: 2.2;
}

.bot-spark {
  fill: var(--butter);
  stroke-width: 1.6;
}

.bot-huh {
  fill: var(--butter);
  stroke-width: 1.5;
  paint-order: stroke;
  font: 900 15px/1 var(--font-head);
}

/* The ? in a speech bubble; it turns into a close cross while the chat is open */
.bot-bubble {
  position: absolute;
  top: -10px;
  left: -14px;
  display: grid;
  place-items: center;
  width: 30px;
  height: 30px;
  border: 2.5px solid var(--ink);
  border-radius: 9px 9px 9px 2px;
  background: var(--butter);
  color: var(--ink);
  box-shadow: 2px 2px 0 var(--ink);
  font: 900 1.05rem/1 var(--font-head);
  transform: rotate(-10deg);
  animation: bot-nudge 7s ease-in-out 2s infinite;
  transition: transform 0.35s cubic-bezier(0.34, 1.56, 0.64, 1), background-color 0.25s ease;
}

.bot-bubble b {
  grid-area: 1 / 1;
  transition: opacity 0.2s ease, transform 0.35s cubic-bezier(0.34, 1.56, 0.64, 1);
}

.bot-x {
  font-size: 0.78rem;
  opacity: 0;
  transform: rotate(-120deg) scale(0.3);
}

/* A soft shadow on the ground that shrinks as the bot bobs up */
.bot-ground {
  position: absolute;
  bottom: -9px;
  left: 50%;
  width: 46px;
  height: 8px;
  margin-left: -23px;
  border-radius: 50%;
  background: rgba(29, 21, 40, 0.28);
  animation: bot-ground 3.2s ease-in-out infinite;
  transition: opacity 0.2s ease;
}

/* Hover: head tilts, eyes widen, antenna wiggles, the ? hops */
@media (hover: hover) {
  .chat-bot:hover .bot-svg {
    transform: rotate(-9deg) scale(1.07);
    filter: drop-shadow(6px 6px 0 var(--shadow));
  }

  .chat-bot:hover .bot-eyes-open {
    transform: translateY(-1.5px) scale(1.2);
  }

  .chat-bot:hover .bot-antenna {
    animation: bot-wiggle 0.7s ease-in-out;
  }

  .chat-bot:hover .bot-bulb {
    fill: var(--mint);
  }

  .chat-bot:hover .bot-bubble {
    animation: none;
    transform: translate(-3px, -6px) rotate(-20deg) scale(1.15);
  }
}

.chat-bot:active .bot-svg {
  transform: scale(0.92, 0.86);
  transition-duration: 0.1s;
}

/* Open: the bot springs up onto the window, happy-eyed */
.chatbot.is-open .chat-bot {
  animation: bot-rise 0.7s cubic-bezier(0.3, 0.7, 0.3, 1) both;
}

.chatbot.is-closing .chat-bot {
  animation: bot-drop 0.6s linear both;
}

.chatbot.is-open .bot-ground {
  opacity: 0;
}

.chatbot.is-open .bot-eyes-open {
  opacity: 0;
  transform: scale(0.4);
}

.chatbot.is-open .bot-eyes-happy {
  opacity: 1;
  transform: none;
}

.chatbot.is-open .bot-bubble {
  animation: none;
  background: var(--lavender);
  transform: rotate(0deg);
}

.chatbot.is-open .bot-q {
  opacity: 0;
  transform: rotate(120deg) scale(0.3);
}

.chatbot.is-open .bot-x {
  opacity: 1;
  transform: none;
}

.chatbot.is-open .bot-bulb {
  fill: var(--mint);
}

/* Thinking: eyes scan side to side, the bulb blinks */
.chatbot.is-busy .bot-eyes-happy {
  opacity: 0;
}

.chatbot.is-busy .bot-eyes-open {
  opacity: 1;
  transform: none;
  animation: bot-scan 1.1s ease-in-out infinite;
}

.chatbot.is-busy .bot-bulb {
  animation: bot-bulb 0.55s steps(1) infinite;
}

/* Moods: script.js sets data-mood when an answer starts (the model picks it,
   see api/chat.js) and clears it a few seconds after. Each one is a face held
   for that long plus one motion as it lands. */

.chatbot[data-mood] .bot-eyes-happy {
  opacity: 0;
  transform: scale(0.4);
}

.chatbot[data-mood] .bot-eyes-open {
  opacity: 1;
  transform: none;
}

.chatbot[data-mood] .bot-mouth-smile {
  opacity: 0;
}

/* Nod: dips its head twice, eyes looking down with it */
.chatbot[data-mood="nod"] .bot-mouth-smile {
  opacity: 1;
}

.chatbot[data-mood="nod"] .bot-body {
  animation: mood-nod 0.9s ease-in-out both;
}

.chatbot[data-mood="nod"] .bot-face {
  animation: mood-nod-face 0.9s ease-in-out both;
}

/* Yes: happy eyes, a grin, a hop and a sparkle */
.chatbot[data-mood="yes"] .bot-eyes-open {
  opacity: 0;
  transform: scale(0.4);
}

.chatbot[data-mood="yes"] .bot-eyes-happy {
  opacity: 1;
  transform: none;
}

.chatbot[data-mood="yes"] .bot-mouth-grin {
  opacity: 1;
}

.chatbot[data-mood="yes"] .bot-bulb {
  fill: var(--butter);
}

.chatbot[data-mood="yes"] .bot-body {
  animation: mood-hop 0.75s ease both;
}

.chatbot[data-mood="yes"] .bot-spark {
  opacity: 1;
  animation: mood-pop 0.45s ease-out 0.25s both, mood-twinkle 1.4s ease-in-out 0.7s infinite;
}

/* No: half-shut eyes, a flat mouth, and a shake of the head */
.chatbot[data-mood="no"] .bot-eyes-open {
  transform: scaleY(0.45);
}

.chatbot[data-mood="no"] .bot-brow {
  opacity: 1;
  transform: translateY(1.5px);
}

.chatbot[data-mood="no"] .bot-mouth-flat {
  opacity: 1;
}

.chatbot[data-mood="no"] .bot-body {
  animation: mood-shake 0.9s ease-in-out both;
}

.chatbot[data-mood="no"] .bot-face {
  animation: mood-shake-face 0.9s ease-in-out both;
}

/* Maybe: head cocked, one eye big and one small, a raised brow, a wobbly
   mouth and a ? over its head */
.chatbot[data-mood="maybe"] .bot-svg {
  transform: rotate(9deg);
}

.chatbot[data-mood="maybe"] .bot-eye-l {
  transform: scale(1.25);
}

.chatbot[data-mood="maybe"] .bot-eye-r {
  transform: scale(0.7);
}

.chatbot[data-mood="maybe"] .bot-brow {
  opacity: 1;
}

.chatbot[data-mood="maybe"] .bot-brow-l {
  transform: translateY(-2.5px) rotate(-12deg);
}

.chatbot[data-mood="maybe"] .bot-brow-r {
  transform: translateY(1.5px);
}

.chatbot[data-mood="maybe"] .bot-mouth-wavy {
  opacity: 1;
}

.chatbot[data-mood="maybe"] .bot-antenna {
  animation: bot-wiggle 0.9s ease-in-out 0.2s;
}

.chatbot[data-mood="maybe"] .bot-body {
  animation: mood-ponder 1s ease-in-out both;
}

.chatbot[data-mood="maybe"] .bot-huh {
  opacity: 1;
  animation: mood-pop 0.45s ease-out 0.2s both, mood-huh 1.6s ease-in-out 0.65s infinite;
}

/* Angry: red eyes under scowling brows, a flushed screen, an anger mark
   and a fuming shudder */
.chatbot[data-mood="angry"] .bot-eyes-open {
  transform: translateY(1px) scaleY(0.7);
}

.chatbot[data-mood="angry"] .bot-eyes-open circle {
  fill: var(--bot-hot);
}

.chatbot[data-mood="angry"] .bot-brow {
  opacity: 1;
  stroke: var(--bot-hot);
}

.chatbot[data-mood="angry"] .bot-brow-l {
  transform: translateY(1.5px) rotate(22deg);
}

.chatbot[data-mood="angry"] .bot-brow-r {
  transform: translateY(1.5px) rotate(-22deg);
}

.chatbot[data-mood="angry"] .bot-mouth-frown {
  opacity: 1;
  stroke: var(--bot-hot);
}

.chatbot[data-mood="angry"] .bot-screen {
  fill: #4A1730;
}

.chatbot[data-mood="angry"] .bot-bulb {
  fill: var(--bot-hot);
  animation: mood-alarm 0.4s steps(1) 4;
}

.chatbot[data-mood="angry"] .bot-body {
  animation: mood-fume 0.8s linear both;
}

.chatbot[data-mood="angry"] .bot-vein {
  opacity: 1;
  animation: mood-pop 0.3s ease-out both, mood-throb 0.6s ease-in-out 0.3s infinite;
}

/* Sad: droopy eyes and brows, a frown, a falling tear, the antenna wilts
   and the whole bot slumps */
.chatbot[data-mood="sad"] .bot-svg {
  transform: translateY(3px) scale(0.96);
}

.chatbot[data-mood="sad"] .bot-eyes-open {
  transform: translateY(1.5px) scaleY(0.75);
}

.chatbot[data-mood="sad"] .bot-brow {
  opacity: 1;
}

.chatbot[data-mood="sad"] .bot-brow-l {
  transform: rotate(-18deg);
}

.chatbot[data-mood="sad"] .bot-brow-r {
  transform: rotate(18deg);
}

.chatbot[data-mood="sad"] .bot-mouth-frown {
  opacity: 1;
}

.chatbot[data-mood="sad"] .bot-antenna {
  transform: rotate(28deg);
}

.chatbot[data-mood="sad"] .bot-bulb {
  fill: var(--lavender);
}

.chatbot[data-mood="sad"] .bot-body {
  animation: mood-sigh 1.1s ease-in-out both;
}

.chatbot[data-mood="sad"] .bot-tear {
  opacity: 1;
  animation: mood-tear 1.8s ease-in 0.4s infinite both;
}

/* The window */

.chat {
  position: absolute;
  right: 0;
  bottom: 0;
  z-index: 1;
  display: flex;
  flex-direction: column;
  width: var(--chat-w);
  height: var(--chat-h);
  box-shadow: 6px 6px 0 var(--shadow);
  visibility: hidden;
  /* Folded into the bot's corner when closed */
  clip-path: inset(calc(100% - var(--bot)) 0 0 calc(100% - var(--bot)) round 20px);
  transition: clip-path 0.45s cubic-bezier(0.7, 0, 0.3, 1), visibility 0s linear 0.45s;
}

.chatbot.is-open .chat {
  visibility: visible;
  clip-path: inset(-4px -12px -12px -4px round var(--radius));
  transition: clip-path 0.6s cubic-bezier(0.22, 1, 0.36, 1) 0.06s, visibility 0s;
}

/* Leave room on the right of the bar for the bot perched there */
.chat .window-bar {
  padding-right: calc(var(--bot) + 18px);
}

.chat > * {
  opacity: 0;
  transform: translateY(10px);
  transition: opacity 0.15s ease, transform 0.15s ease;
}

.chatbot.is-open .chat > * {
  opacity: 1;
  transform: none;
  transition: opacity 0.35s ease, transform 0.45s cubic-bezier(0.22, 1, 0.36, 1);
}

.chatbot.is-open .chat > :nth-child(1) { transition-delay: 0.18s; }
.chatbot.is-open .chat > :nth-child(2) { transition-delay: 0.24s; }
.chatbot.is-open .chat > :nth-child(3) { transition-delay: 0.3s; }
.chatbot.is-open .chat > :nth-child(4) { transition-delay: 0.34s; }

.chat-log {
  display: flex;
  flex: 1;
  flex-direction: column;
  gap: 12px;
  min-height: 0;
  padding: 16px 14px 18px;
  overflow-y: auto;
  overscroll-behavior: contain;
  background-color: var(--card);
  background-image: radial-gradient(var(--t-soft-rule) 1px, transparent 1.2px);
  background-size: 16px 16px;
}

.chat-thread {
  display: flex;
  flex-direction: column;
  gap: 12px;
}

.chat-thread:empty {
  display: none;
}

.chat-msg {
  max-width: 88%;
  margin: 0;
  padding: 10px 13px;
  border: 2px solid var(--line);
  border-radius: 10px;
  box-shadow: 3px 3px 0 var(--shadow);
  font: 400 0.9rem/1.5 var(--font-body);
  overflow-wrap: anywhere;
}

.chat-msg.from-bot {
  align-self: flex-start;
  border-bottom-left-radius: 2px;
  background: var(--bg);
  color: var(--fg);
  transform-origin: 0 100%;
  animation: chat-msg-bot 0.4s cubic-bezier(0.34, 1.4, 0.64, 1) both;
}

.chat-msg.from-user {
  align-self: flex-end;
  border-bottom-right-radius: 2px;
  background: var(--amethyst);
  color: var(--cream);
  transform-origin: 100% 100%;
  animation: chat-msg-user 0.3s cubic-bezier(0.34, 1.4, 0.64, 1) both;
}

.chat-msg p,
.chat-msg ul {
  margin: 0;
}

.chat-msg p + p,
.chat-msg p + ul,
.chat-msg ul + p {
  margin-top: 8px;
}

.chat-msg ul {
  padding-left: 18px;
}

.chat-msg li + li {
  margin-top: 3px;
}

.chat-msg strong {
  font-weight: 700;
}

.chat-msg code {
  padding: 0 4px;
  border-radius: 3px;
  background: var(--t-soft-rule);
  font: 0.85em var(--font-mono);
}

.chat-msg a {
  color: var(--accent-text);
  font-weight: 700;
  text-decoration: underline;
  text-decoration-thickness: 2px;
  text-underline-offset: 2px;
}

.chat-msg.is-error {
  background: var(--butter);
  color: var(--ink);
}

/* Three dots while the bot thinks */
.chat-typing {
  display: inline-flex;
  gap: 5px;
  padding: 4px 2px;
}

.chat-typing i {
  width: 7px;
  height: 7px;
  border-radius: 50%;
  background: var(--accent-text);
  animation: chat-dot 1s ease-in-out infinite;
}

.chat-typing i:nth-child(2) { animation-delay: 0.15s; }
.chat-typing i:nth-child(3) { animation-delay: 0.3s; }

/* The loader bubble: dots plus "Thinking…" (or "Typing…" between messages) */
.chat-msg.is-loading {
  display: inline-flex;
  align-items: center;
  gap: 8px;
}

.chat-loading {
  color: var(--muted);
  font: 700 0.74rem/1 var(--font-mono);
}

/* One reply can be a few bubbles in a row */
.chat-msg.from-bot + .chat-msg.from-bot {
  margin-top: -4px;
}

/* Links on a line of their own: wide buttons, and square tiles for profiles */
.chat-actions,
.chat-tiles {
  display: grid;
  gap: 8px;
  margin-top: 10px;
}

.chat-msg > .chat-actions:first-child,
.chat-msg > .chat-tiles:first-child {
  margin-top: 0;
}

.chat-tiles {
  grid-template-columns: repeat(auto-fill, minmax(76px, 1fr));
}

.chat-msg .chat-action,
.chat-msg .chat-tile {
  border: 2px solid var(--line);
  border-radius: 8px;
  color: var(--ink);
  box-shadow: 3px 3px 0 var(--shadow);
  text-decoration: none;
  cursor: pointer;
  transition: transform 0.12s ease, box-shadow 0.12s ease;
}

.chat-action {
  display: flex;
  align-items: center;
  gap: 10px;
  width: 100%;
  padding: 8px 10px;
  background: var(--butter);
  font: inherit;
  text-align: left;
}

.chat-action.is-spot {
  background: var(--mint);
}

.chat-msg .chat-action:hover,
.chat-msg .chat-tile:hover {
  transform: translate(-1px, -1px);
  box-shadow: 4px 4px 0 var(--shadow);
}

.chat-msg .chat-action:active,
.chat-msg .chat-tile:active {
  transform: translate(2px, 2px);
  box-shadow: 1px 1px 0 var(--shadow);
}

.chat-action-icon {
  display: grid;
  flex-shrink: 0;
  place-items: center;
  width: 30px;
  height: 30px;
  border: 2px solid var(--ink);
  border-radius: 6px;
  background: var(--cream);
}

.chat-action-icon svg,
.tile-email .chat-tile-icon svg {
  width: 18px;
  height: 18px;
  fill: none;
  stroke: currentColor;
  stroke-width: 2;
  stroke-linecap: round;
  stroke-linejoin: round;
}

.chat-action-text {
  display: flex;
  flex: 1;
  flex-direction: column;
  min-width: 0;
}

.chat-action-text b {
  font: 700 0.86rem/1.25 var(--font-body);
}

.chat-action-text small,
.chat-tile small {
  overflow: hidden;
  font: 400 0.66rem/1.3 var(--font-mono);
  white-space: nowrap;
  text-overflow: ellipsis;
  opacity: 0.75;
}

.chat-action-go {
  font-weight: 900;
  transition: transform 0.15s ease;
}

.chat-action:hover .chat-action-go {
  transform: translateX(3px);
}

.chat-tile {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 3px;
  min-width: 0;
  aspect-ratio: 1;
  padding: 8px 6px;
  background: var(--lavender);
  text-align: center;
}

.chat-msg .tile-github { background: var(--ink); color: var(--cream); }
.chat-msg .tile-linkedin { background: var(--amethyst); color: var(--cream); }
.chat-msg .tile-x { background: var(--butter); }
.chat-msg .tile-email { background: var(--mint); }

.chat-tile-icon svg {
  display: block;
  width: 24px;
  height: 24px;
  fill: currentColor;
}

.tile-email .chat-tile-icon svg {
  width: 24px;
  height: 24px;
}

.chat-tile b {
  font: 800 0.78rem/1.1 var(--font-body);
}

.chat-tile small {
  max-width: 100%;
}

/* The bot pointing at part of the page: a ring around it, the rest dimmed,
   a "Right here!" tag, and the bot's eyes turned that way */
.spotlight {
  position: fixed;
  z-index: 47;
  border: 3px solid var(--ink);
  border-radius: 12px;
  box-shadow: 0 0 0 4px var(--butter), 0 0 0 200vmax rgba(29, 21, 40, 0.5);
  pointer-events: none;
  animation: spot-in 0.45s cubic-bezier(0.34, 1.56, 0.64, 1) both;
  transition: opacity 0.3s ease;
}

/* Above the sticky header, for the CV button */
.spotlight.is-high {
  z-index: 51;
}

.spotlight.is-leaving {
  opacity: 0;
}

.spotlight-tag {
  position: absolute;
  bottom: calc(100% + 10px);
  left: 10px;
  padding: 6px 10px;
  border: 2.5px solid var(--ink);
  border-radius: 8px 8px 8px 2px;
  background: var(--butter);
  color: var(--ink);
  box-shadow: 3px 3px 0 var(--ink);
  font: 900 0.8rem/1 var(--font-head);
  text-transform: uppercase;
  white-space: nowrap;
  animation: spot-tag 1s ease-in-out 0.3s infinite;
}

.spotlight.tag-below .spotlight-tag {
  top: calc(100% + 10px);
  bottom: auto;
  border-radius: 2px 8px 8px 8px;
}

.bot-face {
  transition: translate 0.25s ease;
}

.chatbot.is-pointing .bot-face {
  translate: var(--look-x, 0) var(--look-y, 0);
}

@keyframes spot-in {
  from { opacity: 0; transform: scale(1.08); }
}

@keyframes spot-tag {
  0%, 100% { transform: translateY(0); }
  50% { transform: translateY(-4px); }
}

/* Starter questions; gone once the visitor asks something */
.chat-suggest {
  display: flex;
  flex-wrap: wrap;
  gap: 8px;
}

.chat-suggest[hidden] {
  display: none;
}

.chat-chip {
  padding: 7px 10px;
  border: 2px solid var(--line);
  border-radius: 6px;
  background: var(--bg);
  color: var(--fg);
  box-shadow: 2px 2px 0 var(--shadow);
  font: 700 0.78rem/1.25 var(--font-body);
  text-align: left;
  cursor: pointer;
  transition: transform 0.12s ease, box-shadow 0.12s ease, background-color 0.2s ease;
}

.chat-chip:hover {
  background: var(--butter);
  color: var(--ink);
  transform: translate(-1px, -1px);
  box-shadow: 3px 3px 0 var(--shadow);
}

.chat-chip:active {
  transform: translate(2px, 2px);
  box-shadow: 0 0 0 var(--shadow);
}

.chat-form {
  display: flex;
  align-items: flex-end;
  gap: 10px;
  padding: 12px 12px 8px;
  border-top: var(--bw) solid var(--line);
  background: var(--card);
}

.chat-input {
  flex: 1;
  min-width: 0;
  max-height: 120px;
  padding: 10px 12px;
  border: 2px solid var(--line);
  border-radius: 6px;
  background: var(--bg);
  color: var(--fg);
  font: 400 0.92rem/1.4 var(--font-body);
  resize: none;
}

.chat-input::placeholder {
  color: var(--muted);
}

.chat-input:focus {
  outline: 3px solid var(--focus);
  outline-offset: 1px;
}

.chat-send {
  display: grid;
  flex-shrink: 0;
  place-items: center;
  width: 44px;
  height: 44px;
  padding: 0;
  border: var(--bw) solid var(--line);
  border-radius: 6px;
  background: var(--butter);
  color: var(--ink);
  box-shadow: 3px 3px 0 var(--shadow);
  cursor: pointer;
  transition: transform 0.12s ease, box-shadow 0.12s ease;
}

.chat-send svg {
  width: 20px;
  height: 20px;
  fill: none;
  stroke: currentColor;
  stroke-width: 2.5;
  stroke-linecap: round;
  stroke-linejoin: round;
  transition: transform 0.2s ease;
}

.chat-send:hover {
  transform: translate(-1px, -1px);
  box-shadow: 4px 4px 0 var(--shadow);
}

.chat-send:hover svg {
  transform: translateX(2px);
}

.chat-send:active {
  transform: translate(3px, 3px);
  box-shadow: 0 0 0 var(--shadow);
}

.chat-send:disabled {
  opacity: 0.5;
  cursor: progress;
}

.chat-fine {
  margin: 0;
  padding: 0 12px 10px;
  background: var(--card);
  color: var(--muted);
  font: 400 0.68rem/1.4 var(--font-mono);
}

/* Inside a notes dialog it pops back in once the dialog has settled */
.case .chatbot {
  animation: player-in 0.45s cubic-bezier(0.34, 1.4, 0.64, 1) both;
}

/* Out of sight under the intro, then it pops up with the page */
.intro-play .chatbot {
  visibility: hidden;
}

.intro-reveal .chatbot {
  animation: player-in 0.6s cubic-bezier(0.34, 1.4, 0.64, 1) 0.8s both;
}

@keyframes bot-bob {
  0%, 100% { transform: translateY(0); }
  50% { transform: translateY(-7px); }
}

@keyframes bot-ground {
  0%, 100% { transform: scaleX(1); opacity: 1; }
  50% { transform: scaleX(0.7); opacity: 0.6; }
}

@keyframes bot-blink {
  0%, 90%, 100% { transform: scaleY(1); }
  93% { transform: scaleY(0.1); }
  96% { transform: scaleY(1); }
}

@keyframes bot-wiggle {
  0%, 100% { transform: rotate(0deg); }
  20% { transform: rotate(-22deg); }
  45% { transform: rotate(16deg); }
  70% { transform: rotate(-8deg); }
}

@keyframes bot-nudge {
  0%, 86%, 100% { transform: rotate(-10deg); }
  89% { transform: translateY(-7px) rotate(-22deg); }
  92% { transform: translateY(0) rotate(4deg); }
  95% { transform: translateY(-3px) rotate(-14deg); }
}

@keyframes bot-scan {
  0%, 100% { transform: translateX(-2.5px); }
  50% { transform: translateX(2.5px); }
}

@keyframes bot-bulb {
  0% { fill: var(--mint); }
  50% { fill: var(--butter); }
}

/* Crouch, launch (stretched), overshoot, settle onto the window */
@keyframes bot-rise {
  0% { transform: translateY(0) scale(1, 1); }
  14% { transform: translateY(4px) scale(1.16, 0.82); }
  50% { transform: translateY(calc(var(--lift) - 34px)) scale(0.9, 1.12); }
  72% { transform: translateY(calc(var(--lift) + 3px)) scale(1.1, 0.9); }
  86% { transform: translateY(calc(var(--lift) - 4px)) scale(0.98, 1.02); }
  100% { transform: translateY(var(--lift)) scale(1, 1); }
}

/* A little hop off the window, a fall, and a squashy landing */
@keyframes bot-drop {
  0% { transform: translateY(var(--lift)) scale(1, 1); animation-timing-function: cubic-bezier(0.3, 0, 0.6, 0.4); }
  16% { transform: translateY(calc(var(--lift) - 18px)) scale(0.95, 1.06); animation-timing-function: cubic-bezier(0.5, 0, 1, 0.6); }
  56% { transform: translateY(0) scale(0.9, 1.12); animation-timing-function: ease-out; }
  68% { transform: translateY(5px) scale(1.2, 0.78); animation-timing-function: ease-out; }
  82% { transform: translateY(-6px) scale(0.96, 1.05); animation-timing-function: ease-in; }
  100% { transform: translateY(0) scale(1, 1); }
}

@keyframes mood-nod {
  0%, 100% { transform: none; }
  20%, 60% { transform: translateY(2.5px) scale(1.03, 0.95); }
  40%, 80% { transform: translateY(-1.5px); }
}

@keyframes mood-nod-face {
  0%, 100% { transform: none; }
  20%, 60% { transform: translateY(2.5px); }
  40%, 80% { transform: translateY(-1px); }
}

@keyframes mood-hop {
  0%, 100% { transform: none; }
  15% { transform: translateY(2px) scale(1.12, 0.86); }
  45% { transform: translateY(-12px) scale(0.92, 1.1); }
  70% { transform: translateY(1px) scale(1.1, 0.9); }
  85% { transform: translateY(-3px) scale(0.98, 1.02); }
}

@keyframes mood-shake {
  0%, 100% { transform: none; }
  15% { transform: translateX(-3px) rotate(-8deg); }
  35% { transform: translateX(3px) rotate(8deg); }
  55% { transform: translateX(-2px) rotate(-6deg); }
  75% { transform: translateX(1px) rotate(3deg); }
}

@keyframes mood-shake-face {
  0%, 100% { transform: none; }
  15% { transform: translateX(-3px); }
  35% { transform: translateX(3px); }
  55% { transform: translateX(-2px); }
  75% { transform: translateX(1px); }
}

@keyframes mood-ponder {
  0%, 100% { transform: none; }
  30% { transform: rotate(-10deg); }
  65% { transform: rotate(6deg); }
}

@keyframes mood-fume {
  0%, 100% { transform: none; }
  12% { transform: scale(1.08, 0.94); }
  20%, 40%, 60% { transform: translateX(-2px) scale(1.04); }
  30%, 50%, 70% { transform: translateX(2px) scale(1.04); }
  85% { transform: scale(1.02); }
}

@keyframes mood-sigh {
  0%, 100% { transform: none; }
  35% { transform: translateY(-3px) scale(1.02, 1.04); }
}

@keyframes mood-pop {
  0% { opacity: 0; transform: scale(0.2) rotate(-30deg); }
  60% { opacity: 1; transform: scale(1.3) rotate(10deg); }
  100% { opacity: 1; transform: none; }
}

@keyframes mood-twinkle {
  0%, 100% { transform: none; }
  50% { transform: scale(0.7) rotate(45deg); }
}

@keyframes mood-huh {
  0%, 100% { transform: none; }
  50% { transform: translateY(-2px) rotate(12deg); }
}

@keyframes mood-throb {
  0%, 100% { transform: none; }
  50% { transform: scale(1.25); }
}

@keyframes mood-alarm {
  0% { fill: var(--bot-hot); }
  50% { fill: var(--butter); }
}

@keyframes mood-tear {
  0% { opacity: 0; transform: translateY(-2px) scale(0.4); }
  20% { opacity: 1; transform: none; }
  75% { opacity: 1; transform: translateY(7px) scaleY(1.1); }
  100% { opacity: 0; transform: translateY(9px); }
}

@keyframes chat-msg-bot {
  from { opacity: 0; transform: translate(-8px, 8px) scale(0.85) rotate(-2deg); }
}

@keyframes chat-msg-user {
  from { opacity: 0; transform: translate(8px, 8px) scale(0.85) rotate(2deg); }
}

@keyframes chat-dot {
  0%, 60%, 100% { transform: translateY(0); opacity: 0.45; }
  30% { transform: translateY(-5px); opacity: 1; }
}

@media (max-width: 560px) {
  .chatbot {
    --bot: 58px;
    --chat-w: calc(100vw - 24px);
    --chat-h: min(520px, calc(100dvh - 150px));
    right: max(12px, env(safe-area-inset-right));
    bottom: max(14px, env(safe-area-inset-bottom));
  }

  .bot-bubble {
    width: 26px;
    height: 26px;
    font-size: 0.92rem;
  }
}

@media (prefers-reduced-motion: reduce) {
  .bot-float,
  .bot-ground,
  .bot-eyes,
  .bot-bubble,
  .chatbot.is-busy .bot-eyes-open,
  .chatbot.is-busy .bot-bulb,
  .chatbot[data-mood] .bot-body,
  .chatbot[data-mood] .bot-face,
  .chatbot[data-mood] .bot-antenna,
  .chatbot[data-mood] .bot-bulb,
  .chatbot[data-mood] .bot-tear,
  .chatbot[data-mood] .bot-vein,
  .chatbot[data-mood] .bot-spark,
  .chatbot[data-mood] .bot-huh {
    animation: none;
  }

  .chatbot.is-open .chat-bot {
    animation: none;
    transform: translateY(var(--lift));
  }

  .chatbot.is-closing .chat-bot {
    animation: none;
  }

  .chat,
  .chatbot.is-open .chat {
    clip-path: none;
    transition: none;
  }

  .chat-msg.from-bot,
  .chat-msg.from-user,
  .spotlight,
  .spotlight-tag {
    animation: none;
  }
}

@media print {
  .chatbot,
  .spotlight {
    display: none;
  }
}

/* ---------- Case study dialogs ---------- */

.case {
  width: min(840px, calc(100vw - 32px));
  max-height: calc(100dvh - 48px);
  margin: auto;
  padding: 0;
  border: 3px solid var(--line);
  border-radius: var(--radius);
  background: var(--bg);
  color: var(--fg);
  box-shadow: 10px 10px 0 var(--amethyst);
  overscroll-behavior: contain;
}

.case::backdrop {
  background: rgba(29, 21, 40, 0.6);
}

.case[open] {
  animation: case-in 0.25s cubic-bezier(0.16, 1, 0.3, 1);
}

@keyframes case-in {
  from {
    opacity: 0;
    transform: translate(10px, 10px);
  }
}

.case > .window-bar {
  position: sticky;
  top: 0;
  z-index: 1;
}

.case-close {
  margin-left: auto;
  display: grid;
  place-items: center;
  width: 28px;
  height: 28px;
  border: 2px solid var(--cream);
  border-radius: 4px;
  background: var(--amethyst);
  color: var(--cream);
  font-size: 0.85rem;
  font-weight: 700;
  cursor: pointer;
}

.case-close:hover {
  background: var(--butter);
  color: var(--ink);
}

.case-inner {
  padding: clamp(22px, 5vw, 44px);
}

.case-inner > h2 {
  margin-top: 10px;
  font-weight: 900;
  font-size: clamp(2rem, 6vw, 3rem);
  text-transform: uppercase;
}

.case-grid {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 20px;
  margin-top: 28px;
}

.case-box {
  padding: 18px 20px;
  border: var(--bw) solid var(--line);
  border-radius: 6px;
  background: var(--card);
  box-shadow: 4px 4px 0 var(--lavender);
}

.case-grid h3,
.notes-title {
  font-family: var(--font-mono);
  font-size: 0.74rem;
  font-weight: 700;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--accent-text);
  margin-bottom: 8px;
}

.case-grid p {
  font-size: 0.95rem;
  color: var(--card-muted);
}

.case-approach {
  margin-top: 22px;
  padding: 16px 20px;
  border: var(--bw) solid var(--line);
  border-radius: 6px;
  background: var(--amethyst);
  color: var(--cream);
  box-shadow: 4px 4px 0 var(--butter);
  font-size: 0.95rem;
}

.notes-title {
  margin-top: 34px;
  margin-bottom: 14px;
}

.case-shots {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 22px 20px;
}

.case-shot {
  margin: 0;
}

.case-shot-lead {
  grid-column: 1 / -1;
}

.case-shot img {
  display: block;
  width: 100%;
  height: auto;
  aspect-ratio: 16 / 10;
  object-fit: cover;
  object-position: top;
  border: var(--bw) solid var(--line);
  border-radius: 6px;
  box-shadow: 4px 4px 0 var(--lavender);
}

/* Phone screenshots stand whole on the project colour inside the same 16:10 frame */
.case-shot img.shot-phone {
  object-fit: contain;
  object-position: center;
  padding: 14px 0;
}

.case-shot figcaption {
  margin-top: 10px;
  font-family: var(--font-mono);
  font-size: 0.72rem;
  font-weight: 700;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--muted);
}

/* ---------- Screenshot lightbox ---------- */

.project-shot,
.case-shot img {
  cursor: zoom-in;
}

.project-shot:focus-visible,
.case-shot img:focus-visible {
  outline: 3px solid var(--butter);
  outline-offset: 3px;
}

.lightbox {
  max-width: calc(100vw - 32px);
  max-height: calc(100dvh - 32px);
  margin: auto;
  padding: 0;
  border: 3px solid var(--ink);
  border-radius: var(--radius);
  background: var(--paper);
  box-shadow: 10px 10px 0 var(--amethyst);
  overflow: hidden;
  --line: var(--ink);
}

.lightbox::backdrop {
  background: rgba(29, 21, 40, 0.8);
}

.lightbox[open] {
  display: flex;
  flex-direction: column;
  animation: case-in 0.25s cubic-bezier(0.16, 1, 0.3, 1);
}

.lightbox-img {
  display: block;
  width: auto;
  height: auto;
  max-width: calc(100vw - 38px);
  /* Leaves room for the window bar, the borders and the page margin */
  max-height: calc(100dvh - 76px);
  object-fit: contain;
  cursor: zoom-out;
  touch-action: pan-y pinch-zoom;
  user-select: none;
}

.lightbox-img.from-next {
  animation: shot-from-next 0.28s cubic-bezier(0.16, 1, 0.3, 1);
}

.lightbox-img.from-prev {
  animation: shot-from-prev 0.28s cubic-bezier(0.16, 1, 0.3, 1);
}

@keyframes shot-from-next {
  from {
    opacity: 0;
    transform: translateX(28px);
  }
}

@keyframes shot-from-prev {
  from {
    opacity: 0;
    transform: translateX(-28px);
  }
}

.lightbox-count {
  margin-left: auto;
  flex-shrink: 0;
  font-family: var(--font-mono);
  font-size: 0.74rem;
  font-weight: 700;
  letter-spacing: 0.04em;
}

.lightbox-count + .case-close {
  margin-left: 0;
}

.lightbox-stage {
  position: relative;
  overflow: hidden;
}

/* Chevrons sit over the image's edges, centred vertically */
.lightbox-nav {
  position: absolute;
  top: 50%;
  display: grid;
  place-items: center;
  width: 44px;
  height: 44px;
  padding: 0;
  border: var(--bw) solid var(--ink);
  border-radius: 6px;
  background: var(--butter);
  color: var(--ink);
  box-shadow: 3px 3px 0 var(--ink);
  cursor: pointer;
  translate: 0 -50%;
  transition: transform 0.15s ease, box-shadow 0.15s ease;
}

.lightbox-prev {
  left: 12px;
}

.lightbox-next {
  right: 12px;
}

.lightbox-nav:hover {
  transform: translate(-1px, -1px);
  box-shadow: 4px 4px 0 var(--ink);
}

.lightbox-nav:active {
  transform: translate(2px, 2px);
  box-shadow: 1px 1px 0 var(--ink);
}

.lightbox-nav:focus-visible {
  outline: 3px solid var(--amethyst);
  outline-offset: 2px;
}

.lightbox-nav svg {
  width: 22px;
  height: 22px;
  fill: none;
  stroke: currentColor;
  stroke-width: 3;
  stroke-linecap: round;
  stroke-linejoin: round;
}

.lightbox.is-single .lightbox-nav,
.lightbox.is-single .lightbox-count {
  display: none;
}

.lightbox.is-single .case-close {
  margin-left: auto;
}

@media (max-width: 560px) {
  .lightbox-nav {
    width: 38px;
    height: 38px;
  }

  .lightbox-prev {
    left: 8px;
  }

  .lightbox-next {
    right: 8px;
  }
}

.notes {
  border: var(--bw) solid var(--line);
  border-radius: 6px;
  background: var(--card);
  overflow: hidden;
}

.notes section {
  padding: 18px 20px;
}

.notes section + section {
  border-top: 2px dashed var(--t-soft-rule);
}

.notes h4 {
  font-weight: 800;
  font-size: 1rem;
  text-transform: uppercase;
  margin-bottom: 6px;
}

.notes p {
  font-size: 0.94rem;
  color: var(--card-muted);
}

/* "How it works" line under a feature */
.notes .note-how {
  margin-top: 10px;
  padding-left: 12px;
  border-left: 3px solid var(--lavender);
  font-size: 0.9rem;
}

.note-how span {
  margin-right: 8px;
  font-family: var(--font-mono);
  font-size: 0.7rem;
  font-weight: 700;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--accent-text);
}

.case .stack {
  margin: 26px 0 20px;
}

.fineprint {
  margin-top: 16px;
  font-size: 0.8rem;
  color: var(--muted);
}

/* ---------- Reveal on scroll ---------- */

.js .reveal {
  opacity: 0;
  translate: 0 20px;
  transition: opacity 0.5s ease, translate 0.5s cubic-bezier(0.16, 1, 0.3, 1),
    transform 0.15s ease, box-shadow 0.15s ease;
}

.js .reveal.is-visible {
  opacity: 1;
  translate: 0 0;
}

/* ---------- Intro sequence ----------
   A letterboxed three-scene film (hello → Bogor → creations) that plays once
   per session. script.js drives the timeline by toggling classes; every
   scene takes its colours from the same band tokens as the page, so it
   follows the light/dark theme. */

.intro-play {
  overflow: hidden;
}

/* is-revealing keeps the overlay up for the curtain lift after the page unlocks */
.intro-play .intro,
.intro.is-revealing {
  display: grid;
}

.intro {
  position: fixed;
  inset: 0;
  z-index: 1000;
  grid-template-rows: auto minmax(0, 1fr) auto;
  background: var(--ink);
  color: var(--cream);
}

.intro-letterbox {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 20px;
  height: clamp(48px, 9vh, 84px);
  padding: 0 var(--gutter);
  font-family: var(--font-mono);
  font-size: 0.7rem;
  font-weight: 700;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: rgba(255, 248, 236, 0.7);
}

.intro-letterbox > * {
  opacity: 0;
  transition: opacity 0.5s ease 0.4s;
}

.intro.is-open .intro-letterbox > * {
  opacity: 1;
}

.intro-timecode {
  font-variant-numeric: tabular-nums;
  color: var(--butter);
}

.intro-progress {
  flex: 1;
  max-width: 420px;
  height: 4px;
  background: rgba(255, 248, 236, 0.16);
}

.intro-progress i {
  display: block;
  height: 100%;
  background: var(--butter);
  transform: scaleX(0);
  transform-origin: left;
}

.intro-skip,
.intro-sound {
  display: inline-flex;
  align-items: center;
  gap: 10px;
  min-height: 36px;
  padding: 0 12px;
  border: 2px solid var(--cream);
  border-radius: 6px;
  background: transparent;
  color: var(--cream);
  box-shadow: 3px 3px 0 var(--amethyst);
  font: inherit;
  letter-spacing: inherit;
  text-transform: inherit;
  cursor: pointer;
  transition: transform 0.12s ease, box-shadow 0.12s ease, background-color 0.2s ease, color 0.2s ease;
}

.intro-skip:hover,
.intro-sound:hover {
  background: var(--butter);
  color: var(--ink);
  transform: translate(-2px, -2px);
  box-shadow: 5px 5px 0 var(--amethyst);
}

.intro-skip kbd {
  padding: 1px 5px;
  border: 1.5px solid currentColor;
  border-radius: 3px;
  font: inherit;
  font-size: 0.6rem;
}

/* Pushes itself and the skip button to the right of the progress bar */
.intro-sound {
  margin-left: auto;
}

.intro-sound + .intro-skip {
  margin-left: -8px;
}

.intro-sound svg {
  width: 16px;
  height: 16px;
  fill: none;
  stroke: currentColor;
  stroke-width: 2;
  stroke-linecap: round;
  stroke-linejoin: round;
}

/* The screen opens like a shutter, then scenes wipe across it */

.intro-screen {
  position: relative;
  overflow: hidden;
  clip-path: inset(50% 0 50% 0);
}

.intro.is-open .intro-screen {
  animation: intro-open 0.8s cubic-bezier(0.7, 0, 0.2, 1) forwards;
}

.intro-scene {
  --fg: var(--t-fg);
  --muted: var(--t-muted);
  --card: var(--t-card);
  --card-fg: var(--t-fg);
  --line: var(--t-line);
  --shadow: var(--t-shadow);
  position: absolute;
  inset: 0;
  display: flex;
  flex-direction: column;
  justify-content: center;
  padding: 24px max(var(--gutter), calc((100% - var(--maxw)) / 2 + var(--gutter)));
  background: var(--bg);
  color: var(--fg);
  visibility: hidden;
  overflow: hidden;
}

.intro-scene.is-past {
  visibility: visible;
  z-index: 1;
}

.intro-scene.is-active {
  visibility: visible;
  z-index: 2;
  animation: intro-wipe-scene 0.65s cubic-bezier(0.75, 0, 0.2, 1) both;
}

.scene-hello {
  --bg: var(--c-cream);
  --accent-text: var(--a-cream);
  background-image:
    linear-gradient(var(--t-grid) 1px, transparent 1px),
    linear-gradient(90deg, var(--t-grid) 1px, transparent 1px);
  background-size: 44px 44px;
}

.scene-hello.is-active {
  animation: none;
}

.scene-place {
  --bg: var(--c-mint);
  --accent-text: var(--a-mint);
}

.scene-work {
  --bg: var(--c-butter);
  --accent-text: var(--a-butter);
}

/* Scene content. --d offsets each scene's choreography past its wipe. */

.intro-scene {
  --d: 0.45s;
}

.scene-hello {
  --d: 0.5s;
}

.intro-label {
  position: relative;
  align-self: flex-start;
  font-family: var(--font-mono);
  font-size: clamp(0.72rem, 1.4vw, 0.9rem);
  font-weight: 700;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--accent-text);
}

.intro-title {
  margin-top: clamp(12px, 2.5vh, 24px);
  font-weight: 900;
  font-size: clamp(2.6rem, 11vw, 9rem);
  line-height: 0.92;
  letter-spacing: -0.03em;
  text-transform: uppercase;
}

.intro-title-sm {
  font-size: clamp(2.3rem, 8vw, 6.4rem);
}

.intro-line {
  display: block;
  overflow: hidden;
  padding: 0.04em 0 0.06em;
}

.intro-line > span {
  display: inline-block;
}

.intro-name,
.scene-work .intro-line:last-child {
  overflow: visible;
  padding-top: 0.1em;
}

.intro-scene .mark {
  border-width: 4px;
  box-shadow: 8px 8px 0 var(--butter);
}

.scene-work .mark {
  box-shadow: 8px 8px 0 var(--shadow);
}

.intro-outline {
  color: transparent;
  -webkit-text-stroke: clamp(2px, 0.3vw, 4px) var(--fg);
  text-shadow: 0.06em 0.06em 0 var(--accent-text);
}

.intro-sub {
  margin-top: clamp(20px, 4vh, 36px);
  font-family: var(--font-mono);
  font-size: clamp(0.78rem, 1.6vw, 1rem);
  color: var(--muted);
}

.intro-scene.is-active .intro-label {
  animation: intro-type 0.7s steps(30, end) var(--d) both;
}

.intro-scene.is-active .intro-line > span {
  animation: intro-rise 0.75s cubic-bezier(0.16, 1, 0.3, 1) calc(var(--d) + 0.25s) both;
}

.intro-scene.is-active .intro-line + .intro-line > span {
  animation-delay: calc(var(--d) + 0.4s);
}

.intro-scene.is-active .intro-line > span.mark {
  animation: intro-pop 0.7s cubic-bezier(0.34, 1.56, 0.64, 1) calc(var(--d) + 0.55s) both;
}

.intro-scene.is-active .intro-sub {
  animation: intro-fade-up 0.6s ease calc(var(--d) + 1.05s) both;
}

/* Scene 2: Bogor is Kota Hujan, the city of rain */

.intro-rain {
  position: absolute;
  inset: 0;
  pointer-events: none;
}

.intro-rain i {
  position: absolute;
  top: 0;
  width: 3px;
  height: 11vh;
  border-radius: 3px;
  background: var(--fg);
  opacity: 0.14;
  animation: intro-rain 1.1s linear infinite;
}

.intro-rain i:nth-child(1) { left: 6%; animation-delay: -0.2s; }
.intro-rain i:nth-child(2) { left: 14%; animation-delay: -0.7s; animation-duration: 0.9s; }
.intro-rain i:nth-child(3) { left: 23%; animation-delay: -0.4s; }
.intro-rain i:nth-child(4) { left: 31%; animation-delay: -1s; animation-duration: 1.3s; }
.intro-rain i:nth-child(5) { left: 40%; animation-delay: -0.1s; }
.intro-rain i:nth-child(6) { left: 49%; animation-delay: -0.6s; animation-duration: 1s; }
.intro-rain i:nth-child(7) { left: 57%; animation-delay: -0.9s; }
.intro-rain i:nth-child(8) { left: 66%; animation-delay: -0.3s; animation-duration: 1.2s; }
.intro-rain i:nth-child(9) { left: 74%; animation-delay: -0.8s; }
.intro-rain i:nth-child(10) { left: 83%; animation-delay: -0.5s; animation-duration: 0.95s; }
.intro-rain i:nth-child(11) { left: 91%; animation-delay: -1.05s; }
.intro-rain i:nth-child(12) { left: 99%; animation-delay: -0.35s; animation-duration: 1.15s; }

/* Scene 1: a photo window slides in beside the name */
.intro-photo {
  position: absolute;
  top: 50%;
  right: max(var(--gutter), calc((100% - var(--maxw)) / 2 + var(--gutter)));
  width: clamp(220px, 26vw, 340px);
  margin-top: calc(clamp(220px, 26vw, 340px) / -2);
  transform: rotate(3deg);
}

.intro-photo .window-bar {
  min-height: 32px;
}

.intro-photo img {
  display: block;
  width: 100%;
  height: auto;
  aspect-ratio: 1;
  object-fit: cover;
}

.intro-scene.is-active .intro-photo {
  animation: intro-card-slide 0.7s cubic-bezier(0.34, 1.56, 0.64, 1) calc(var(--d) + 0.85s) both;
}

/* Too narrow to sit beside the name: drop it under the text */
@media (max-width: 900px) {
  .intro-photo {
    position: relative;
    top: auto;
    right: auto;
    align-self: flex-start;
    width: clamp(150px, 34vw, 220px);
    margin-top: 28px;
  }
}

.intro-postcard {
  position: absolute;
  right: max(var(--gutter), calc((100% - var(--maxw)) / 2 + var(--gutter)));
  bottom: 12%;
  width: min(340px, 80vw);
  transform: rotate(3deg);
}

.intro-postcard .window-bar {
  min-height: 32px;
}

.intro-postcard-body {
  display: flex;
  align-items: center;
  gap: 14px;
  padding: 16px 18px;
}

.intro-postcard-body strong {
  display: block;
  font-family: var(--font-head);
  font-weight: 800;
  font-size: 1.05rem;
}

.intro-postcard-body span span {
  display: block;
  font-size: 0.82rem;
  color: var(--muted);
}

.intro-pin {
  flex-shrink: 0;
  width: 26px;
  height: 26px;
  border: 3px solid var(--line);
  border-radius: 50% 50% 50% 0;
  background: var(--amethyst);
  transform: rotate(-45deg);
}

.intro-scene.is-active .intro-postcard {
  animation: intro-card-slide 0.7s cubic-bezier(0.34, 1.56, 0.64, 1) calc(var(--d) + 0.85s) both;
}

.intro-scene.is-active .intro-pin {
  animation: intro-pin-drop 0.6s cubic-bezier(0.34, 1.56, 0.64, 1) calc(var(--d) + 1.25s) both;
}

/* Scene 3: the four projects deal onto the table */

.intro-deck {
  display: flex;
  gap: clamp(12px, 2vw, 24px);
  margin-top: clamp(24px, 5vh, 48px);
}

.intro-card {
  --r: 0deg;
  flex: 1;
  max-width: 230px;
  box-shadow: 5px 5px 0 var(--shadow);
  transform: rotate(var(--r));
}

.intro-card:nth-child(1) { --r: -4deg; }
.intro-card:nth-child(2) { --r: 2deg; }
.intro-card:nth-child(3) { --r: -2deg; }
.intro-card:nth-child(4) { --r: 4deg; }

.intro-card .window-bar {
  min-height: 24px;
  padding: 0 8px;
}

.intro-card .dots {
  gap: 4px;
}

.intro-card .dots i {
  width: 8px;
  height: 8px;
  border-width: 1.5px;
}

.intro-card-face {
  display: flex;
  flex-direction: column;
  justify-content: flex-end;
  height: clamp(76px, 15vh, 140px);
  padding: 12px 14px;
  color: var(--ink);
}

.intro-card-face.v-amethyst {
  color: var(--cream);
}

.intro-card-face span {
  font-family: var(--font-head);
  font-weight: 900;
  font-size: clamp(0.9rem, 1.8vw, 1.25rem);
  line-height: 1;
  text-transform: uppercase;
}

.intro-card-face small {
  margin-top: 4px;
  font-family: var(--font-mono);
  font-size: 0.64rem;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  opacity: 0.8;
}

.intro-scene.is-active .intro-card {
  animation: intro-deal 0.65s cubic-bezier(0.34, 1.4, 0.64, 1) calc(var(--d) + 0.9s) both;
}

.intro-scene.is-active .intro-card:nth-child(2) { animation-delay: calc(var(--d) + 1.02s); }
.intro-scene.is-active .intro-card:nth-child(3) { animation-delay: calc(var(--d) + 1.14s); }
.intro-scene.is-active .intro-card:nth-child(4) { animation-delay: calc(var(--d) + 1.26s); }

/* The last scene holds: a slow camera push while the closing line lands */

.scene-work.is-active {
  animation:
    intro-wipe-scene 0.65s cubic-bezier(0.75, 0, 0.2, 1) both,
    intro-push 4.1s cubic-bezier(0.3, 0, 0.6, 1) both;
}

.scene-work.is-active .intro-sub {
  animation-delay: calc(var(--d) + 2s);
}

.intro-arrow {
  display: inline-block;
  color: var(--accent-text);
  font-weight: 700;
}

.intro-scene.is-active .intro-arrow {
  animation: intro-bob 0.9s ease-in-out calc(var(--d) + 2.4s) infinite;
}

/* Exit: four colour columns sweep up over the film, then off the top of the page */

.intro-wipe {
  position: absolute;
  inset: 0;
  display: flex;
  pointer-events: none;
}

.intro-wipe i {
  --i: 0;
  flex: 1;
  transform: translateY(101%);
}

.intro-wipe i:nth-child(1) { background: var(--amethyst); }
.intro-wipe i:nth-child(2) { --i: 1; background: var(--lavender); }
.intro-wipe i:nth-child(3) { --i: 2; background: var(--mint); }
.intro-wipe i:nth-child(4) { --i: 3; background: var(--butter); }

.intro.is-covering .intro-wipe i {
  animation: intro-cover 0.6s cubic-bezier(0.7, 0, 0.3, 1) calc(var(--i) * 90ms) forwards;
}

.intro.is-revealing {
  background: transparent;
}

/* display, not visibility: the active scene sets visibility: visible and would show through */
.intro.is-revealing > :not(.intro-wipe) {
  display: none;
}

.intro.is-revealing .intro-wipe i {
  transform: none;
  animation: intro-uncover 0.6s cubic-bezier(0.7, 0, 0.3, 1) calc(var(--i) * 90ms) forwards;
}

/* Once the curtain lifts, the hero assembles itself */

.intro-reveal .hero-copy > * {
  animation: intro-fade-up 0.7s cubic-bezier(0.16, 1, 0.3, 1) both;
}

.intro-reveal .hero-copy > :nth-child(2) { animation-delay: 0.08s; }
.intro-reveal .hero-copy > :nth-child(3) { animation-delay: 0.16s; }
.intro-reveal .hero-copy > :nth-child(4) { animation-delay: 0.24s; }
.intro-reveal .hero-copy > :nth-child(5) { animation-delay: 0.32s; }

.intro-reveal .hero-visual {
  animation: intro-card-slide 0.8s cubic-bezier(0.34, 1.3, 0.64, 1) 0.2s both;
}

@media (max-width: 700px) {
  .intro-top > :first-child {
    display: none;
  }

  .intro-top {
    justify-content: flex-end;
  }

  .intro-postcard {
    position: relative;
    right: auto;
    bottom: auto;
    align-self: flex-start;
    margin-top: 32px;
  }

  .intro-deck {
    display: grid;
    grid-template-columns: repeat(2, minmax(0, 1fr));
    max-width: 420px;
  }

  .intro-card {
    max-width: none;
  }
}

/* ============ Loader and start screen ============ */

/* Sits over the intro on the first visit of a session: a loader, then a
   spotlight that swings onto the Start button. */

.intro-gate .gate {
  display: grid;
}

.gate {
  --beam: 255, 240, 205;
  position: fixed;
  inset: 0;
  z-index: 1001;
  place-items: center;
  overflow: hidden;
  background: var(--ink);
  color: var(--cream);
  font-family: var(--font-mono);
}

.gate-loader,
.gate-stage {
  grid-area: 1 / 1;
}

.gate-label {
  margin: 0;
  font-size: 0.72rem;
  font-weight: 700;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--lavender);
}

/* Loader */

.gate-loader {
  display: grid;
  justify-items: center;
  gap: 16px;
  width: min(360px, calc(100% - 2 * var(--gutter)));
  transition: opacity 0.3s ease, transform 0.4s cubic-bezier(0.7, 0, 0.3, 1), visibility 0s linear 0.4s;
}

.gate-loader .gate-label::after {
  content: '_';
  animation: gate-blink 1s steps(1) infinite;
}

.gate-count {
  margin: 0;
  font-family: var(--font-head);
  font-size: clamp(4.5rem, 16vw, 7.5rem);
  font-weight: 900;
  line-height: 0.9;
  font-variant-numeric: tabular-nums;
  color: transparent;
  -webkit-text-stroke: 2px var(--cream);
  text-shadow: 0.05em 0.05em 0 var(--amethyst);
}

.gate-bar {
  width: 100%;
  height: 24px;
  padding: 3px;
  border: var(--bw) solid var(--cream);
  border-radius: 4px;
  box-shadow: 4px 4px 0 var(--amethyst);
}

.gate-bar i {
  display: block;
  height: 100%;
  background: repeating-linear-gradient(90deg, var(--butter) 0 12px, transparent 12px 16px);
  clip-path: inset(0 calc((1 - var(--p, 0)) * 100%) 0 0);
}

.gate.is-lit .gate-loader {
  opacity: 0;
  transform: translateY(24px);
  visibility: hidden;
}

/* The spotlight hangs from the top edge and pivots on its lamp */

.gate-light {
  position: absolute;
  top: 0;
  left: 50%;
  width: 0;
  height: 100%;
  transform-origin: 50% 0;
  opacity: 0;
  pointer-events: none;
}

.gate-lamp {
  position: absolute;
  top: -6px;
  left: -34px;
  z-index: 1;
  width: 68px;
  height: 40px;
  border: 3px solid var(--cream);
  border-top: 0;
  border-radius: 0 0 16px 16px;
  background: #2b2138;
  box-shadow: 4px 4px 0 var(--amethyst);
}

.gate-lamp::after {
  content: '';
  position: absolute;
  right: 8px;
  bottom: -4px;
  left: 8px;
  height: 5px;
  border-radius: 3px;
  background: rgb(var(--beam));
  box-shadow: 0 0 22px 6px rgba(var(--beam), 0.65);
}

.gate-beam {
  position: absolute;
  top: 0;
  left: -75vh;
  width: 150vh;
  height: 74vh;
  background: conic-gradient(
    from 162deg at 50% 0,
    transparent 0deg,
    rgba(var(--beam), 0.3) 10deg,
    rgba(var(--beam), 0.3) 26deg,
    transparent 36deg
  );
  -webkit-mask-image: linear-gradient(to bottom, #000 35%, transparent);
  mask-image: linear-gradient(to bottom, #000 35%, transparent);
}

/* Dust drifting through the light */
.gate-beam i {
  position: absolute;
  top: var(--y);
  left: calc(50% + var(--x));
  width: 3px;
  height: 3px;
  border-radius: 50%;
  background: rgb(var(--beam));
  opacity: 0.25;
  animation: gate-dust var(--t, 5s) ease-in-out var(--dl, 0s) infinite alternate;
}

.gate-beam i:nth-child(1) { --x: -4vh; --y: 18vh; --t: 5s; }
.gate-beam i:nth-child(2) { --x: 3vh; --y: 26vh; --t: 6.5s; --dl: -2s; }
.gate-beam i:nth-child(3) { --x: -9vh; --y: 38vh; --t: 5.5s; --dl: -1s; }
.gate-beam i:nth-child(4) { --x: 8vh; --y: 34vh; --t: 7s; --dl: -3s; }
.gate-beam i:nth-child(5) { --x: -1vh; --y: 46vh; --t: 6s; --dl: -4s; }
.gate-beam i:nth-child(6) { --x: 12vh; --y: 50vh; --t: 5s; --dl: -2.5s; }
.gate-beam i:nth-child(7) { --x: -13vh; --y: 54vh; --t: 6.8s; --dl: -1.5s; }
.gate-beam i:nth-child(8) { --x: 5vh; --y: 12vh; --t: 7.5s; --dl: -3.5s; }

/* Start button, standing in the pool of light */

.gate-stage {
  display: grid;
  justify-items: center;
  padding: 0 var(--gutter);
  text-align: center;
  visibility: hidden;
  opacity: 0;
}

.gate-spot {
  position: relative;
  z-index: 0;
  margin: 22px 0 44px;
}

.gate-pool {
  position: absolute;
  bottom: -52px;
  left: 50%;
  z-index: -1;
  width: 460px;
  height: 110px;
  margin-left: -230px;
  background: radial-gradient(closest-side, rgba(var(--beam), 0.34), transparent);
  opacity: 0;
}

.gate-start {
  display: inline-flex;
  align-items: center;
  gap: 14px;
  padding: 16px 38px 16px 30px;
  border: 3px solid var(--cream);
  border-radius: 8px;
  background: var(--butter);
  color: var(--ink);
  box-shadow: 6px 6px 0 var(--amethyst);
  font-family: var(--font-head);
  font-size: clamp(1.9rem, 7vw, 2.7rem);
  font-weight: 900;
  letter-spacing: 0.03em;
  text-transform: uppercase;
  cursor: pointer;
  filter: brightness(0.3);
  transition: transform 0.12s ease, box-shadow 0.12s ease;
}

.gate-start svg {
  width: 0.75em;
  height: 0.75em;
  fill: currentColor;
}

.gate-start:hover {
  transform: translate(-2px, -2px);
  box-shadow: 8px 8px 0 var(--amethyst);
}

.gate-start:active {
  transform: translate(4px, 4px);
  box-shadow: 2px 2px 0 var(--amethyst);
}

.gate-hint {
  margin: 0;
  font-size: 0.7rem;
  font-weight: 700;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: rgba(255, 248, 236, 0.7);
}

.gate-quiet {
  margin-top: 10px;
  padding: 4px;
  border: 0;
  background: none;
  color: var(--lavender);
  font: inherit;
  font-size: 0.72rem;
  text-decoration: underline;
  text-underline-offset: 3px;
  cursor: pointer;
}

.gate-quiet:hover {
  color: var(--cream);
}

/* Lit: the lamp stutters on pointing off to the side, swings onto the
   button and lights it up, then keeps a slow sway. */

.gate.is-lit .gate-light {
  opacity: 1;
  animation:
    gate-sweep 1.4s cubic-bezier(0.45, 0, 0.25, 1) 0.6s both,
    gate-sway 7s ease-in-out 2s infinite;
}

.gate.is-lit .gate-beam,
.gate.is-lit .gate-lamp::after {
  animation: gate-flicker 0.7s linear both;
}

.gate.is-lit .gate-stage {
  visibility: visible;
  opacity: 1;
  transition: opacity 0.3s ease 0.3s;
}

.gate.is-lit .gate-pool {
  animation: gate-pool-on 0.35s ease 1.5s both;
}

.gate.is-lit .gate-start {
  animation: gate-lit 0.35s ease 1.5s both;
}

.gate.is-lit .gate-stage .gate-label,
.gate.is-lit .gate-hint,
.gate.is-lit .gate-quiet {
  animation: intro-fade-up 0.5s ease 1.75s both;
}

/* Leaving: the light cuts out with a clunk and the stage goes dark */

.gate.is-leaving .gate-light,
.gate.is-leaving .gate-pool {
  animation: none;
  opacity: 0;
}

.gate.is-leaving .gate-start {
  animation: none;
  filter: brightness(0.3);
}

.gate.is-leaving .gate-stage {
  opacity: 0;
  transition: opacity 0.3s ease 0.15s;
}

@keyframes gate-blink {
  50% { opacity: 0; }
}

@keyframes gate-flicker {
  0% { opacity: 0; }
  12% { opacity: 1; }
  20% { opacity: 0.1; }
  32% { opacity: 1; }
  42% { opacity: 0.3; }
  52%, 100% { opacity: 1; }
}

@keyframes gate-sweep {
  from { rotate: -32deg; }
  60% { rotate: 7deg; }
  82% { rotate: -2deg; }
  to { rotate: 0deg; }
}

@keyframes gate-sway {
  0%, 100% { rotate: 0deg; }
  25% { rotate: 1.5deg; }
  75% { rotate: -1.5deg; }
}

@keyframes gate-pool-on {
  from { opacity: 0; }
  to { opacity: 1; }
}

@keyframes gate-lit {
  from { filter: brightness(0.3); }
  to { filter: none; }
}

@keyframes gate-dust {
  from { opacity: 0.15; translate: 0 0; }
  to { opacity: 0.9; translate: 10px -22px; }
}

/* ============ Doodles ============ */

/* Hand-drawn arrows with a scribbled label: either side of Start, and the
   pointers that show once the intro ends. Each arrow draws itself in, then
   keeps nudging toward what it points at. */

.doodle {
  --dw: 52px;
  position: absolute;
  display: flex;
  flex-direction: column;
  color: var(--doodle);
  pointer-events: none;
}

.doodle[hidden] {
  display: none;
}

.doodle svg {
  width: var(--dw);
  height: auto;
  overflow: visible;
  fill: none;
  stroke: currentColor;
  stroke-width: 3;
  stroke-linecap: round;
  stroke-linejoin: round;
}

.doodle path {
  stroke-dasharray: 1;
  stroke-dashoffset: 1;
}

.doodle-text {
  font: 700 clamp(1.15rem, 4.4vw, 1.45rem)/1.05 var(--font-hand);
  white-space: nowrap;
}

/* Start screen: "press" on the left, "me!" on the right */

.gate-doodle {
  --doodle: var(--butter);
  --dw: clamp(44px, 12vw, 70px);
}

.gate-doodle .doodle-text {
  color: var(--cream);
  opacity: 0;
}

.gate-doodle-left {
  right: calc(100% + 6px);
  bottom: 32%;
  align-items: flex-start;
  --nx: 5px;
}

.gate-doodle-left .doodle-text {
  rotate: -8deg;
}

.gate-doodle-right {
  left: calc(100% + 12px); /* clear of the button's shadow */
  top: 36%;
  align-items: flex-end;
  --nx: -5px;
}

.gate-doodle-right svg {
  scale: -1 -1;
}

.gate-doodle-right .doodle-text {
  rotate: 6deg;
}

.gate.is-lit .gate-doodle path {
  animation: doodle-draw 0.45s ease-out 2s forwards;
}

.gate.is-lit .gate-doodle path + path {
  animation-duration: 0.2s;
  animation-delay: 2.4s;
}

.gate.is-lit .gate-doodle .doodle-text {
  animation: doodle-pop 0.35s cubic-bezier(0.34, 1.6, 0.64, 1) 2.5s both;
}

.gate.is-lit .gate-doodle svg {
  animation: doodle-nudge 0.9s ease-in-out 2.8s infinite alternate;
}

/* After the intro: a label on a scrap of paper and an arrow at the target */

.tips {
  position: fixed;
  inset: 0;
  z-index: 60;
  opacity: 0;
  pointer-events: none;
  transition: opacity 0.3s ease;
}

.tips.is-on {
  opacity: 1;
}

.tip {
  position: fixed;
}

.tip .doodle-text {
  max-width: min(260px, 72vw);
  padding: 4px 9px 5px;
  border: 2px solid var(--ink);
  border-radius: 4px;
  background: var(--butter);
  color: var(--ink);
  box-shadow: 3px 3px 0 var(--t-shadow);
  white-space: normal;
  text-wrap: balance;
}

/* Hangs under the cog, arrow curling up into it */
.tip-settings {
  top: var(--y);
  right: var(--r);
  align-items: flex-end;
  --nx: 2px;
  --ny: -3px;
}

.tip-settings .doodle-text {
  max-width: min(260px, var(--mw));
  margin-right: 10px;
  text-align: right;
  rotate: 3deg;
}

/* Sits over the player, arrow curling down onto the play button */
.tip-music {
  top: var(--y);
  left: var(--x);
  translate: 0 -100%;
  align-items: flex-start;
  --nx: -2px;
  --ny: 3px;
}

.tip-music .doodle-text {
  margin-left: 30px;
  rotate: -3deg;
}

.tips.is-on .tip path {
  animation: doodle-draw 0.4s ease-out 0.15s forwards;
}

.tips.is-on .tip path + path {
  animation-duration: 0.2s;
  animation-delay: 0.5s;
}

.tips.is-on .tip .doodle-text {
  animation: doodle-pop 0.35s cubic-bezier(0.34, 1.6, 0.64, 1) both;
}

.tips.is-on .tip svg {
  animation: doodle-nudge 0.8s ease-in-out 0.8s infinite alternate;
}

@keyframes doodle-draw {
  to { stroke-dashoffset: 0; }
}

@keyframes doodle-pop {
  from { opacity: 0; scale: 0.6; }
  to { opacity: 1; scale: 1; }
}

@keyframes doodle-nudge {
  to { translate: var(--nx, 0) var(--ny, 0); }
}

/* Footer replay */

.replay-intro,
.footer-sound {
  padding: 0;
  border: 0;
  background: none;
  color: var(--butter);
  font: inherit;
  letter-spacing: inherit;
  text-transform: inherit;
  text-decoration: underline;
  text-decoration-thickness: 1.5px;
  text-underline-offset: 3px;
  cursor: pointer;
}

.replay-intro:hover,
.footer-sound:hover {
  color: var(--cream);
}

@keyframes intro-open {
  to { clip-path: inset(0 0 0 0); }
}

@keyframes intro-wipe-scene {
  from { clip-path: inset(0 0 0 100%); }
  to { clip-path: inset(0 0 0 0); }
}

@keyframes intro-type {
  from { clip-path: inset(0 100% 0 0); }
  to { clip-path: inset(0 0 0 0); }
}

@keyframes intro-rise {
  from { transform: translateY(110%); }
  to { transform: none; }
}

@keyframes intro-pop {
  0% { opacity: 0; transform: scale(0.3) rotate(-14deg); }
  60% { opacity: 1; transform: scale(1.1) rotate(2deg); }
  100% { opacity: 1; transform: scale(1) rotate(-2deg); }
}

@keyframes intro-fade-up {
  from { opacity: 0; transform: translateY(16px); }
  to { opacity: 1; transform: none; }
}

@keyframes intro-rain {
  from { transform: translate(0, -15vh) rotate(15deg); }
  to { transform: translate(-32vh, 110vh) rotate(15deg); }
}

@keyframes intro-card-slide {
  from { opacity: 0; transform: translateX(80px) rotate(10deg); }
}

@keyframes intro-pin-drop {
  from { opacity: 0; transform: translateY(-30px) rotate(-45deg); }
}

@keyframes intro-deal {
  from { opacity: 0; transform: translateY(140%) rotate(calc(var(--r) * -4)); }
}

@keyframes intro-push {
  from { scale: 1; }
  to { scale: 1.05; }
}

@keyframes intro-bob {
  50% { transform: translateY(5px); }
}

@keyframes intro-cover {
  to { transform: none; }
}

@keyframes intro-uncover {
  to { transform: translateY(-101%); }
}

/* ---------- Scroll interludes ----------
   Decorative strips between sections. script.js sets --p from 0 (strip
   just entering the bottom of the viewport) to 1 (leaving the top), and
   each vehicle crosses the strip with it. */

.interlude {
  --p: 0.5;
  position: relative;
  height: clamp(150px, 16vw, 200px);
  overflow: hidden;
  background: var(--bg);
  background-image: var(--texture, none);
  background-size: var(--texture-size, auto);
  container-type: inline-size;
  pointer-events: none;
}

/* Easter eggs: the vehicles play a sound when clicked */
.interlude [data-egg] {
  pointer-events: auto;
  cursor: pointer;
}

/* Sea: lives inside the hero so the grid runs on, and its waves become the work band's top edge */

.band:has(> .interlude-sea) {
  padding-bottom: 0;
}

.band:has(> .interlude-sea) + .band {
  border-top: 0;
}

.interlude-sea {
  margin-top: clamp(40px, 6vw, 72px);
  background: none;
}

.sea-ship {
  position: absolute;
  left: 0;
  bottom: -2px;
  width: clamp(170px, 21vw, 250px);
  transform: translateX(calc(var(--p) * (100cqw + 100%) - 100%));
  will-change: transform;
}

.sea-bob {
  transform-origin: 50% 85%;
  animation: sea-bob 3.2s ease-in-out infinite;
}

.sea-ship svg {
  overflow: visible;
  stroke: var(--ink);
  stroke-width: 3.5;
  stroke-linejoin: round;
}

.ship-hull { fill: var(--amethyst); }
.ship-stripe { fill: var(--butter); stroke-width: 2.5; }
.ship-cabin,
.c-paper { fill: var(--paper); }
.ship-win { fill: var(--ink); stroke: none; }
.ship-funnel { fill: var(--amethyst-deep); }
.ship-funnel-band { fill: var(--butter); stroke: none; }
.c-butter { fill: var(--butter); }
.c-mint { fill: var(--mint); }
.c-lavender { fill: var(--lavender); }
.c-amethyst { fill: #a97fd8; }
.ship-ribs { fill: none; stroke-width: 2; opacity: 0.35; }

.ship-wake {
  fill: none;
  stroke: var(--shadow);
  stroke-width: 3;
  stroke-linecap: round;
  stroke-dasharray: 10 8;
  animation: sea-wake 0.8s linear infinite;
}

.ship-smoke circle {
  fill: var(--paper);
  stroke-width: 2.5;
  transform-box: fill-box;
  transform-origin: center;
  animation: sea-smoke 2.4s ease-out infinite both;
}

.ship-smoke circle:nth-child(2) { animation-delay: 0.8s; }
.ship-smoke circle:nth-child(3) { animation-delay: 1.6s; }

.sea-waves {
  --wave: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 80 30' preserveAspectRatio='none'%3E%3Cpath d='M0 10Q20 0 40 10T80 10V30H0Z'/%3E%3C/svg%3E");
  position: absolute;
  left: 0;
  right: -160px;
  bottom: 0;
  height: 32px;
  transform: translateX(calc(var(--p) * -160px));
}

.sea-waves::before,
.sea-waves::after {
  content: '';
  position: absolute;
  inset: 0;
  -webkit-mask: var(--wave) repeat-x 0 0 / 80px 100%;
  mask: var(--wave) repeat-x 0 0 / 80px 100%;
}

.sea-waves::before {
  background: var(--shadow);
}

.sea-waves::after {
  top: 3.5px;
  background: var(--c-lavender);
}

/* Sky: banner plane heading west, clouds drifting the other way */

.sky-cloud {
  position: absolute;
  width: clamp(80px, 10vw, 130px);
  fill: var(--card);
  stroke: var(--line);
  stroke-width: 3;
  stroke-linejoin: round;
  filter: drop-shadow(4px 4px 0 var(--shadow));
  transform: translateX(calc(var(--p) * var(--drift)));
}

.cloud-a { left: 8%; top: 14%; --drift: 90px; }
.cloud-b { left: 52%; top: 58%; --drift: 50px; width: clamp(60px, 7vw, 90px); }
.cloud-c { left: 80%; top: 8%; --drift: 120px; }

.sky-plane {
  position: absolute;
  left: 0;
  top: 50%;
  display: flex;
  align-items: center;
  margin-top: -28px;
  transform: translateX(calc((1 - var(--p)) * (100cqw + 100%) - 100%));
  will-change: transform;
}

.sky-plane svg {
  flex: none;
  width: clamp(88px, 9vw, 120px);
  overflow: visible;
  stroke: var(--ink);
  stroke-width: 3;
  stroke-linejoin: round;
  filter: drop-shadow(4px 4px 0 var(--shadow));
  animation: sky-bob 2.6s ease-in-out infinite;
}

.plane-body { fill: var(--butter); }
.plane-stripe { fill: none; stroke: var(--amethyst); stroke-width: 3.5; }
.plane-glass { fill: var(--lavender); }
.plane-wing { fill: var(--lavender); }
.plane-hub { fill: var(--ink); }

.plane-prop {
  fill: var(--ink);
  stroke: none;
  transform-box: fill-box;
  transform-origin: center;
  animation: sky-prop 0.14s linear infinite;
}

.sky-rope {
  flex: none;
  width: clamp(28px, 5vw, 64px);
  height: 2.5px;
  margin-left: -4px;
  background: var(--fg);
}

.sky-banner {
  flex: none;
  padding: 8px 14px;
  border: var(--bw) solid var(--ink);
  background: var(--paper);
  color: var(--ink);
  box-shadow: 4px 4px 0 var(--shadow);
  font-family: var(--font-mono);
  font-size: 0.8rem;
  font-weight: 700;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  white-space: nowrap;
  transform-origin: left center;
  animation: sky-flutter 1.1s ease-in-out infinite;
}

/* Mail: a paper plane loops along a path that fills in behind it */

.mail-trail {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
}

.mail-path {
  fill: none;
  stroke: var(--accent-text);
  stroke-width: 2.5;
  stroke-linecap: round;
  stroke-dasharray: 2 10;
}

.mail-reveal {
  fill: none;
  stroke: #fff;
  stroke-width: 12;
}

.mail-plane {
  position: absolute;
  left: 0;
  top: 0;
  width: 58px;
  margin: -19px 0 0 -29px;
  overflow: visible;
  stroke: var(--ink);
  stroke-width: 2.5;
  stroke-linejoin: round;
  filter: drop-shadow(3px 3px 0 var(--shadow));
}

.mail-wing { fill: var(--paper); }
.mail-keel { fill: var(--lavender); }

/* City: a salaryman walks in place mid-strip; the skyline and pavement
   slide past and his stride follows the scroll, so he only walks while you do */

.city-skyline {
  position: absolute;
  left: 0;
  bottom: 18px;
  width: calc(100% + 800px);
  max-width: none;
  height: 120px;
  transform: translateX(calc(var(--p) * -300px));
  will-change: transform;
}

.city-bldg {
  fill: var(--card);
  stroke: var(--ink);
  stroke-width: 3;
}

#city-windows rect { fill: var(--lavender); }
.city-win { fill: url(#city-windows); }
.city-fill { fill: url(#city-block); }

.pavement {
  position: absolute;
  left: 0;
  right: 0;
  bottom: 0;
  height: 18px;
  border-top: var(--bw) solid var(--ink);
  background: var(--card) repeating-linear-gradient(90deg, var(--ink) 0 2.5px, transparent 2.5px 44px);
  background-position: calc(var(--p) * -800px) 0;
}

.city-walker {
  --step: sin(var(--p) * 60rad);
  position: absolute;
  left: 50%;
  bottom: 12px;
  width: clamp(62px, 7vw, 80px);
  margin-left: calc(clamp(62px, 7vw, 80px) / -2);
}

.city-walker svg {
  overflow: visible;
  stroke: var(--ink);
  stroke-width: 2.5;
  stroke-linejoin: round;
  filter: drop-shadow(3px 3px 0 var(--shadow));
  translate: 0 calc(cos(var(--p) * 120rad) * -1.5px);
}

.sm-suit,
.sm-leg path:first-child,
.sm-arm > path:first-child { fill: var(--amethyst-deep); }
.sm-far { filter: brightness(0.7); }
.sm-skin,
.sm-shirt { fill: var(--paper); }
.sm-tie { fill: var(--butter); }
.sm-hair,
.sm-shoe,
.sm-eye { fill: var(--ink); }
.sm-eye { stroke: none; }
.sm-glasses,
.sm-case-handle { fill: none; stroke-width: 2; }
.sm-case { fill: var(--butter); }
.sm-case-line { fill: none; stroke-width: 1.5; }

.sm-leg { transform-origin: 40px 88px; }
.sm-arm { transform-origin: 40.5px 42px; }
.sm-leg.sm-near { transform: rotate(calc(var(--step) * 24deg)); }
.sm-leg.sm-far { transform: rotate(calc(var(--step) * -24deg)); }
.sm-arm.sm-near { transform: rotate(calc(var(--step) * -16deg)); }
.sm-arm.sm-far { transform: rotate(calc(var(--step) * 20deg)); }

/* Crane: trolley runs along the gantry, toolbox swinging below it */

.crane-beam {
  position: absolute;
  left: 0;
  right: 0;
  top: 18px;
  height: 16px;
  border-block: var(--bw) solid var(--ink);
  background: repeating-linear-gradient(-45deg, var(--ink) 0 6px, var(--paper) 6px 14px);
}

.crane-trolley {
  position: absolute;
  left: 0;
  top: 34px;
  width: clamp(110px, 12vw, 150px);
  transform: translateX(calc(var(--p) * (100cqw + 100%) - 100%));
  will-change: transform;
}

.crane-trolley::before {
  content: '';
  display: block;
  width: 40px;
  height: 14px;
  margin: 0 auto;
  border: var(--bw) solid var(--ink);
  border-top: 0;
  background: var(--amethyst-deep);
}

.crane-swing {
  transform-origin: 50% 0;
  animation: crane-swing 2.6s ease-in-out infinite;
}

.crane-cable {
  display: block;
  width: 2.5px;
  height: clamp(18px, 4vw, 44px);
  margin: 0 auto;
  background: var(--fg);
}

.crane-swing svg {
  overflow: visible;
  margin-top: -2px;
  stroke: var(--ink);
  stroke-width: 3;
  stroke-linejoin: round;
  filter: drop-shadow(4px 4px 0 var(--shadow));
}

.tb-ring,
.tb-sling,
.tb-handle,
.tb-lid { fill: none; }
.tb-ring,
.tb-sling { stroke: var(--fg); stroke-width: 2.5; }
.tb-handle { stroke-width: 5; }
.tb-pencil { fill: var(--butter); }
.tb-driver { stroke-width: 3.5; stroke-linecap: round; }
.tb-grip { fill: var(--mint); }
.tb-body { fill: var(--amethyst); }
.tb-latch { fill: var(--butter); }

@keyframes crane-swing {
  0%, 100% { transform: rotate(-4deg); }
  50% { transform: rotate(4deg); }
}

@keyframes sea-bob {
  0%, 100% { transform: translateY(0) rotate(-1.5deg); }
  50% { transform: translateY(3px) rotate(1.5deg); }
}

@keyframes sea-wake {
  to { stroke-dashoffset: 18; }
}

@keyframes sea-smoke {
  from { opacity: 1; transform: none; }
  to { opacity: 0; transform: translate(-34px, -16px) scale(1.8); }
}

@keyframes sky-bob {
  50% { transform: translateY(-5px) rotate(-2deg); }
}

@keyframes sky-prop {
  50% { transform: scaleY(0.15); }
}

@keyframes sky-flutter {
  50% { transform: skewY(-2deg) translateY(1px); }
}

/* ---------- Responsive ---------- */

/* Laptop widths: make room for the nav, the logo and every header button,
   in the longer translations too */
@media (max-width: 1320px) {
  .header-inner {
    gap: 16px;
  }

  .nav {
    gap: 2px;
  }

  .nav a {
    padding: 8px;
  }

  .nav a::after {
    left: 8px;
    right: 8px;
  }
}

@media (max-width: 1200px) {
  .cv-long {
    display: none;
  }

  /* The language code alone says enough here */
  .lang-toggle {
    justify-content: center;
    width: 40px;
    padding: 0;
  }

  .lang-toggle svg {
    display: none;
  }
}

@media (max-width: 1040px) {
  .header-inner {
    gap: 12px;
  }

  .nav a {
    padding: 8px 5px;
    font-size: 0.7rem;
    letter-spacing: 0.05em;
  }

  .nav a::after {
    left: 5px;
    right: 5px;
  }
}

@media (max-width: 960px) {
  .toolboxes {
    grid-template-columns: repeat(2, minmax(0, 1fr));
  }

  .nav {
    display: none;
  }

  .header-actions {
    margin-left: auto;
  }

  .hero-grid {
    grid-template-columns: 1fr;
  }

  .hero-visual {
    max-width: 560px;
  }

  .exp-grid {
    grid-template-columns: 1fr;
    max-width: 640px;
    margin: 0 auto;
  }

  .exp-edu {
    grid-row: auto;
  }
}

@media (max-width: 820px) {
  /* Too narrow for a fan: deal into a grid with a slight tilt instead */
  .capabilities {
    display: grid;
    grid-template-columns: repeat(2, minmax(0, 1fr));
    gap: 20px;
    padding: 0;
  }

  .capability {
    width: auto;
    min-height: 0;
    padding: 26px 24px 52px;
  }

  .capability + .capability {
    margin-left: 0;
  }

  .capability:nth-child(n) { --y: 0px; --r: 1.2deg; }
  .capability:nth-child(odd) { --r: -1.2deg; }

  .projects {
    grid-template-columns: 1fr;
  }

  .footer-grid {
    grid-template-columns: 1fr 1fr;
  }

  .footer-brand {
    grid-column: 1 / -1;
  }
}

@media (max-width: 560px) {
  body {
    font-size: 16px;
  }

  .case-grid,
  .case-shots {
    grid-template-columns: 1fr;
  }

  /* Capability cards stay two up, shrunk to fit */
  .capabilities {
    gap: 14px;
  }

  .capability {
    padding: 18px 14px 40px;
  }

  .capability .icon-box {
    width: 42px;
    height: 42px;
  }

  .capability .icon-box svg {
    width: 20px;
    height: 20px;
  }

  .capability h3 {
    margin-top: 14px;
    font-size: 0.92rem;
  }

  .capability p {
    margin-top: 8px;
    font-size: 0.82rem;
    line-height: 1.5;
  }

  .capability::after {
    right: 14px;
    bottom: 12px;
    font-size: 0.7rem;
  }

  .hero-facts {
    grid-template-columns: 1fr 1fr;
  }

  .hero-facts div:last-child {
    grid-column: 1 / -1;
  }

  .hero-visual {
    padding: 50px 8px 56px 0;
  }

  .float-live {
    left: 0;
  }

  .more-list li {
    grid-template-columns: 1fr;
    padding: 18px;
  }

  .more-meta {
    grid-row: 1;
    text-align: left;
  }

  .more-meta br {
    display: none;
  }

  .more-sep {
    display: inline;
  }

  /* Social buttons as an even 2 × 2 grid */
  .socials {
    display: grid;
    grid-template-columns: repeat(2, minmax(0, 1fr));
    gap: 14px;
    width: 100%;
    max-width: 360px;
  }

  .socials .hc {
    display: block;
  }

  .socials .social {
    display: flex;
    justify-content: center;
  }

  .tbox-open-head {
    grid-template-columns: minmax(0, 1fr) auto;
    padding-bottom: 14px;
  }

  /* Open toolbox: tools two up so the box stays about one screen tall */
  .tbox-tray {
    grid-template-columns: repeat(2, minmax(0, 1fr));
    gap: 10px;
    margin: 0 10px 10px;
    padding: 12px;
  }

  .tbox-tray li {
    grid-template-columns: minmax(0, 1fr);
    padding: 10px;
    box-shadow: 3px 3px 0 var(--ink);
  }

  /* An odd one out takes the full last row */
  .tbox-tray li:last-child:nth-child(odd) {
    grid-column: 1 / -1;
  }

  /* One column: the number sits on its own row above the logos */
  .tbox-tray li > *,
  .tbox-tray li::before,
  .tbox-tray li:has(.tool-logos)::before {
    grid-column: 1;
    grid-row: auto;
  }

  .tbox-tray li::before {
    padding: 0 0 4px;
    font-size: 0.66rem;
  }

  .tbox-tray strong {
    font-size: 0.8rem;
    overflow-wrap: break-word;
  }

  .tbox-tray span {
    font-size: 0.66rem;
  }

  .tbox-open-head > .icon-box {
    display: none;
  }

  .cv-long {
    display: none;
  }

  /* Room for logo, theme, CV and contact on one line */
  .header-inner {
    gap: 12px;
  }

  .header-actions {
    gap: 8px;
  }

  .theme-toggle,
  .sound-toggle {
    width: 36px;
    height: 36px;
  }

  .lang-toggle {
    width: 36px;
    height: 36px;
  }

  .header-actions .btn svg {
    flex-shrink: 0;
  }

  .header-actions .btn-accent svg {
    display: none;
  }

  .header-actions .btn {
    padding: 0 12px;
  }
}

/* The smallest phones (320–380px): two capability cards side by side leave
   about 105px for a title, so stack them; pull the greeting bubbles in so the
   last one stays on screen. */
@media (max-width: 380px) {
  .capabilities {
    grid-template-columns: 1fr;
  }

  .lang-bubbles {
    right: 12px;
  }

  .lang-bubble {
    padding: 6px 10px 7px;
    font-size: 0.85rem;
  }

  .lb-en { --x: 78px; }
  .lb-ja { --x: 152px; }
}

/* Touch screens have no Esc key, and the progress bar needs the room */
@media (hover: none) {
  .intro-skip kbd {
    display: none;
  }
}

/* Phones: language, theme and sound fold into the cog's dropdown */
@media (max-width: 560px) {
  /* The panel hangs from the header's right edge (the sticky header is its
     containing block), not from the cog, which sits left of CV and Contact. */
  .settings {
    display: block;
  }

  .settings-toggle {
    display: grid;
  }

  .settings-panel {
    display: none;
    position: absolute;
    top: calc(100% + 10px);
    right: var(--gutter);
    z-index: 60;
    width: 260px;
    max-width: calc(100vw - 2 * var(--gutter));
    border: var(--bw) solid var(--line);
    border-radius: var(--radius);
    background: var(--card);
    color: var(--card-fg);
    box-shadow: 6px 6px 0 var(--shadow);
    overflow: hidden;
    animation: lang-pop 0.18s cubic-bezier(0.16, 1, 0.3, 1);
  }

  .settings.is-open .settings-panel {
    display: block;
  }

  .settings .settings-bar {
    display: flex;
  }

  .settings-bar .window-title {
    font: 700 0.7rem var(--font-mono);
  }

  .settings-row {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 12px;
    padding: 10px 14px;
  }

  .settings-row + .settings-row {
    border-top: 2px dashed var(--t-soft-rule);
  }

  .settings-label {
    display: block;
    font: 700 0.7rem var(--font-mono);
    letter-spacing: 0.08em;
    text-transform: uppercase;
    color: var(--t-muted);
  }

  /* The language list opens inside the panel, under its row */
  .settings-lang {
    flex-wrap: wrap;
  }

  .settings-lang .lang-picker {
    display: contents;
  }

  .settings-lang .lang-menu {
    position: static;
    flex-basis: 100%;
    width: auto;
    max-width: none;
    border-width: 2px;
    border-radius: 6px;
    box-shadow: none;
    animation: none;
  }

  .settings-lang .lang-menu .window-bar {
    display: none;
  }

  .settings-lang .lang-list {
    max-height: min(40vh, 300px);
  }
}

@media (max-width: 400px) {
  .header-inner {
    gap: 8px;
  }

  .header-actions {
    gap: 6px;
  }

  .header-actions .btn {
    padding: 0 9px;
  }

  .header-actions .btn svg {
    display: none;
  }
}

/* Hover cards only make sense with a real pointer */
@media (hover: none) {
  .hc-card {
    display: none;
  }
}

@media (prefers-reduced-motion: reduce) {
  html {
    scroll-behavior: auto;
  }

  *,
  *::before,
  *::after {
    animation-duration: 0.01ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 0.01ms !important;
  }

  .js .reveal {
    opacity: 1;
    translate: none;
  }

  /* Keep the greetings on show, fanned out, instead of looping */
  .lang-bubble {
    animation: none;
    opacity: 1;
    translate: 0 -8px;
    rotate: var(--tilt);
  }
}
