/*
  verysmallgames.studio — stylesheet.

  One file, cascade layers, two visual identities:
  - Studio identity (black / white / mustard, Space Grotesk) is the default
    on :root and drives the header mark, the global footer band, and every
    page except the content well of the two below.
  - Game identity (delicatessen palette, Big Shoulders + Space Grotesk) is
    scoped to [data-theme="cheesy"], set on <body> in /cheesy/ and
    /cheesy/press/ only. Header and footer inside those pages reference the
    chrome tokens directly, never the themeable surface and text aliases,
    so they never flip themes.

  Self-hosted fonts referenced below don't exist as files yet, see
  PLACEHOLDERS.md once it's produced (step 9). Fallback stacks keep the page
  legible in the meantime.
*/

@font-face {
  font-family: 'Big Shoulders';
  src: url('/assets/fonts/big-shoulders/BigShoulders-SemiBold.woff2') format('woff2');
  font-weight: 600;
  font-style: normal;
  font-display: swap;
}

@font-face {
  font-family: 'Big Shoulders';
  src: url('/assets/fonts/big-shoulders/BigShoulders-Black.woff2') format('woff2');
  font-weight: 900;
  font-style: normal;
  font-display: swap;
}

@font-face {
  font-family: 'Space Grotesk';
  src: url('/assets/fonts/space-grotesk/SpaceGrotesk-Regular.woff2') format('woff2');
  font-weight: 400;
  font-style: normal;
  font-display: swap;
}

@font-face {
  font-family: 'Space Grotesk';
  src: url('/assets/fonts/space-grotesk/SpaceGrotesk-Medium.woff2') format('woff2');
  font-weight: 500;
  font-style: normal;
  font-display: swap;
}

@font-face {
  font-family: 'Space Grotesk';
  src: url('/assets/fonts/space-grotesk/SpaceGrotesk-Bold.woff2') format('woff2');
  font-weight: 700;
  font-style: normal;
  font-display: swap;
}

@layer reset, tokens, base, layout, components, utilities;

/* ============================== RESET =================================== */

@layer reset {

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

  * {
    margin: 0;
  }

  html {
    -webkit-text-size-adjust: 100%;
    text-size-adjust: 100%;
  }

  img,
  picture,
  svg,
  video {
    display: block;
    max-inline-size: 100%;
    block-size: auto;
  }

  input,
  button,
  textarea,
  select {
    font: inherit;
    color: inherit;
  }

  button {
    background: none;
    border: none;
    cursor: pointer;
    text-align: inherit;
  }

  ul,
  ol {
    list-style: none;
    padding-inline-start: 0;
  }

  a {
    color: inherit;
  }
}

/* ============================== TOKENS =================================== */

@layer tokens {
  :root {
    /* ---- Primitives: studio brand ---- */
    --colour-studio-black: #000000;
    --colour-studio-white: #ffffff;
    --colour-studio-mustard: #e49e01;

    /* ---- Primitives: game (salumeria) ---- */
    --colour-butcher-paper: #e8dcc8;
    --colour-paper-white: #f2ecdf;
    --colour-tile: #2b4a43;
    --colour-cured: #a23b2e;
    --colour-rind: #c9a24b;
    --colour-ink: #1f1b16;

    /* ---- Type ---- */
    --font-display: 'Big Shoulders', 'Arial Narrow', sans-serif;
    --font-body: 'Space Grotesk', system-ui, -apple-system, 'Segoe UI', sans-serif;

    --step--1: clamp(0.9rem, 0.87rem + 0.15vw, 1rem);
    --step-0: 1.125rem;
    --step-1: clamp(1.35rem, 1.25rem + 0.5vw, 1.5rem);
    --step-2: clamp(1.7rem, 1.5rem + 1vw, 2.05rem);
    --step-3: clamp(2.1rem, 1.8rem + 1.5vw, 2.65rem);
    --step-4: clamp(2.65rem, 2.1rem + 2.5vw, 3.6rem);
    --step-5: clamp(3.3rem, 2.4rem + 4.5vw, 5.7rem);

    --measure: 65ch;

    /* ---- Space ---- */
    --space-3xs: 0.25rem;
    --space-2xs: 0.5rem;
    --space-xs: 0.75rem;
    --space-sm: 1rem;
    --space-md: 1.5rem;
    --space-lg: 2rem;
    --space-xl: 3rem;
    --space-2xl: 4.5rem;
    --space-3xl: 7rem;

    /* ---- Shape ---- */
    --radius-sm: 0.125rem;
    --border-width: 2px;

    /* ---- Motion ---- */
    --duration-fast: 150ms;
    --duration-base: 250ms;
    --duration-slow: 500ms;
    --ease-standard: cubic-bezier(0.4, 0, 0.2, 1);

    /* ---- Touch ---- */
    --target-min: 44px;

    /* ---- Semantic aliases: studio identity (default) ---- */
    --surface-page: var(--colour-studio-white);
    --surface-raised: var(--colour-studio-white);
    --surface-inverted: var(--colour-studio-black);
    --text-body: var(--colour-studio-black);
    --text-muted: color-mix(in srgb, var(--colour-studio-black) 65%, var(--colour-studio-white));
    --text-inverted: var(--colour-studio-white);
    --border-color: var(--colour-studio-black);
    --action-primary-bg: var(--colour-studio-black);
    --action-primary-text: var(--colour-studio-white);
    --decoration-accent: var(--colour-studio-mustard);
    --focus-ring: var(--colour-studio-black);
    --focus-ring-on-dark: var(--colour-studio-mustard);

    /* ---- Chrome: always studio, never themed ---- */
    --chrome-header-bg: var(--colour-studio-white);
    --chrome-header-text: var(--colour-studio-black);
    --chrome-footer-bg: var(--colour-studio-black);
    --chrome-footer-text: var(--colour-studio-white);
    --chrome-footer-muted: color-mix(in srgb, var(--colour-studio-white) 70%, var(--colour-studio-black));
    --chrome-footer-accent: var(--colour-studio-mustard);
  }

  /* ---- Game identity: scoped to the content well of /cheesy/ pages ---- */
  body[data-theme='cheesy'] {
    --surface-page: var(--colour-butcher-paper);
    --surface-raised: var(--colour-paper-white);
    --surface-inverted: var(--colour-tile);
    --text-body: var(--colour-ink);
    --text-muted: color-mix(in srgb, var(--colour-ink) 65%, var(--colour-butcher-paper));
    --text-inverted: var(--colour-paper-white);
    --border-color: var(--colour-ink);
    --action-primary-bg: var(--colour-cured);
    --action-primary-text: var(--colour-paper-white);
    --decoration-accent: var(--colour-rind);
    --focus-ring: var(--colour-ink);
    --focus-ring-on-dark: var(--colour-rind);
    font-family: var(--font-body);
  }

  @media (prefers-reduced-motion: reduce) {
    :root {
      --duration-fast: 0ms;
      --duration-base: 0ms;
      --duration-slow: 0ms;
    }
  }
}

/* ============================== BASE =================================== */

@layer base {
  html {
    color-scheme: light;
  }

  body {
    background-color: var(--surface-page);
    color: var(--text-body);
    font-family: var(--font-body);
    font-size: var(--step-0);
    line-height: 1.5;
  }

  body[data-theme='cheesy'] h1,
  body[data-theme='cheesy'] h2,
  body[data-theme='cheesy'] h3 {
    font-family: var(--font-display);
  }

  h1,
  h2,
  h3,
  h4 {
    line-height: 1.05;
    text-wrap: balance;
  }

  h1 {
    font-size: var(--step-5);
  }

  h2 {
    font-size: var(--step-3);
  }

  h3 {
    font-size: var(--step-2);
  }

  h4 {
    font-size: var(--step-1);
  }

  p {
    max-inline-size: var(--measure);
  }

  a {
    text-underline-offset: 0.15em;
  }

  :focus-visible {
    outline: var(--border-width) solid var(--focus-ring);
    outline-offset: 2px;
  }

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

/* ============================== LAYOUT =================================== */

@layer layout {
  .site-header {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: var(--space-md);
    padding: var(--space-sm) var(--space-md);
    background-color: var(--chrome-header-bg);
    color: var(--chrome-header-text);
  }

  .site-header__mark {
    display: flex;
    align-items: center;
  }

  .site-header nav ul {
    display: flex;
    flex-wrap: wrap;
    gap: var(--space-sm) var(--space-md);
  }

  .site-header nav a,
  .site-footer nav a {
    text-decoration: none;
  }

  .site-header nav a:hover,
  .site-header nav a:focus-visible,
  .site-footer nav a:hover,
  .site-footer nav a:focus-visible {
    text-decoration: underline;
  }

  /* :not([hidden]) rather than a bare .sticky-bar — an author `display`
     declaration overrides the browser's own [hidden] { display: none },
     regardless of the attribute being present. Scoping display to the
     not-hidden state is what lets JS (step 5) reveal this by removing the
     attribute, instead of the bar ignoring `hidden` and covering the real
     header permanently. */
  .sticky-bar:not([hidden]) {
    position: fixed;
    inset-inline: 0;
    top: 0;
    z-index: 10;
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: var(--space-md);
    block-size: 56px;
    padding-inline: var(--space-md);
    background-color: var(--chrome-header-bg);
    color: var(--chrome-header-text);
    border-block-end: var(--border-width) solid var(--chrome-header-text);
  }

  main {
    display: flex;
    flex-direction: column;
  }

  main>section {
    padding: var(--space-2xl) var(--space-md);
    scroll-margin-top: 56px;
  }

  /* The first section under the header shouldn't repeat that section
     rhythm's top padding — it's what sets the page's opening pace, not a
     section arriving mid-scroll. .hero ignores this anyway (its content
     is pinned to the bottom of a near-full-height box), but .about on the
     landing page was reading as a dead gap under the header. */
  main>section:first-child {
    padding-block-start: var(--space-lg);
  }

  .site-footer {
    display: flex;
    flex-direction: column;
    gap: var(--space-md);
    padding: var(--space-xl) var(--space-md);
    background-color: var(--chrome-footer-bg);
    color: var(--chrome-footer-text);
  }

  .site-footer nav ul {
    display: flex;
    flex-wrap: wrap;
    gap: var(--space-sm) var(--space-md);
  }

  .social-links {
    display: flex;
    flex-wrap: wrap;
    gap: var(--space-sm) var(--space-md);
    color: var(--chrome-footer-muted);
  }

  /* ---- Studio landing page intro ---- */
  .about {
    display: flex;
    flex-direction: column;
    justify-content: flex-end;
    min-block-size: 60vh;
    gap: var(--space-md);
  }

  /* ---- Page background (studio pages) ----
     Fixed behind the whole page. Sits at z-index -1, which in painting
     order is behind the header/main/footer's own normal-flow content —
     but the header still needs its own opaque background-color (it has
     one: --chrome-header-bg) since z-index alone doesn't hide the image,
     it only paints it earlier; without an opaque header background the
     photo would show through there too. */
  .page-background {
    position: fixed;
    inset: 0;
    z-index: -1;
    inline-size: 100%;
    block-size: 100%;
    object-fit: cover;
    object-position: center 60%;
  }

  /* ---- Hero ---- */
  .hero {
    position: relative;
    display: flex;
    flex-direction: column;
    justify-content: flex-end;
    min-block-size: calc(100svh - 56px);
    gap: var(--space-md);
    padding-block-end: var(--space-2xl);
    overflow: hidden;
  }

  .hero>* {
    position: relative;
    z-index: 1;
  }

  /* .hero-3d-mount is the hero's background layer — declared AFTER .hero>*
     on purpose. Both selectors are single-class specificity, so without
     this ordering the rule above (added later in the project) would win
     the position/z-index tie and silently pull it back into normal flow,
     undoing its full-bleed background positioning. */
  .hero-3d-mount {
    position: absolute;
    inset: 0;
    z-index: 0;
  }

  .hero h1 {
    max-inline-size: 16ch;
  }

  .hero p {
    max-inline-size: 40ch;
  }

  .scroll-cue {
    align-self: flex-end;
    font-size: var(--step-2);
  }

  /* ---- Pitch: asymmetric 5/7 split ---- */
  .pitch {
    display: grid;
    gap: var(--space-lg);
  }

  @media (min-width: 60em) {
    .pitch {
      grid-template-columns: 5fr 7fr;
      align-items: start;
    }
  }

  .media-slot {
    background-color: var(--surface-inverted);
  }

  .media-slot--16x9 {
    aspect-ratio: 16 / 9;
    overflow: hidden;
  }

  .media-slot--16x9 img,
  .media-slot--16x9 video {
    inline-size: 100%;
    block-size: 100%;
    object-fit: cover;
  }

  /* ---- Moments: staggered, not a card grid ---- */
  .moments {
    display: grid;
    gap: var(--space-xl);
  }

  @media (min-width: 60em) {
    .moments {
      grid-template-columns: repeat(12, 1fr);
    }

    .moment--1 {
      grid-column: 1 / 9;
    }

    .moment--2 {
      grid-column: 6 / 13;
      margin-block-start: var(--space-2xl);
    }

    .moment--3 {
      grid-column: 2 / 10;
      margin-block-start: var(--space-xl);
    }
  }

  .moment figure {
    display: flex;
    flex-direction: column;
    gap: var(--space-xs);
  }

  .moment img {
    aspect-ratio: 16 / 9;
    inline-size: 100%;
    object-fit: cover;
    background-color: var(--surface-inverted);
  }

  /* ---- Narrator audio ---- */
  .narrator-section {
    display: flex;
    flex-direction: column;
    gap: var(--space-md);
  }

  /* ---- Puzzle ---- */
  /* :not([hidden]) — same reasoning as .sticky-bar above: an author
     `display` declaration overrides the browser's own
     [hidden] { display: none } regardless of the attribute being present.
     The puzzle section is hidden for the initial launch (see the HTML
     comment above it); without this, it would render anyway. */
  .puzzle:not([hidden]) {
    display: grid;
    gap: var(--space-lg);
  }

  @media (min-width: 60em) {
    .puzzle:not([hidden]) {
      grid-template-columns: 5fr 7fr;
      align-items: start;
    }
  }

  .keypad {
    background-color: var(--surface-inverted);
    color: var(--text-inverted);
    padding: var(--space-lg);
    display: flex;
    flex-direction: column;
    gap: var(--space-md);
  }

  .keypad :focus-visible {
    outline-color: var(--focus-ring-on-dark);
  }

  .reward-panel {
    grid-column: 1 / -1;
    display: flex;
    flex-direction: column;
    gap: var(--space-md);
    padding-block-start: var(--space-lg);
    border-block-start: var(--border-width) solid var(--border-color);
  }

  /* ---- People (studio + landing page) ---- */
  .team>.text-panel {
    margin-block-end: var(--space-lg);
  }

  .people {
    display: grid;
    gap: var(--space-lg);
  }

  @media (min-width: 48em) {
    .people {
      grid-template-columns: repeat(3, 1fr);
    }
  }

  .person {
    display: flex;
    flex-direction: column;
    gap: var(--space-2xs);
  }

  .person img {
    aspect-ratio: 1 / 1;
    object-fit: cover;
    background-color: var(--surface-inverted);
    margin-block-end: var(--space-2xs);
  }

  /* ---- Fact sheets ---- */
  .fact-sheet dl,
  .brand dl {
    display: grid;
    grid-template-columns: max-content 1fr;
    column-gap: var(--space-lg);
    row-gap: var(--space-sm);
  }

  .fact-sheet dt,
  .brand dt {
    font-weight: 500;
  }

  /* ---- Downloads ---- */
  .downloads-list {
    display: flex;
    flex-direction: column;
    gap: var(--space-sm);
  }

  /* ---- Privacy ---- */
  .privacy-block {
    display: flex;
    flex-direction: column;
    gap: var(--space-lg);
  }

  .privacy-block section {
    display: flex;
    flex-direction: column;
    gap: var(--space-2xs);
  }
}

/* ============================== COMPONENTS =============================== */

@layer components {
  .skip-link {
    position: fixed;
    inset-block-start: -100%;
    inset-inline-start: var(--space-sm);
    z-index: 100;
    padding: var(--space-2xs) var(--space-sm);
    background-color: var(--colour-studio-mustard);
    color: var(--colour-studio-black);
    text-decoration: none;
  }

  .skip-link:focus-visible {
    inset-block-start: var(--space-sm);
  }

  .wishlist-button {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    min-block-size: var(--target-min);
    padding-inline: var(--space-md);
    background-color: var(--action-primary-bg);
    color: var(--action-primary-text);
    font-weight: 500;
    border: var(--border-width) solid transparent;
  }

  .wishlist-button--footer,
  .wishlist-button--compact {
    background-color: var(--chrome-footer-accent);
    color: var(--colour-studio-black);
  }

  .site-header .wishlist-button--compact {
    background-color: var(--colour-studio-black);
    color: var(--colour-studio-white);
  }

  .wishlist-button:disabled,
  .wishlist-button[aria-disabled='true'] {
    background-color: transparent;
    color: var(--text-muted);
    border-color: var(--text-muted);
    border-style: dashed;
    cursor: not-allowed;
  }

  .cta {
    display: inline-flex;
    align-items: center;
    min-block-size: var(--target-min);
    padding-inline: var(--space-md);
    background-color: var(--action-primary-bg);
    color: var(--action-primary-text);
    font-weight: 500;
  }

  /* ---- Narrator voice ----
     Block form (p.narrator) is the "plain sentence, then him" aside pattern
     from section 8: italic, indented, a perforated rule standing in for a
     price-tag tear-line, and a leading em dash — the typographic convention
     for a spoken interjection. The sr-only "Narrator:" text inside each of
     these (added in markup, not here) is what actually lets assistive tech
     tell commentary from instruction; the dash is decorative on top of that.

     Inline form (span.narrator) is used where the line IS a control's whole
     label (a button's visible text) — no plain-language sentence sits next
     to it to disambiguate, so it gets italics only, no dash, no indent,
     so it doesn't disrupt the control's layout. */
  p.narrator {
    display: block;
    max-inline-size: 45ch;
    margin-inline-start: var(--space-xs);
    font-family: var(--font-body);
    font-style: italic;
  }

  p.narrator::before {
    content: '\2014\a0';
    font-style: normal;
  }

  span.narrator {
    display: inline;
    font-style: italic;
  }

  /* ---- Audio player ---- */
  /* :not([hidden]) — same reasoning as .sticky-bar/.puzzle above. */
  .audio-player:not([hidden]) {
    display: flex;
    flex-direction: column;
    gap: var(--space-sm);
  }

  .audio-player__controls {
    display: flex;
    align-items: center;
    gap: var(--space-md);
    flex-wrap: wrap;
  }

  .audio-player__toggle {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    min-inline-size: var(--target-min);
    min-block-size: var(--target-min);
    padding-inline: var(--space-md);
    background-color: var(--action-primary-bg);
    color: var(--action-primary-text);
  }

  .audio-player__progress {
    flex: 1 1 12rem;
    inline-size: auto;
    block-size: 0.5rem;
    accent-color: var(--decoration-accent);
  }

  .audio-player__time {
    font-variant-numeric: tabular-nums;
    white-space: nowrap;
  }

  /* ---- Keypad ---- */
  .keypad__display {
    display: flex;
    gap: var(--space-2xs);
    font-family: var(--font-body);
    font-variant-numeric: tabular-nums;
    font-size: var(--step-3);
  }

  .keypad__display span[data-keypad-slot] {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    inline-size: 1.4em;
    block-size: 1.4em;
    border: var(--border-width) solid var(--text-inverted);
  }

  .keypad__keys {
    display: grid;
    grid-template-columns: repeat(3, minmax(var(--target-min), 1fr));
    gap: var(--space-2xs);
  }

  .keypad__keys button {
    min-inline-size: var(--target-min);
    min-block-size: var(--target-min);
    background-color: var(--text-inverted);
    color: var(--surface-inverted);
    font-size: var(--step-1);
  }

  .keypad__keys button:hover {
    background-color: var(--decoration-accent);
  }

  .keypad__response {
    min-block-size: 1.5em;
  }

  .skip-puzzle {
    position: relative;
    display: inline-flex;
    min-block-size: var(--target-min);
    align-items: center;
  }

  .skip-puzzle__label--hover {
    position: absolute;
    inset-inline-start: 0;
    opacity: 0;
  }

  .skip-puzzle:hover .skip-puzzle__label:not(.skip-puzzle__label--hover),
  .skip-puzzle:focus-visible .skip-puzzle__label:not(.skip-puzzle__label--hover) {
    opacity: 0;
  }

  .skip-puzzle:hover .skip-puzzle__label--hover,
  .skip-puzzle:focus-visible .skip-puzzle__label--hover {
    opacity: 1;
  }

  /* ---- Person ---- */
  .person__name {
    font-size: var(--step-1);
  }

  .person__role {
    color: var(--text-muted);
  }
}

/* ============================== UTILITIES ================================ */

@layer utilities {
  /* Translucent (85% of --surface-page), by request — worth knowing this
     means contrast against .page-background is no longer strictly
     guaranteed the way a fully opaque panel would be; it'll read fine
     against most of the photo but could get tight over its darkest areas.
     Sized to fit its content (fit-content) rather than stretched
     full-width, and grouped per logical block (a heading with its
     paragraph, a person's whole name/role/bio) rather than one panel per
     line, so the photo stays visible around a handful of cards instead of
     behind a wall of small boxes. */
  .text-panel {
    display: flex;
    flex-direction: column;
    gap: var(--space-2xs);
    inline-size: fit-content;
    max-inline-size: 100%;
    background-color: color-mix(in srgb, var(--surface-page) 85%, transparent);
    padding: var(--space-2xs) var(--space-sm);
  }

  /* Person cards are peers in a row, not independent standalone blocks —
     fit-content here would leave three differently sized panels since
     each person's bio is a different length. .people is a grid with the
     default stretch behavior, so each .person <li> already matches the
     row's tallest card; flex:1 here makes the panel itself fill that
     matched height, and full inline-size matches the panel's width to its
     card instead of its own text. */
  .person .text-panel {
    inline-size: 100%;
    flex: 1;
  }

  .visually-hidden {
    position: absolute;
    inline-size: 1px;
    block-size: 1px;
    overflow: hidden;
    clip-path: inset(50%);
    white-space: nowrap;
  }
}