/* Joshtified 2026 - the design layer.
   Loaded after site.css so it can replace the older card-and-eyebrow system
   without editing 2,000 lines of it.

   House style: no pills, no stadium badges, no equal rounded cards in a row,
   no numbered eyebrows. Every block below earns its shape. */

@font-face {
  font-family: 'Bricolage';
  src: url('/assets/fonts/bricolage-latin.woff2') format('woff2');
  font-weight: 200 800;
  font-style: normal;
  font-display: block;
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
@font-face {
  font-family: 'Bricolage';
  src: url('/assets/fonts/bricolage-latin-ext.woff2') format('woff2');
  font-weight: 200 800;
  font-style: normal;
  font-display: swap;
  unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C4, U+2113, U+2C60-2C7F, U+A720-A7FF;
}

/* Two free faces, self-hosted so the page still talks to nobody.
   Titan One is the shout (Rodrigo Fuenzalida, SIL OFL, via Google Fonts).
   Silkscreen is the machine voice (Jason Kottke, SIL OFL). */
@font-face {
  font-family: 'Titan One';
  src: url('/assets/fonts/titan-one-latin.woff2') format('woff2');
  font-weight: 400;
  font-style: normal;
  font-display: block;
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
@font-face {
  font-family: 'Silkscreen';
  src: url('/assets/fonts/silkscreen-400-latin.woff2') format('woff2');
  font-weight: 400;
  font-style: normal;
  font-display: block;
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
@font-face {
  font-family: 'Silkscreen';
  src: url('/assets/fonts/silkscreen-700-latin.woff2') format('woff2');
  font-weight: 700;
  font-style: normal;
  font-display: block;
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}

:root {
  --ink: #141020;
  --ink-soft: #3c3450;
  --ink-faint: #5a5170;
  --chalk: #f4f1f9;
  --violet: #5b21b6;
  --violet-bright: #7c3aed;
  --ember: #f04a2a;
  --rule: rgba(20, 16, 32, .16);
  --rule-soft: rgba(20, 16, 32, .09);
  --display: 'Bricolage', 'Segoe UI', system-ui, sans-serif;
  --shout: 'Titan One', 'Bricolage', 'Segoe UI', system-ui, sans-serif;
  --pixel: 'Silkscreen', ui-monospace, 'Cascadia Mono', monospace;
  --r-xs: 4px;
  --r-sm: 8px;
  --r-md: 14px;
  --r-lg: 22px;
  --ease: cubic-bezier(.16, .84, .28, 1);
}

html { background: var(--chalk); scroll-behavior: smooth; }
body {
  font-family: var(--display);
  color: var(--ink);
  font-optical-sizing: auto;
  -webkit-font-smoothing: antialiased;
}
h1, h2, h3, h4, .hero-display, .project-row-name, .project-card-copy strong {
  font-family: var(--display);
  font-optical-sizing: auto;
  letter-spacing: -.035em;
  text-wrap: balance;
}

.wrap { width: min(1240px, calc(100% - 56px)); }

/* Old decoration that encoded nothing. */
.eyebrow { display: none; }
.funnel-num { display: none; }

/* ------------------------------------------------------- page transitions */
/* Cross-document view transitions give a real fade between pages in current
   Chrome; everything else falls back to the entry animation below. */
@view-transition { navigation: auto; }

::view-transition-old(root) {
  animation: page-out .26s cubic-bezier(.4, 0, 1, 1) both;
}
::view-transition-new(root) {
  animation: page-in .44s cubic-bezier(.16, .84, .28, 1) both;
}
@keyframes page-out { to { opacity: 0; transform: scale(.988); } }
@keyframes page-in { from { opacity: 0; transform: scale(1.012); } }

/* Fallback entry: the page itself lifts in. Named fade-up so the hero's own
   line-rise animation keeps working underneath it. */
@keyframes page-rise { from { opacity: 0; transform: translateY(10px); } }
main { animation: page-rise .5s var(--ease) both; }

/* Islands of content that arrive one after another on a project page. */
@keyframes step-in {
  from { opacity: 0; transform: translateY(16px); }
  to { opacity: 1; transform: translateY(0); }
}
@keyframes step-in-left {
  from { opacity: 0; transform: translateX(-14px); }
  to { opacity: 1; transform: translateX(0); }
}

/* ---------------------------------------------------------------- header */
/* The bar is not there until you scroll. At the top of the page it is type over
   the background with nothing behind it; once content moves under it, a light
   scrim fades in with the hairline. No white slab. */
.site-head {
  background: transparent;
  border-bottom: 1px solid transparent;
  backdrop-filter: none;
  transition: background .35s ease, border-color .35s ease;
}
.site-head.scrolled {
  background: linear-gradient(to bottom, rgba(244, 241, 249, .93), rgba(244, 241, 249, .74) 72%, rgba(244, 241, 249, .5));
  border-bottom-color: var(--rule-soft);
  backdrop-filter: blur(12px) saturate(1.15);
}
.head-in { min-height: 64px; }
/* The bar is set in the display face and carries a hairline that fills as the
   page is read. That is the one piece of chrome, so it is drawn, not borrowed. */
.head-progress {
  position: absolute;
  left: 0; right: 0; bottom: -1px;
  height: 2px;
  overflow: hidden;
  pointer-events: none;
}
.head-progress i {
  display: block;
  width: 100%; height: 100%;
  background: linear-gradient(90deg, var(--violet), #d946ef 62%, var(--ember));
  transform: scaleX(0);
  transform-origin: 0 50%;
  transition: transform .14s linear;
}
.brand { font-weight: 600; letter-spacing: -.03em; font-size: 18px; gap: 10px; }
.brand-mark {
  width: 30px; height: 30px; border-radius: 3px 12px 3px 3px;
  background: var(--ink); color: var(--chalk);
  font-family: var(--shout); font-weight: 400; font-size: 17px;
  padding-top: 1px;
  box-shadow: 3px 3px 0 rgba(91, 33, 182, .3);
  transition: background .2s ease, transform .35s var(--ease), box-shadow .3s ease;
}
.brand:hover .brand-mark { background: var(--violet); transform: rotate(-6deg); box-shadow: 4px 4px 0 rgba(240, 74, 42, .32); }
.brand-word,
.brand > span:not(.brand-mark) {
  font-family: var(--pixel);
  font-size: 14px;
  font-weight: 700;
  letter-spacing: .03em;
  line-height: 1;
  padding-top: 4px;
}
.nav a {
  font-size: 14.5px; color: var(--ink-soft); padding: 8px 10px; border-radius: 4px;
  transition: color .16s ease, background .16s ease;
}
/* A link draws its own underline from the left instead of lighting a pill. */
.nav a { position: relative; border-radius: 3px; }
.nav a::after {
  content: "";
  position: absolute;
  left: 10px; right: 10px; bottom: 5px;
  height: 2px; border-radius: 2px;
  background: var(--violet);
  transform: scaleX(0);
  transform-origin: 0 50%;
  transition: transform .34s var(--ease);
}
.nav a:hover::after, .nav a.current::after { transform: scaleX(1); }
.nav a:hover, .nav a[aria-current="page"], .nav a.current { background: transparent; color: var(--violet); }
.nav .nav-mail {
  background: var(--ink); border-color: var(--ink); color: var(--chalk);
  border-radius: 3px 12px 12px 12px; font-size: 13.5px;
}
.nav .nav-mail::after { display: none; }
.nav .nav-mail:hover { background: var(--violet); border-color: var(--violet); color: #fff; }

/* The phone menu button is two rules that fold into an X. */
.menu-glyph { display: inline-flex; flex-direction: column; gap: 4px; width: 17px; }
.menu-glyph i { display: block; height: 2px; border-radius: 2px; background: currentColor; transition: transform .3s var(--ease), width .25s ease; }
.menu-glyph i:last-child { width: 11px; align-self: flex-end; }
.menu-btn[aria-expanded="true"] .menu-glyph i:first-child { transform: translateY(3px) rotate(45deg); }
.menu-btn[aria-expanded="true"] .menu-glyph i:last-child { width: 17px; transform: translateY(-3px) rotate(-45deg); }

/* ------------------------------------------------------------------ hero */
.hero {
  position: relative;
  min-height: 84svh;
  display: flex;
  align-items: flex-end;
  padding: 0 0 36px;
  overflow: hidden;
  border-bottom: 1px solid var(--rule);
  background: transparent;
}
.hero-signal {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  display: block;
  z-index: 0;
  opacity: .85;
}
.hero-in { position: relative; z-index: 1; }
.hero-kicker {
  display: flex; align-items: center; gap: 10px;
  font-size: 13.5px; letter-spacing: .01em;
  color: var(--ink-faint);
  margin: 0 0 clamp(22px, 4vh, 44px);
}
/* The painted background: a low resolution canvas stretched over the page. */
.bg-canvas {
  position: fixed;
  inset: 0;
  width: 100%;
  height: 100%;
  display: block;
  z-index: -2;
  pointer-events: none;
}
/* The two hero lines are set in Titan One: one weight, very wide, and it brings
   its own bounce, so the tracking comes back to zero and the line box opens up
   to stop the ascenders being clipped. */
.hero-display {
  margin: 0;
  font-family: var(--shout);
  font-size: clamp(36px, 7vw, 116px);
  line-height: 1.08;
  font-weight: 400;
  letter-spacing: .005em;
  color: var(--ink);
  font-synthesis: none;
  text-rendering: optimizeLegibility;
  -webkit-font-smoothing: antialiased;
}
.hero-display .line { display: block; overflow: hidden; padding: .06em 0 .02em; }
.hero-display .line > span {
  display: block;
  transform: translateY(102%);
  animation: line-rise 1.05s var(--ease) forwards;
}
.hero-display .line:nth-child(2) > span { animation-delay: .10s; }
.hero-display .line:nth-child(3) > span { animation-delay: .20s; }
@keyframes line-rise { to { transform: translateY(0); } }

/* The letters. Splitting a line into characters loses kerning pairs, so the
   split lines get a hair more tracking to compensate. */
.hero-display .split { letter-spacing: 0; }
.split { letter-spacing: -.012em; }
/* Letters lift on their own schedule: a random period and phase are stamped on
   each one when the line is split, so the letters that move are never the same
   handful twice. Transform only, so it stays on the compositor. */
.word { display: inline-block; white-space: nowrap; }
.ch {
  display: inline-block;
  animation: bob var(--bobd, 7.2s) cubic-bezier(.42, .05, .58, .95) infinite;
  animation-delay: var(--bob, 0s);
}
@keyframes bob {
  0%, 64%, 100% { transform: translateY(0); }
  76% { transform: translateY(-.045em); }
  88% { transform: translateY(-.002em); }
}
/* A hair of light behind the display type, so the heavy shapes separate from a
   wash that is never quite one colour. Not a fill: the ink stays ink. */
.hero-display,
.section-head h2,
.project-page-head h1,
.foot-mark {
  text-shadow: 0 0 12px rgba(255, 255, 255, .40), 0 1px 0 rgba(255, 255, 255, .35);
}
/* The footer mark is decoration, so it moves more slowly than the headline. */
.foot-mark .ch { animation-duration: calc(var(--bobd, 7.2s) * 1.7); }
.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;
  user-select: none;
}


/* ---------------------------------------------------------------- the glow */
/* One soft light behind the hero lines. It is a halo, not a fill: the letters
   keep their ink and the light only brightens the paper under them, so the
   headline stays readable on every part of the wash. Positioned from --mx and
   --my, which site.js sets from the pointer once per frame. */
/* The halo sits on the headline, not on a line: the line boxes clip their
   content for the rise animation, which would cut a hard edge through it.
   It is a blurred disc rather than a gradient patch, because a patch the size
   of the box shows its corners the moment the pointer reaches one. */
.hero-display { position: relative; }
.hero-display::before {
  content: "";
  position: absolute;
  left: var(--mx, 30%);
  top: var(--my, 45%);
  width: 34vmax;
  height: 34vmax;
  margin: 0;
  transform: translate(-50%, -50%);
  border-radius: 50%;
  background: rgba(255, 255, 255, .62);
  filter: blur(48px);
  opacity: .62;
  z-index: -1;
  pointer-events: none;
}

.hero-bottom {
  display: grid;
  grid-template-columns: minmax(0, 1.15fr) minmax(0, .85fr);
  gap: 34px;
  align-items: end;
  margin-top: clamp(24px, 4vh, 44px);
  padding-top: 22px;
  border-top: 1px solid var(--rule);
}
.hero-lede {
  margin: 0;
  font-size: clamp(17px, 1.4vw, 20px);
  line-height: 1.58;
  color: var(--ink);
  max-width: 52ch;
}
.hero-lede strong { color: var(--ink); font-weight: 600; }
.hero-actions { display: flex; flex-wrap: wrap; gap: 10px; justify-content: flex-end; }

.btn {
  border-radius: var(--r-xs);
  font-family: var(--display);
  font-size: 15px;
  font-weight: 500;
  padding: 13px 20px;
  border: 1px solid var(--rule);
  background: transparent;
  color: var(--ink);
  box-shadow: none;
  transition: background .18s ease, color .18s ease, border-color .18s ease, transform .18s ease;
}
.btn:hover { background: rgba(20, 16, 32, .05); border-color: rgba(20, 16, 32, .34); transform: translateY(-1px); }
.btn.primary {
  background: var(--ink); border-color: var(--ink); color: var(--chalk);
  box-shadow: 0 12px 26px -14px rgba(20, 16, 32, .6);
}
.btn.primary:hover { background: var(--violet); border-color: var(--violet); color: #fff; }

/* --------------------------------------------------------------- sections */
.section, .section:first-of-type, .section:nth-of-type(odd), #work.section {
  background: transparent;
  border-top: 1px solid var(--rule-soft);
  padding: clamp(44px, 6vw, 84px) 0;
  transition: border-top-color .55s ease;
}
/* Landing on a section from a menu draws its top rule in violet, so the jump
   has an end as well as a start. */
.section.arrived { border-top-color: rgba(91, 33, 182, .55); }
.section-head {
  display: grid;
  grid-template-columns: minmax(0, 1.3fr) minmax(0, .7fr);
  gap: clamp(20px, 3.4vw, 52px);
  align-items: end;
}
/* Section headings join the hero in the display face. The body stays in
   Bricolage, because a shout is only a shout if the rest is spoken. */
.section-head h2 {
  font-family: var(--shout);
  font-size: clamp(28px, 4.6vw, 56px);
  font-weight: 400;
  letter-spacing: .004em;
  line-height: 1.06;
  padding-bottom: .05em;
  max-width: 24ch;
  margin: 0;
  font-synthesis: none;
  text-rendering: optimizeLegibility;
}
.section-head p {
  font-size: 15.5px; line-height: 1.62; color: var(--ink-soft);
  max-width: 38ch; margin: 0 0 .3em; justify-self: end;
}

/* ------------------------------------------------------------- hop cards */
.funnel { display: grid; grid-template-columns: repeat(3, 1fr); gap: 0; border-top: 1px solid var(--rule); }
.funnel-card {
  display: flex; flex-direction: column; gap: 12px;
  padding: 28px 24px 24px;
  background: transparent; border: 0; border-right: 1px solid var(--rule-soft);
  border-radius: 0; box-shadow: none;
  color: var(--ink);
  transition: background .2s ease, padding-left .3s var(--ease);
}
.funnel-card:last-child { border-right: 0; }
.funnel-card:hover { background: rgba(91, 33, 182, .05); border-color: var(--rule-soft); box-shadow: none; padding-left: 30px; }
.funnel-card h3 { font-size: 25px; font-weight: 420; letter-spacing: -.03em; margin: 0; }
.funnel-card p { font-size: 15px; line-height: 1.55; color: var(--ink-soft); margin: 0; }
.funnel-go { margin-top: auto; font-size: 14px; color: var(--violet); font-weight: 550; display: inline-flex; align-items: center; gap: 8px; }
.funnel-go svg { transition: transform .3s var(--ease); }
.funnel-card:hover .funnel-go svg { transform: translateX(5px); }

/* ---------------------------------------------------------- product shelf */
.featured-projects {
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: 12px;
  margin-top: clamp(26px, 3.4vw, 40px);
}
.featured-projects .project-card {
  position: relative;
  display: block;
  min-height: clamp(360px, 44vw, 520px);
  border: 0;
  border-radius: var(--r-md);
  overflow: hidden;
  background: #191226;
  box-shadow: none;
  isolation: isolate;
  transition: transform .5s var(--ease), box-shadow .5s ease;
}
.featured-projects .project-card:hover { transform: translateY(-4px); box-shadow: 0 26px 50px -30px rgba(20, 16, 32, .7); }
.featured-projects .project-card::after {
  content: "";
  position: absolute; inset: 0;
  background: linear-gradient(185deg, rgba(20, 16, 32, 0) 30%, rgba(16, 12, 26, .58) 60%, rgba(14, 10, 22, .95) 92%);
  z-index: 1;
  transition: opacity .4s ease;
}
.featured-projects .project-card-image {
  position: absolute;
  inset: 0;
  display: grid;
  border: 0;
  aspect-ratio: auto;
  height: 100%;
  background: #191226;
}
.featured-projects .project-card-image img {
  width: 100%; height: 100%;
  min-height: 0; max-height: none;
  object-fit: cover;
  object-position: top center;
  transition: transform 1s var(--ease), opacity .4s ease;
  opacity: .96;
}
.featured-projects .project-card:hover .project-card-image img { transform: scale(1.05); }
.featured-projects .project-card-copy {
  position: absolute;
  inset: auto 0 0 0;
  z-index: 2;
  display: block;
  padding: 22px 22px 24px;
  background: none;
  color: #f7f4ff;
}
/* The badge used to be a stadium outline. Now it is a ruled label. */
.featured-projects .project-kicker {
  display: inline-block;
  font-size: 11.5px;
  font-weight: 600;
  letter-spacing: .1em;
  text-transform: uppercase;
  color: #ffcdbe;
  background: none;
  border: 0;
  border-radius: 0;
  padding: 0 0 4px;
  margin-bottom: 10px;
  border-bottom: 1px solid rgba(255, 205, 190, .5);
}
.featured-projects .project-card-copy strong {
  display: block;
  font-size: clamp(26px, 2.5vw, 36px);
  font-weight: 370;
  letter-spacing: -.04em;
  color: #fff;
  line-height: 1;
}
.featured-projects .project-summary-tagline {
  display: block;
  margin-top: 9px;
  font-size: 14.5px;
  line-height: 1.48;
  color: #ded6f0;
  max-width: 34ch;
}
.featured-projects .project-summary-action {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  margin-top: 15px;
  font-size: 14px;
  color: #fff;
  border-bottom: 1px solid rgba(255, 255, 255, .45);
  padding-bottom: 2px;
}
.featured-projects .project-summary-action svg { transition: transform .3s var(--ease); }
.featured-projects .project-card:hover .project-summary-action svg { transform: translateX(4px); }

/* PlayPort's three shots as a collage inside the tall card. */
.featured-projects .project-card-image--playport {
  grid-template-columns: 1fr 1fr;
  grid-template-rows: 1.25fr .75fr;
  gap: 2px;
}
.featured-projects .project-card-image--playport img:first-child { grid-row: 1 / span 2; }

/* --------------------------------------------------------- project index */
.project-list {
  margin-top: clamp(26px, 3.6vw, 44px);
  border-top: 1px solid var(--rule);
}
.project-list .project-row {
  display: grid;
  grid-template-columns: minmax(0, 1fr) minmax(0, 1.05fr) auto;
  align-items: baseline;
  gap: 26px;
  padding: 17px 0 18px;
  border: 0;
  border-bottom: 1px solid var(--rule-soft);
  text-decoration: none;
  position: relative;
  transition: none;
}
.project-list .project-row::after { display: none; }
.project-list .project-row-name {
  font-size: clamp(26px, 3.1vw, 42px);
  font-weight: 350;
  letter-spacing: -.045em;
  color: var(--ink);
  transition: color .2s ease, transform .4s var(--ease);
}
.project-list .project-row-description { font-size: 15px; line-height: 1.5; color: var(--ink-soft); }
.project-list .project-row-status {
  font-size: 12.5px;
  letter-spacing: .04em;
  text-transform: uppercase;
  font-weight: 600;
  color: var(--violet);
  opacity: .85;
  text-align: right;
  white-space: nowrap;
}
.project-list .project-row:hover .project-row-name { color: var(--violet); transform: translateX(10px); }
.project-list .project-row:hover .project-row-description { color: var(--ink); }

/* -------------------------------------------------------------- downloads */
/* An inventory, not a grid of cards. Four numbered rows on the page's own
   paper: the ordinal in the margin, the build as a name and one line, and the
   two things you can do with it on the right. Nothing is boxed, so nothing
   looks like it came out of a builder. */
.dl-list {
  list-style: none;
  margin: clamp(24px, 3.2vw, 38px) 0 0;
  padding: 0;
  border-top: 1px solid var(--rule);
}
.dl-row {
  position: relative;
  display: grid;
  grid-template-columns: clamp(56px, 7vw, 96px) minmax(0, 1fr) auto;
  gap: clamp(16px, 2.6vw, 38px);
  align-items: center;
  padding: clamp(20px, 2.6vw, 30px) 0;
  border-bottom: 1px solid var(--rule-soft);
  isolation: isolate;
}
/* The wash arrives from the left, so pointing at a row moves the page rather
   than lighting a box. */
.dl-row::before {
  content: "";
  position: absolute;
  inset: 0 -16px;
  z-index: -1;
  border-radius: 3px 22px 3px 3px;
  background: linear-gradient(98deg, rgba(91, 33, 182, .1), rgba(217, 70, 239, .05) 52%, rgba(244, 241, 249, 0) 84%);
  opacity: 0;
  transform: scaleX(.9);
  transform-origin: 0 50%;
  transition: opacity .45s ease, transform .6s var(--ease);
}
.dl-row:hover::before, .dl-row:focus-within::before { opacity: 1; transform: none; }
.dl-ord {
  font-family: var(--shout);
  font-size: clamp(30px, 4vw, 54px);
  line-height: 1;
  color: rgba(20, 16, 32, .17);
  user-select: none;
  transition: color .35s ease, transform .5s var(--ease);
}
.dl-row:hover .dl-ord,
.dl-row:focus-within .dl-ord { color: var(--violet); transform: rotate(-4deg) translateY(-2px); }
.dl-body { min-width: 0; }
.dl-name {
  margin: 0;
  font-family: var(--shout);
  font-weight: 400;
  font-size: clamp(22px, 2.5vw, 34px);
  line-height: 1.08;
  letter-spacing: .01em;
  font-synthesis: none;
}
.dl-name a { display: inline-flex; align-items: baseline; gap: 8px; color: var(--ink); text-decoration: none; }
.dl-name svg {
  width: 15px; height: 15px;
  color: var(--violet);
  opacity: 0;
  transform: translate(-4px, 4px);
  transition: opacity .25s ease, transform .35s var(--ease);
}
.dl-name a:hover { color: var(--violet); }
.dl-name a:hover svg, .dl-name a:focus-visible svg { opacity: 1; transform: none; }
.dl-tags {
  display: flex; flex-wrap: wrap; align-items: center; gap: 7px;
  margin: 7px 0 0;
  font-size: 11px; letter-spacing: .09em; text-transform: uppercase; font-weight: 600;
  color: var(--ink-faint);
}
.dl-tags span + span::before { content: "/"; margin-right: 7px; color: rgba(20, 16, 32, .26); }
.dl-line { margin: 9px 0 0; font-size: 14.5px; line-height: 1.55; color: var(--ink-soft); max-width: 60ch; }
/* The checksum is a promise, not a paragraph: it opens when asked. */
.dl-hash { margin-top: 9px; }
.dl-hash summary {
  width: max-content;
  cursor: pointer;
  font-size: 10.5px; letter-spacing: .1em; text-transform: uppercase; font-weight: 600;
  color: var(--ink-faint);
  transition: color .2s ease;
}
.dl-hash summary:hover { color: var(--violet); }
.dl-hash code {
  display: block;
  margin-top: 5px; padding: 0;
  border: 0; background: none; border-radius: 0;
  font-family: ui-monospace, 'Cascadia Mono', Consolas, monospace;
  font-size: 10.5px; line-height: 1.5; color: var(--ink-faint);
  word-break: break-all;
  cursor: copy;
}
.dl-hash code.copied { color: var(--violet); }
.dl-hash code.copied::after {
  content: " / copied";
  font-size: 9.5px;
  letter-spacing: .09em;
  text-transform: uppercase;
}
.dl-act { display: flex; flex-direction: column; align-items: flex-end; gap: 9px; }
/* A ticket corner rather than a stadium. The sheen crosses once on hover. */
.dl-get {
  position: relative;
  display: inline-flex; align-items: center; gap: 12px;
  padding: 13px 18px 13px 20px;
  border-radius: 3px 16px 16px 16px;
  overflow: hidden;
  background: var(--ink); color: var(--chalk);
  font-size: 14.5px; font-weight: 550; text-decoration: none; white-space: nowrap;
  box-shadow: 0 16px 30px -22px rgba(20, 16, 32, .9);
  transition: background .3s ease, transform .3s var(--ease), box-shadow .3s ease;
}
.dl-get::before {
  content: "";
  position: absolute; inset: 0;
  background: linear-gradient(100deg, transparent 12%, rgba(255, 255, 255, .22) 46%, transparent 72%);
  transform: translateX(-130%);
  transition: transform .7s var(--ease);
}
.dl-get > * { position: relative; z-index: 1; }
.dl-get svg { width: 16px; height: 16px; transition: transform .35s var(--ease); }
.dl-get:hover { background: var(--violet); transform: translateY(-2px); box-shadow: 0 18px 30px -20px rgba(91, 33, 182, .8); }
.dl-get:hover::before { transform: translateX(130%); }
.dl-get:hover svg { transform: translateY(2px); }
.dl-get-size {
  padding-left: 11px;
  border-left: 1px solid rgba(255, 255, 255, .24);
  font-size: 10.5px; letter-spacing: .1em; text-transform: uppercase; opacity: .72;
}
.dl-notes {
  display: inline-flex; align-items: center; gap: 6px;
  font-size: 12.5px; font-weight: 600; color: var(--violet); text-decoration: none;
}
.dl-notes svg { width: 13px; height: 13px; transition: transform .3s var(--ease); }
.dl-notes:hover svg { transform: translateX(4px); }
/* The build-choice aside is a margin note, not a panel. */
.dl-note {
  margin-top: clamp(22px, 3vw, 34px);
  padding: 4px 0 4px 16px;
  border: 0;
  border-left: 2px solid var(--violet);
  border-radius: 0;
  font-size: 13.5px;
  line-height: 1.64;
  color: var(--ink-soft);
}
.dl-note b { font-weight: 650; color: var(--ink); }

/* The machine voice. The pixel face belongs to numbers, build names and the
   mark; the shout stays for sentences. */
.dl-ord, .dl-name, .foot-mark {
  font-family: var(--pixel);
  font-weight: 700;
  letter-spacing: 0;
}
.dl-name { font-size: clamp(17px, 1.9vw, 25px); line-height: 1.3; }
.dl-ord { font-size: clamp(22px, 2.8vw, 34px); }
.foot-mark { font-family: var(--pixel); font-weight: 700; font-size: clamp(40px, 10vw, 138px); letter-spacing: 0; }
.dl-hash summary { font-family: var(--pixel); font-weight: 400; font-size: 9.5px; letter-spacing: .04em; }
.dl-tags { font-family: var(--pixel); font-weight: 400; font-size: 10px; letter-spacing: .06em; }

/* --------------------------------------------------------------- contact */
/* A postal composition: the envelope floats, the form is the letter. */
#contact.section { border-top: 1px solid var(--rule); }
.post {
  display: grid;
  grid-template-columns: minmax(0, .92fr) minmax(0, 1.08fr);
  gap: clamp(28px, 4.4vw, 64px);
  align-items: start;
}
.post .contact,
.post-form-wrap {
  display: contents;
}

/* --- the envelope ------------------------------------------------------- */
.post-art { position: relative; padding-top: 12px; }
.env {
  position: relative;
  width: 100%;
  max-width: 460px;
  transform-origin: 50% 90%;
  animation: env-float 7s ease-in-out infinite;
  filter: drop-shadow(0 30px 40px rgba(45, 25, 66, .22));
}
.env-svg { display: block; width: 100%; height: auto; overflow: visible; }
@keyframes env-float {
  0%, 100% { transform: translateY(0) rotate(-1.6deg); }
  50% { transform: translateY(-14px) rotate(1.2deg); }
}
.env-letter {
  transform-box: fill-box;
  transform-origin: 50% 100%;
  transition: transform .7s var(--ease);
}
.post-art:hover .env-letter { transform: translateY(-26px) rotate(-1.5deg); }
.env-flap-shadow { opacity: .5; transition: opacity .5s ease; }
.post-art:hover .env-flap-shadow { opacity: .2; }
.env-stamp {
  transform-box: fill-box;
  transform-origin: 50% 50%;
  animation: stamp-nudge 9s ease-in-out infinite;
}
@keyframes stamp-nudge {
  0%, 92%, 100% { transform: rotate(0) scale(1); }
  95% { transform: rotate(-4deg) scale(1.04); }
}
.env-caption {
  margin: 22px 0 0;
  font-size: 14px;
  line-height: 1.6;
  color: var(--ink-faint);
  max-width: 34ch;
}
.env-caption b { color: var(--ink); font-weight: 600; }

/* --- the letter (form) -------------------------------------------------- */
.post-body h2 {
  font-size: clamp(30px, 3.8vw, 50px);
  font-weight: 340;
  letter-spacing: -.045em;
  line-height: 1.02;
  margin: 0;
}
.post-lede {
  margin: 14px 0 0;
  font-size: 16px;
  line-height: 1.62;
  color: var(--ink-soft);
  max-width: 54ch;
}
.post .form { margin-top: 24px; }
.field > span { font-size: 12px; letter-spacing: .04em; text-transform: uppercase; color: var(--ink-faint); font-weight: 600; }
.field input, .field textarea, .field select {
  border-radius: var(--r-sm);
  border-color: var(--rule);
  background: rgba(255, 255, 255, .8);
  transition: border-color .2s ease, box-shadow .25s ease, background .2s ease;
}
.field input:focus, .field textarea:focus, .field select:focus {
  border-color: var(--violet);
  background: #fff;
  box-shadow: 0 0 0 4px rgba(124, 58, 237, .13);
  outline: none;
}

/* --- bottom strip: direct mail plus the mailing list -------------------- */
.post-foot {
  display: grid;
  grid-template-columns: minmax(0, .92fr) minmax(0, 1.08fr);
  gap: clamp(24px, 4.4vw, 64px);
  align-items: start;
  margin-top: clamp(36px, 5vw, 62px);
  padding-top: 26px;
  border-top: 1px solid var(--rule);
}
.post-label {
  display: block;
  font-size: 12px;
  letter-spacing: .1em;
  text-transform: uppercase;
  font-weight: 650;
  color: var(--ink-faint);
  margin-bottom: 8px;
}
.mail-big {
  display: inline-block;
  font-size: clamp(22px, 2.6vw, 34px);
  font-weight: 380;
  letter-spacing: -.035em;
  color: var(--ink);
  text-decoration: none;
  background-image: linear-gradient(var(--violet), var(--violet));
  background-repeat: no-repeat;
  background-size: 100% 1px;
  background-position: 0 100%;
  padding-bottom: 4px;
  transition: color .2s ease, background-size .4s var(--ease);
}
.mail-big:hover { color: var(--violet); background-size: 0 1px; background-position: 100% 100%; }
.post-note { margin: 12px 0 0; font-size: 13.5px; line-height: 1.6; color: var(--ink-faint); max-width: 40ch; }
.post-note a { color: var(--violet); }

.subscribe { position: relative; }
.subscribe h4 { font-size: 18px; font-weight: 500; letter-spacing: -.02em; margin: 0 0 6px; }
.subscribe p { font-size: 14px; line-height: 1.6; color: var(--ink-soft); margin: 0 0 12px; max-width: 46ch; }
.subscribe .field-row { display: grid; grid-template-columns: 1.3fr 1fr; gap: 10px; }

/* ---------------------------------------------------------------- footer */
.site-foot {
  border-top: 1px solid var(--rule);
  background: transparent;
  padding: clamp(36px, 5vw, 64px) 0 26px;
  overflow: hidden;
}
/* The mark is set as a watermark, cropped by the viewport edge on purpose. */
.foot-mark {
  font-family: var(--shout);
  font-size: clamp(56px, 15.5vw, 220px);
  font-weight: 400;
  letter-spacing: .01em;
  line-height: .82;
  color: rgba(20, 16, 32, .1);
  margin: 0 0 clamp(26px, 3vw, 40px);
  user-select: none;
  white-space: nowrap;
}
.foot-grid {
  display: grid;
  grid-template-columns: minmax(0, 1.35fr) minmax(0, .7fr) minmax(0, 1fr);
  gap: clamp(22px, 3.4vw, 48px);
  padding-bottom: 26px;
  border-bottom: 1px solid var(--rule-soft);
}
.foot-blurb p, .foot-notes p, .foot-links a, .foot-rule span {
  font-size: 14px;
  line-height: 1.66;
  color: var(--ink-soft);
  margin: 0;
}
.foot-blurb p + p, .foot-notes p + p { margin-top: 12px; }
.foot-blurb p:first-child { color: var(--ink); }
/* Each small paragraph gets a label in the display face. On a phone that turns
   a wall of text into something with an index. */
.foot-blurb b, .foot-notes b {
  display: block;
  font-family: var(--shout);
  font-weight: 400;
  font-size: 12px;
  letter-spacing: .06em;
  color: var(--violet);
  margin-bottom: 2px;
}
/* Jokes sit in the italic, like a note in the margin. */
.foot-blurb em, .foot-notes em { font-style: normal; color: var(--violet); }
.foot-links { display: flex; flex-direction: column; gap: 7px; align-items: flex-start; }
.foot-links a { color: var(--ink-soft); text-decoration: none; border-bottom: 1px solid transparent; transition: color .18s ease, border-color .18s ease; }
.foot-links a:hover { color: var(--violet); border-color: rgba(91, 33, 182, .4); }
.foot-rule {
  display: flex;
  flex-wrap: wrap;
  gap: 8px 22px;
  align-items: baseline;
  justify-content: space-between;
  padding-top: 20px;
}
.foot-rule span { font-size: 12.5px; color: var(--ink-faint); }
.foot-rule .foot-top {
  display: inline-flex; align-items: center; gap: 6px;
  font-size: 12.5px; font-weight: 600; color: var(--violet); text-decoration: none;
}
.foot-rule .foot-top svg { transition: transform .3s var(--ease); }
.foot-rule .foot-top:hover svg { transform: translate(2px, -2px); }

/* ------------------------------------------------------------ project pages */
.project-page { background: transparent; padding: 28px 0 64px; }
.breadcrumbs { color: var(--ink-faint); font-size: 13px; }
.breadcrumbs a { color: var(--violet); }
/* site.css keeps a near-white rule for the current crumb from the dark theme; on
   chalk that is invisible text. */
.breadcrumbs [aria-current="page"] { color: var(--ink-soft); }
.project-page-head { max-width: 900px; padding: clamp(30px, 4.4vw, 56px) 0 26px; }
.project-page-head h1 {
  font-family: var(--shout);
  font-size: clamp(34px, 5.6vw, 72px);
  font-weight: 400;
  letter-spacing: .004em;
  line-height: 1.04;
  padding-bottom: .05em;
  font-synthesis: none;
  text-rendering: optimizeLegibility;
  margin-top: 10px;
}
/* The kicker is a ruled label now, not a numbered pill. */
.project-page-head .project-kicker {
  display: inline-block;
  font: 600 11.5px/1.4 var(--display);
  letter-spacing: .11em;
  text-transform: uppercase;
  color: var(--violet);
  background: none;
  border: 0;
  border-radius: 0;
  padding: 0 0 5px;
  border-bottom: 1px solid rgba(91, 33, 182, .3);
}
.project-page-head .project-summary-tagline {
  color: var(--ink-soft);
  font-size: clamp(17px, 1.7vw, 21px);
  line-height: 1.55;
  margin-top: 16px;
  max-width: 60ch;
}
.project-page .project-detail { border: 0; margin: 0; }
.project-page .project-case {
  display: grid;
  grid-template-columns: minmax(0, 1.02fr) minmax(0, .98fr);
  gap: clamp(24px, 3.4vw, 48px);
  padding: 0;
  border: 0;
  margin: 0;
  background: transparent;
  box-shadow: none;
  align-items: start;
}
.project-page .project-case .case-media {
  position: sticky;
  top: 88px;
  display: grid;
  gap: 10px;
  order: 0;
}
.project-page .project-case .case-body { max-width: 560px; order: 1; }
/* A page with no screenshots has no second column to fill, so it gets one column
   and a wider measure rather than a half-empty grid. */
.project-page .project-case:not(:has(.case-media)) { grid-template-columns: minmax(0, 1fr); }
.project-page .project-case:not(:has(.case-media)) .case-body { max-width: 740px; }
.project-page .project-case:not(:has(.case-media)) .p-desc { font-size: 17.5px; max-width: 66ch; }
.project-page .project-case .p-title,
.project-page .project-case .p-tagline { display: none; }
.project-page .project-case .case-body > .case-top { order: 0; margin-bottom: 14px; }
.project-page .project-case .case-body > .p-links { order: 1; margin: 0 0 18px; flex-direction: row; flex-wrap: wrap; gap: 10px; }
.project-page .project-case .case-body > .p-desc { order: 2; }
.project-page .project-case .case-body > .p-points { order: 3; }
.project-page .project-case .case-body > .facts { order: 4; }

/* A credit for this project sits under the facts, added by site.js only when the
   owner approved one for this slug: a thin rule, a small label, the names. The
   link is there only when the owner left that switch on, so a name without a site
   still reads as a proper credit. */
.project-page .project-case .case-body > .credit-line {
  order: 5;
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  gap: 4px 14px;
  margin-top: 18px;
  padding-top: 14px;
  border-top: 1px solid var(--rule);
}
.credit-label {
  margin: 0;
  font-family: var(--pixel);
  font-size: 10px;
  line-height: 1.6;
  letter-spacing: .12em;
  text-transform: uppercase;
  color: var(--ink-faint);
}
.credit-names {
  display: flex;
  flex-wrap: wrap;
  gap: 4px 16px;
  margin: 0;
  padding: 0;
  list-style: none;
}
.credit-names li { font-size: 14.5px; line-height: 1.6; color: var(--ink); }
.credit-names a {
  display: inline-flex;
  align-items: baseline;
  gap: 4px;
  color: var(--ink);
  font-weight: 600;
  text-decoration: none;
  border-bottom: 1px solid var(--rule);
}
.credit-names a:hover,
.credit-names a:focus-visible { color: var(--violet); border-bottom-color: var(--violet); }
.credit-arrow { font-size: 11px; color: var(--violet); }

/* On a phone the label goes on its own line: two names plus "Made with" cannot
   share 340px without one of them breaking mid-word. */
@media (max-width: 620px) {
  .project-page .project-case .case-body > .credit-line {
    flex-direction: column;
    align-items: flex-start;
    gap: 6px;
  }
}

/* Readability: bigger, looser, darker than the shared card styles. */
.project-page .project-case .p-desc {
  color: var(--ink-soft);
  font-size: 17px;
  line-height: 1.68;
  max-width: 60ch;
  margin-top: 0;
}
.project-page .project-case .p-desc + .p-desc { margin-top: 14px; }
.project-page .project-case .p-points { margin-top: 20px; gap: 10px; }
.project-page .project-case .p-points li {
  color: var(--ink-soft);
  font-size: 15.5px;
  line-height: 1.6;
  padding-left: 26px;
}
.project-page .project-case .p-points svg { color: var(--violet); width: 15px; height: 15px; top: 5px; }
.project-page .project-case .facts {
  max-width: none;
  margin-top: 24px;
  padding-top: 16px;
  border-top: 1px solid var(--rule);
  border-color: var(--rule);
  gap: 9px 18px;
}
.project-page .project-case .facts div { font-size: 13.5px; }
.project-page .project-case .facts span:first-child { color: var(--ink-faint); }
.project-page .project-case .facts span:last-child { color: var(--ink); font-size: 13px; font-weight: 600; }

/* Galleries: the wide shot (the hub, the console) takes the full column and the
   supporting shots sit in a row under it, instead of the wide shot being squeezed
   into one third of a third. */
.project-page .visualiser-gallery { grid-template-columns: repeat(3, minmax(0, 1fr)); }
.project-page .visualiser-shot--wide { grid-column: 1 / -1; }
.project-page .visualiser-gallery .shot img { aspect-ratio: 16 / 9; object-fit: cover; max-height: none; }
.project-page .visualiser-gallery .shot--wide img,
.project-page .visualiser-gallery .visualiser-shot--wide img { aspect-ratio: 16 / 9; object-fit: contain; background: #0f0d16; }

/* Screenshots: rounded, no dark frame, caption as a ruled line. */
.project-page .project-case .shot {
  border: 1px solid var(--rule-soft);
  border-radius: var(--r-md);
  background: #fff;
  box-shadow: 0 16px 34px -26px rgba(40, 22, 55, .5);
  overflow: hidden;
  animation: step-in .6s var(--ease) both;
}
.project-page .project-case .shot:nth-child(1) { animation-delay: .06s; }
.project-page .project-case .shot:nth-child(2) { animation-delay: .14s; }
.project-page .project-case .shot:nth-child(3) { animation-delay: .2s; }
.project-page .project-case .shot:nth-child(4) { animation-delay: .26s; }
.project-page .project-case .shot:nth-child(5) { animation-delay: .32s; }
.project-page .project-case .shot img { max-height: 430px; object-fit: contain; background: #0f0d16; }
.project-page .project-case .shot figcaption {
  color: var(--ink-faint);
  background: #fff;
  border-color: var(--rule-soft);
  font-size: 12px;
  padding: 9px 12px;
}
/* The head of a project page arrives in sequence. */
.project-page .breadcrumbs { animation: step-in-left .5s var(--ease) both; }
.project-page .project-page-head h1 { animation: step-in .55s var(--ease) .06s both; }
.project-page .project-page-head .project-summary-tagline { animation: step-in .55s var(--ease) .12s both; }
.project-page .project-page-foot {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 18px;
  margin-top: clamp(28px, 4vw, 48px);
  padding-top: 22px;
  border-top: 1px solid var(--rule);
}
.project-page-foot > a:first-child { color: var(--violet); font-weight: 600; }

/* ------------------------------------------------------------ manual pages */
/* The install blocks were white cards sitting on a painted background, which is
   the same problem as the download cards. They are manual pages now: a rule, a
   heading, numbered steps with the numeral hanging in the margin. */
.install-block, .dl-note {
  background: transparent;
  border: 0;
  border-top: 1px solid var(--rule);
  border-radius: 0;
  box-shadow: none;
  padding: 26px 0 6px;
}
.install-block h3 {
  font-size: clamp(20px, 2.3vw, 27px);
  font-weight: 420;
  letter-spacing: -.03em;
  margin: 0 0 6px;
}
.steps {
  list-style: none;
  counter-reset: step;
  margin: 10px 0 0;
  padding: 0;
  display: block;
}
.steps li {
  counter-increment: step;
  position: relative;
  padding: 13px 0 14px 52px;
  border-bottom: 1px solid var(--rule-soft);
  font-size: 15.5px;
  line-height: 1.62;
  color: var(--ink-soft);
}
.steps li:last-child { border-bottom: 0; padding-bottom: 6px; }
.steps li::before {
  content: counter(step);
  position: absolute;
  left: 0;
  top: 9px;
  font-family: var(--shout);
  font-size: 27px;
  line-height: 1;
  color: rgba(91, 33, 182, .32);
  transition: color .25s ease, transform .3s var(--ease);
}
.steps li:hover::before { color: var(--violet); transform: rotate(-6deg) scale(1.12); }
.steps b { color: var(--ink); font-weight: 600; }
.install-block .small, .dl-note {
  font-size: 13.5px;
  line-height: 1.62;
  color: var(--ink-faint);
}
.install-block .small { margin: 12px 0 0; }
.install-block a { color: var(--violet); }
/* Inline code and the shell block: violet tint, then a dark panel. */
code {
  background: rgba(91, 33, 182, .09);
  border: 1px solid rgba(91, 33, 182, .16);
  border-radius: 4px;
  color: #4a1d8f;
  font-family: ui-monospace, 'Cascadia Mono', Consolas, monospace;
  font-size: .88em;
  padding: 1px 5px;
}
pre {
  background: var(--ink);
  color: #efe9fa;
  border: 0;
  border-radius: var(--r-sm);
  padding: 16px 18px;
  margin: 14px 0 0;
  overflow-x: auto;
  white-space: pre-wrap;
  overflow-wrap: anywhere;
  font-size: 13px;
  line-height: 1.7;
}
/* The inline rule for code sets white-space: pre, which stops the block from
   wrapping inside the panel even when the panel asks it to. */
pre code { background: none; border: 0; padding: 0; color: inherit; font-size: inherit; white-space: inherit; }

/* ------------------------------------------------------------- the wobble */
/* Everything you can point at leans a little. Small angles, quick settle, and
   only on the thing under the cursor. */
@keyframes wobble {
  0% { transform: rotate(0) scale(1); }
  20% { transform: rotate(-2.6deg) scale(1.04); }
  44% { transform: rotate(2.1deg) scale(1.03); }
  68% { transform: rotate(-1.1deg) scale(1.01); }
  100% { transform: rotate(0) scale(1); }
}
@keyframes wobble-soft {
  0% { transform: rotate(0); }
  30% { transform: rotate(-1.4deg); }
  62% { transform: rotate(1deg); }
  100% { transform: rotate(0); }
}
.btn:hover, .btn:focus-visible,
.p-icon:hover,
.mail-big:hover,
.textlink:hover,
.foot-links a:hover,
.dl-head h3:hover,
.funnel-card h3:hover {
  animation: wobble .6s var(--ease);
}
.project-card:hover .project-card-copy strong,
.project-row:hover .project-row-name,
.brand:hover .brand-mark {
  animation: wobble-soft .65s var(--ease);
}
.install-block .steps li:hover b { animation: wobble-soft .6s var(--ease); display: inline-block; }

/* The nav answers when it is clicked: a squish, then back. */
@keyframes tap-pop {
  0% { transform: scale(1); }
  35% { transform: scale(.9) rotate(-1.5deg); }
  70% { transform: scale(1.06) rotate(1deg); }
  100% { transform: scale(1); }
}
.nav a.tapped, .foot-links a.tapped { animation: tap-pop .44s var(--ease); }

/* Arriving at a section fades its heading up, so the jump has an end. */
@keyframes section-arrive {
  0% { opacity: .2; transform: translateY(8px); }
  100% { opacity: 1; transform: none; }
}
.section.arrived h2:first-of-type { animation: section-arrive .55s var(--ease) both; }

/* The open phone menu cascades instead of appearing. */
.nav.open a { animation: nav-item .34s var(--ease) both; }
.nav.open a:nth-child(1) { animation-delay: .02s; }
.nav.open a:nth-child(2) { animation-delay: .06s; }
.nav.open a:nth-child(3) { animation-delay: .10s; }
.nav.open a:nth-child(4) { animation-delay: .14s; }
.nav.open a:nth-child(5) { animation-delay: .18s; }
@keyframes nav-item { from { opacity: 0; transform: translateY(-6px) scale(.97); } }

/* ---------------------------------------------------------------- reveal */
.reveal, .stagger { opacity: 0; transform: translateY(18px); transition: opacity .62s ease, transform .62s var(--ease); }
.reveal.in, .stagger.in { opacity: 1; transform: none; }

/* Project page meta, set as rules rather than pills: the number is a pixel
   folio mark and the tags are rulings, so the head reads like a spec sheet. */
.project-page .case-top .case-num {
  width: auto; height: auto;
  padding: 0;
  border: 0; border-radius: 0;
  background: none;
  color: var(--violet);
  font-family: var(--pixel);
  font-size: 12px;
  letter-spacing: 0;
}
.project-page .p-tags { gap: 8px; }
.project-page .case-top .p-tags .tag {
  padding: 2px 6px;
  border: 1px solid var(--rule);
  border-radius: 2px;
  background: transparent;
  color: var(--ink-faint);
  font-family: var(--pixel);
  font-size: 9px;
  letter-spacing: .04em;
}

/* -------------------------------------------------------------------- hub */
/* The board is a ledger, not a dashboard of cards: a rule per row, numbers in
   the display face, states in the machine voice, and body text at reading size.
   Nothing here is smaller than 12.5px, and nothing that carries meaning is set
   in the pixel face. */
.hub { padding: clamp(26px, 4vw, 52px) 0 clamp(44px, 6vw, 84px); }
/* A calm page under a working board: the painted, moving background stays off
   here (site.js skips it too) so nothing shifts under small text. */
.hub-page .bg-canvas { display: none; }

.hub-head {
  display: grid;
  grid-template-columns: minmax(0, 1fr) auto;
  gap: clamp(20px, 3vw, 44px);
  align-items: start;
  padding-bottom: 22px;
  border-bottom: 1px solid var(--rule);
}
.hub-head h1 {
  font-size: clamp(38px, 5.2vw, 62px);
  line-height: .96;
  margin: 8px 0 12px;
  letter-spacing: -.045em;
}
.hub-lede {
  margin: 0;
  font-size: 17px;
  line-height: 1.6;
  color: var(--ink-soft);
  max-width: 58ch;
}
.hub-user {
  min-width: 214px;
  padding-left: 18px;
  border-left: 2px solid var(--violet);
}
.hub-user-label {
  margin: 0;
  font-family: var(--pixel);
  font-size: 11.5px;
  letter-spacing: .08em;
  text-transform: uppercase;
  color: var(--ink-faint);
}
.hub-user-mail {
  margin: 4px 0 6px;
  font-size: 15px;
  font-weight: 600;
  color: var(--ink);
  overflow-wrap: anywhere;
}
.hub-user-flag { margin: 0 0 10px; }
.hub-user form { margin: 0; }

.hub-flag {
  display: inline-flex;
  align-items: center;
  gap: 5px;
  font-size: 13px;
  font-weight: 500;
}
.hub-flag .icon { width: 14px; height: 14px; }
.hub-flag.is-ok { color: #14683f; }
.hub-flag.is-warn { color: var(--ember); }

.hub-stats {
  display: grid;
  grid-template-columns: repeat(4, minmax(0, 1fr));
  margin: 26px 0 clamp(24px, 3vw, 36px);
  border-bottom: 1px solid var(--rule-soft);
}
.hub-stats > div {
  min-width: 0;
  padding: 0 clamp(12px, 2vw, 22px) 16px 0;
  border-right: 1px solid var(--rule-soft);
}
.hub-stats > div:last-child { border-right: 0; }
.hub-stats dt {
  font-family: var(--pixel);
  font-size: 11.5px;
  letter-spacing: .05em;
  text-transform: uppercase;
  color: var(--ink-soft);
}
.hub-stats dd {
  margin: 8px 0 0;
  font-family: var(--display);
  font-size: clamp(30px, 3.8vw, 44px);
  font-weight: 600;
  letter-spacing: -.05em;
  line-height: 1;
  color: var(--ink);
}
.hub-stats dd.hub-stats-date {
  font-size: clamp(17px, 1.8vw, 22px);
  letter-spacing: -.02em;
  line-height: 1.25;
  padding-top: 6px;
}

.hub-grid {
  display: grid;
  grid-template-columns: minmax(0, 1fr) minmax(0, 318px);
  gap: clamp(24px, 3.4vw, 54px);
  align-items: start;
}
.hub-col { min-width: 0; }
.hub-side { display: grid; gap: 22px; align-content: start; }

/* ---- search and filters */
.hub-filter {
  display: grid;
  grid-template-columns: minmax(0, 1fr) auto;
  gap: 10px;
  align-items: stretch;
}
.hub-search {
  display: flex;
  align-items: center;
  gap: 10px;
  min-width: 0;
  padding: 0 14px;
  border: 1px solid rgba(20, 16, 32, .26);
  border-radius: var(--r-sm);
  background: #fff;
  transition: border-color .16s ease, box-shadow .16s ease;
}
.hub-search .icon { width: 18px; height: 18px; flex: none; color: var(--ink-faint); }
.hub-search input {
  width: 100%;
  min-width: 0;
  padding: 13px 0;
  border: 0;
  background: none;
  font: inherit;
  font-size: 16px;
  color: var(--ink);
}
.hub-search input::placeholder { color: var(--ink-faint); }
.hub-search input:focus { outline: none; }
.hub-search:focus-within {
  border-color: var(--violet);
  box-shadow: 0 0 0 3px rgba(91, 33, 182, .12);
}

.hub-btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 7px;
  padding: 11px 16px;
  border: 1px solid rgba(20, 16, 32, .26);
  border-radius: var(--r-sm);
  background: #fff;
  color: var(--ink);
  font: inherit;
  font-size: 14.5px;
  font-weight: 600;
  text-decoration: none;
  cursor: pointer;
  transition: background .16s ease, border-color .16s ease, color .16s ease;
}
.hub-btn:hover { border-color: var(--violet); color: var(--violet); }
.hub-btn:focus-visible { outline: 2px solid var(--violet); outline-offset: 2px; }
.hub-btn .icon { width: 15px; height: 15px; }
.hub-btn--primary { background: var(--ink); border-color: var(--ink); color: var(--chalk); }
.hub-btn--primary:hover { background: var(--violet); border-color: var(--violet); color: #fff; }
.hub-btn--quiet { padding: 8px 13px; font-size: 13.5px; }

.hub-tabs {
  display: flex;
  flex-wrap: wrap;
  gap: 8px 22px;
  margin: 18px 0 0;
  padding-bottom: 14px;
  border-bottom: 1px solid var(--rule-soft);
}
.hub-tabs a {
  font-size: 15px;
  color: var(--ink-soft);
  text-decoration: none;
  border-bottom: 2px solid transparent;
  padding-bottom: 3px;
  transition: color .16s ease, border-color .16s ease;
}
.hub-tabs a span {
  font-family: var(--mono);
  font-size: 11.5px;
  color: var(--ink-faint);
  margin-left: 4px;
}
.hub-tabs a:hover { color: var(--violet); }
.hub-tabs a.is-on { color: var(--violet); border-bottom-color: var(--violet); }

.hub-count {
  margin: 14px 0 0;
  font-size: 14.5px;
  color: var(--ink-soft);
}
.hub-count a { color: var(--violet); }

/* ---- the rows */
.hub-list { list-style: none; margin: 6px 0 0; padding: 0; }
.hub-row { padding: 22px 0 24px; border-bottom: 1px solid var(--rule-soft); }
.hub-row-head {
  display: flex;
  align-items: center;
  gap: 12px;
  flex-wrap: wrap;
}
.hub-row-name {
  margin: 0;
  font-size: clamp(22px, 2.2vw, 27px);
  letter-spacing: -.035em;
}
.hub-row-name a {
  color: var(--ink);
  text-decoration: none;
  border-bottom: 1px solid transparent;
  transition: color .16s ease, border-color .16s ease;
}
.hub-row-name a:hover { color: var(--violet); border-bottom-color: rgba(91, 33, 182, .4); }

.hub-state {
  padding: 3px 7px;
  border: 1px solid var(--rule);
  border-radius: 2px;
  font-family: var(--pixel);
  font-size: 11px;
  letter-spacing: .05em;
  text-transform: uppercase;
  color: var(--ink-soft);
  white-space: nowrap;
}
.hub-state--download { color: var(--violet); border-color: rgba(91, 33, 182, .3); background: rgba(91, 33, 182, .05); }
.hub-state--sale { color: var(--ember); border-color: rgba(240, 74, 42, .3); background: rgba(240, 74, 42, .05); }
.hub-state--live { color: #14683f; border-color: rgba(20, 104, 63, .3); background: rgba(20, 104, 63, .05); }

.hub-row-meta {
  margin: 0 0 0 auto;
  font-family: var(--display);
  font-size: 13.5px;
  font-variant-numeric: tabular-nums;
  color: var(--ink-faint);
}
.hub-row-meta span { opacity: .5; margin: 0 3px; }

.hub-line {
  margin: 9px 0 0;
  font-size: 16px;
  line-height: 1.6;
  color: var(--ink-soft);
  max-width: 64ch;
}

.hub-notes { list-style: none; margin: 14px 0 0; padding: 0; display: grid; gap: 8px; }
.hub-note {
  display: grid;
  grid-template-columns: 88px minmax(0, 1fr);
  gap: 14px;
  align-items: baseline;
}
.hub-note time {
  font-family: var(--mono);
  font-size: 12.5px;
  color: var(--ink-faint);
  white-space: nowrap;
}
.hub-note-title { font-size: 15px; line-height: 1.55; color: var(--ink-soft); }

.hub-links { display: flex; gap: 18px; flex-wrap: wrap; margin: 16px 0 0; }
.hub-link {
  font-size: 14px;
  color: var(--violet);
  text-decoration: none;
  border-bottom: 1px solid rgba(91, 33, 182, .3);
  padding-bottom: 1px;
}
.hub-link:hover { color: var(--ink); border-bottom-color: var(--ink); }
.hub-link--build { color: var(--ink); border-bottom-color: var(--rule); }
.hub-link--build:hover { color: var(--violet); border-bottom-color: var(--violet); }

/* ---- the side panels */
.hub-panel { border-top: 2px solid var(--ink); padding-top: 16px; }
.hub-panel h2 { font-size: 17px; margin: 0 0 8px; letter-spacing: -.02em; }
.hub-panel h3 { font-size: 15px; margin: 0; letter-spacing: -.01em; }
.hub-panel-line {
  margin: 0 0 14px;
  font-size: 14px;
  line-height: 1.55;
  color: var(--ink-soft);
  overflow-wrap: anywhere;
}
.hub-panel-line b { color: var(--ink); font-weight: 600; }
.hub-prefs { display: grid; gap: 11px; }
.hub-check {
  display: grid;
  grid-template-columns: 18px minmax(0, 1fr);
  gap: 10px;
  align-items: start;
  font-size: 14px;
  line-height: 1.55;
  color: var(--ink-soft);
  cursor: pointer;
}
.hub-check input {
  width: 18px;
  height: 18px;
  margin: 3px 0 0;
  accent-color: var(--violet);
  flex: none;
}
.hub-pass {
  display: grid;
  gap: 10px;
  margin-top: 20px;
  padding-top: 16px;
  border-top: 1px solid var(--rule-soft);
}
.hub-panel-note { margin: 0; font-size: 13px; line-height: 1.55; color: var(--ink-faint); }
.hub-panel-list {
  list-style: none;
  margin: 0;
  padding: 0;
  display: grid;
  gap: 9px;
  font-size: 14.5px;
  line-height: 1.55;
  color: var(--ink-soft);
}
.hub-panel-list li { position: relative; padding-left: 14px; }
.hub-panel-list li::before {
  content: "";
  position: absolute;
  left: 0;
  top: .58em;
  width: 5px;
  height: 5px;
  background: var(--violet);
}

/* ---- forms */
.hub-form { display: grid; gap: 14px; }
.hub-field { display: grid; gap: 6px; font-size: 14px; color: var(--ink-soft); }
.hub-field span { font-weight: 500; }
.hub-field em { font-style: normal; color: var(--ink-faint); font-size: 13px; }
.hub-field input, .hub-field textarea {
  font: inherit;
  font-size: 16px;
  padding: 11px 12px;
  border: 1px solid rgba(20, 16, 32, .26);
  border-radius: var(--r-sm);
  background: #fff;
  color: var(--ink);
}
.hub-field textarea { resize: vertical; min-height: 96px; line-height: 1.55; }
.hub-field input:focus, .hub-field textarea:focus {
  outline: none;
  border-color: var(--violet);
  box-shadow: 0 0 0 3px rgba(91, 33, 182, .12);
}

.hub-say {
  margin: 18px 0 0;
  padding: 12px 15px;
  border-left: 3px solid var(--violet);
  background: rgba(91, 33, 182, .06);
  font-size: 15px;
  line-height: 1.5;
  color: var(--ink);
}
.hub-say.is-error { border-left-color: var(--ember); background: rgba(240, 74, 42, .07); }

/* ---- signed out */
.hub-gate {
  max-width: 452px;
  margin: clamp(16px, 4vw, 48px) auto;
  display: grid;
  gap: 18px;
}
.hub-gate-head h1 {
  font-size: clamp(34px, 5vw, 46px);
  line-height: 1;
  margin: 8px 0 10px;
  letter-spacing: -.045em;
}
.hub-gate-head p { margin: 0; font-size: 16px; line-height: 1.6; color: var(--ink-soft); }
.hub-gate-note { margin: 0; font-size: 14px; line-height: 1.55; color: var(--ink-soft); }
.hub-gate-note a { color: var(--violet); }

/* ---- one project */
.hub-detail-head {
  max-width: 76ch;
  margin-top: 10px;
  padding-bottom: 22px;
  border-bottom: 1px solid var(--rule);
}
.hub-detail-head h1 {
  font-size: clamp(34px, 5vw, 58px);
  line-height: .98;
  margin: 8px 0 12px;
  letter-spacing: -.045em;
}
.hub-detail-lede { margin: 0; font-size: 17.5px; line-height: 1.6; color: var(--ink-soft); }
.hub-facts {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(150px, 1fr));
  gap: 16px 24px;
  margin: 24px 0 0;
}
.hub-facts div { min-width: 0; }
.hub-facts dt {
  font-family: var(--pixel);
  font-size: 11.5px;
  letter-spacing: .05em;
  text-transform: uppercase;
  color: var(--ink-soft);
}
.hub-facts dd { margin: 5px 0 0; font-size: 15.5px; color: var(--ink); overflow-wrap: anywhere; }
.hub-facts dd a { color: var(--violet); text-decoration: none; border-bottom: 1px solid rgba(91, 33, 182, .3); }
.hub-facts dd .icon { width: 12px; height: 12px; }

.hub-history { margin: clamp(26px, 4vw, 42px) 0 0; max-width: 84ch; }
.hub-history-head {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 16px;
  flex-wrap: wrap;
  padding-bottom: 10px;
  border-bottom: 1px solid var(--rule);
}
.hub-history-head h2 { font-size: 24px; margin: 0; letter-spacing: -.03em; }
.hub-history-head p { margin: 0; font-size: 13.5px; color: var(--ink-faint); }
.hub-history-list { list-style: none; margin: 0; padding: 0; }
.hub-history-item { padding: 20px 0; border-bottom: 1px solid var(--rule-soft); }
.hub-history-meta {
  display: flex;
  gap: 12px;
  align-items: center;
  font-family: var(--mono);
  font-size: 12.5px;
  color: var(--ink-faint);
}
.hub-history-meta span { text-transform: uppercase; letter-spacing: .04em; font-size: 11px; }
.hub-history-item h3 { font-size: 19px; line-height: 1.35; margin: 7px 0 6px; letter-spacing: -.02em; }
.hub-history-item p {
  margin: 0;
  font-size: 15.5px;
  line-height: 1.66;
  color: var(--ink-soft);
  max-width: 78ch;
}
.hub-back { margin: 26px 0 0; }

.hub-empty {
  margin-top: 22px;
  padding: 24px;
  border: 1px solid var(--rule);
  border-radius: var(--r-sm);
}
.hub-empty h1, .hub-empty h2 { font-size: 22px; margin: 0 0 6px; letter-spacing: -.02em; }
.hub-empty p { margin: 0; font-size: 15px; line-height: 1.6; color: var(--ink-soft); }
.hub-empty p + p { margin-top: 10px; }
.hub-empty a { color: var(--violet); }
.hub-empty .btn { display: inline-block; margin-top: 14px; }

/* ---- source, builds and the people list */
.hub-files { margin: clamp(26px, 4vw, 42px) 0 0; max-width: 84ch; }
.hub-section-head {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 16px;
  flex-wrap: wrap;
  padding-bottom: 10px;
  border-bottom: 1px solid var(--rule);
}
.hub-section-head h2 { font-size: 24px; margin: 0; letter-spacing: -.03em; }
.hub-section-head p { margin: 0; font-size: 13.5px; color: var(--ink-faint); }
.hub-section-note { margin: 14px 0 0; font-size: 15px; line-height: 1.6; color: var(--ink-soft); }

.hub-file-list { list-style: none; margin: 0; padding: 0; }
.hub-file-row {
  display: grid;
  grid-template-columns: 20px minmax(0, 1fr) auto;
  gap: 14px;
  align-items: center;
  padding: 14px 0;
  border-bottom: 1px solid var(--rule-soft);
}
.hub-file-icon { color: var(--violet); display: flex; }
.hub-file-icon .icon { width: 16px; height: 16px; }
.hub-file-name { margin: 0; font-size: 15.5px; font-weight: 600; color: var(--ink); overflow-wrap: anywhere; }
.hub-file-meta { margin: 4px 0 0; font-family: var(--mono); font-size: 12.5px; color: var(--ink-faint); overflow-wrap: anywhere; }
.hub-badge {
  display: inline-block;
  margin-left: 6px;
  padding: 2px 7px;
  border: 1px solid rgba(240, 74, 42, .35);
  border-radius: 999px;
  background: rgba(240, 74, 42, .07);
  color: var(--ember);
  font-family: var(--pixel);
  font-size: 10.5px;
  letter-spacing: .04em;
  text-transform: uppercase;
  vertical-align: 2px;
}

.hub-code-actions { display: flex; align-items: center; gap: 18px; flex-wrap: wrap; margin: 16px 0 6px; }
.hub-btn-size { margin-left: 8px; font-family: var(--mono); font-size: 12px; opacity: .72; }
.hub-code-note { font-size: 13.5px; line-height: 1.55; color: var(--ink-faint); max-width: 52ch; }
.hub-path { margin: 14px 0 0; font-family: var(--mono); font-size: 13px; color: var(--ink-soft); overflow-wrap: anywhere; }
.hub-path a { color: var(--violet); }

.hub-tree { list-style: none; margin: 10px 0 0; padding: 0; }
.hub-tree-row {
  display: grid;
  grid-template-columns: 18px minmax(0, 1fr) auto auto;
  gap: 12px;
  align-items: center;
  padding: 9px 0;
  border-bottom: 1px solid var(--rule-soft);
  font-size: 14.5px;
}
.hub-tree-row .icon { width: 15px; height: 15px; color: var(--ink-faint); }
.hub-tree-row a { color: var(--ink); text-decoration: none; overflow-wrap: anywhere; }
.hub-tree-row a:hover { color: var(--violet); text-decoration: underline; text-underline-offset: 3px; }
.hub-tree-size { font-family: var(--mono); font-size: 12.5px; color: var(--ink-faint); white-space: nowrap; }
.hub-tree-get { font-size: 12.5px; white-space: nowrap; }

.hub-invite { display: grid; gap: 11px; margin: 6px 0 14px; }
.hub-people { list-style: none; margin: 0; padding: 0; }
.hub-person {
  display: grid;
  grid-template-columns: minmax(0, 1fr) auto;
  gap: 10px;
  align-items: center;
  padding: 11px 0;
  border-top: 1px solid var(--rule-soft);
}
.hub-person-mail { margin: 0; font-size: 14px; font-weight: 600; color: var(--ink); overflow-wrap: anywhere; }
.hub-person-meta { margin: 3px 0 0; font-size: 12.5px; line-height: 1.45; color: var(--ink-faint); }
.hub-person-link { margin: 5px 0 0; font-size: 12.5px; line-height: 1.45; color: var(--ink-faint); }
.hub-person-link a { color: var(--violet); }

/* ---- picking projects */
.hub-pick { margin: 0; padding: 0; border: 0; }
.hub-pick legend {
  padding: 0 0 6px;
  font-size: 13.5px;
  font-weight: 500;
  color: var(--ink-soft);
}
.hub-pick-grid {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 2px 10px;
  max-height: 210px;
  overflow-y: auto;
  padding: 8px 10px;
  border: 1px solid var(--rule-soft);
  border-radius: var(--r-sm);
  background: #fff;
}
.hub-pick-item {
  display: flex;
  align-items: center;
  gap: 7px;
  padding: 4px 0;
  font-size: 13.5px;
  line-height: 1.35;
  color: var(--ink-soft);
  cursor: pointer;
}
.hub-pick-item input { accent-color: var(--violet); flex: none; }
.hub-pick-item span { overflow-wrap: anywhere; }

.hub-access { margin-top: 8px; }
.hub-access summary {
  cursor: pointer;
  font-size: 13px;
  color: var(--violet);
  width: fit-content;
}
.hub-access summary:hover { text-decoration: underline; text-underline-offset: 3px; }
.hub-access form { display: grid; gap: 9px; margin-top: 10px; }
.hub-access .hub-pick-grid { grid-template-columns: 1fr; max-height: 190px; }

/* ---- a collaborator's note */
.hub-notesback { margin-top: clamp(26px, 4vw, 42px); }
.hub-note-form { display: grid; gap: 12px; margin: 16px 0 4px; max-width: 62ch; }
.hub-notesback-list { list-style: none; margin: 20px 0 0; padding: 0; }
.hub-notesback-item { padding: 15px 0; border-bottom: 1px solid var(--rule-soft); }
.hub-notesback-meta {
  margin: 0 0 6px;
  font-family: var(--mono);
  font-size: 12.5px;
  color: var(--ink-faint);
}
.hub-notesback-meta b { color: var(--ink-soft); font-weight: 600; }
.hub-notesback-text { margin: 0; font-size: 15.5px; line-height: 1.64; color: var(--ink); max-width: 76ch; }
.hub-btn--danger { color: var(--ember); border-color: rgba(240, 74, 42, .35); }
.hub-btn--danger:hover { background: rgba(240, 74, 42, .08); }

/* ---- one file, shown as text */
.code-page { padding: clamp(20px, 3vw, 40px) 0 70px; }
.code-view { max-width: 1160px; }
.code-view-head {
  display: flex;
  align-items: flex-start;
  justify-content: space-between;
  gap: 18px;
  flex-wrap: wrap;
  margin: 16px 0 18px;
}
.code-view-head h1 {
  margin: 0;
  font-family: var(--mono);
  font-size: clamp(16px, 2vw, 20px);
  line-height: 1.4;
  letter-spacing: 0;
  overflow-wrap: anywhere;
}
.code-view-meta { margin: 7px 0 0; font-family: var(--mono); font-size: 12.5px; color: var(--ink-faint); }
.code-body {
  margin: 0;
  padding: 18px 20px;
  max-height: 74vh;
  overflow: auto;
  border: 1px solid var(--rule);
  border-radius: var(--r-sm);
  background: rgba(20, 12, 40, .04);
  font-family: var(--mono);
  font-size: 13px;
  line-height: 1.62;
  tab-size: 2;
  color: var(--ink);
}
.hub-empty-inline { margin: 0; font-size: 15px; line-height: 1.6; color: var(--ink-soft); }

@media (max-width: 900px) {
  .hub-head { grid-template-columns: minmax(0, 1fr); }
  .hub-user {
    padding: 14px 0 0;
    border-left: 0;
    border-top: 1px solid var(--rule-soft);
  }
  .hub-user form { display: inline-block; }
  .hub-stats { grid-template-columns: 1fr 1fr; }
  .hub-stats > div { padding-bottom: 14px; }
  .hub-stats > div:nth-child(2) { border-right: 0; }
  .hub-stats > div:nth-child(1), .hub-stats > div:nth-child(2) {
    border-bottom: 1px solid var(--rule-soft);
    padding-bottom: 16px;
    margin-bottom: 14px;
  }
  .hub-grid { grid-template-columns: minmax(0, 1fr); }
  .hub-tabs {
    flex-wrap: nowrap;
    overflow-x: auto;
    gap: 18px;
    -webkit-overflow-scrolling: touch;
    scrollbar-width: none;
  }
  .hub-tabs::-webkit-scrollbar { display: none; }
  .hub-tabs a { flex: none; }
  .hub-row-meta { margin-left: 0; flex-basis: 100%; }
  .hub-note { grid-template-columns: 76px minmax(0, 1fr); gap: 10px; }
}

@media (max-width: 520px) {
  /* Two notes per project is enough on a phone; the project page carries the
     rest, and three long titles per row is what made this read as a wall. */
  .hub-notes .hub-note:nth-child(3) { display: none; }
  .hub-filter { grid-template-columns: minmax(0, 1fr); }
  .hub-filter .hub-btn { justify-content: center; }
  .hub-note { grid-template-columns: minmax(0, 1fr); gap: 2px; }
  .hub-note time { margin-top: 4px; }
  .hub-row { padding: 18px 0 20px; }
  .hub-tree-row { grid-template-columns: 18px minmax(0, 1fr); row-gap: 3px; }
  .hub-tree-size { grid-column: 2; }
  .hub-tree-get { grid-column: 2; }
  .hub-file-row { grid-template-columns: 20px minmax(0, 1fr); row-gap: 8px; }
  .hub-file-row .hub-btn { grid-column: 2; justify-self: start; }
  .hub-stats dd { font-size: 30px; }
}

/* ------------------------------------------------------------ account pass */
/* The password change is a second, quieter box inside the signed-in panel: a
   rule above it, two fields side by side, and the warning under the button. */
.account-in .account-pass {
  border-top: 1px solid var(--rule-soft);
  padding-top: 18px;
  margin-top: 2px;
}
.account-pass h4 {
  font-size: 16px;
  font-weight: 600;
  letter-spacing: -.02em;
  margin: 0 0 12px;
}
.account-pass-row {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 10px;
}
.account-pass-note {
  margin: 10px 0 0;
  font-size: 12.5px;
  line-height: 1.55;
  color: var(--ink-faint);
  max-width: 56ch;
}

/* ------------------------------------------------------------------ phone */
/* A separate composition, not a shrunken desktop. The shelf becomes a
   horizontal deck, the index becomes a stacked list with the status above
   the name, and the envelope moves above the form. */
@media (max-width: 900px) {
  .wrap { width: calc(100% - 36px); }
  .head-in { min-height: 56px; }
  .brand { font-size: 16.5px; gap: 9px; }
  .brand-word { font-size: 12.5px; }
  .brand-mark { width: 27px; height: 27px; font-size: 15px; border-radius: 3px 11px 3px 3px; box-shadow: 2px 2px 0 rgba(91, 33, 182, .3); }

  /* The menu drops in rather than appearing. */
  .menu-btn {
    border-radius: 3px 11px 11px 11px;
    border-color: var(--rule);
    color: var(--ink);
    background: transparent;
    font-family: var(--pixel);
    font-size: 9px;
    text-transform: uppercase;
    padding: 8px 11px;
    letter-spacing: .06em;
    gap: 8px;
  }
  .menu-btn:hover { background: rgba(91, 33, 182, .07); }
  .nav {
    top: 58px;
    left: 18px;
    right: 18px;
    padding: 8px;
    gap: 2px;
    border: 1px solid var(--rule);
    border-radius: var(--r-md);
    background: rgba(255, 255, 255, .97);
    backdrop-filter: blur(12px);
    box-shadow: 0 26px 50px -30px rgba(45, 25, 65, .6);
  }
  .nav.open { animation: nav-drop .28s var(--ease) both; }
  @keyframes nav-drop { from { opacity: 0; transform: translateY(-8px); } }
  .nav a { padding: 11px 13px; border-radius: var(--r-xs); font-size: 15px; }
  .nav .nav-mail { margin-top: 6px; text-align: center; }

  .hero { min-height: auto; padding: 84px 0 28px; }
  .hero-kicker { font-size: 12px; margin-bottom: 18px; }
  .hero-display { font-size: clamp(30px, 8.4vw, 54px); letter-spacing: 0; }
  .hero-bottom { grid-template-columns: 1fr; gap: 20px; margin-top: 22px; padding-top: 18px; }
  .hero-lede { font-size: 16.5px; line-height: 1.6; }
  .hero-actions { justify-content: stretch; }
  .hero-actions .btn { flex: 1 1 auto; text-align: center; justify-content: center; }

  .section, .section:first-of-type, .section:nth-of-type(odd), #work.section { padding: 34px 0; }
  .section-head { grid-template-columns: 1fr; gap: 14px; }
  .section-head h2 { font-size: clamp(27px, 8vw, 40px); line-height: 1.08; }
  .section-head p { font-size: 14px; justify-self: start; max-width: none; margin: 0; }

  .funnel { grid-template-columns: 1fr; }
  .funnel-card { min-height: 0; border-right: 0; border-bottom: 1px solid var(--rule-soft); padding: 20px 0; }
  .funnel-card:hover { padding-left: 0; background: transparent; }
  .funnel-card h3 { font-size: 21px; }
  .funnel-card p { font-size: 14px; }

  /* The shelf scrolls sideways, one card at a time, with the next one peeking. */
  .featured-projects {
    display: flex;
    gap: 10px;
    margin-inline: -18px;
    padding: 0 18px 6px;
    overflow-x: auto;
    scroll-snap-type: x mandatory;
    scroll-padding-left: 18px;
    -webkit-overflow-scrolling: touch;
    scrollbar-width: none;
  }
  .featured-projects::-webkit-scrollbar { display: none; }
  .featured-projects .project-card {
    flex: 0 0 82%;
    min-height: 0;
    aspect-ratio: 3 / 4;
    scroll-snap-align: start;
  }
  .featured-projects .project-card-copy strong { font-size: 27px; }
  .featured-projects .project-summary-tagline { font-size: 13.5px; }

  .project-list { margin-top: 24px; }
  .project-list .project-row {
    grid-template-columns: 1fr;
    grid-template-areas: "status" "name" "desc";
    gap: 3px;
    padding: 15px 0 16px;
  }
  .project-list .project-row-name { grid-area: name; font-size: 23px; letter-spacing: -.04em; }
  .project-list .project-row-status { grid-area: status; text-align: left; font-size: 10.5px; letter-spacing: .1em; }
  .project-list .project-row-status::after { content: " /"; }
  .project-list .project-row-description { grid-area: desc; font-size: 13.5px; line-height: 1.5; }
  .project-list .project-row:hover .project-row-name { transform: none; }

  /* Each build becomes a tap rather than a paragraph: the ordinal moves to
     the corner, the two actions sit side by side underneath. */
  .dl-list { margin-top: 20px; }
  .dl-row { grid-template-columns: minmax(0, 1fr); gap: 14px; padding: 20px 0 22px; align-items: start; }
  .dl-ord { position: absolute; top: 12px; right: 0; font-size: 30px; color: rgba(20, 16, 32, .13); }
  .dl-name { font-size: 18px; padding-right: 46px; }
  .dl-tags { font-size: 8.5px; gap: 6px; }
  .dl-line { font-size: 14px; line-height: 1.52; margin-top: 7px; }
  .dl-act { flex-direction: row; align-items: center; justify-content: space-between; gap: 12px; width: 100%; }
  .dl-get { flex: 1 1 auto; justify-content: center; padding: 14px 16px; }
  .dl-notes { flex: 0 0 auto; font-size: 12.5px; }
  .dl-row::before { inset: 0 -18px; border-radius: 2px; }

  .post { grid-template-columns: 1fr; gap: 30px; }
  .post-art { padding-top: 0; }
  .env { max-width: 300px; margin-inline: auto; }
  .env-caption { text-align: center; margin-inline: auto; font-size: 13px; }
  .post-body h2 { font-size: clamp(27px, 7.4vw, 36px); }
  .post-lede { font-size: 15px; }
  .post-foot { grid-template-columns: 1fr; gap: 22px; padding-top: 22px; }
  .mail-big { font-size: 22px; }
  .subscribe .field-row { grid-template-columns: 1fr; }

  .site-foot { padding: 34px 0 24px; }
  .foot-mark { font-size: clamp(34px, 11vw, 60px); margin-bottom: 22px; }
  .foot-grid { grid-template-columns: 1fr; gap: 24px; padding-bottom: 22px; }
  /* The links become a two-column index with rules, which reads as a menu
     rather than as one more run of sentences. */
  .foot-links {
    display: grid;
    grid-template-columns: 1fr 1fr;
    gap: 0;
    border-top: 1px solid var(--rule-soft);
  }
  .foot-links a {
    display: flex; align-items: center; justify-content: space-between;
    padding: 10px 2px;
    border-bottom: 1px solid var(--rule-soft);
    font-size: 13.5px;
  }
  .foot-links a:nth-child(odd) { padding-right: 14px; }
  .foot-links a:nth-child(even) { padding-left: 14px; }
  .foot-links a::after { content: "↗"; color: var(--violet); font-size: 11px; }
  .foot-blurb p, .foot-notes p { font-size: 13.5px; line-height: 1.6; }
  .foot-blurb p + p, .foot-notes p + p { margin-top: 14px; }
  .foot-blurb b, .foot-notes b { font-size: 11px; }
  .foot-rule { flex-direction: column; align-items: flex-start; gap: 7px; padding-top: 18px; }
  .foot-rule span { font-size: 11.5px; }

  .project-page { padding: 20px 0 48px; }
  .project-page-head { padding: 22px 0 20px; }
  .project-page-head h1 { font-size: clamp(32px, 9vw, 44px); }
  .project-page-head .project-summary-tagline { font-size: 16px; margin-top: 12px; }
  /* Media above the text. A plain stack of screenshots scrolls sideways like the
     shelf; the two galleries keep their own layouts from the base stylesheet. */
  .project-page .project-case { grid-template-columns: 1fr; gap: 24px; }
  .project-page .project-case .case-media { position: static; }
  .project-page .project-case .case-media:not(.visualiser-gallery):not(.playport-gallery) {
    display: flex;
    gap: 10px;
    margin-inline: -18px;
    padding: 0 18px 4px;
    overflow-x: auto;
    scroll-snap-type: x mandatory;
    scroll-padding-left: 18px;
    scrollbar-width: none;
  }
  .project-page .project-case .case-media::-webkit-scrollbar { display: none; }
  .project-page .project-case .case-media:not(.visualiser-gallery):not(.playport-gallery) > * { flex: 0 0 88%; scroll-snap-align: start; }
  /* The galleries become one sideways row here. The three-column rule above
     outranks the older phone rules in site.css, so the row is declared again:
     a shot that spans "all columns" of a collapsed grid is a nine-pixel sliver. */
  .project-page .visualiser-gallery {
    grid-template-columns: none;
    grid-auto-columns: 84%;
    grid-auto-flow: column;
    gap: 12px;
    margin-inline: -18px;
    padding: 0 18px 8px;
    overflow-x: auto;
    scroll-snap-type: x mandatory;
    scroll-padding-left: 18px;
    scrollbar-width: none;
  }
  .project-page .visualiser-gallery::-webkit-scrollbar { display: none; }
  .project-page .visualiser-gallery > * { min-width: 0; scroll-snap-align: start; }
  .project-page .visualiser-gallery .visualiser-shot--wide { grid-column: auto; }
  .project-page .visualiser-gallery .visualiser-shot--wide img { aspect-ratio: 16 / 9; object-fit: cover; max-height: none; }
  .project-page .project-case .case-media .shot img { max-height: 260px; }
  .project-page .project-case .case-body { max-width: none; }
  .project-page .project-case .p-desc { font-size: 16px; line-height: 1.64; }
  .project-page .project-case .p-points li { font-size: 15px; line-height: 1.58; }
  .project-page .project-case .facts { grid-template-columns: 1fr; }
  .project-page-foot { flex-direction: column; align-items: stretch; gap: 12px; }
  .project-page-foot .btn { justify-content: center; }
}

/* Small phones: drop the last of the padding and let type breathe down. */
@media (max-width: 420px) {
  .wrap { width: calc(100% - 28px); }
  .hero { padding-top: 74px; }
  .hero-display { font-size: 30px; }
  .split { letter-spacing: -.042em; }
  .featured-projects { margin-inline: -14px; padding-inline: 14px; }
  .featured-projects .project-card { flex-basis: 88%; }
  .project-page .project-case .case-media:not(.visualiser-gallery):not(.playport-gallery) { margin-inline: -14px; padding-inline: 14px; }
  .post .form .field-row { grid-template-columns: 1fr; }
  .account-pass-row { grid-template-columns: 1fr; }
}

/* Reduced motion: keep the cross-fades, drop every movement. The one exception
   is the envelope, which is asked to float; it keeps a slow drift with no
   rotation and no scale, the part that actually unsettles people. */
@media (prefers-reduced-motion: reduce) {
  ::view-transition-old(root) { animation: page-fade-out .2s ease both; }
  ::view-transition-new(root) { animation: page-fade-in .34s ease both; }
  @keyframes page-fade-out { to { opacity: 0; } }
  @keyframes page-fade-in { from { opacity: 0; } }

  main { animation: page-fade-in .34s ease both; }
  .hero-display .line > span { animation: none; transform: none; }
  .featured-projects .project-card:hover .project-card-image img { transform: none; }
  .project-list .project-row:hover .project-row-name { transform: none; }

  .ch { animation: none; }

  .env { animation: env-drift 9s ease-in-out infinite; }
  @keyframes env-drift {
    0%, 100% { transform: translateY(0); }
    50% { transform: translateY(-8px); }
  }
  .env-stamp { animation: none; }
  .post-art:hover .env-letter { transform: translateY(-14px); }

  .project-page .project-case .shot { animation: page-fade-in .5s ease both; }
  .project-page .breadcrumbs,
  .project-page .project-page-head h1,
  .project-page .project-page-head .project-summary-tagline { animation: page-fade-in .5s ease both; }
  .project-page .project-page-head h1 { animation-delay: .04s; }
  .project-page .project-page-head .project-summary-tagline { animation-delay: .08s; }

  .reveal, .stagger { transform: none; transition: opacity .4s ease; }
}
