/* ============================================================
   Home v8 — Forest primary × Ink Blue handwriting × Aubergine
   Cream canvas / cream cards / dark navy mockups / forest CTAs
   ============================================================ */

:root {
  /* Brand — Forest (primary) */
  --primary:        #1F4D3A;
  --primary-active: #143426;
  --primary-soft:   #B4CDC0;
  --primary-tint:   rgba(31,77,58,0.08);

  /* Handwriting — Ink Blue */
  --ink-blue:       #1A3550;
  --ink-blue-soft:  #4A6B8C;

  /* Ink */
  --ink:            #141413;
  --body-strong:    #252523;
  --body:           #3d3d3a;
  --muted:          #6c6a64;
  --muted-soft:     #8e8b82;

  /* Surface */
  --canvas:               #fcfcfb;
  --surface-soft:         #f4f3ee;
  --surface-card:         #ece9e0;
  --surface-cream-strong: #e2dccf;
  --hairline:             #e7e3da;
  --hairline-soft:        #eeebe3;

  /* Dark */
  --surface-dark:          #181715;
  --surface-dark-elevated: #252320;
  --surface-dark-soft:     #1f1e1b;
  --hairline-dark:         #2f2d29;
  --on-dark:               #faf9f5;
  --on-dark-soft:          #a09d96;
  --on-dark-faint:         #6c6a64;
  --on-primary:            #ffffff;

  --r-sm: 6px;
  --r-md: 8px;
  --r-lg: 12px;
  --r-xl: 16px;
  --r-pill: 9999px;

  --pad-page: 24px;
  --max-w: 1320px;
}
@media (min-width: 768px)  { :root { --pad-page: 48px; } }
@media (min-width: 1200px) { :root { --pad-page: 72px; } }

*, *::before, *::after { box-sizing: border-box; }
html { -webkit-text-size-adjust: 100%; }
body {
  margin: 0;
  background: var(--canvas);
  color: var(--body);
  font-family: var(--font-body);
  font-weight: 400;
  font-size: 16px;
  line-height: 1.55;
  -webkit-font-smoothing: antialiased;
  text-rendering: optimizeLegibility;
}
::selection { background: var(--primary-soft); color: var(--ink); }
a { color: inherit; text-decoration: none; transition: color .15s ease, background .15s ease; }
p { margin: 0; text-wrap: pretty; }
h1, h2, h3, h4 { margin: 0; font-weight: 400; color: var(--ink); }

/* Italic emphasis inherits the parent's font; weight bumped for contrast. */
em {
  font-family: inherit;
  font-style: italic;
  font-weight: 500;
}

/* ============================================================
   Buttons
   ============================================================ */

.btn {
  display: inline-flex;
  align-items: center;
  gap: 12px;
  padding: 17px 28px;
  font-family: 'JetBrains Mono', ui-monospace, monospace;
  font-size: 14px;
  font-weight: 600;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  line-height: 1;
  border-radius: 2px;
  border: 1.5px solid var(--ink);
  cursor: pointer;
  transition: transform .16s ease, box-shadow .16s ease, background .16s ease, color .16s ease;
  white-space: nowrap;
}

/* Solid green button for LIGHT backgrounds — hard ink offset shadow */
.btn--primary {
  background: var(--primary);
  color: #fff;
  border-color: var(--ink);
  box-shadow: 7px 7px 0 var(--ink);
}
.btn--primary:hover {
  background: var(--primary-active);
  transform: translate(-4px, -4px);
  box-shadow: 11px 11px 0 var(--ink);
}

/* Secondary (light bg) — same brutalist frame, cream fill */
.btn--secondary {
  background: var(--canvas);
  color: var(--ink);
  border-color: var(--ink);
  box-shadow: 7px 7px 0 var(--ink);
}
.btn--secondary:hover {
  background: var(--surface-card);
  transform: translate(-4px, -4px);
  box-shadow: 11px 11px 0 var(--ink);
}

/* Text link (light bg) — underlined, no box */
.btn--text {
  background: transparent;
  color: var(--ink);
  border: 0;
  border-radius: 0;
  padding: 16px 2px;
  box-shadow: none;
  text-decoration: underline;
  text-decoration-thickness: 2px;
  text-underline-offset: 7px;
  text-decoration-color: var(--ink);
}
.btn--text:hover {
  color: var(--primary);
  text-decoration-color: var(--primary);
}

/* ============================================================
   HERO — cream, 6/6 split, VT323 pixel display
   ============================================================ */

/* Bottom padding lighter than top — pairs with #work's reduced top
   padding to let the first card peek below the fold. */
.hero { padding: 60px 0 32px; }
.hero__inner {
  max-width: var(--max-w);
  margin: 0 auto;
  padding: 0 var(--pad-page);
  display: grid;
  grid-template-columns: 1.15fr 1fr;
  gap: 32px;
  align-items: center;
}
@media (max-width: 980px) {
  .hero { padding: 56px 0 64px; }
  .hero__inner { grid-template-columns: 1fr; gap: 48px; }
  .hero__right { justify-content: center; }
  .portrait { max-width: 300px; }
}

.hero__display {
  font-size: clamp(58px, 8vw, 104px);
  line-height: 0.96;
  color: var(--ink);
  margin: 0 0 28px;
  text-wrap: balance;
  max-width: 16ch;
}
.hero__display em {
  color: var(--ink);
  font-style: normal;
}
.hero__lead {
  font-size: 17px;
  line-height: 1.6;
  color: var(--body);
  max-width: 52ch;
  margin: 0 0 14px;
}
.hero__lead--sub {
  /* Match the main lead's body colour — the sub paragraph keeps
     its own bottom margin (creates breathing room before the CTAs)
     but reads at the same tone as the lead above it, per design
     direction to keep the whole hero block visually consistent. */
  margin: 0 0 32px;
}
.hero__lead em {
  font-style: normal;
  color: var(--ink);
  font-weight: 500;
}
.hero__cta {
  display: flex;
  align-items: center;
  gap: 32px;
  flex-wrap: wrap;
  margin-bottom: 40px;
}
.hero__facts {
  list-style: none;
  padding: 24px 0 0;
  margin: 0;
  border-top: 1px solid var(--hairline);
  max-width: 520px;
  display: flex;
  flex-direction: column;
  gap: 10px;
}
.hero__facts li {
  display: grid;
  grid-template-columns: 140px 1fr;
  gap: 16px;
  font-size: 14px;
  align-items: baseline;
}
.hero__facts-k {
  color: var(--muted);
  font-weight: 500;
  letter-spacing: 0.02em;
}
.hero__facts-v {
  color: var(--ink);
  font-weight: 500;
}

/* Hero left needs relative positioning so handwritten note can pin */
.hero__left { position: relative; }
.hero__right { position: relative; display: flex; justify-content: flex-start; }
/* Portrait card */
.portrait {
  margin: 0;
  position: relative;
  width: 100%;
  max-width: 380px;
}
.portrait__media {
  background: transparent;
  border-radius: 0;
  display: flex;
  justify-content: flex-start;
  align-items: flex-end;
  position: relative;
  overflow: visible;
}
.portrait__media::after { content: none; }
.portrait__img {
  display: block;
  width: 100%;
  height: auto;
  max-width: 100%;
  image-rendering: pixelated;
  image-rendering: crisp-edges;
}

/* Sprite-sheet portrait — plays a 20-frame run ONCE on hover.
   ------------------------------------------------------------
   Sprite is 16640×1104 = 20 frames @ 832×1104 each (aspect 832/1104).
   The viewport is sized to FIT ONE WHOLE FRAME so nothing is ever
   cropped during the animation (the character's hair blows out
   to the sides mid-walk, and we want all of it visible).
   ============================================================ */
.portrait__sprite {
  /* Drive everything from --el-h. Width is computed from the
     frame aspect ratio so each frame fits perfectly. */
  --el-h: 676px;
  --frames: 20;
  --frame-w: calc(var(--el-h) * 832 / 1104); /* ≈ 509.22px */

  position: relative;
  width: var(--frame-w);
  height: var(--el-h);
  flex-shrink: 0;
  margin: -30px;

  background-image: url('hero-sprite.png');
  background-repeat: no-repeat;
  background-size: auto var(--el-h);
  background-position: 0 center;     /* frame 0, fully visible */

  image-rendering: pixelated;
  image-rendering: crisp-edges;
}

/* Reaction sprite — layered as an overlay, NEVER swapped onto the
   parent's background. This is the key to "image never disappears":
   the walk sheet underneath is always painted, the reaction sheet
   only fades on top while .is-reacting is active. Sitting at
   opacity: 0 from first paint also forces the browser to decode
   hero-sprite2.png up front, so there's no decode flicker on the
   first click. */
.portrait__sprite-react {
  position: absolute;
  inset: 0;
  pointer-events: none;
  background-image: url('hero-sprite2.png');
  background-repeat: no-repeat;
  background-size: auto var(--el-h);
  background-position: 0 center;
  image-rendering: pixelated;
  image-rendering: crisp-edges;
  opacity: 0;
}

.portrait:hover .portrait__sprite,
.portrait__sprite:focus-visible {
  animation: portrait-walk 1s steps(20, jump-none) 1 forwards;
}

/* Cooldown after a click reaction — suppress the hover walk for a beat,
   but never block another click reaction. */
.portrait.is-cooldown:hover .portrait__sprite:not(.is-reacting),
.portrait.is-cooldown .portrait__sprite:focus-visible:not(.is-reacting) {
  animation: none;
}

@keyframes portrait-walk {
  from { background-position-x: 0; }
  /* Shift by one full frame-width per step → all 20 frames shown
     left-anchored, never cropped. */
  to   { background-position-x: calc(var(--frame-w) * (var(--frames) - 1) * -1); }
}

/* Click-to-play — a 9-frame peace-sign reaction (hero-sprite2.png,
   7488×1104 = 9 frames @ 832×1104). Same frame size as the walk sprite,
   so the character's size and position are unchanged. The reaction
   plays on the .portrait__sprite-react overlay element.

   While reacting, suppress the walk sprite background on the parent
   so the static walk character can't peek through the transparent
   edges of the reaction frames. The walk image reappears the moment
   .is-reacting is removed (on animationend), with the overlay fading
   out at the same time, so neither layer is invisible at any point.

   Animation is driven inline from JS (so rapid retriggers can restart
   the keyframes without ever flipping opacity back to 0). The class
   only controls visibility + base-sprite suppression. */
.portrait__sprite.is-reacting {
  background-image: none;
}
.portrait__sprite.is-reacting .portrait__sprite-react {
  --frames: 9;
  opacity: 1;
}

@keyframes portrait-react {
  from { background-position-x: 0; }
  to   { background-position-x: calc(var(--frame-w) * (var(--frames) - 1) * -1); }
}

@media (prefers-reduced-motion: reduce) {
  .portrait:hover .portrait__sprite { animation: none; }
  .portrait__sprite.is-reacting .portrait__sprite-react { animation: none; opacity: 1; background-position-x: 0; }
}
.portrait__placeholder {
  position: relative;
  z-index: 1;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 12px;
  color: var(--muted-soft);
}
.portrait__placeholder svg {
  width: 56px;
  height: 56px;
  color: var(--muted-soft);
}
.portrait__placeholder span {
  font-size: 12px;
  font-weight: 500;
  letter-spacing: 0.12em;
  text-transform: uppercase;
}
.portrait figcaption {
  display: flex;
  gap: 12px;
  align-items: baseline;
  font-size: 13px;
  line-height: 1.5;
  color: var(--muted);
  margin-top: 16px;
}
.portrait__fig {
  font-size: 12px;
  font-weight: 500;
  letter-spacing: 0.06em;
  color: var(--primary);
  white-space: nowrap;
}

/* ============================================================
   SECTIONS
   ============================================================ */

.section {
  max-width: var(--max-w);
  margin: 0 auto;
  padding: 96px var(--pad-page);
}
.section--narrow { max-width: 880px; }

.section__head { margin-bottom: 48px; }
.section__title {
  font-size: clamp(32px, 4.2vw, 48px);
  font-weight: 400;
  line-height: 1.08;
  letter-spacing: -0.02em;
  color: var(--ink);
  max-width: 22ch;
  text-wrap: balance;
}
.section__title em {
  color: inherit;
  font-weight: 700;
  font-style: normal;
}

/* ============================================================
   TITLES — display + heading tracks
   ------------------------------------------------------------
   --font-display : VT323 pixel font for hero + section titles.
                    tokens.css is the canonical source for both
                    --font-display and --font-heading; the
                    fallbacks below are a safety net.
   --font-heading : system-ui sans for card titles inside the
                    cs-rail (.cs-card__title etc.).
   ============================================================ */
:root {
  --font-display: 'VT323', ui-monospace, SFMono-Regular, Menlo, monospace;
  --font-heading: system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;
}

.hero__display, .hero__display em,
.section__title, .section__title em {
  font-family: var(--font-display);
}

/* Hero display: VT323 looks best at its native weight + tracking */
.hero__display,
.hero__display em {
  font-weight: 400;
  letter-spacing: 0;
}

/* Section titles: a touch tighter, full weight */
.section__title,
.section__title em {
  font-weight: 700;
  letter-spacing: -0.02em;
}

.hero__display em,
.section__title em {
  font-style: normal;
}

/* ============================================================
   PIXEL TITLES — VT323 on section titles (matches hero)
   Placed last so it wins over the system-ui display overrides.
   VT323 has a small x-height + single weight, so sizes are
   bumped and tracking reset to 0.
   ============================================================ */
.section__title,
.section__title em {
  font-family: 'VT323', ui-monospace, monospace;
  font-weight: 400;
  letter-spacing: 0;
}
.section__title {
  font-size: clamp(42px, 5.6vw, 66px);
  line-height: 0.98;
  max-width: 26ch;
}

/* ============================================================
   CLOSER — "Let's talk" + now playing  (replaces callout + dark footer)
   ============================================================ */
.closer {
  background: var(--canvas);
  color: var(--ink);
  padding: 128px 0 56px;
}
.closer__inner {
  max-width: var(--max-w);
  margin: 0 auto;
  padding: 0 var(--pad-page);
}
.closer__title {
  font-family: 'VT323', ui-monospace, monospace;
  font-weight: 400;
  font-size: clamp(52px, 8vw, 100px);
  line-height: 0.86;
  letter-spacing: 0;
  color: var(--ink);
  margin: 0 0 40px;
}
.closer__cta {
  display: flex;
  gap: 20px;
  flex-wrap: wrap;
  margin-bottom: 0;
}
/* Buttons recoloured for the dark closer — light brutalist offset */
.closer .btn--primary {
  border-color: var(--ink);
  box-shadow: 6px 6px 0 var(--ink);
}
.closer .btn--primary:hover {
  box-shadow: 10px 10px 0 var(--ink);
}
.closer .btn--secondary {
  background: var(--canvas);
  color: var(--ink);
  border-color: var(--ink);
  box-shadow: 6px 6px 0 var(--ink);
}
.closer .btn--secondary:hover {
  background: var(--surface-card);
  box-shadow: 10px 10px 0 var(--ink);
}

/* Now playing */
.nowplaying { max-width: 460px; }
.nowplaying__label {
  display: flex;
  align-items: center;
  gap: 12px;
  font-family: 'JetBrains Mono', ui-monospace, monospace;
  font-size: 13px;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--muted);
  margin: 0 0 18px;
}
.nowplaying__eq { display: inline-flex; align-items: flex-end; gap: 3px; height: 15px; }
.nowplaying__eq i {
  width: 3px;
  display: block;
  background: var(--ink-blue);
  transform-origin: bottom;
  animation: eq 1s ease-in-out infinite;
}
.nowplaying__eq i:nth-child(1) { height: 7px;  animation-delay: 0s;   }
.nowplaying__eq i:nth-child(2) { height: 15px; animation-delay: .22s; }
.nowplaying__eq i:nth-child(3) { height: 10px; animation-delay: .44s; }
@keyframes eq { 0%,100% { transform: scaleY(.4); } 50% { transform: scaleY(1); } }
@media (prefers-reduced-motion: reduce) { .nowplaying__eq i { animation: none; } }

.nowplaying__card {
  display: flex;
  align-items: center;
  gap: 18px;
  padding: 14px 16px;
  background: var(--canvas);
  border: 1.5px solid var(--ink);
  border-radius: 2px;
  box-shadow: 4px 4px 0 var(--ink);
  transition: transform .16s ease, box-shadow .16s ease;
}
.nowplaying__card:hover {
  transform: translate(-2px, -2px);
  box-shadow: 6px 6px 0 var(--ink);
}
.nowplaying__art {
  width: 56px;
  height: 56px;
  flex: none;
  border-radius: 2px;
  background: linear-gradient(135deg, var(--primary), var(--primary-active));
}
.nowplaying__meta { display: flex; flex-direction: column; gap: 3px; margin-right: auto; min-width: 0; }
.nowplaying__track { font-weight: 600; color: var(--ink); font-size: 16px; }
.nowplaying__sub { color: var(--muted); font-size: 14px; }
.nowplaying__spotify { width: 26px; height: 26px; color: var(--primary); fill: currentColor; flex: none; }

/* ============================================================
   SITE FOOTER — light, with a perched pixel crow facing left
   ============================================================ */
.sitefoot { position: relative; background: var(--canvas); }
.sitefoot__perch {
  position: relative;
  max-width: var(--max-w);
  margin: 0 auto;
  border-top: 1px solid var(--hairline);
  height: 0;
}
.sitefoot__bird {
  position: absolute;
  bottom: 0;
  right: 88px;
  width: 92px;
  height: auto;
  image-rendering: pixelated;
  image-rendering: crisp-edges;
  pointer-events: none;
  filter: none;
}
.sitefoot__bar {
  max-width: var(--max-w);
  margin: 0 auto;
  padding: 30px var(--pad-page);
  display: flex;
  justify-content: space-between;
  align-items: center;
  gap: 16px;
  font-family: 'JetBrains Mono', ui-monospace, monospace;
  font-size: 13px;
  letter-spacing: 0.03em;
  color: var(--muted);
}

/* ============================================================
   BEHIND THE CANVAS — full-bleed dark personal section with a
   pixel-art dissolve edge top & bottom
   ============================================================ */
.behind {
  position: relative;
  margin-top: 132px;
  margin-bottom: 0;
  background: var(--surface-dark);
  color: var(--on-dark);
  border-bottom-left-radius: 40px;
  border-bottom-right-radius: 40px;
}
.behind__edge {
  position: absolute;
  left: 0;
  right: 0;
  height: 198px;
  background-image: url(media/pixel-edge.svg);
  background-repeat: repeat-x;
  background-position: left bottom;
  background-size: auto 198px;
  image-rendering: pixelated;
  pointer-events: none;
}
.behind__edge--top { top: -197px; }

@media (max-width: 860px) {
  .behind { margin-top: 112px; margin-bottom: 112px; }
}
@media (max-width: 768px) {
  .closer { padding: 88px 0 48px; }
  .closer__cta { margin-bottom: 0; }
  .sitefoot__bird { right: 24px; width: 76px; }
  .sitefoot__bar { flex-direction: column; align-items: flex-start; gap: 6px; font-size: 12px; }
}

/* ============================================================
   SELECTED WORK — large split "dossier" cards in a horizontal
   rail. Dark left panel (org · title · stat lines), light
   portrait shot card on the right. Arrow nav + drag scroll.
   ============================================================ */
:root { --cs-accent: #87C9A6; }

/* Pull the work section up so the first cards peek under the hero —
   gives a clear scroll affordance instead of leaving 96px of dead
   space above the rail. Bottom padding stays trimmed to 56px to feed
   into the experience section's reduced top padding below. */
#work {
  padding-top: 24px;
  padding-bottom: 56px;
}

/* Editorial pagination bar — counter on the left, prev/next arrows on
   the right. Sits above the rail; because it lives inside `#work`
   (a `.section` that already provides max-width + `--pad-page`
   gutters), the pager intentionally adds *no* horizontal padding or
   max-width of its own. That keeps its left edge perfectly flush
   with the first card's left edge (the rail's full-bleed gutter
   resolves to the same x as the section's content edge). */
.cs-pager {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 24px;
  margin-bottom: 20px;
}

/* "01 / 06" — leading-zero padded current index + total. Monospace so
   the figures don't shift width as you advance ("01" → "10" stays
   visually anchored). The slash and total stay muted; only the
   current number takes the ink color, so the eye reads "where am I"
   at a glance. */
.cs-pager__counter {
  font-family: 'JetBrains Mono', ui-monospace, monospace;
  font-size: 14px;
  font-weight: 500;
  letter-spacing: 0.08em;
  color: var(--muted);
  font-variant-numeric: tabular-nums;
}
.cs-pager__counter b {
  font-weight: 500;
  color: var(--ink);
}

.cs-pager__arrows {
  display: flex;
  gap: 18px;
}

/* Bare-bones glyph arrows — no circle, no fill, no shadow. Match the
   minimal pagination vocabulary of the counter on the left. Disabled
   state dims to 0.25 opacity so it's still legible but clearly inert. */
.cs-pager__arrow {
  appearance: none;
  -webkit-appearance: none;
  width: 36px;
  height: 36px;
  padding: 0;
  border: 0;
  background: transparent;
  color: var(--ink);
  cursor: pointer;
  display: grid;
  place-items: center;
  transition: opacity .15s ease, transform .18s cubic-bezier(.2, .7, .2, 1);
}
.cs-pager__arrow svg {
  width: 24px;
  height: 24px;
  display: block;
}
.cs-pager__arrow:hover:not(:disabled) {
  transform: translateX(3px);
}
.cs-pager__arrow[data-cs-prev]:hover:not(:disabled) {
  transform: translateX(-3px);
}
.cs-pager__arrow:focus-visible {
  outline: 2px solid var(--ink);
  outline-offset: 4px;
}
.cs-pager__arrow:disabled {
  opacity: 0.25;
  cursor: default;
}

.cs-rail {
  /* Full-bleed: span the entire viewport regardless of the parent
     section's max-width container, so cards don't clip on a wide
     screen against the centred 1320px section bounds. */
  --rail-gutter: max(var(--pad-page), calc((100vw - var(--max-w)) / 2 + var(--pad-page)));
  width: 100vw;
  margin-left: calc(50% - 50vw);
  margin-right: calc(50% - 50vw);

  /* Inner padding aligns the first / last card with the page's
     normal content gutter (where the section above and below
     start). On narrow screens this collapses to --pad-page. */
  padding-left: var(--rail-gutter);
  padding-right: var(--rail-gutter);
  /* Top/bottom breathing room so the -8px hover lift on .cs-card
     isn't clipped by overflow-y: hidden (required because the rail
     scrolls horizontally). */
  padding-top: 16px;
  padding-bottom: 22px;

  /* Snap-align should respect the same gutter so arrow nav lands
     cards at the gutter, not flush against the viewport edge. */
  scroll-padding-left: var(--rail-gutter);

  overflow-x: auto;
  overflow-y: hidden;
  -webkit-overflow-scrolling: touch;
  scroll-snap-type: x proximity;
  scrollbar-width: none;
  -ms-overflow-style: none;
  cursor: grab;
}
.cs-rail::-webkit-scrollbar { display: none; }
.cs-rail.is-dragging { cursor: grabbing; }
.cs-rail.is-dragging a { pointer-events: none; }
/* While the rail is being dragged (or coasting on flick momentum),
   suppress text-selection and native image/anchor drag previews so
   the gesture stays glued to the pointer. */
.cs-rail.is-dragging,
.cs-rail.is-dragging * {
  user-select: none;
  -webkit-user-select: none;
}
.cs-rail img,
.cs-rail video {
  -webkit-user-drag: none;
  user-drag: none;
  -webkit-touch-callout: none;
}

.cs-rail__track {
  display: flex;
  gap: 28px;
  width: max-content;
}

.cs-card {
  scroll-snap-align: start;
  flex: 0 0 auto;
  width: min(1040px, 86vw);
  height: 560px;
  display: flex;
  border-radius: 28px;
  overflow: hidden;
  background: var(--surface-dark);
  color: var(--on-dark);
  text-decoration: none;
  transition:
    transform .42s cubic-bezier(.2, .7, .2, 1),
    background-color .42s cubic-bezier(.2, .7, .2, 1);
  will-change: transform;
}
/* Hover / focus — no soft drop shadow. The card steps forward (-8px)
   while its inner dark panel warms one notch (surface-dark →
   surface-dark-elevated), so elevation reads through tone, not haze.
   Supporting cues: the cs-shot image scales (1.045) and the corner
   "↗" affordance fades in — both already wired below. */
.cs-card:hover,
.cs-card:focus-visible {
  transform: translateY(-8px);
  background-color: var(--surface-dark-elevated);
}
/* Cancel the lift while the rail is being drag-scrolled so cards don't
   appear to "wobble" mid-gesture. */
.cs-rail.is-dragging .cs-card:hover,
.cs-rail.is-dragging .cs-card:focus-visible {
  transform: none;
  background-color: var(--surface-dark);
}

/* Left — dark panel: org eyebrow, big title, stat lines pinned bottom */
.cs-card__left {
  flex: 1 1 auto;
  min-width: 0;
  padding: 48px 44px;
  display: flex;
  flex-direction: column;
}
.cs-eyebrow {
  display: inline-flex;
  align-items: center;
  gap: 10px;
  margin: 0;
  font-family: 'JetBrains Mono', ui-monospace, monospace;
  font-size: 14px;
  font-weight: 500;
  letter-spacing: 0.04em;
  color: var(--cs-accent);
}
.cs-eyebrow__mark { font-size: 17px; line-height: 1; }
.cs-card__title {
  margin: 20px 0 0;
  font-family: var(--font-heading);
  font-weight: 700;
  letter-spacing: -0.025em;
  line-height: 1.05;
  font-size: clamp(28px, 2.8vw, 44px);
  color: var(--on-dark);
  text-wrap: balance;
  max-width: 18ch;
}
.cs-stats {
  list-style: none;
  margin: auto 0 0;
  padding: 0;
  display: flex;
  flex-direction: column;
  gap: 15px;
}
.cs-stats li {
  display: flex;
  align-items: center;
  gap: 14px;
  font-size: 17px;
  line-height: 1.25;
  color: var(--on-dark-soft);
}
.cs-stats__ico {
  width: 18px;
  height: 18px;
  flex: none;
  color: var(--cs-accent);
  display: block;
}
.cs-stats__ico svg { width: 100%; height: 100%; display: block; image-rendering: pixelated; }
.cs-stats em { font-style: normal; color: var(--on-dark); }

/* Right — light portrait shot card */
.cs-card__right {
  flex: 0 0 auto;
  padding: 28px 28px 28px 8px;
  display: flex;
}
.cs-shot {
  position: relative;
  width: 400px;
  height: 100%;
  border-radius: 18px;
  overflow: hidden;
  background: linear-gradient(165deg, var(--surface-shot), var(--surface-shot-warm));
}
.cs-shot img,
.cs-shot image-slot {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  display: block;
  object-fit: contain;
  object-position: center;
  transition: transform .55s cubic-bezier(.2, .7, .2, 1);
}
.cs-shot--cover img { object-fit: cover; }

/* Digital economy — PNG already has its own art-directed lavender blob,
   so the cream cs-shot backdrop fights with it. Drop it and let the
   dark card surface show through transparent areas of the PNG. */
.cs-shot--digital-economy {
  background: transparent;
}

/* Avatars editor — proto video sits inside a minimalistic phone frame,
   centered on a soft cream canvas. The video is paused (poster visible)
   at rest and starts playing when the card receives hover or focus. */
.cs-shot--avatar-editor {
  background:
    radial-gradient(120% 100% at 50% 0%, #FFFFFF 0%, #F5F2EC 70%, #ECE6DA 100%);
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 22px 0;
}

/* Klarna — same minimalist device frame as Avatars Editor, but with a
   soft blush gradient that nods to Klarna's pink brand without going
   loud. White at the top so the device's top edge keeps catching the
   highlight, then easing into pink as it falls toward the base. */
.cs-shot--klarna {
  background:
    radial-gradient(120% 100% at 50% 0%, #FFFFFF 0%, #FCEBF1 70%, #F7D6E2 100%);
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 22px 0;
}

.cs-device {
  position: relative;
  height: 100%;
  /* iPhone-ish 9:19.5 portrait ratio for the device outline. */
  aspect-ratio: 9 / 19.5;
  max-height: 100%;
  border-radius: 32px;
  background: #0E0E10;
  box-shadow:
    0 1px 0 rgba(255, 255, 255, 0.55) inset,
    0 0 0 1px rgba(20, 20, 24, 0.18),
    0 14px 28px -16px rgba(20, 20, 24, 0.45),
    0 4px 10px -6px rgba(20, 20, 24, 0.25);
  padding: 6px;
  transition: transform .55s cubic-bezier(.2, .7, .2, 1),
              box-shadow .55s cubic-bezier(.2, .7, .2, 1);
}
.cs-device__screen {
  position: relative;
  width: 100%;
  height: 100%;
  border-radius: 26px;
  overflow: hidden;
  background: #0E0E10;
}
.cs-device__video {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  display: block;
  object-fit: cover;
  object-position: center;
}
.cs-card:hover .cs-device,
.cs-card:focus-visible .cs-device {
  transform: translateY(-2px);
  box-shadow:
    0 1px 0 rgba(255, 255, 255, 0.6) inset,
    0 0 0 1px rgba(20, 20, 24, 0.22),
    0 22px 38px -18px rgba(20, 20, 24, 0.5),
    0 6px 14px -8px rgba(20, 20, 24, 0.3);
}

@media (prefers-reduced-motion: reduce) {
  .cs-device {
    transition: none;
  }
}

/* Delivery Hero — two phone screens (splash + map address picker)
   shown side by side directly on the dark card surface (no cream
   canvas). Phones keep their native 9:19.5 proportions: they fill the
   cs-shot vertically when room allows, and scale down proportionally
   if the canvas width would otherwise force a horizontal overflow.
   The pair gently bobs in alternation so the cover reads as an alive
   flow (like a GIF). On hover/focus the animation pauses at the lifted
   neutral and the shadow deepens. */
.cs-shot--dh-pair {
  background: transparent;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 0;
  /* Establishes the sizing context for container queries (cqh/cqw)
     used by .cs-pair__phone below — lets each phone reference the
     cs-shot's own width and height, not the document viewport. */
  container-type: size;
}
.cs-pair {
  position: relative;
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 14px;
}
.cs-pair__phone {
  position: relative;
  margin: 0;
  /* Pick the smaller of:
       — 100cqh        (cs-shot height — fills vertically when there's room)
       — width-derived (each phone gets (cs-shot-width − 14px gap)/2 of
                        width; multiply by 19.5/9 to convert back to the
                        height that preserves the 9:19.5 phone ratio)
     Whichever is smaller wins, so the pair never overflows and the
     phones never get squished. With aspect-ratio: 9/19.5 the browser
     derives width from height, keeping proportions exact. */
  height: min(100cqh, calc((100cqw - 14px) / 2 * 19.5 / 9));
  aspect-ratio: 9 / 19.5;
  border-radius: 22px;
  overflow: hidden;
  background: #0E0E10;
  box-shadow:
    0 0 0 1px rgba(20, 20, 24, 0.10),
    0 10px 22px -14px rgba(0, 0, 0, 0.55),
    0 3px 8px -4px rgba(0, 0, 0, 0.35);
  transition: transform .55s cubic-bezier(.2, .7, .2, 1),
              box-shadow .55s cubic-bezier(.2, .7, .2, 1);
  animation: cs-pair-bob 3.2s ease-in-out infinite;
  will-change: transform;
}
.cs-pair__phone img {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  display: block;
  object-fit: cover;
  object-position: center top;
}
.cs-pair__phone--b {
  /* Offset phase so the two phones rise and fall in counterpoint —
     creates that subtle GIF-like alive feel without ever being noisy. */
  animation-delay: -1.6s;
}
@keyframes cs-pair-bob {
  0%, 100% { transform: translateY(0); }
  50%      { transform: translateY(-5px); }
}
/* On card hover/focus the bob pauses at a small lift, shadow deepens.
   This trades the "always alive" loop for a focused, intentional state.
   Shadows are tuned for the dark card background — pure black drops +
   a faint inset highlight so the lift reads on a near-charcoal surface. */
.cs-card:hover .cs-pair__phone,
.cs-card:focus-visible .cs-pair__phone {
  animation-play-state: paused;
  transform: translateY(-3px);
  box-shadow:
    0 0 0 1px rgba(255, 255, 255, 0.06),
    0 18px 36px -18px rgba(0, 0, 0, 0.7),
    0 6px 14px -6px rgba(0, 0, 0, 0.45);
}

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

/* Prompt-to-character — designed placeholder cover. Forest-green
   gradient that echoes the brand primary, with a soft mint glow,
   so the rail never reveals a generic grey box for work that
   hasn't shipped its hero yet. */
.cs-shot--prompt {
  background:
    radial-gradient(120% 90% at 100% 0%, rgba(135,201,166,0.22), transparent 60%),
    radial-gradient(120% 90% at 0% 100%, rgba(255,255,255,0.06), transparent 55%),
    linear-gradient(135deg, #0a1411 0%, #143426 55%, #1F4D3A 100%);
}
.cs-shot__poster {
  position: absolute;
  inset: 0;
  display: flex;
  flex-direction: column;
  justify-content: center;
  align-items: center;
  gap: 18px;
  padding: 32px;
  text-align: center;
  color: #fff;
}
.cs-shot__poster-eyebrow {
  font-family: 'JetBrains Mono', ui-monospace, monospace;
  font-size: 11px;
  font-weight: 500;
  letter-spacing: 0.18em;
  text-transform: uppercase;
  color: rgba(255, 255, 255, 0.55);
}
.cs-shot__poster-title {
  font-family: 'VT323', ui-monospace, monospace;
  font-size: clamp(38px, 4.4vw, 64px);
  font-weight: 400;
  line-height: 1;
  letter-spacing: 0.01em;
  color: #f4eeff;
}
.cs-shot__poster-title em {
  font-style: normal;
  color: var(--cs-accent);
}
.cs-shot__poster-cap {
  font-family: 'JetBrains Mono', ui-monospace, monospace;
  font-size: 10.5px;
  font-weight: 500;
  letter-spacing: 0.22em;
  text-transform: uppercase;
  color: rgba(255, 255, 255, 0.42);
  margin-top: 6px;
}
.cs-card__go {
  position: absolute;
  right: 18px;
  top: 18px;
  z-index: 2;
  width: 40px;
  height: 40px;
  border-radius: 50%;
  display: grid;
  place-items: center;
  background: rgba(255,255,255,.94);
  color: var(--ink);
  font-size: 17px;
  opacity: 0;
  transform: translateY(-6px);
  transition: opacity .3s ease, transform .3s ease;
}
.cs-card:hover .cs-card__go { opacity: 1; transform: translateY(0); }

/* Vision card — pixel-art lock badge sits top-left of the poster shot,
   visible at rest so the locked state is obvious before hover. */
.cs-card__lock {
  position: absolute;
  left: 18px;
  top: 18px;
  z-index: 2;
  width: 28px;
  height: 28px;
  display: grid;
  place-items: center;
  background: rgba(255, 255, 255, 0.12);
  border: 1px solid rgba(255, 255, 255, 0.18);
  border-radius: 6px;
  color: rgba(255, 255, 255, 0.78);
  backdrop-filter: blur(4px);
  -webkit-backdrop-filter: blur(4px);
}
.cs-card__lock svg {
  width: 14px;
  height: 14px;
  image-rendering: pixelated;
}
.cs-card--vision .cs-shot__poster-eyebrow {
  color: var(--cs-accent);
  letter-spacing: 0.22em;
}

@media (max-width: 860px) {
  #work .section__head { flex-wrap: wrap; }
  .cs-card {
    flex-direction: column;
    width: 84vw;
    height: auto;
  }
  .cs-card__left { padding: 32px 28px; order: 2; }
  .cs-card__title { margin-top: 14px; }
  .cs-stats { margin-top: 26px; }
  .cs-card__right { padding: 20px 20px 0; order: 1; }
  /* Uniform image canvas across every card on mobile so the rail
     reads as a tidy row of identically-sized covers rather than a
     ragged set of varying aspect ratios. The portrait phone frames
     (avatars-editor, delivery-hero pair) center themselves inside
     this canvas via their own intrinsic sizing rules. */
  .cs-shot,
  .cs-shot--avatar-editor,
  .cs-shot--klarna,
  .cs-shot--dh-pair {
    width: 100%;
    height: auto;
    aspect-ratio: 4 / 3;
  }
}

@media (prefers-reduced-motion: reduce) {
  .cs-card { transition: background-color .2s ease; }
  .cs-card:hover,
  .cs-card:focus-visible { transform: none; }
}

/* ============================================================
   ABOUT — centered editorial bio with polaroid snapshots
   (replaces the old two-column "Behind the canvas" layout)
   ============================================================ */
.about {
  position: relative;
  max-width: var(--max-w);
  margin: 0 auto;
  padding: 120px var(--pad-page) 128px;
}
/* About eyebrow ("BEHIND THE CANVAS") is left-aligned so it follows
   the same label-placement convention used elsewhere on the page
   (Selected Work, 15+ Years of Design, etc.) — the pixel pause-mark
   sits flush with the start of the body copy below, rather than
   floating mid-column. inline-flex keeps the mark + text on the same
   baseline without the parent flex container forcing center alignment. */
.about__eyebrow {
  display: inline-flex;
  align-items: center;
  gap: 11px;
  margin: 0 0 68px;
  font-family: 'JetBrains Mono', ui-monospace, monospace;
  font-size: 12px;
  font-weight: 500;
  letter-spacing: 0.22em;
  text-transform: uppercase;
  color: var(--on-dark-soft);
}
.about__mark { display: inline-flex; color: var(--on-dark-strong); }
.about__mark svg { width: 14px; height: 12px; display: block; }

.about__col { max-width: 720px; margin: 0 auto; }
.about__p {
  margin: 0 0 30px;
  font-size: clamp(21px, 2.1vw, 29px);
  line-height: 1.5;
  letter-spacing: -0.01em;
  color: var(--on-dark-warm);
  text-wrap: pretty;
}
.about__col .about__p:last-child { margin-bottom: 0; }
.about__p em {
  font-family: inherit;
  font-style: normal;
  font-weight: 400;
  color: inherit;
}
.about__p a {
  color: var(--cs-accent);
  text-decoration: none;
  transition: color .15s ease, opacity .15s ease;
}
.about__p a:hover {
  color: var(--cs-accent);
  opacity: 0.78;
}

.about__photos {
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  align-items: center;
  gap: 44px;
  max-width: 940px;
  margin: 76px auto 84px;
}
.polaroid {
  --tilt: 0deg;
  margin: 0;
  padding: 0;
  background: transparent;
  box-shadow: none;
  transform: rotate(var(--tilt));
  transition: transform .45s cubic-bezier(.2, .7, .2, 1);
  will-change: transform;
}
.polaroid--a { --tilt: -4deg; }
.polaroid--b { --tilt: 1.5deg; }
.polaroid--c { --tilt: -2.2deg; }

.polaroid__media {
  width: 250px;
  height: 320px;
  overflow: hidden;
  background: transparent;
  border-radius: 0;
  box-shadow:
    0 1px 2px rgba(0, 0, 0, 0.18),
    0 14px 28px -10px rgba(0, 0, 0, 0.45);
  transition:
    box-shadow .45s cubic-bezier(.2, .7, .2, 1),
    transform .45s cubic-bezier(.2, .7, .2, 1);
}
.polaroid__media img,
.polaroid__media video {
  display: block;
  width: 100%;
  height: 100%;
  object-fit: cover;
  border: 0;
  outline: 0;
  background: transparent;
}

.polaroid:hover {
  transform: rotate(0deg) translateY(-6px) scale(1.03);
}
.polaroid:hover .polaroid__media {
  box-shadow:
    0 2px 4px rgba(0, 0, 0, 0.22),
    0 28px 48px -14px rgba(0, 0, 0, 0.55);
}

@media (prefers-reduced-motion: reduce) {
  .polaroid,
  .polaroid__media { transition: none; }
  .polaroid:hover { transform: rotate(var(--tilt)) translateY(-2px); }
}

@media (max-width: 820px) {
  .about { padding: 88px var(--pad-page) 96px; }
  .about__eyebrow { margin-bottom: 48px; }
  .about__photos { gap: 26px; margin: 56px auto 62px; }
  .polaroid__media { width: 200px; height: 256px; }
}
@media (max-width: 560px) {
  .polaroid--a, .polaroid--b, .polaroid--c { --tilt: 0deg; }
  .polaroid:hover { transform: translateY(-4px) scale(1.02); }
}

/* ============================================================
   EXPERIENCE — Editorial work history. Bigger type + clear
   row hierarchy + a soft warm hover state per row, drawing
   inspiration from Emanuel Serbanoiu's portfolio. Each row is
   a 5-column grid: logo / company / role / duration / years
   plus an absolutely-positioned arrow cue that appears on hover
   to signal the row is interactive.
   ============================================================ */

.experience {
  /* Top padding intentionally lighter than bottom — the work section
     above already provides upper breathing room. The previous 40px
     margin-top stacked redundantly on this and pushed the eyebrow
     ~280px below the cards; removed. */
  padding: 56px var(--pad-page) 128px;
  background: var(--canvas);
}

.experience__inner {
  max-width: 1080px;
  margin: 0 auto;
}

.experience__eyebrow {
  font-family: 'JetBrains Mono', ui-monospace, monospace;
  font-size: 12px;
  font-weight: 600;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--muted);
  margin-bottom: 64px;
  display: block;
}

.experience__list {
  display: flex;
  flex-direction: column;
  gap: 4px;
}

.experience__item {
  position: relative;
  display: grid;
  grid-template-columns: 64px minmax(160px, 1fr) minmax(240px, 1.5fr) 110px 130px;
  align-items: center;
  gap: 32px;
  padding: 26px 24px 26px 20px;
  border-radius: 10px;
  background-color: transparent;
  outline: none;
  text-decoration: none;
  color: inherit;
  transition:
    background-color .35s cubic-bezier(.2, .7, .2, 1),
    transform .35s cubic-bezier(.2, .7, .2, 1);
  cursor: pointer;
}
.experience__item:focus-visible {
  outline: 2px solid var(--ink);
  outline-offset: 2px;
}

.experience__logo {
  grid-column: 1;
  height: 56px;
  width: 56px;
  object-fit: contain;
  transition: transform .35s cubic-bezier(.2, .7, .2, 1);
}

.experience__company {
  grid-column: 2;
  font-family: var(--font-heading);
  font-size: clamp(22px, 2.6vw, 34px);
  font-weight: 600;
  color: var(--ink);
  margin: 0;
  line-height: 1.08;
  letter-spacing: -0.02em;
  transition: color .35s ease, transform .35s cubic-bezier(.2, .7, .2, 1);
}

.experience__role {
  grid-column: 3;
  font-family: var(--font-body);
  color: var(--muted-soft);
  font-size: 16px;
  line-height: 1.55;
  margin: 0;
  transition: color .35s ease;
  /* Stack rest + impact layers in the same cell so the row height
     stays stable across the swap. The cell sizes to whichever layer
     is taller; both layers vertically center inside it. */
  position: relative;
  display: grid;
  grid-template-areas: "stack";
  align-items: center;
  min-width: 0;
}
.experience__role-layer {
  grid-area: stack;
  min-width: 0;
  transition:
    opacity .32s cubic-bezier(.2, .7, .2, 1),
    transform .42s cubic-bezier(.2, .7, .2, 1),
    color .35s ease;
  will-change: transform, opacity;
}
.experience__role-layer--rest {
  opacity: 1;
  transform: translateY(0);
}
.experience__role-layer--impact {
  opacity: 0;
  transform: translateY(10px);
  color: var(--body);
  pointer-events: none;
}
.experience__item:hover .experience__role-layer--rest,
.experience__item:focus-visible .experience__role-layer--rest {
  opacity: 0;
  transform: translateY(-10px);
}
.experience__item:hover .experience__role-layer--impact,
.experience__item:focus-visible .experience__role-layer--impact {
  opacity: 1;
  transform: translateY(0);
  /* Tiny delay so the rest copy is already starting to dissolve before
     the impact copy slides into the same slot — keeps the swap legible
     instead of two layers crossfading on top of each other. */
  transition-delay: .06s;
}

.experience__duration {
  grid-column: 4;
  text-align: right;
  font-family: var(--font-mono);
  font-size: 12px;
  font-weight: 500;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--muted);
  justify-self: end;
  margin: 0;
  transition: color .35s ease;
}

.experience__years {
  grid-column: 5;
  text-align: right;
  font-family: var(--font-mono);
  font-size: 13px;
  font-weight: 500;
  letter-spacing: 0.06em;
  color: var(--body-strong);
  justify-self: end;
  margin: 0;
  white-space: nowrap;
  transition: color .35s ease;
}

/* Interactive hover state — warm tint, logo pop, company shifts
   to ink-strong, role/duration to body. */
.experience__item:hover,
.experience__item:focus-visible {
  background-color: var(--surface-soft);
  transform: translateX(-2px);
}
.experience__item:hover .experience__logo,
.experience__item:focus-visible .experience__logo {
  transform: scale(1.06) rotate(-2deg);
}
.experience__item:hover .experience__company,
.experience__item:focus-visible .experience__company {
  color: var(--body-strong);
  transform: translateX(4px);
}
/* Role colour is now driven by the layer system below — the rest
   layer keeps its muted tone, the impact layer comes in at body
   strength on hover. */
.experience__item:hover .experience__duration,
.experience__item:focus-visible .experience__duration {
  color: var(--body-strong);
}

@media (prefers-reduced-motion: reduce) {
  .experience__item,
  .experience__logo,
  .experience__company { transition: none; }
  .experience__item:hover,
  .experience__item:focus-visible { transform: none; }
  .experience__item:hover .experience__logo,
  .experience__item:focus-visible .experience__logo { transform: none; }
  .experience__item:hover .experience__company,
  .experience__item:focus-visible .experience__company { transform: none; }
  /* Reduced motion still gets the impact swap on hover, but with no
     translate and an instant fade — the content change is preserved,
     the motion isn't. */
  .experience__role-layer { transition: opacity .15s linear; transform: none !important; }
  .experience__item:hover .experience__role-layer--impact,
  .experience__item:focus-visible .experience__role-layer--impact { transition-delay: 0s; }
}

/* Responsive */
@media (max-width: 1024px) {
  .experience { padding: 48px var(--pad-page) 104px; }
  .experience__item {
    grid-template-columns: 52px minmax(140px, 1fr) minmax(180px, 1.3fr) 92px 106px;
    gap: 22px;
    padding: 22px 18px;
  }
  .experience__logo { height: 48px; width: 48px; }
}

@media (max-width: 880px) {
  .experience { padding: 40px var(--pad-page) 88px; }
  .experience__eyebrow { margin-bottom: 48px; }
  .experience__item {
    grid-template-columns: 48px minmax(130px, 1fr) 92px;
    gap: 14px 18px;
    padding: 22px 16px;
  }
  .experience__logo {
    grid-column: 1;
    grid-row: 1 / 4;
    align-self: start;
    margin-top: 4px;
    height: 44px;
    width: 44px;
  }
  .experience__company {
    grid-column: 2;
    grid-row: 1;
  }
  .experience__role {
    grid-column: 2 / span 2;
    grid-row: 2;
    font-size: 15px;
  }
  .experience__duration {
    grid-column: 3;
    grid-row: 1;
    align-self: start;
    margin-top: 8px;
  }
  .experience__years {
    grid-column: 2 / span 2;
    grid-row: 3;
    text-align: left;
    justify-self: start;
    font-size: 12px;
    color: var(--muted);
  }
  .experience__cue { display: none; }
  .experience__item:hover .experience__company,
  .experience__item:focus-visible .experience__company { transform: none; }
}

@media (max-width: 480px) {
  .experience { padding: 32px var(--pad-page) 72px; }
  .experience__eyebrow { font-size: 11px; margin-bottom: 32px; }
  .experience__item {
    grid-template-columns: 38px 1fr 80px;
    gap: 10px 12px;
    padding: 18px 12px;
  }
  .experience__logo { height: 36px; width: 36px; margin-top: 2px; }
  .experience__role { font-size: 14px; }
  .experience__duration { font-size: 11px; margin-top: 4px; }
  .experience__years { font-size: 11px; }
}

/* ============================================================
   BIRD BUDDY — pixel-art companion in the bottom-right corner
   Click the bird → a tiny pixel speech bubble pops above with a
   randomised, positive tech-flavoured message. Bubble auto-hides
   after a few seconds. Reduced-motion friendly: no idle bob, no
   transitions when the user prefers reduced motion.
   ============================================================ */
.bird-buddy {
  /* Perches at the right edge of the footer (.sitefoot is position:relative).
     Scrolls with the page so the bird only enters the frame when the user
     reaches the bottom — no longer floating over dark content mid-scroll. */
  position: absolute;
  right: max(20px, env(safe-area-inset-right));
  bottom: 16px;
  z-index: 2;
  /* The wrapper itself shouldn't intercept clicks — only the bird
     button does. The bubble is decorative + a11y status. */
  pointer-events: none;
  display: flex;
  flex-direction: column;
  align-items: flex-end;
  gap: 6px;
}

.bird-buddy__btn {
  pointer-events: auto;
  appearance: none;
  border: 0;
  background: transparent;
  padding: 6px;
  margin: 0;
  cursor: pointer;
  line-height: 0;
  border-radius: 4px;
  -webkit-tap-highlight-color: transparent;
}
.bird-buddy__svg {
  display: block;
  width: 48px;
  height: auto;
  image-rendering: pixelated;
  image-rendering: crisp-edges;
  /* Idle bob: a single-pixel hover loop gives the bird a sense of
     life. Disabled under prefers-reduced-motion below. */
  animation: bird-bob 3.2s ease-in-out infinite;
  transition: transform .25s cubic-bezier(.2, .7, .2, 1);
  transform-origin: center;
}
.bird-buddy__btn:hover .bird-buddy__svg,
.bird-buddy__btn:focus-visible .bird-buddy__svg {
  /* Pause the bob and tilt slightly toward the cursor on hover. */
  animation-play-state: paused;
  transform: translateY(-2px) rotate(-4deg);
}
.bird-buddy__btn:active .bird-buddy__svg {
  transform: translateY(0) rotate(2deg);
}
.bird-buddy__btn:focus-visible {
  outline: 2px solid var(--ink);
  outline-offset: 3px;
}

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

/* Pixel speech bubble — pure pixel-art look using a 9-slice
   border-image. The 9x9 source SVG holds stepped pixel-art corners
   and 1-pixel black edges; border-image stretches only the edges,
   keeping corners crisp at any size. The interior is filled by the
   `fill` keyword on border-image-slice (so the bubble is white
   inside without any background-color). The bubble sizes itself to
   the text — no fixed width. */
.bird-buddy__bubble {
  pointer-events: none;
  display: inline-block;
  align-self: flex-end;     /* stay pinned to the right above the bird */
  position: relative;
  margin: 0 6px 0 0;
  padding: 6px 12px 5px;

  /* 9-slice pixel-art rounded rectangle.
     Source: 9x9 grid, 3-cell corners, 1-cell edges.
     Border-width 3px renders each source cell at 1 screen pixel,
     so the stroke reads as a 1px hairline regardless of bubble size. */
  border: 3px solid transparent;
  border-image-source: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' width='9' height='9' viewBox='0 0 9 9' shape-rendering='crispEdges'><rect x='2' y='1' width='5' height='7' fill='%23ffffff'/><rect x='1' y='2' width='7' height='5' fill='%23ffffff'/><rect x='2' y='0' width='5' height='1' fill='%23141413'/><rect x='2' y='8' width='5' height='1' fill='%23141413'/><rect x='0' y='2' width='1' height='5' fill='%23141413'/><rect x='8' y='2' width='1' height='5' fill='%23141413'/><rect x='1' y='1' width='1' height='1' fill='%23141413'/><rect x='7' y='1' width='1' height='1' fill='%23141413'/><rect x='1' y='7' width='1' height='1' fill='%23141413'/><rect x='7' y='7' width='1' height='1' fill='%23141413'/></svg>");
  border-image-slice: 3 fill;
  border-image-width: 3;
  border-image-repeat: stretch;
  background: transparent;

  font-family: var(--font-display, 'VT323', monospace);
  font-size: 20px;
  line-height: 1.05;
  letter-spacing: 0.01em;
  color: var(--ink, #141413);
  white-space: nowrap;       /* messages are short by design — keep on one line */

  opacity: 0;
  transform: translateY(4px) scale(0.96);
  transform-origin: bottom right;
  transition:
    opacity .2s ease-out,
    transform .2s cubic-bezier(.2, .7, .2, 1);
}
.bird-buddy__bubble.is-visible {
  opacity: 1;
  transform: translateY(0) scale(1);
}

.bird-buddy__bubble-text {
  display: inline-block;
}

/* Pixel tail — small 5x4 sprite that tucks under the bubble's
   bottom edge. Its top row is white so it cleanly punches through
   the bubble's bottom border at the attach point. */
.bird-buddy__bubble-tail {
  position: absolute;
  right: 14px;
  bottom: -9px;            /* top 3px overlaps the bubble's bottom border */
  width: 15px;             /* 5 cells × 3px */
  height: 12px;            /* 4 cells × 3px */
  display: block;
  background-image: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' width='5' height='4' viewBox='0 0 5 4' shape-rendering='crispEdges'><rect x='0' y='0' width='5' height='1' fill='%23ffffff'/><rect x='1' y='1' width='3' height='1' fill='%23ffffff'/><rect x='2' y='2' width='1' height='1' fill='%23ffffff'/><rect x='0' y='1' width='1' height='1' fill='%23141413'/><rect x='4' y='1' width='1' height='1' fill='%23141413'/><rect x='1' y='2' width='1' height='1' fill='%23141413'/><rect x='3' y='2' width='1' height='1' fill='%23141413'/><rect x='2' y='3' width='1' height='1' fill='%23141413'/></svg>");
  background-repeat: no-repeat;
  background-size: 100% 100%;
  image-rendering: pixelated;
  image-rendering: crisp-edges;
}

@media (max-width: 480px) {
  .bird-buddy { right: 14px; bottom: 12px; }
  .bird-buddy__svg { width: 40px; }
  .bird-buddy__bubble { font-size: 18px; }
}

@media (prefers-reduced-motion: reduce) {
  .bird-buddy__svg { animation: none; transition: none; }
  .bird-buddy__btn:hover .bird-buddy__svg,
  .bird-buddy__btn:focus-visible .bird-buddy__svg,
  .bird-buddy__btn:active  .bird-buddy__svg { transform: none; }
  .bird-buddy__bubble { transition: none; }
}
