/* Blocky Bits — marketing site
   Tokens mirror Classic theme from DesignTokens+StandardThemes.swift.
   If hex values diverge from Swift, update Swift, not this file. */

:root {
  /* Brand palette (Classic theme — light) */
  --bb-red:    #F24D4D;
  --bb-blue:   #337FF2;
  --bb-green:  #4DD966;
  --bb-yellow: #F2E64D;
  --bb-orange: #F29933;
  --bb-purple: #B366E6;
  --bb-pink:   #F280CC;

  /* Surface */
  --bb-bg:        #FFFFFF;
  --bb-bg-alt:    #FAFAFA;
  --bb-fg:        #111111;
  --bb-fg-muted:  #555555;
  --bb-empty:     rgba(204, 204, 204, 0.20);
  --bb-grid-line: rgba(128, 128, 128, 0.30);

  /* Type */
  --bb-font: -apple-system, BlinkMacSystemFont, "SF Pro Text", "Inter",
             system-ui, sans-serif;
  --bb-fs-xs:  0.8rem;
  --bb-fs-sm:  1rem;
  --bb-fs-md:  1.25rem;
  --bb-fs-lg:  1.563rem;
  --bb-fs-xl:  1.953rem;
  --bb-fs-2xl: 2.441rem;
  --bb-fs-3xl: 3.052rem;

  /* Spacing (4px base) */
  --bb-sp-1: 0.25rem;
  --bb-sp-2: 0.5rem;
  --bb-sp-3: 0.75rem;
  --bb-sp-4: 1rem;
  --bb-sp-6: 1.5rem;
  --bb-sp-8: 2rem;
  --bb-sp-12: 3rem;
  --bb-sp-16: 4rem;
  --bb-sp-20: 5rem;
  --bb-sp-24: 6rem;

  /* Radii */
  --bb-radius-sm:   2px;
  --bb-radius-md:   6px;
  --bb-radius-lg:   10px;

  /* Motion */
  --bb-ease: cubic-bezier(0.2, 0.8, 0.2, 1);
  --bb-dur-fast: 150ms;
  --bb-dur-base: 280ms;
  --bb-dur-slow: 600ms;
}

@media (prefers-color-scheme: dark) {
  :root {
    --bb-red:    #E63333;
    --bb-blue:   #1A66E6;
    --bb-green:  #33B34D;
    --bb-yellow: #E6CC33;
    --bb-orange: #E6801A;
    --bb-purple: #994DCC;
    --bb-pink:   #E666B3;

    --bb-bg:        #000000;
    --bb-bg-alt:    #0A0A0A;
    --bb-fg:        #F2F2F2;
    --bb-fg-muted:  #999999;
    --bb-empty:     rgba(204, 204, 204, 0.30);
    --bb-grid-line: rgba(128, 128, 128, 0.30);
  }
}

/* Reset */
*, *::before, *::after { box-sizing: border-box; }
html, body { margin: 0; padding: 0; }
html {
  scroll-behavior: smooth;
  scroll-padding-top: 4rem;
}
@media (prefers-reduced-motion: reduce) { html { scroll-behavior: auto; } }
img, svg { display: block; max-width: 100%; }
/* Every screenshot is wrapped in a <picture> for AVIF/WebP negotiation, and the
   wrapper must not become a box: the device frames size their <img> as a
   percentage of an absolutely-positioned .screen and lean on object-fit: cover.
   A block wrapper would break that chain — its own auto height becomes the
   percentage basis, so `height: 100%` collapses to the image's intrinsic ratio
   (the MacBook screenshot fell 9px short of its cutout). `display: contents`
   makes the wrapper generate no box at all, leaving the original box tree. */
picture { display: contents; }
a { color: var(--bb-blue); text-decoration: none; }
a:hover { text-decoration: underline; }

body {
  font-family: var(--bb-font);
  font-size: var(--bb-fs-sm);
  line-height: 1.5;
  color: var(--bb-fg);
  background: var(--bb-bg);
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
}

:focus-visible {
  outline: 2px solid var(--bb-blue);
  outline-offset: 2px;
  border-radius: var(--bb-radius-sm);
}

/* Layout */
.container {
  max-width: 64rem;
  margin: 0 auto;
  padding: 0 var(--bb-sp-4);
}

/* ───────── Top navbar ───────── */
.site-nav {
  position: sticky;
  top: 0;
  z-index: 10;
  background: color-mix(in oklab, var(--bb-bg) 80%, transparent);
  backdrop-filter: saturate(180%) blur(14px);
  -webkit-backdrop-filter: saturate(180%) blur(14px);
  border-bottom: 1px solid var(--bb-grid-line);
}
/* Extend the frosted-glass effect into the iOS Safari top inset / overscroll
   region above the navbar. Without this, scrolling reveals raw page content
   in the area between the navbar's top edge and the status bar. The pseudo
   reaches well above the viewport (large negative top) so it always covers
   the rubber-band region during pull-down. */
.site-nav::before {
  content: "";
  position: absolute;
  left: 0;
  right: 0;
  bottom: 100%;
  height: 100vh;
  background: inherit;
  backdrop-filter: inherit;
  -webkit-backdrop-filter: inherit;
  pointer-events: none;
}
.site-nav .container {
  display: flex;
  align-items: center;
  justify-content: space-between;
  height: 3.5rem;
}
.site-nav .brand {
  font-weight: 700;
  letter-spacing: -0.02em;
  color: var(--bb-fg);
  font-size: var(--bb-fs-md);
}
.site-nav .brand:hover { text-decoration: none; }
.site-nav nav {
  display: flex;
  gap: var(--bb-sp-6);
  align-items: center;
}
.site-nav nav a {
  font-size: var(--bb-fs-sm);
  font-weight: 500;
  color: var(--bb-fg-muted);
}
.site-nav nav a:hover { color: var(--bb-fg); text-decoration: none; }
.site-nav nav a.cta {
  color: var(--bb-bg);
  background: var(--bb-fg);
  padding: 0.4rem 0.875rem;
  border-radius: 999px;
  font-weight: 600;
}
.site-nav nav a.cta:hover { opacity: 0.85; color: var(--bb-bg); }

@media (max-width: 480px) {
  .site-nav nav .nav-hide-sm { display: none; }
}

main > section { padding: var(--bb-sp-16) 0; }
.section-alt   { background: var(--bb-bg-alt); }

/* Typography */
h1, h2, h3 {
  font-weight: 700;
  letter-spacing: -0.02em;
  margin: 0 0 var(--bb-sp-4);
  line-height: 1.15;
}
h1 { font-size: clamp(2.5rem, 6vw, 5rem); }
h2 { font-size: clamp(1.75rem, 3.2vw, 2.75rem); margin-bottom: var(--bb-sp-6); }
h3 { font-size: var(--bb-fs-lg); }
p  { margin: 0 0 var(--bb-sp-4); max-width: 60ch; }

/* ───────── Hero ───────── */
.hero { padding-top: var(--bb-sp-20); padding-bottom: var(--bb-sp-16); }
.hero .container {
  display: grid;
  gap: var(--bb-sp-12);
  align-items: center;
}
@media (min-width: 768px) {
  .hero .container { grid-template-columns: 1.1fr 1fr; gap: var(--bb-sp-16); }
}

.wordmark {
  font-weight: 800;
  letter-spacing: -0.04em;
  margin: 0 0 var(--bb-sp-6);
}

.tagline {
  font-size: clamp(1.125rem, 1.5vw, 1.375rem);
  color: var(--bb-fg);
  max-width: 28ch;
  margin: 0 0 var(--bb-sp-3);
  font-weight: 500;
}
.tagline-sub {
  font-size: var(--bb-fs-sm);
  color: var(--bb-fg-muted);
  max-width: 36ch;
  margin: 0 0 var(--bb-sp-8);
}

.cta-row {
  display: flex;
  flex-wrap: wrap;
  gap: var(--bb-sp-4);
  align-items: center;
}
.app-store-badge {
  display: inline-block;
  transition: transform var(--bb-dur-fast) var(--bb-ease);
}
.app-store-badge:hover { transform: translateY(-2px); }
/* Both colour variants ship as inline <svg>, expanded by the svg directive in
   Scripts/build_web.py: two <img> elements would both download even with one
   display:none'd, and both came off a third-party host. `width: auto` against
   the 119.66x40 viewBox lands at 167.5px — identical to the 120x40 raster the
   remote endpoint used to serve, so nothing reflows. */
.app-store-badge > svg { height: 56px; width: auto; }
.app-store-badge .badge-light { display: block; }
.app-store-badge .badge-dark  { display: none; }
@media (prefers-color-scheme: dark) {
  .app-store-badge .badge-light { display: none; }
  .app-store-badge .badge-dark  { display: block; }
}

.cta-secondary {
  font-size: var(--bb-fs-sm);
  font-weight: 500;
  color: var(--bb-fg-muted);
}
.cta-secondary:hover { color: var(--bb-fg); }

/* ───────── Phone (bezel + screenshot) ─────────
   Bezel asset: 1470×3000 (resized in repo to 490×1000).
   Inner screen rect — measured from the alpha mask, including the
   dynamic-island row so aspect = 0.46025 (matches screenshot 1206/2622):
     left   5.1020%
     top    2.2000%
     width  89.7959%
     height 95.6000%
*/
.phone {
  position: relative;
  width: 100%;
  max-width: 320px;
  aspect-ratio: 1470 / 3000;
  margin: 0 auto;
}
.phone .screen {
  position: absolute;
  left: 5.1020%;
  top: 2.2000%;
  width: 89.7959%;
  height: 95.6000%;
  overflow: hidden;
  /* Bezel inner corner is circular at 183px radius (13.86% of screen width).
     CSS border-radius percentages resolve per-axis (elliptical) — to get a
     circle, give both axes the matching pixel proportion: 13.86% horiz,
     6.38% vert. This way the masked screen corner sits exactly inside the
     bezel's rounded cutout at every render size. */
  border-radius: 13.86% / 6.38%;
  background: var(--bb-bg);
}
.phone .screen img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  display: block;
}

/* The hero clip sits in the same box as a screenshot would, and is cropped the
   same way. Kept separate from the img rule above so the bezel geometry has one
   owner: change the inset there and the video follows. */
.phone .screen .hero-video {
  width: 100%;
  height: 100%;
  object-fit: cover;
  display: block;
}

/* Themes story: one board re-themed, not 12 different games.
   `picture { display: contents }` means the <picture> generates no box, so the
   stacking and the animation both live on the <img> — but the frames are still
   separate <picture> elements in the DOM, which is what nth-of-type selects on.
   12 frames x 1.5s = an 18s loop with a 0.4s crossfade. At this pace the
   eye reads the transitions rather than the frames, which is why the capture
   order alternates light/dark and warm/cool — two similar palettes back to back
   look like a stutter instead of a change.
   Delays run backwards so each frame fades in exactly as its predecessor fades
   out; forward delays leave a dark gap at every transition. Opacities sum to 1
   throughout, so there is neither a gap nor a flash. */
.screen.theme-fade img {
  position: absolute;
  inset: 0;
  opacity: 0;
  animation: bb-theme-fade 18s linear infinite;
}
.screen.theme-fade picture:nth-of-type(1) img { animation-delay: 0s; }
.screen.theme-fade picture:nth-of-type(2) img { animation-delay: -16.5s; }
.screen.theme-fade picture:nth-of-type(3) img { animation-delay: -15.0s; }
.screen.theme-fade picture:nth-of-type(4) img { animation-delay: -13.5s; }
.screen.theme-fade picture:nth-of-type(5) img { animation-delay: -12.0s; }
.screen.theme-fade picture:nth-of-type(6) img { animation-delay: -10.5s; }
.screen.theme-fade picture:nth-of-type(7) img { animation-delay: -9.0s; }
.screen.theme-fade picture:nth-of-type(8) img { animation-delay: -7.5s; }
.screen.theme-fade picture:nth-of-type(9) img { animation-delay: -6.0s; }
.screen.theme-fade picture:nth-of-type(10) img { animation-delay: -4.5s; }
.screen.theme-fade picture:nth-of-type(11) img { animation-delay: -3.0s; }
.screen.theme-fade picture:nth-of-type(12) img { animation-delay: -1.5s; }

@keyframes bb-theme-fade {
  0%, 6.1111%   { opacity: 1; }
  8.3333%, 97.7778% { opacity: 0; }
  100%              { opacity: 1; }
}

/* An endlessly cycling image is exactly what this setting is for. Hold the
   first frame; the story still reads, because the copy carries it. */
@media (prefers-reduced-motion: reduce) {
  .screen.theme-fade img { animation: none; opacity: 0; }
  .screen.theme-fade picture:nth-of-type(1) img { opacity: 1; }
}

.phone .bezel {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  pointer-events: none;
  user-select: none;
  z-index: 1;
}

/* Soft brand-tint backdrop behind hero phone */
.phone-stage {
  position: relative;
  display: flex;
  justify-content: center;
  padding: var(--bb-sp-8) 0;
}
.phone-stage::before {
  content: "";
  position: absolute;
  inset: 0;
  background:
    radial-gradient(circle at 30% 30%,
      color-mix(in oklab, var(--bb-blue) 18%, transparent),
      transparent 60%),
    radial-gradient(circle at 75% 70%,
      color-mix(in oklab, var(--bb-purple) 14%, transparent),
      transparent 60%);
  /* PERF NOTE: this blur is not free — a ~500px blurred layer in the LCP
     region — but it is not removable either. It does real design work: the
     gradients are clipped by `inset: 0`, and the blur both feathers that clip
     and diffuses the wash across the stage. Dropping it (with the box bled
     out to hide the clip) makes the glow read as a hard, saturated lavender
     wash over the whole hero column. Keep it unless the hero is redesigned. */
  filter: blur(40px);
  z-index: 0;
}
.phone-stage .phone { position: relative; z-index: 1; max-width: 360px; }

/* ───────── At-a-glance strip ─────────
   Tight 4-up row of one-liners. Lives just below the hero. */
.glance {
  display: grid;
  gap: var(--bb-sp-6) var(--bb-sp-8);
  grid-template-columns: 1fr;
}
@media (min-width: 640px) { .glance { grid-template-columns: repeat(2, 1fr); } }
@media (min-width: 1024px) { .glance { grid-template-columns: repeat(4, 1fr); } }

.glance-item {
  display: flex;
  gap: var(--bb-sp-4);
  align-items: flex-start;
}
.glance-item .glyph { flex-shrink: 0; margin-top: 4px; }
.glance-item h3 {
  font-size: var(--bb-fs-md);
  margin: 0 0 var(--bb-sp-1);
  letter-spacing: -0.01em;
}
.glance-item p {
  font-size: var(--bb-fs-sm);
  color: var(--bb-fg-muted);
  margin: 0;
}

/* Block-grid mini-glyph (2×2 of brand-colored squares) */
.glyph {
  display: grid;
  grid-template-columns: repeat(2, 12px);
  grid-auto-rows: 12px;
  gap: 3px;
}
.glyph span {
  border-radius: 2px;
  background: var(--g-color, var(--bb-blue));
}
.glyph span.dim { background: var(--bb-empty); }
.c-blue   .glyph { --g-color: var(--bb-blue); }
.c-green  .glyph { --g-color: var(--bb-green); }
.c-orange .glyph { --g-color: var(--bb-orange); }
.c-purple .glyph { --g-color: var(--bb-purple); }
.c-pink   .glyph { --g-color: var(--bb-pink); }

/* ───────── Feature stories (alternating phone/text rows) ───────── */
.stories { display: grid; gap: var(--bb-sp-24); }
@media (max-width: 767px) { .stories { gap: var(--bb-sp-16); } }

.story {
  display: grid;
  gap: var(--bb-sp-8);
  align-items: center;
}
@media (min-width: 768px) {
  .story { grid-template-columns: 1fr 1fr; gap: var(--bb-sp-16); }
  .story.flip .story-phone { order: 2; }
  .story.flip .story-copy  { order: 1; }
}

.story-phone { display: flex; justify-content: center; }
.story-phone .phone { max-width: 280px; }
.story-phone .tablet { max-width: 320px; }

.story-copy .eyebrow {
  font-size: var(--bb-fs-xs);
  font-weight: 600;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  margin: 0 0 var(--bb-sp-3);
  color: var(--g-color, var(--bb-blue));
}
.story-copy h3 {
  font-size: clamp(1.5rem, 2.6vw, 2.25rem);
  letter-spacing: -0.02em;
  line-height: 1.15;
  margin: 0 0 var(--bb-sp-4);
}
.story-copy p {
  font-size: var(--bb-fs-md);
  color: var(--bb-fg-muted);
  line-height: 1.55;
  margin: 0;
  max-width: 36ch;
}

/* ───────── Device universality strip ─────────
   The four platforms render as hand-drawn line-art SVG glyphs sharing a
   common baseline. Per-platform screenshots already appear inside the
   feature stories above, so this strip stays a compact summary. */
.device-strip {
  display: grid;
  gap: var(--bb-sp-8);
  grid-template-columns: repeat(2, 1fr);
  justify-items: center;
  align-items: end;
  margin-top: var(--bb-sp-8);
}
@media (min-width: 1024px) { .device-strip { grid-template-columns: repeat(4, 1fr); } }

.device-tile {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: var(--bb-sp-4);
}
.device-tile .glyph-frame {
  width: 100%;
  max-width: 72px;
  aspect-ratio: 1 / 1;
  display: flex;
  align-items: center;
  justify-content: center;
  color: var(--bb-fg-muted);
}
.device-tile .glyph-frame svg {
  width: 100%;
  height: 100%;
  stroke: currentColor;
  fill: none;
  stroke-width: 1.5;
  stroke-linecap: round;
  stroke-linejoin: round;
}
.device-label {
  font-size: var(--bb-fs-sm);
  color: var(--bb-fg);
  font-weight: 500;
}

/* ───────── iPad frame (used inside feature stories) ─────────
   Mirrors the `.phone` pattern: a bezel <img> sits over a masked
   `.screen` <div>. Bezel asset: ipad-air-13-purple.png (2300×2980).
   Screenshot source: iPad Pro 13" at 2064×2752.
   The `.screen` is deliberately sized slightly larger than the bezel's
   alpha cutout (5.0% / 3.5% / 90.0% / 93.0% vs measured 5.478% / 4.161%
   / 89.043% / 91.678%) so the screenshot extends a few pixels past the
   bezel's inner edge on every side. The bezel image on top covers the
   overshoot, and the extra reach hides bezel rim-highlight pixels that
   would otherwise show as thin white lines at the top/bottom. No corner
   radius — the bezel covers the screenshot's square corners. */
.tablet {
  position: relative;
  width: 100%;
  aspect-ratio: 2300 / 2980;
  margin: 0 auto;
}
.tablet .screen {
  position: absolute;
  left: 5.0%;
  top: 3.5%;
  width: 90.0%;
  height: 93.0%;
  overflow: hidden;
  border-radius: 0;
  background: #000;
}
.tablet .screen img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  display: block;
}
.tablet .bezel {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  pointer-events: none;
  user-select: none;
  z-index: 1;
}

/* ───────── MacBook showcase ─────────
   Mirrors the .phone/.tablet pattern: a bezel <img> sits over a masked
   `.screen` <div>. Bezel asset: macbook-air-13-midnight.png (1200×790); its
   transparent screen cutout measures 902×586 at (149, 102), i.e. left 12.42%,
   top 12.91%, width 75.17%, height 74.18% of the bezel. The macOS shot is the
   opaque 2880×1800 App Store capture, cover-fit into that rectangle. */
.laptop {
  position: relative;
  width: 100%;
  max-width: 460px;
  aspect-ratio: 1200 / 790;
  margin: 0 auto;
}
.laptop .screen {
  position: absolute;
  left: 12.4167%;
  top: 12.9114%;
  width: 75.1667%;
  height: 74.1772%;
  overflow: hidden;
  border-radius: 1.5% / 2.3%;
  background: var(--bb-bg);
}
.laptop .screen img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  display: block;
}
.laptop .bezel {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  pointer-events: none;
  user-select: none;
  z-index: 1;
}

/* ───────── Vision showcase (drop-in render, no device bezel) ─────────
   Unlike .phone/.tablet/.laptop, the visionOS shot is a complete immersive
   room scene, placed as a plain centered <img> — no masked .screen, no bezel
   overlay. Rounded + shadowed so it sits as a framed still. */
.story-media { align-items: center; }
.story-media .vision-shot {
  width: 100%;
  height: auto;
  display: block;
  border-radius: 18px;
  box-shadow: 0 24px 60px -24px rgba(0, 0, 0, 0.5);
  max-width: 560px;
}

/* Section copy */
.device-section .section-head {
  text-align: center;
  max-width: 38ch;
  margin: 0 auto var(--bb-sp-4);
}
.device-section .section-head p {
  margin: 0 auto;
  color: var(--bb-fg-muted);
  font-size: var(--bb-fs-md);
}

.story.c-blue   .story-copy { --g-color: var(--bb-blue); }
.story.c-green  .story-copy { --g-color: var(--bb-green); }
.story.c-orange .story-copy { --g-color: var(--bb-orange); }
.story.c-purple .story-copy { --g-color: var(--bb-purple); }
.story.c-pink   .story-copy { --g-color: var(--bb-pink); }

/* ───────── About the developer ───────── */
.about .about-inner {
  max-width: 42rem;
  margin: 0 auto;
  text-align: center;
}
.about h2 {
  font-size: clamp(1.5rem, 2.6vw, 2.25rem);
  margin-bottom: var(--bb-sp-6);
}
.about p {
  font-size: var(--bb-fs-md);
  color: var(--bb-fg-muted);
  line-height: 1.6;
  margin-left: auto;
  margin-right: auto;
}
.about .about-sign {
  margin-top: var(--bb-sp-4);
  color: var(--bb-fg);
  font-weight: 500;
  font-size: var(--bb-fs-sm);
}

/* ───────── Footer ───────── */
.site-footer {
  border-top: 1px solid var(--bb-grid-line);
  padding: var(--bb-sp-8) 0;
  font-size: var(--bb-fs-xs);
  color: var(--bb-fg-muted);
}
.site-footer .container {
  display: flex;
  flex-wrap: wrap;
  gap: var(--bb-sp-6) var(--bb-sp-8);
  align-items: center;
  justify-content: space-between;
}
.site-footer .meta { display: flex; flex-direction: column; gap: var(--bb-sp-1); }
.site-footer a {
  color: var(--bb-fg);
  text-decoration: underline;
  text-decoration-color: var(--bb-grid-line);
  text-underline-offset: 2px;
  transition: text-decoration-color var(--bb-dur-fast) var(--bb-ease);
}
.site-footer a:hover { text-decoration-color: var(--bb-fg); }
.site-footer nav { display: flex; gap: var(--bb-sp-4); flex-wrap: wrap; }

/* ───────── Legal pages ───────── */
.legal {
  max-width: 40rem;
  margin: 0 auto;
  padding: var(--bb-sp-12) var(--bb-sp-4);
  line-height: 1.65;
}
.legal .back-link { display: inline-block; margin-bottom: var(--bb-sp-8); font-size: var(--bb-fs-sm); }
.legal h1 { font-size: clamp(2rem, 4vw, 2.75rem); }
.legal h2 {
  font-size: var(--bb-fs-md);
  color: var(--bb-blue);
  border-left: 3px solid var(--bb-blue);
  padding-left: var(--bb-sp-3);
  margin-top: var(--bb-sp-8);
  margin-bottom: var(--bb-sp-3);
}
.legal p, .legal li { font-size: var(--bb-fs-sm); }
.legal ul { padding-left: 1.25rem; }
.legal .updated { color: var(--bb-fg-muted); font-size: var(--bb-fs-xs); margin-bottom: var(--bb-sp-8); }

/* Reduced motion */
@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after {
    animation-duration: 0.01ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 0.01ms !important;
  }
}
