/* ==========================================================================
   jamielewis.design — v6.1
   dark only. mobile-first. no dependencies.

   CHANGES FROM v6.0
   1. .case__caption — captions under case-study shots. uxleague still runs
      without them; POWWR needs them, because four unlabelled screenshots of
      an enterprise tender flow read as wallpaper. Uses the same 01/02/03
      index device as the services list, so it's language the site already
      speaks.
   2. The case-study shot stagger is no longer a hard-coded nth-child list
      capped at three images. script.js writes each shot's index to --sd and
      the delay is calculated from --shot-start / --shot-stagger (both fed
      from #motion-config). Any number of shots now staggers correctly.

   CHANGES FROM v5.2
   1. .card__stroke (the rotating hover trace) is gone entirely — markup,
      keyframes, reduced-motion clause. The eye cursor stays.
   2. The featured-work carousel is gone. Replaced by .work-grid — two per
      row, inside the page gutter like every other section.
   3. .case-study (inline accordion) replaced by .case — a full-screen
      takeover with its own room, its own scroll, and a deep-linkable hash.
   ========================================================================== */

/* --------------------------------------------------------------- tokens */

:root {
  /* type — "plain" for now: whatever the OS considers its best sans */
  --font: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica,
          Arial, "Helvetica Neue", sans-serif;

  /* rhythm */
  --gutter: clamp(1.25rem, 5vw, 5rem);
  --nav-h: 4rem;
  --section-gap: clamp(5rem, 14vw, 11rem);
  --radius: 2px;

  /* motion */
  --ease-out: cubic-bezier(0.16, 1, 0.3, 1);
  --ease-soft: cubic-bezier(0.2, 0.8, 0.2, 1);

  /* palette — one state. the site is dark; there is no other. */
  --bg:        #0B0B0C;
  --bg-2:      #121214;
  --panel:     #1A1A1D;
  --line:      #26262A;
  --line-hot:  #3A3A40;
  --text:      #F2F0EC;
  --muted:     #8C8880;
  --menu-bg:   #000000;   /* fully opaque — the overlay goes blacker than the page */
  --accent:    #B8C0FF;
  --accent-ink:#0B0B0C;

  --pastel-1:  #A8E6CF;   /* mint       */
  --pastel-2:  #B8C0FF;   /* periwinkle */
  --pastel-3:  #FFC9DE;   /* blush      */

  --ice:       #7DF2FF;   /* the work section's one signal colour */

  --thumb:     linear-gradient(155deg, #2A2A2F 0%, #1D1D21 100%);

  /* tells the UA to paint form controls, scrollbars and the caret dark too */
  color-scheme: dark;
}

/* ----------------------------------------------------------- base reset */

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

html {
  -webkit-text-size-adjust: 100%;
  scroll-behavior: smooth;
  scroll-padding-top: calc(var(--nav-h) + 1rem);
}

body {
  margin: 0;
  background: var(--bg);
  color: var(--text);
  font-family: var(--font);
  font-size: clamp(1rem, 0.94rem + 0.3vw, 1.0625rem);
  line-height: 1.55;
  font-weight: 400;
  letter-spacing: -0.005em;
  -webkit-font-smoothing: antialiased;
  overflow-x: hidden;
}

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

h1, h2, h3, p, ul, ol { margin: 0; }
ul, ol { list-style: none; padding: 0; }

a { color: inherit; }

button {
  font: inherit;
  color: inherit;
  background: none;
  border: 0;
  cursor: pointer;
}

::selection { background: var(--accent); color: var(--accent-ink); }

:focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: 3px;
  border-radius: var(--radius);
}

.sr-only {
  position: absolute;
  width: 1px; height: 1px;
  padding: 0; margin: -1px;
  overflow: hidden;
  clip: rect(0 0 0 0);
  clip-path: inset(50%);
  white-space: nowrap;
  border: 0;
}

.skip-link {
  position: fixed;
  top: 0.5rem; left: 0.5rem;
  z-index: 200;
  padding: 0.65rem 1rem;
  background: var(--accent);
  color: var(--accent-ink);
  text-decoration: none;
  border-radius: var(--radius);
  transform: translateY(-160%);
  transition: transform 220ms var(--ease-out);
}
.skip-link:focus-visible { transform: none; }

/* ------------------------------------------------------------------ nav */

.nav {
  position: fixed;
  inset: 0 0 auto 0;
  z-index: 120;               /* above the menu overlay: the X stays clickable */
  height: var(--nav-h);
  padding: 0 var(--gutter);
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 1rem;
  background: color-mix(in srgb, var(--bg) 78%, transparent);
  backdrop-filter: saturate(140%) blur(12px);
  -webkit-backdrop-filter: saturate(140%) blur(12px);
}

/* when the menu is open the bar sits on the opaque overlay, not the page */
.menu-open .nav {
  background: var(--menu-bg);
  backdrop-filter: none;
  -webkit-backdrop-filter: none;
}

.nav__brand {
  display: inline-flex;
  align-items: center;
  gap: 0.65rem;
  text-decoration: none;
  border-radius: 999px;
}

.nav__avatar {
  width: 2.125rem;
  height: 2.125rem;
  border-radius: 999px;
  object-fit: cover;
  border: 1px solid var(--line-hot);
  filter: grayscale(1);
  transition: filter 400ms var(--ease-soft), transform 400ms var(--ease-out);
}
.nav__brand:hover .nav__avatar,
.nav__brand:focus-visible .nav__avatar { filter: none; transform: scale(1.06); }

.nav__wordmark {
  font-size: 0.875rem;
  letter-spacing: 0.01em;
  color: var(--muted);
}
@media (max-width: 30rem) { .nav__wordmark { display: none; } }

.nav__toggle {
  display: inline-flex;
  align-items: center;
  gap: 0.7rem;
  padding: 0.5rem 0;
  border-radius: var(--radius);
}

.nav__toggle-label {
  font-size: 0.8125rem;
  letter-spacing: 0.08em;
  text-transform: lowercase;
  color: var(--text);
}

/* hamburger → X. two bars is all it takes to make a clean cross. */
.icon {
  position: relative;
  width: 1.5rem;
  height: 1.5rem;
  display: block;
}
.icon__bar {
  position: absolute;
  left: 0;
  width: 100%;
  height: 1.5px;
  background: currentColor;
  transition: transform 380ms var(--ease-soft);
  will-change: transform;
}
.icon__bar:nth-child(1) { top: 0.5rem; }
.icon__bar:nth-child(2) { top: 0.9375rem; }

/* desktop-only hover flip — pointer devices only, never on touch */
@media (hover: hover) and (pointer: fine) {
  .nav__toggle:hover .icon__bar:nth-child(1),
  .nav__toggle:focus-visible .icon__bar:nth-child(1) {
    transform: translateY(0.21875rem) rotate(45deg);
  }
  .nav__toggle:hover .icon__bar:nth-child(2),
  .nav__toggle:focus-visible .icon__bar:nth-child(2) {
    transform: translateY(-0.21875rem) rotate(-45deg);
  }
}

/* open state wins on every device and stays X */
.nav__toggle[aria-expanded="true"] .icon__bar:nth-child(1) {
  transform: translateY(0.21875rem) rotate(45deg);
}
.nav__toggle[aria-expanded="true"] .icon__bar:nth-child(2) {
  transform: translateY(-0.21875rem) rotate(-45deg);
}

/* ----------------------------------------------------------------- menu */

.menu {
  position: fixed;
  inset: 0;
  z-index: 110;
  background: var(--menu-bg);   /* pure black / pure white. zero transparency. */
  display: flex;
  opacity: 0;
  transition: opacity 260ms var(--ease-soft);
}
.menu[hidden] { display: none; }
.menu.is-open { opacity: 1; }

.menu__inner {
  width: 100%;
  padding: calc(var(--nav-h) + clamp(2rem, 8vh, 5rem)) var(--gutter) calc(var(--gutter) + env(safe-area-inset-bottom));
  display: flex;
  flex-direction: column;
  justify-content: space-between;
  gap: clamp(2rem, 6vh, 3rem);
  overflow-y: auto;
  overscroll-behavior: contain;
  -webkit-overflow-scrolling: touch;
}

.menu__list { display: flex; flex-direction: column; gap: clamp(0.25rem, 2vh, 1rem); }

.menu__item a {
  display: inline-block;
  padding: 0.15em 0;
  font-size: clamp(1.875rem, 8vw, 3.25rem);
  line-height: 1.06;
  font-weight: 500;
  letter-spacing: -0.035em;
  text-transform: lowercase;
  text-decoration: none;
  color: color-mix(in srgb, var(--text) 92%, transparent);
  transition: color 220ms var(--ease-soft), transform 420ms var(--ease-out);
}
@media (hover: hover) and (pointer: fine) {
  .menu__item a:hover { color: var(--accent); transform: translateX(0.5rem); }
}

/* staggered entrance, driven by --i set in the markup order */
.menu.is-open .menu__item {
  animation: menuIn 520ms var(--ease-out) both;
  animation-delay: calc(60ms + var(--i, 0) * 55ms);
}
@keyframes menuIn {
  from { opacity: 0; transform: translateY(0.9rem); }
  to   { opacity: 1; transform: none; }
}

/* the foot is just the contact block now. .contact-block already carries its
   own hairline and top spacing, so the foot needs no rule of its own here —
   a second border would draw the same line twice. (Rail mode pins it with
   margin-top: auto in the 64rem block.) */

/* ----------------------------------------------------------------- hero */

.hero {
  min-height: 100vh;
  min-height: 100svh;
  padding: calc(var(--nav-h) + 2rem) var(--gutter) clamp(3rem, 8vh, 5rem);
  display: flex;
  flex-direction: column;
  justify-content: center;
  gap: clamp(1.75rem, 5vh, 3.25rem);
  position: relative;
}

.hero__h1 {
  /* THE KNOB. the vh term is deliberate: the headline has to fill the screen,
     so it has to know how tall the screen is, not just how wide.
     (vh, not svh — it stays put when mobile browser chrome collapses.) */
  font-size: clamp(2rem, 2.6vw + 3.4vh + 0.4rem, 6rem);
  line-height: 1.055;
  letter-spacing: -0.035em;
  font-weight: 500;
  text-wrap: pretty;
  color: var(--muted);
  max-width: 26ch;
  max-width: min(100%, 26ch);
}
/* tighter measure once there's room: more lines = the block still fills the screen */
@media (min-width: 48rem) { .hero__h1 { max-width: min(100%, 20ch); } }

/* the joke sets up, the payoff lands — so the payoff gets full contrast */
.hero__h1 .lit { color: var(--text); }

/* word-level entrance. only active once JS has split the headline. */
.hero__h1.is-split .word {
  display: inline-block;
  opacity: 0;
  transform: translateY(0.42em);
  animation: wordIn var(--word-dur, 820ms) var(--word-ease, cubic-bezier(0.16,1,0.3,1)) both;
  animation-delay: calc(var(--start, 120ms) + var(--i) * var(--stagger, 16ms));
  /* no will-change: it promoted every word to its own compositor layer
     permanently and never released them, so a theme swap had to re-rasterise
     40+ layers in the element that fills the screen. A one-shot entrance
     doesn't need the hint — it needs the layers gone afterwards. */
}
@keyframes wordIn {
  to { opacity: 1; transform: none; }
}

.hero__h2 {
  font-size: clamp(1.0625rem, 0.95rem + 1.1vw, 1.75rem);
  font-weight: 400;
  line-height: 1.25;
  letter-spacing: -0.015em;
  color: var(--muted);
  opacity: 0;
  animation: fadeUp 700ms var(--ease-out) 780ms both;
}
@keyframes fadeUp {
  from { opacity: 0; transform: translateY(0.6rem); }
  to   { opacity: 1; transform: none; }
}

.flip-line {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  column-gap: 0.4ch;
  row-gap: 0.1em;
}
.flip-line__static { color: var(--text); }

/* the flip window: a grid stack, so its box is the tallest/widest phrase.
   no measuring, no layout jump when the text changes length. */
.flip {
  display: inline-grid;
  overflow: hidden;
  perspective: 700px;
}
.flip__item {
  grid-area: 1 / 1;
  transform: rotateX(90deg);
  opacity: 0;
  transform-origin: 50% 50%;
  backface-visibility: hidden;
  transition:
    transform var(--flip-dur, 620ms) var(--flip-ease, cubic-bezier(0.2,0.8,0.2,1)),
    opacity calc(var(--flip-dur, 620ms) * 0.55) linear;
  will-change: transform, opacity;
  color: var(--pastel-1);
}
.flip__item.is-in   { transform: rotateX(0deg);   opacity: 1; }
.flip__item.is-out  { transform: rotateX(-90deg); opacity: 0; }

.hero__cue {
  position: absolute;
  left: var(--gutter);
  bottom: clamp(1.25rem, 3vh, 2rem);
  width: 1px;
  height: clamp(1.75rem, 4vh, 2.75rem);
  background: linear-gradient(to bottom, var(--line-hot), transparent);
  animation: cue 2.6s var(--ease-soft) infinite;
}
@keyframes cue {
  0%, 100% { transform: scaleY(0.35); transform-origin: top; opacity: 0.4; }
  50%      { transform: scaleY(1);    transform-origin: top; opacity: 1; }
}

/* -------------------------------------------------------------- section */

.section {
  padding-block: var(--section-gap);
  border-top: 1px solid var(--line);
}
/* CHANGED: work now sits in the gutter with everything else — no edge bleed. */
.section--work,
.section--journal,
.section--services,
.section--contact { padding-inline: var(--gutter); }

.section__head {
  max-width: 46rem;
  margin-bottom: clamp(2.25rem, 6vw, 3.75rem);
}

.section__title {
  font-size: clamp(1.75rem, 1.2rem + 2.4vw, 3.25rem);
  font-weight: 500;
  line-height: 1.05;
  letter-spacing: -0.035em;
  text-transform: lowercase;
  margin-bottom: 0.75rem;
}
.section__note {
  color: var(--muted);
  max-width: 52ch;
  text-wrap: pretty;
}

/* ---------------------------------------------- journal (homepage teaser) */

/* single featured post, styled as a quiet threshold rather than a "card" —
   the site has no card chrome outside .card's work thumbnails, and this
   isn't visual work, so it earns its own restrained treatment: a hairline
   frame, generous padding, everything reachable as one link target. */
.journal-feature {
  position: relative;
  max-width: 46rem;
  padding: clamp(1.75rem, 4vw, 2.75rem);
  border: 1px solid var(--line);
  border-radius: var(--radius);
  transition: border-color 260ms var(--ease-soft), transform 300ms var(--ease-out);
}
@media (hover: hover) and (pointer: fine) {
  .journal-feature:hover { border-color: var(--line-hot); transform: translateY(-2px); }
}

.journal-feature__meta {
  display: flex;
  gap: 0.5em;
  font-size: 0.8125rem;
  letter-spacing: 0.04em;
  color: var(--muted);
  margin-bottom: 0.85rem;
  font-variant-numeric: tabular-nums;
}

.journal-feature__title {
  font-size: clamp(1.375rem, 1.1rem + 1.3vw, 2rem);
  font-weight: 500;
  line-height: 1.15;
  letter-spacing: -0.025em;
  margin-bottom: 0.85rem;
  max-width: 22ch;
}
.journal-feature__title a {
  text-decoration: none;
  /* the whole block is clickable via the ::after stretch below, but this
     stays a real link too — keyboard and screen-reader users get a normal
     in-flow target, not just a hover affordance on the div. */
}
.journal-feature__title a::after {
  content: "";
  position: absolute;
  inset: 0;
}
.journal-feature__title a:focus-visible::after {
  outline: 2px solid var(--accent);
  outline-offset: 3px;
  border-radius: var(--radius);
}

.journal-feature__excerpt {
  color: var(--muted);
  max-width: 52ch;
  text-wrap: pretty;
  margin-bottom: 1.25rem;
}

.journal-feature__link {
  position: relative;
  z-index: 1;             /* sits above the stretched title link, own tab stop */
  display: inline-flex;
  font-size: 0.9375rem;
  color: var(--text);
  text-decoration: none;
  border-bottom: 1px solid var(--line-hot);
  padding-bottom: 0.1em;
  transition: color 220ms var(--ease-soft), border-color 220ms var(--ease-soft);
}
.journal-feature__link:hover { color: var(--accent); border-color: var(--accent); }

/* reveal on scroll */
[data-reveal] {
  opacity: 0;
  transform: translateY(1.25rem);
  transition: opacity 700ms var(--ease-out), transform 700ms var(--ease-out);
}
[data-reveal].is-visible { opacity: 1; transform: none; }

/* ==========================================================================
   featured work — a two-up grid
   CHANGED: the carousel is gone. It bled past the gutter, hid two of the
   five projects behind a scroll gesture, and made the section read as a
   widget rather than as the page. A grid puts all five on the page, keeps
   the site's left edge unbroken, and needs no JS at all.
   ========================================================================== */

.work-grid {
  display: grid;
  grid-template-columns: 1fr;
  gap: clamp(2.75rem, 6vw, 4.5rem) clamp(1.5rem, 3vw, 2.5rem);
}
/* two per row from 48rem (768px). Below that a single column reads better
   than two 160px-wide thumbnails. */
@media (min-width: 48rem) {
  .work-grid { grid-template-columns: repeat(2, minmax(0, 1fr)); }
}

/* a light sequential stagger on the reveal — enough to read as choreography,
   short enough that nobody waits for it */
.work-grid .card:nth-child(1) { --rd: 0; }
.work-grid .card:nth-child(2) { --rd: 1; }
.work-grid .card:nth-child(3) { --rd: 2; }
.work-grid .card:nth-child(4) { --rd: 3; }
.work-grid .card:nth-child(5) { --rd: 4; }
.work-grid .card[data-reveal] { transition-delay: calc(var(--rd, 0) * 70ms); }

.card__thumb {
  position: relative;
  overflow: hidden;
  /* 4/3, not 1/1: these are product screenshots, and a square crop was
     eating a third of every one of them. */
  aspect-ratio: 4 / 3;
  background: var(--thumb);
  background-size: cover;
  background-position: center;
  background-repeat: no-repeat;
  border: 1px solid var(--line);
  border-radius: var(--radius);
  margin-bottom: 1.15rem;
  transition: transform 500ms var(--ease-out), border-color 300ms var(--ease-soft);
}

/* a <button> is shrink-to-fit, so aspect-ratio was resolving against ~0
   content width and collapsing the thumb to a stub. it also carries UA
   padding, which pushed the case-study title out of alignment. An inline
   <a> has the same problem, so both interactive thumbs share the fix. */
.card__thumb--btn,
.card__thumb--link {
  display: block;
  width: 100%;
  padding: 0;
  appearance: none;
  -webkit-appearance: none;
  text-align: left;
  text-decoration: none;
}

/* two interactive cards, two different destinations: one opens a case study
   in place, one leaves the site. The eye cursor says "there's something
   here" on both — this glyph says which kind of something. */
.card__ext {
  position: absolute;
  top: 0.75rem;
  right: 0.75rem;
  width: 2rem;
  height: 2rem;
  display: grid;
  place-items: center;
  border-radius: 999px;
  background: color-mix(in srgb, var(--bg) 72%, transparent);
  -webkit-backdrop-filter: blur(6px);
  backdrop-filter: blur(6px);
  color: var(--ice);
  opacity: 0;
  transform: translate3d(0, -4px, 0);
  transition: opacity 240ms var(--ease-soft), transform 380ms var(--ease-out);
}
.card__ext svg { width: 0.95rem; height: 0.95rem; }

@media (hover: hover) and (pointer: fine) {
  .card--link:hover .card__ext { opacity: 1; transform: none; }
}
.card--link:focus-within .card__ext { opacity: 1; transform: none; }

@media (hover: hover) and (pointer: fine) {
  .card:hover .card__thumb { transform: translateY(-4px); border-color: var(--line-hot); }

  /* THE EYE. The rotating stroke is gone; this is the whole hover state now.
     32px is the largest size every browser reliably honours for a custom
     cursor; 16 16 puts the pupil under the pointer, not the corner.
     TO NARROW IT to only the cards that actually open something, change the
     selector below to:  .card--case .card__thumb  */
  .card__thumb {
    cursor: url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 32 32"><path d="M16 7.5c7 0 12 6.2 12 8.5s-5 8.5-12 8.5S4 18.3 4 16s5-8.5 12-8.5z" fill="rgba(11,11,12,0.55)" stroke="%237DF2FF" stroke-width="1.7"/><circle cx="16" cy="16" r="3.5" fill="%237DF2FF"/></svg>') 16 16, pointer;
  }
}

/* the stroke used to be the keyboard signal too. this replaces it — same
   information, no animation, and it lives outside the hover query on purpose. */
.card:focus-within .card__thumb {
  border-color: var(--ice);
  transform: translateY(-4px);
}

.card__title {
  font-size: 1.0625rem;
  font-weight: 500;
  line-height: 1.25;
  letter-spacing: -0.015em;
  text-transform: lowercase;   /* keeps real casing in the DOM for search + screen readers */
  margin-bottom: 0.45rem;
}
.card__bio {
  color: var(--muted);
  font-size: 0.9375rem;
  line-height: 1.5;
  text-wrap: pretty;
  max-width: 44ch;
}
.card__bio a {
  color: var(--text);
  text-decoration: underline;
  text-underline-offset: 0.18em;
  text-decoration-thickness: 1px;
  text-decoration-color: var(--line-hot);
  transition: color 200ms var(--ease-soft), text-decoration-color 200ms var(--ease-soft);
}
.card__bio a:hover { color: var(--accent); text-decoration-color: var(--accent); }

/* the title on an interactive card is a button (case study) or a link
   (outbound), but either way it must read as the same heading */
.card__title-btn,
.card__link {
  padding: 0;
  text-align: left;
  font: inherit;
  letter-spacing: inherit;
  color: inherit;
  text-decoration: none;
  border-bottom: 1px solid transparent;
  transition: color 200ms var(--ease-soft), border-color 200ms var(--ease-soft);
}
@media (hover: hover) and (pointer: fine) {
  .card--case:hover .card__title-btn,
  .card--link:hover .card__link { color: var(--ice); border-bottom-color: var(--ice); }
}
.card--case:focus-within .card__title-btn,
.card--link:focus-within .card__link { color: var(--ice); border-bottom-color: var(--ice); }

/* ------------------------------------------------------------- services */

.services {
  display: grid;
  gap: 0;
  border-top: 1px solid var(--line);
  max-width: 68rem;
}

.service {
  display: grid;
  grid-template-columns: 2.5rem 1fr;
  gap: 0.5rem 1.25rem;
  padding: clamp(1.75rem, 4vw, 2.75rem) 0;
  border-bottom: 1px solid var(--line);
}
@media (min-width: 56rem) {
  .service {
    grid-template-columns: 3.5rem 16rem 1fr;
    align-items: start;
    gap: 2rem;
  }
}

.service__num {
  font-size: 0.75rem;
  letter-spacing: 0.1em;
  color: var(--muted);
  padding-top: 0.42em;
  font-variant-numeric: tabular-nums;
}
.service__title {
  font-size: clamp(1.25rem, 1rem + 1.1vw, 1.75rem);
  font-weight: 500;
  letter-spacing: -0.025em;
  line-height: 1.15;
  text-transform: lowercase;
}
.service__copy {
  color: var(--muted);
  max-width: 48ch;
  text-wrap: pretty;
}
@media (max-width: 55.999rem) {
  .service__copy { grid-column: 2; }
}

/* ----------------------------------------------------------------- form */

.form { max-width: 34rem; }

.field { margin-bottom: 1.5rem; }

.field label {
  display: block;
  font-size: 0.8125rem;
  letter-spacing: 0.05em;
  color: var(--muted);
  margin-bottom: 0.5rem;
  text-transform: lowercase;
}

.form input[type="text"],
.form input[type="email"],
.form textarea {
  width: 100%;
  padding: 0.85rem 0;
  font: inherit;
  color: var(--text);
  background: transparent;
  border: 0;
  border-bottom: 1px solid var(--line-hot);
  border-radius: 0;
  resize: vertical;
  transition: border-color 240ms var(--ease-soft);
}
.form input:focus,
.form textarea:focus {
  outline: none;
  border-bottom-color: var(--accent);
}
.form input:focus-visible,
.form textarea:focus-visible { outline: none; }

.hp { position: absolute; left: -9999px; width: 1px; height: 1px; overflow: hidden; }

.btn {
  margin-top: 0.75rem;
  padding: 0.95rem 2rem;
  background: var(--text);
  color: var(--bg);
  border-radius: 999px;
  font-size: 0.9375rem;
  letter-spacing: 0.01em;
  text-transform: lowercase;
  transition: transform 300ms var(--ease-out), background-color 240ms var(--ease-soft);
}
.btn:hover { transform: translateY(-2px); background: var(--accent); color: var(--accent-ink); }
.btn[disabled] { opacity: 0.45; pointer-events: none; }

.form__status {
  margin-top: 1rem;
  min-height: 1.5em;
  font-size: 0.9375rem;
  color: var(--muted);
}
.form__status.is-ok  { color: var(--pastel-1); }
.form__status.is-bad { color: var(--pastel-3); }

/* -------------------------------------------------------- journal (page) */

.journal-header {
  padding: calc(var(--nav-h) + clamp(2.5rem, 8vh, 4.5rem)) var(--gutter) clamp(2rem, 6vh, 3rem);
  max-width: 46rem;
  margin-inline: auto;
}
.journal-header__title {
  font-size: clamp(2rem, 1.3rem + 3vw, 3.5rem);
  font-weight: 500;
  line-height: 1.05;
  letter-spacing: -0.035em;
  text-transform: lowercase;
  margin-bottom: 0.75rem;
}
.journal-header__note {
  color: var(--muted);
  max-width: 46ch;
  text-wrap: pretty;
}

.journal-post {
  padding: clamp(2rem, 6vh, 3rem) var(--gutter) 0;
  max-width: 40rem;
  margin-inline: auto;
  border-top: 1px solid var(--line);
}

.journal-post__head { margin-bottom: clamp(2rem, 5vh, 2.75rem); }

.journal-post__meta {
  display: flex;
  gap: 0.5em;
  font-size: 0.8125rem;
  letter-spacing: 0.04em;
  color: var(--muted);
  margin-bottom: 1rem;
  font-variant-numeric: tabular-nums;
}

.journal-post__title {
  font-size: clamp(1.75rem, 1.2rem + 2.4vw, 3rem);
  font-weight: 500;
  line-height: 1.12;
  letter-spacing: -0.03em;
  text-wrap: pretty;
}

/* reading column: generous leading, restrained measure, quiet rhythm.
   only .journal-post's own <p>/<em>/<strong>/<a> — never bleeds into the
   rest of the site's tighter UI type. */
.journal-post p {
  margin-bottom: 1.5em;
  max-width: 38rem;
  line-height: 1.7;
  text-wrap: pretty;
}
.journal-post p:last-of-type { margin-bottom: 0; }

.journal-post em { color: var(--text); font-style: italic; }
.journal-post strong { color: var(--text); font-weight: 600; }

.journal-post__sign {
  margin-top: 2.5rem !important;
  color: var(--muted);
}

.journal-more {
  padding: clamp(2.5rem, 6vh, 3.5rem) var(--gutter) clamp(3rem, 8vh, 5rem);
  max-width: 40rem;
  margin-inline: auto;
  color: var(--muted);
  font-size: 0.9375rem;
}

/* --------------------------------------------------------------- footer */

.footer {
  padding: 2.5rem var(--gutter) calc(2.5rem + env(safe-area-inset-bottom));
  border-top: 1px solid var(--line);
  display: flex;
  flex-wrap: wrap;
  justify-content: space-between;
  gap: 0.5rem 2rem;
  font-size: 0.8125rem;
  color: var(--muted);
}

/* -------------------------------------------------- state: menu is open */

html.menu-open { overflow: hidden; }

/* ----------------------------------------------- rail: brand + contact */

/* the top bar already carries avatar + name on mobile — this is rail-only */
.menu__brand { display: none; }

.menu__item a[aria-current="page"] { color: var(--accent); }

.contact-block {
  margin-top: 1.5rem;
  padding-top: 1.5rem;
  border-top: 1px solid var(--line);
}

.contact-block__label {
  font-size: 0.75rem;
  letter-spacing: 0.14em;
  text-transform: lowercase;
  color: var(--muted);
  margin-bottom: 0.9rem;
}

.contact-block__list {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: 0.15rem;
}

/* the copy button and the linkedin link are the same object visually —
   one is a <button> only because clipboard access needs a real activation. */
.contact-link {
  display: inline-block;
  padding: 0.2em 0;
  font-size: 1.0625rem;
  line-height: 1.35;
  letter-spacing: -0.015em;
  text-align: left;
  text-transform: lowercase;
  text-decoration: none;
  color: color-mix(in srgb, var(--text) 92%, transparent);
  transition: color 200ms var(--ease-soft), transform 320ms var(--ease-out);
}
@media (hover: hover) and (pointer: fine) {
  .contact-link:hover { color: var(--accent); transform: translateX(0.3rem); }
}

.contact-block__clock {
  margin-top: 1.25rem;
  font-size: 0.8125rem;
  letter-spacing: 0.06em;
  color: var(--muted);
  font-variant-numeric: tabular-nums;
}

/* ---------------------------------------------------------------- toast */

.toast {
  position: fixed;
  z-index: 200;
  left: 50%;
  bottom: calc(1.25rem + env(safe-area-inset-bottom));
  max-width: calc(100vw - 2rem);
  padding: 0.7rem 1.15rem;
  background: var(--text);
  color: var(--bg);
  border-radius: 999px;
  font-size: 0.875rem;
  letter-spacing: -0.005em;
  opacity: 0;
  pointer-events: none;
  transform: translate(-50%, 0.75rem);
  /* transform + opacity only — compositor work, no layout, no paint storm */
  transition: opacity 220ms var(--ease-soft), transform 380ms var(--ease-out);
}
.toast.is-up  { opacity: 1; transform: translate(-50%, 0); }
.toast.is-bad { background: var(--pastel-3); color: #0B0B0C; }

/* ============================================ desktop: everpresent rail */
/* 64rem = 1024px. Below this nothing changes: the overlay behaves exactly
   as it did. Above it the same block is pinned left and the top bar goes. */

@media (min-width: 64rem) {

  :root {
    --rail-w: 248px;
    --nav-h: 0px;     /* no top bar up here — collapses hero/journal offsets */
  }

  .nav { display: none; }

  body { padding-left: var(--rail-w); }

  /* [hidden] is overridden so the rail paints before JS runs — script.js
     then removes the attribute outright so it's in the a11y tree too. */
  .menu,
  .menu[hidden] {
    display: block;
    position: fixed;
    inset: 0 auto 0 0;
    width: var(--rail-w);
    z-index: 120;
    opacity: 1;
    background: var(--bg);
    border-right: 1px solid var(--line);
  }

  .menu__inner {
    height: 100%;
    padding: 24px;
    justify-content: flex-start;
    gap: 2.75rem;
  }

  .menu__brand {
    display: inline-flex;
    align-items: center;
    gap: 0.7rem;
    text-decoration: none;
    border-radius: 999px;
  }
  .menu__avatar {
    width: 2.5rem;
    height: 2.5rem;
    border-radius: 999px;
    object-fit: cover;
    border: 1px solid var(--line-hot);
    filter: grayscale(1);
    transition: filter 400ms var(--ease-soft), transform 400ms var(--ease-out);
  }
  .menu__brand:hover .menu__avatar,
  .menu__brand:focus-visible .menu__avatar { filter: none; transform: scale(1.06); }

  .menu__name {
    font-size: 0.9375rem;
    font-weight: 500;
    letter-spacing: -0.01em;
  }

  /* links drop to body scale — at rail width the big type was the design,
     here it would just be four lines of wrapped text */
  .menu__list { gap: 0.1rem; }
  .menu__item a {
    font-size: 1.0625rem;
    font-weight: 500;
    line-height: 1.35;
    letter-spacing: -0.015em;
    padding: 0.2em 0;
  }
  .menu__item a:hover { transform: translateX(0.3rem); }
  /* the rail is always there — nothing to stage in */
  .menu.is-open .menu__item { animation: none; }

  /* contact pinned to the bottom of the rail */
  .menu__foot { margin-top: auto; }

  /* centre the toast over the content column, not the whole viewport */
  .toast { left: calc(50% + (var(--rail-w) / 2)); }
}

/* ==========================================================================
   CASE STUDY — the full-screen takeover
   CHANGED: the old inline accordion pushed the page around and left the
   case study reading as a footnote to the card that opened it. This gives
   it the whole screen — the rail included — so the work is the only thing
   in the room. It gets its own scroll, its own quiet top bar, and its own
   URL (#uxleague, #powwr), so it can be linked and the back button closes it.

   Motion: the panel comes up on transform + opacity only (compositor work,
   no layout, no paint). The shots stagger in behind it on the same two
   properties. Nothing here animates width, height or box-shadow.
   ========================================================================== */

.case {
  position: fixed;
  inset: 0;
  z-index: 300;                 /* over the rail (120), the nav (120), the toast (200) */
  overflow-y: auto;
  overscroll-behavior: contain;
  -webkit-overflow-scrolling: touch;
  background: var(--bg);
  opacity: 0;
  transform: translate3d(0, 2.5rem, 0);
  /* sharp entrance, soft settle — --ease-out is the 0.16,1,0.3,1 curve */
  transition: opacity var(--case-close, 380ms) var(--ease-soft),
              transform var(--case-close, 380ms) var(--ease-out);
}
.case[hidden] { display: none; }
.case.is-open {
  opacity: 1;
  transform: none;
  transition-duration: var(--case-open, 560ms);
}

/* body carries overflow-x: hidden, which makes it a scroll container in its
   own right — so the lock has to name both or iOS keeps scrolling behind. */
html.case-open,
html.case-open body { overflow: hidden; }

/* ------------------------------------------------------------- the bar */

.case__bar {
  position: sticky;
  top: 0;
  z-index: 2;
  height: 4rem;
  padding-inline: var(--gutter);
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 1rem;
  background: color-mix(in srgb, var(--bg) 82%, transparent);
  backdrop-filter: saturate(140%) blur(12px);
  -webkit-backdrop-filter: saturate(140%) blur(12px);
  border-bottom: 1px solid transparent;
  transition: border-color 260ms var(--ease-soft);
}
/* the hairline only appears once there's something scrolled under it */
.case.is-scrolled .case__bar { border-bottom-color: var(--line); }

/* a real control. The arrow slides back on hover so the direction of travel
   is legible before the click, not after it. */
.case__crumb {
  display: inline-flex;
  align-items: center;
  gap: 0.5rem;
  padding: 0.4rem 0.5rem 0.4rem 0;
  font-size: 0.8125rem;
  letter-spacing: 0.02em;
  text-transform: lowercase;
  color: var(--muted);
  transition: color 200ms var(--ease-soft);
}
.case__crumb svg {
  width: 0.85rem;
  height: 0.85rem;
  transition: transform 320ms var(--ease-out);
}
.case__crumb:hover { color: var(--text); }
.case__crumb:hover svg { transform: translate3d(-3px, 0, 0); }

.case__close {
  display: inline-flex;
  align-items: center;
  gap: 0.5rem;
  padding: 0.4rem 0;
  font-size: 0.875rem;
  letter-spacing: 0.02em;
  text-transform: lowercase;
  color: var(--muted);
  transition: color 200ms var(--ease-soft);
}
.case__close svg { width: 0.9rem; height: 0.9rem; }
.case__close:hover { color: var(--text); }

/* ------------------------------------------------------- the document */

.case__doc {
  max-width: 74rem;
  margin-inline: auto;
  padding: clamp(3rem, 9vh, 6rem) var(--gutter) clamp(4rem, 12vh, 7rem);
}

.case__kicker {
  font-size: 0.75rem;
  letter-spacing: 0.18em;
  text-transform: lowercase;
  color: var(--ice);
  margin-bottom: 1.25rem;
}

/* the one place on the site the type is allowed to be this big besides the
   hero — it's a title page, and it should feel like one */
.case__title {
  font-size: clamp(2.125rem, 1.2rem + 4.2vw, 4.75rem);
  font-weight: 500;
  line-height: 1.01;
  letter-spacing: -0.042em;
  text-transform: lowercase;
  max-width: 17ch;
  text-wrap: balance;
}
.case__title:focus { outline: none; }   /* programmatic focus target only */
.case__title:focus-visible { outline: 2px solid var(--accent); outline-offset: 6px; }

/* ---------------------------------------------------------- the facts */

/* full column width on purpose: the hairlines line up with the edges of the
   images below them. the measure is held on the <dd>, not on the rules. */
.case__meta {
  margin: clamp(2.5rem, 6vw, 4rem) 0 0;
  border-top: 1px solid var(--line);
}

.case__meta-row {
  display: grid;
  grid-template-columns: 1fr;
  gap: 0.3rem;
  padding: 1.15rem 0;
  border-bottom: 1px solid var(--line);
}
@media (min-width: 40rem) {
  .case__meta-row {
    grid-template-columns: 10rem 1fr;
    gap: 2rem;
    align-items: baseline;
  }
}

.case__meta dt {
  font-size: 0.75rem;
  letter-spacing: 0.14em;
  text-transform: lowercase;
  color: var(--muted);
}
.case__meta dd {
  margin: 0;
  max-width: 52ch;
  text-wrap: pretty;
}

/* --------------------------------------------------------- the images */

/* Full aspect ratio, stacked, no crop. The width/height attributes in the
   markup reserve the exact box before load — set them to the real pixel
   dimensions of each file or you'll ship layout shift. */
.case__shots {
  margin-top: clamp(3rem, 8vw, 5.5rem);
  display: flex;
  flex-direction: column;
  gap: clamp(2.5rem, 6vw, 4.5rem);
}

.case__shot { margin: 0; }

.case__shot img {
  width: 100%;
  height: auto;
  border: 1px solid var(--line);
  border-radius: var(--radius);
  background: var(--thumb);
}

/* -------------------------------------------------------- the captions */

/* NEW in v6.1. Optional — a .case__shot with no figcaption behaves exactly
   as it did (uxleague still runs bare). The index column reuses the 01/02/03
   device from the services list rather than inventing a second one, and it's
   aria-hidden because the order is already carried by the DOM. */
.case__caption {
  display: grid;
  grid-template-columns: 2.25rem 1fr;
  gap: 0 1rem;
  margin-top: 1.15rem;
  max-width: 62ch;
  font-size: 0.9375rem;
  line-height: 1.6;
  color: var(--muted);
  text-wrap: pretty;
}

.case__caption-num {
  font-size: 0.75rem;
  letter-spacing: 0.1em;
  color: var(--ice);
  padding-top: 0.4em;
  font-variant-numeric: tabular-nums;
}

/* they arrive just behind the panel, one after the other.
   CHANGED in v6.1: was a hard-coded nth-child(1..3) list, which silently
   dropped the fourth shot's stagger the moment a case study grew. --sd is
   the shot's index, written by script.js; --shot-start / --shot-stagger come
   from #motion-config. Scales to any number of shots. */
.case.is-open .case__shot {
  animation: caseShotIn 720ms var(--ease-out) both;
  animation-delay: calc(var(--shot-start, 240ms) + var(--sd, 0) * var(--shot-stagger, 110ms));
}

@keyframes caseShotIn {
  from { opacity: 0; transform: translate3d(0, 1.75rem, 0); }
  to   { opacity: 1; transform: none; }
}

/* ----------------------------------------------------------- the foot */

.case__foot {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: space-between;
  gap: 1rem 2rem;
  margin-top: clamp(3rem, 8vw, 5rem);
  padding-top: clamp(1.5rem, 4vw, 2.5rem);
  border-top: 1px solid var(--line);
}

.case__link {
  font-size: 0.9375rem;
  text-decoration: none;
  border-bottom: 1px solid var(--line-hot);
  padding-bottom: 0.1em;
  transition: color 200ms var(--ease-soft), border-color 200ms var(--ease-soft);
}
.case__link:hover { color: var(--ice); border-color: var(--ice); }

.case__back {
  font-size: 0.875rem;
  letter-spacing: 0.02em;
  text-transform: lowercase;
  color: var(--muted);
  transition: color 200ms var(--ease-soft);
}
.case__back:hover { color: var(--text); }

/* ------------------------------------------------------- reduced motion */

@media (prefers-reduced-motion: reduce) {
  html { scroll-behavior: auto; }

  *, *::before, *::after {
    animation-duration: 0.001ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 0.001ms !important;
  }

  .hero__h1.is-split .word { opacity: 1; transform: none; }
  .hero__h2 { opacity: 1; }
  .hero__cue { display: none; }
  [data-reveal] { opacity: 1; transform: none; }

  /* the takeover simply appears — no slide, no stagger */
  .case { transform: none; }
  .case.is-open .case__shot { animation: none; opacity: 1; transform: none; }

  /* no loop: all three phrases just sit there, stacked and readable */
  .flip {
    display: block;
    overflow: visible;
  }
  .flip__item {
    display: block;
    transform: none !important;
    opacity: 1 !important;
  }
  .flip__item:nth-child(1) { color: var(--pastel-1); }
  .flip__item:nth-child(2) { color: var(--pastel-2); }
  .flip__item:nth-child(3) { color: var(--pastel-3); }
}

/* ==========================================================================
   beaver run — a small game, framed as an object on the page
   the site is quiet and dark; the game is loud and cartoon. the frame is
   what makes that read as deliberate: a handheld console sitting on a desk,
   not a stray widget. everything inside the screen belongs to the game's
   palette, everything outside it belongs to the site's.
   ========================================================================== */

.section--game { padding-inline: var(--gutter); }

.game {
  max-width: 26.5rem;
  margin-inline: auto;
}

/* on wide screens a centred console under a left-aligned heading reads as a
   mistake. two columns instead: the argument on the left, the object on the
   right, optically balanced. */
/* 74rem, not 64rem: from 64rem the left rail already takes 248px, so the
   two columns only actually fit a little later than the rail arrives. */
@media (min-width: 74rem) {
  .section--game {
    display: grid;
    grid-template-columns: minmax(15rem, 25rem) auto;
    justify-content: start;
    gap: clamp(2rem, 4.5vw, 5rem);
    align-items: center;
  }
  .section--game .section__head { margin-bottom: 0; }
}

/* ------------------------------------------------------------- the shell */

.game__frame {
  position: relative;
  padding: 0.625rem 0.625rem 0.5rem;
  background: var(--panel);
  border: 1px solid var(--line-hot);
  border-radius: 1.5rem;
  box-shadow:
    inset 0 1px 0 rgba(255, 255, 255, 0.045),
    0 24px 60px -34px #000;
}

.game__screen {
  position: relative;
  aspect-ratio: 3 / 4;
  border-radius: 0.875rem;
  overflow: hidden;
  background: #5C9B00;          /* matches the canvas bank — no flash on load */
  isolation: isolate;
}

.game__canvas {
  display: block;
  width: 100%;
  height: 100%;
  touch-action: manipulation;
}

/* ------------------------------------------------------------------ hud */

.game__hud {
  position: absolute;
  inset: 0.5rem 0.5rem auto;
  z-index: 2;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 1rem;
  pointer-events: none;
  font-size: 0.6875rem;
  letter-spacing: 0.06em;
}

/* pills, so the readout never has to fight whatever the canvas paints under it */
.game__stat,
.game__lives {
  display: flex;
  align-items: center;
  gap: 0.5rem;
  padding: 0.3rem 0.6rem;
  background: rgba(6, 16, 3, 0.62);
  border-radius: 999px;
}

.game__level { color: rgba(255, 255, 255, 0.7); text-transform: lowercase; }
.game__timer { color: #fff; font-variant-numeric: tabular-nums; }

.game__lives { gap: 0.25rem; padding-inline: 0.5rem; }
.game__life { display: block; color: #D79B57; transition: opacity 260ms var(--ease-soft); }
.game__life svg { display: block; width: 1rem; height: 1rem; }
.game__life.is-lost { opacity: 0.22; }

.game__rail {
  position: absolute;
  inset: auto 0 0 0;
  z-index: 2;
  height: 4px;
  background: rgba(6, 16, 3, 0.45);
}
.game__progress {
  display: block;
  height: 100%;
  width: 100%;
  background: #FFD447;
  transform: scaleX(0);
  transform-origin: left center;   /* transform only — no per-frame layout */
}

/* --------------------------------------------------------------- panels */

.game__panel {
  position: absolute;
  inset: 0;
  z-index: 3;
  display: flex;
  flex-direction: column;
  align-items: center;
  /* `safe` matters: on a short phone the start panel's content is taller than
     the screen, and plain `center` overflows in BOTH directions, clipping the
     button out of the top/bottom. `safe center` falls back to flex-start and
     the panel scrolls instead. */
  justify-content: center;
  justify-content: safe center;
  overflow-y: auto;
  overscroll-behavior: contain;
  gap: clamp(0.5rem, 2.4vw, 0.85rem);
  padding: clamp(1rem, 4vw, 1.5rem) clamp(1rem, 4.5vw, 1.35rem);
  text-align: center;
  color: #fff;
  background: rgba(5, 13, 2, 0.84);
  -webkit-backdrop-filter: blur(3px);
  backdrop-filter: blur(3px);
  animation: panelIn 240ms var(--ease-out) both;
}
.game__panel[hidden] { display: none; }

@keyframes panelIn {
  from { opacity: 0; transform: scale(0.985); }
  to   { opacity: 1; transform: none; }
}

.game__panel h3 {
  font-size: clamp(1.25rem, 1rem + 1.6vw, 1.875rem);
  font-weight: 600;
  line-height: 1.05;
  letter-spacing: -0.03em;
  text-transform: lowercase;
}
.game__panel p {
  font-size: 0.875rem;
  line-height: 1.5;
  color: rgba(255, 255, 255, 0.74);
  max-width: 24ch;
  text-wrap: pretty;
}
.game__panel--over h3 {
  text-transform: uppercase;     /* the one shout on the whole site */
  letter-spacing: 0.02em;
  color: #FFC9DE;
}
.game__crash-lives { color: #FFD447 !important; letter-spacing: 0.04em; }

/* chunky toy button — deliberately not .btn, it lives inside the game's world */
.game__btn {
  margin-top: 0.35rem;
  padding: 0.7rem 1.5rem;
  background: #FFD447;
  color: #2C1F08;
  border-radius: 999px;
  box-shadow: 0 3px 0 #C79A00;
  font-size: 0.9375rem;
  font-weight: 600;
  letter-spacing: 0.01em;
  text-transform: lowercase;
  transition: transform 180ms var(--ease-out), box-shadow 180ms var(--ease-out);
}
.game__btn:hover { transform: translateY(-1px); box-shadow: 0 4px 0 #C79A00; }
.game__btn:active { transform: translateY(3px); box-shadow: 0 0 0 #C79A00; }

.game__rules {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: 0.35rem;
  max-width: none !important;
  text-align: left;
  font-size: 0.8125rem;
  color: rgba(255, 255, 255, 0.7);
}
.game__rules span {
  position: relative;
  padding-left: 1rem;      /* hanging bullet — text stays flush left when it wraps */
}
.game__rules span::before {
  content: "";
  position: absolute;
  left: 0;
  top: 0.55em;
  width: 0.375rem; height: 0.375rem;
  border-radius: 999px;
  background: #FFD447;
}

/* ---------------------------------------------------------- celebration */

.game__chips {
  position: absolute;
  inset: 0;
  pointer-events: none;
  overflow: hidden;
}
.game__chip {
  position: absolute;
  left: 50%;
  top: 44%;
  width: var(--cw, 7px);
  height: calc(var(--cw, 7px) * 0.5);
  border-radius: 1px;
  background: #C98B45;
  animation: chipOut 900ms cubic-bezier(0.16, 1, 0.3, 1) var(--cd, 0ms) both;
}
.game__chip:nth-child(3n)   { background: #FFD447; }
.game__chip:nth-child(4n+1) { background: #7DF2FF; }

@keyframes chipOut {
  from { opacity: 0; transform: translate(-50%, -50%) rotate(0deg); }
  18%  { opacity: 1; }
  to   {
    opacity: 0;
    transform: translate(calc(-50% + var(--cx, 0px)), calc(-50% + var(--cy, 0px)))
               rotate(var(--cr, 180deg));
  }
}

/* ---------------------------------------------------------------- award */

.award { display: flex; flex-direction: column; align-items: center; gap: 0.6rem; }

/* column-reverse, not absolute positioning: the award timeline animates
   `transform`, and a transform used for centring would be wiped by it. */
.award__dam {
  position: relative;
  padding-top: 3.1rem;           /* the badge sits in here */
  display: flex;
  flex-direction: column-reverse;
  align-items: center;
  gap: 0.2rem;
  margin-bottom: 0.4rem;
}
.award__log {
  height: 0.7rem;
  border-radius: 999px;
  background: #8B5A2B;
  box-shadow: inset 0 -3px 0 rgba(0, 0, 0, 0.22);
}
.award__log:nth-of-type(1) { width: 9.5rem;  }
.award__log:nth-of-type(2) { width: 8rem;    }
.award__log:nth-of-type(3) { width: 6.25rem; }
.award__log:nth-of-type(4) { width: 4.5rem;  }

.award__badge {
  position: absolute;
  left: 50%;
  top: 0;
  width: 2.6rem; height: 2.6rem;
  margin-left: -1.3rem;
  display: grid;
  place-items: center;
  border-radius: 999px;
  background: radial-gradient(circle at 34% 30%, #FFE99A, #F0B429 62%, #C98A0A);
  color: #4A2E15;
}
.award__badge svg { width: 1.5rem; height: 1.5rem; display: block; }

.award__kicker {
  font-size: 0.6875rem !important;
  letter-spacing: 0.18em;
  text-transform: uppercase;
  color: #FFD447 !important;
}

/* elements are placed by the JSON timeline; --fx/--fy/--fr/--fs come from JS */
[data-award] { will-change: transform, opacity; }
@keyframes awardIn {
  from {
    opacity: 0;
    transform: translate(var(--fx, 0px), var(--fy, 0px))
               rotate(var(--fr, 0deg)) scale(var(--fs, 1));
  }
  to { opacity: 1; transform: none; }
}

/* ------------------------------------------------------------- the deck */

.game__pad {
  display: flex;
  justify-content: center;
  gap: 0.875rem;
  padding: 0.75rem 0 0.125rem;
}

.game__key {
  width: 5.75rem;
  height: 3.5rem;
  display: grid;
  place-items: center;
  border: 1px solid var(--line-hot);
  border-radius: 0.9rem;
  background: var(--bg-2);
  color: var(--text);
  touch-action: none;
  -webkit-user-select: none;
  user-select: none;
  -webkit-tap-highlight-color: transparent;
  transition: transform 120ms var(--ease-out), background-color 120ms linear;
}
.game__key svg { width: 1.5rem; height: 1.5rem; }
.game__key.is-down {
  background: var(--accent);
  color: var(--accent-ink);
  transform: translateY(2px);
}
@media (hover: hover) and (pointer: fine) {
  .game__key:hover { background: #202024; }
}

.game__hint {
  margin-top: 0.5rem;
  text-align: center;
  font-size: 0.75rem;
  letter-spacing: 0.02em;
  color: var(--muted);
}

@media (max-width: 22rem) {
  .game__key { width: 5rem; height: 3.25rem; }
}

/* ------------------------------------------------------- reduced motion */
/* the global reduce block already flattens every animation above. the game
   itself is opt-in behind a start button, so the canvas keeps running —
   what we drop is the decorative layer (handled in js: no wake, no chips). */
