@font-face { font-family: "Switzer"; src: url("/fonts/switzer-300.woff2") format("woff2"); font-weight: 300; font-display: block; }
  @font-face { font-family: "Switzer"; src: url("/fonts/switzer-400.woff2") format("woff2"); font-weight: 400; font-display: block; }
  @font-face { font-family: "Switzer"; src: url("/fonts/switzer-500.woff2") format("woff2"); font-weight: 500; font-display: block; }

  @font-face { font-family: "CHAOS16"; src: url("/fonts/chaos16.woff2") format("woff2"); font-display: block; }

  /* Geist Pixel (Square) — Vercel's bitmap face, SIL OFL. Licence sits
     beside it at /fonts/GeistPixel-OFL.txt, which the licence requires. */
  @font-face { font-family: "Geist Pixel"; src: url("/fonts/GeistPixel-Square.woff2") format("woff2"); font-weight: 400; font-display: block; }

  :root {
    color-scheme: light;
    --orange: #ff8000;
    --paper: #ffffff;
    --ink: #101010;
    --soft: #5b5b58;
    --faint: #9a9a96;
    --hair: #e2e2de;
    --sans: "Switzer", -apple-system, system-ui, sans-serif;
    --mono: ui-monospace, "SF Mono", SFMono-Regular, Menlo, monospace;
  }
  @media (prefers-color-scheme: dark) {
    :root { --paper: #0e0e0d; --ink: #f0f0ec; --soft: #a0a09b; --faint: #62625e; --hair: #262624; }
  }
  :root[data-theme="dark"] { --paper: #0e0e0d; --ink: #f0f0ec; --soft: #a0a09b; --faint: #62625e; --hair: #262624; }
  :root[data-theme="light"] { --paper: #ffffff; --ink: #101010; --soft: #5b5b58; --faint: #9a9a96; --hair: #e2e2de; }

  * { box-sizing: border-box; }
  html { -webkit-text-size-adjust: 100%; }
  body {
    margin: 0;
    background: var(--paper);
    color: var(--ink);
    font-family: var(--sans);
    font-weight: 300;
    font-size: 17px;
    line-height: 1.55;
    letter-spacing: -0.012em;
    -webkit-font-smoothing: antialiased;
  }
  .page { max-width: 860px; margin: 0 auto; padding: 13vh 28px 16vh; }

  /* The only motion on the page: rules draw themselves in, once, on load.
     Everything else is still. */
  hr {
    border: 0;
    height: 1px;
    background: var(--hair);
    margin: 0;
    transform-origin: left;
    animation: draw 0.85s cubic-bezier(0.22, 1, 0.36, 1) forwards;
  }
  @keyframes draw { from { transform: scaleX(0); } to { transform: scaleX(1); } }

  header { display: flex; justify-content: space-between; align-items: baseline; gap: 18px; }
  /* The one piece of the old identity that survives the rewrite: the pixel
     wordmark in orange. Sized up because CHAOS16's glyphs are small on the
     body and would otherwise mush at 17px. */
  .who {
    font-family: "CHAOS16", monospace;
    font-size: 24px;            /* was 23px: off the pixel grid, so it blurred */
    font-weight: 400;
    letter-spacing: 0;
    line-height: 1;
    color: var(--orange);
    -webkit-font-smoothing: none;
    -moz-osx-font-smoothing: unset;
    text-rendering: geometricPrecision;
  }
  .where { font-family: var(--mono); font-size: 11px; letter-spacing: 0.16em; text-transform: uppercase; color: var(--faint); }

  h1 {
    margin: 12vh 0 0;
    font-weight: 300;
    font-size: clamp(40px, 8vw, 76px);
    line-height: 1.02;
    letter-spacing: -0.05em;
    text-wrap: balance;
  }
  h1 em { font-style: normal; color: var(--faint); }

  .intro { margin: 34px 0 0; font-size: 18.5px; color: var(--soft); max-width: 54ch; }
  .intro b { font-weight: 500; color: var(--ink); }

  /* Section labels in CHAOS16. A pixel face only renders sharp when its
     size lands on the grid it was drawn on (8/16/24/32 here) and when the
     tracking is a whole number of pixels; 18px at .04em put every glyph
     between pixels. Anti-aliasing then softened what was left, so it is
     switched off — a bitmap face wants hard edges, not smoothed ones. */
  h2 {
    margin: 0 0 8px;
    font-family: "CHAOS16", monospace;
    font-size: 24px;
    font-weight: 400;
    line-height: 1;
    letter-spacing: 3px;
    text-transform: none;
    color: var(--soft);
    -webkit-font-smoothing: none;
    -moz-osx-font-smoothing: unset;
    text-rendering: geometricPrecision;
  }
  .block { margin-top: 12vh; }
  .block hr { margin-bottom: 26px; }

  /* project rows: name, one line, arrow. Nothing else. */
  .list { margin: 0; padding: 0; list-style: none; }
  .list a {
    display: grid;
    grid-template-columns: 10.5rem 1fr auto;
    gap: 18px;
    align-items: baseline;
    padding: 17px 0;
    text-decoration: none;
    color: inherit;
  }
  .list .nm { font-weight: 400; letter-spacing: -0.02em; }
  .list .ds { color: var(--soft); font-size: 15.5px; }
  .list .go { color: var(--faint); font-size: 14px; transition: transform 0.25s ease, color 0.25s ease; }
  /* A case study is the deeper artifact. Same type as a build note, just
     carried a step closer to the ink so it reads first when scanning. */
  .list .go-study { color: var(--soft); font-weight: 500; }
  .list a:hover .go { transform: translateX(4px); color: var(--ink); }
  .list a:hover .nm { text-decoration: underline; text-underline-offset: 4px; text-decoration-thickness: 1px; }

  .prose p { margin: 0 0 16px; color: var(--soft); max-width: 58ch; }
  .prose b { font-weight: 500; color: var(--ink); }

  .roles { margin: 0; padding: 0; list-style: none; }
  .roles li { font-size: 15.5px; }
  /* the row itself is the control: collapsed it is just company and year,
     and the summary only arrives when asked for */
  .roles .role {
    display: grid;
    grid-template-columns: 1fr auto;
    gap: 18px;
    align-items: baseline;
    width: 100%;
    padding: 15px 0;
    background: none; border: 0; border-radius: 0;
    font: inherit; color: inherit; text-align: left; cursor: pointer;
    position: relative;
  }
  .roles .role::before {
    content: "";
    position: absolute; inset: 0 -12px;
    border-radius: 4px; background: var(--tint);
    opacity: 0; transition: opacity .22s ease;
  }
  .roles .role:hover::before { opacity: 1; }
  .roles .co, .roles .yr { position: relative; }
  .roles .co { transition: transform .3s cubic-bezier(0.22, 1, 0.36, 1); }
  .roles .role:hover .co { transform: translateX(2px); }
  .roles .role[aria-expanded="true"] .co { font-weight: 500; }
  /* the line sits under the company, in the first column only, so the year
     stays pinned to the top right of the row */
  .roles .what {
    margin: 2px 0 18px;
    padding: 0;
    max-width: 64ch;
    list-style: none;
    font-size: 14.5px;
    line-height: 1.55;
    color: var(--soft);
  }
  .roles .what[hidden] { display: none; }
  .roles .what li {
    position: relative;
    padding-left: 16px;
    margin-bottom: 6px;
  }
  /* a hairline tick rather than a bullet dot — quieter, and it matches the
     rules the rest of the page is built from */
  .roles .what li::before {
    content: "";
    position: absolute;
    left: 0; top: .68em;
    width: 7px; height: 1px;
    background: var(--faint);
  }
  .roles .co { font-weight: 400; }
  .roles .co span { color: var(--soft); font-weight: 300; }
  .roles .yr { font-family: var(--mono); font-size: 11.5px; color: var(--faint); white-space: nowrap; padding-top: 4px; }

  footer { margin-top: 12vh; }
  footer hr { margin-bottom: 26px; }
  .ping { font-size: 20px; }
  /* the invitation is the one warm thing in the footer, so it gets the
     wordmark's face and colour. Snapped to 24px with smoothing off, same
     as every other pixel run on the page. */
  .ping a {
    font-family: "CHAOS16", monospace;
    font-size: 24px;
    line-height: 1;
    letter-spacing: 1px;
    color: var(--orange);
    text-decoration: none;
    -webkit-font-smoothing: none;
    -moz-osx-font-smoothing: unset;
    text-rendering: geometricPrecision;
    transition: filter .2s ease;
  }
  .ping a:hover { filter: saturate(1.3) brightness(.94); }
  .links { margin: 22px 0 0; font-family: var(--mono); font-size: 11px; letter-spacing: 0.14em; text-transform: uppercase; color: var(--faint); }
  .links a { color: var(--faint); text-decoration: none; }
  .links a:hover { color: var(--ink); }
  a:focus-visible { outline: 1px solid var(--ink); outline-offset: 4px; }


  /* ── photo wall ─────────────────────────────────────────
     Masonry columns so every photo keeps its own proportions and the
     sizes stay uneven. Captions are hidden until hover, at which point
     the image washes out toward the page colour and the label and date
     resolve over it — the same move as the Framer original, done with a
     scrim so it inverts correctly in dark mode. */
  .wall { columns: 3; column-gap: 14px; margin-top: 4px; }
  .wall figure {
    position: relative;
    margin: 0 0 14px;
    break-inside: avoid;
    overflow: hidden;
    isolation: isolate;
  }
  .wall img {
    display: block;
    width: 100%;
    height: auto;
    background: var(--hair);
    transition: transform 0.7s cubic-bezier(0.22, 1, 0.36, 1);
  }
  .wall .wash {
    position: absolute;
    inset: 0;
    background: var(--paper);
    opacity: 0;
    transition: opacity 0.45s cubic-bezier(0.22, 1, 0.36, 1);
    pointer-events: none;
  }
  .wall figcaption {
    position: absolute;
    inset: auto 0 0 0;
    /* stacked, not side by side: a caption with a real sentence under it
       used to sit in a nowrap row and get clipped at the frame edge */
    display: flex;
    flex-direction: column;
    align-items: flex-start;
    gap: 3px;
    padding: 16px 18px;
    opacity: 0;
    transform: translateY(6px);
    transition: opacity 0.4s ease, transform 0.5s cubic-bezier(0.22, 1, 0.36, 1);
    pointer-events: none;
  }
  .wall figcaption b {
    font-size: 15px;
    font-weight: 400;
    letter-spacing: 0.02em;
    text-transform: uppercase;
    color: var(--ink);
  }
  .wall figcaption span { font-size: 13.5px; line-height: 1.4; color: var(--soft); }

  .wall figure:hover img,
  .wall figure:focus-within img { transform: scale(1.02); }
  .wall figure:hover .wash,
  .wall figure:focus-within .wash { opacity: 0.86; }
  .wall figure:hover figcaption,
  .wall figure:focus-within figcaption { opacity: 1; transform: none; }

  @media (max-width: 620px) { .wall { columns: 2; } }
  @media (hover: none) {
    /* No hover on touch: show the caption over a permanent light scrim. */
    .wall .wash { opacity: 0.8; }
    .wall figcaption { opacity: 1; transform: none; }
  }

  /* ── building: current products, separated from the archive ──
     Two entries, more room each, present tense. The point of the split is
     that a company and a one-day build should not look like the same row. */
  .now { margin: 0; padding: 0; list-style: none; }
  .now li { padding: 22px 0; }
  .now .top { display: flex; align-items: baseline; gap: 12px; flex-wrap: wrap; }
  .now .nm { font-size: 21px; font-weight: 400; letter-spacing: -0.03em; }
  .now .tag {
    font-family: var(--mono); font-size: 10.5px; letter-spacing: 0.14em;
    text-transform: uppercase; color: var(--faint);
  }
  .now .live {
    margin-left: auto; font-family: var(--mono); font-size: 11px;
    letter-spacing: 0.1em; color: var(--soft); text-decoration: none;
    border-bottom: 1px solid var(--hair);
  }
  .now .live:hover { color: var(--ink); border-color: var(--ink); }
  .now p { margin: 10px 0 0; color: var(--soft); font-size: 15.5px; max-width: 60ch; }
  .now p b { font-weight: 500; color: var(--ink); }


  /* ── case study: a window over the page ──
     Colour lives in the hero and nowhere else. Everything below it is the same
     neutral type as the rest of the site, so the header does all the work. */
  .study {
    position: fixed; inset: 0; z-index: 90;
    display: flex; align-items: center; justify-content: center;
    padding: 4vh 20px;
    background: rgba(16, 16, 15, 0.46);
    backdrop-filter: blur(4px); -webkit-backdrop-filter: blur(4px);
    opacity: 0; visibility: hidden;
    transition: opacity 0.3s ease, visibility 0.3s;
  }
  .study[data-open="true"] { opacity: 1; visibility: visible; }
  .study-card {
    position: relative;
    width: min(840px, 100%);
    max-height: 92vh;
    overflow-y: auto;
    overscroll-behavior: contain;
    background: var(--paper);
    border-radius: 20px;
    box-shadow: 0 34px 90px rgba(0,0,0,0.30), 0 2px 10px rgba(0,0,0,0.10);
    transform: translateY(14px) scale(0.985);
    transition: transform 0.36s cubic-bezier(0.22, 1, 0.36, 1);
  }
  .study[data-open="true"] .study-card { transform: none; }

  /* thin, quiet scrollbar. scrollbar-width is deliberately NOT set alongside
     the webkit rules — declaring it makes Chrome switch to the standard
     scrollbar and drop ::-webkit-scrollbar styling entirely. */
  .study-card::-webkit-scrollbar { width: 11px; }
  .study-card::-webkit-scrollbar-track { background: transparent; }
  .study-card::-webkit-scrollbar-thumb {
    background-color: rgba(0,0,0,0.16);
    border-radius: 99px;
    border: 3.5px solid transparent;
    background-clip: content-box;
  }
  .study-card::-webkit-scrollbar-thumb:hover { background-color: rgba(0,0,0,0.30); }
  @supports not selector(::-webkit-scrollbar) {
    .study-card { scrollbar-width: thin; scrollbar-color: rgba(0,0,0,0.22) transparent; }
  }

  /* the hero: colour blooms, then grain over the top so it reads as printed */
  .study-hero {
    position: relative;
    padding: 34px 40px 40px;
    min-height: 340px;
    display: flex;
    flex-direction: column;
    justify-content: flex-end;
    border-radius: 20px 20px 0 0;
    overflow: hidden;
    background:
      radial-gradient(78% 78% at 40% 104%, #de4f26 0%, #e0532b 26%, rgba(224,83,43,0.72) 48%, rgba(224,83,43,0) 76%),
      radial-gradient(86% 84% at 96% -6%, #2f3ed6 0%, #3b49d4 24%, rgba(59,73,212,0.62) 52%, rgba(59,73,212,0) 82%),
      radial-gradient(64% 70% at 108% 74%, #7a63d4 0%, rgba(122,99,212,0.5) 40%, rgba(122,99,212,0) 68%),
      linear-gradient(132deg, #f1f2fc 0%, #e9ecf9 30%, #dfe2f4 58%, #d2d6ec 100%);
  }
  .study-hero::after {
    content: ""; position: absolute; inset: 0;
    background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.72' numOctaves='4' stitchTiles='stitch'/%3E%3C/filter%3E%3Crect width='100%25' height='100%25' filter='url(%23n)'/%3E%3C/svg%3E");
    background-size: 190px 190px;
    opacity: 0.58;
    mix-blend-mode: overlay;
    pointer-events: none;
  }
  .study-mark {
    position: absolute; left: 40px; top: 32px; z-index: 1;
    display: flex; gap: 5px; align-items: center; height: 18px;
    cursor: default;
  }
  .study-mark i {
    display: block; width: 5px; border-radius: 3px; background: #2933c9;
    transform-origin: center;
    transition: height 340ms cubic-bezier(.34, 1.4, .64, 1),
                opacity 200ms ease,
                transform 340ms cubic-bezier(.34, 1.4, .64, 1);
  }
  .study-mark i:nth-child(1) { height: 9px; }
  .study-mark i:nth-child(2) { height: 18px; }
  .study-mark i:nth-child(3) { height: 12px; }

  /* the initials the bars resolve into */
  .study-mark b {
    position: absolute; left: 0; top: 50%;
    font-family: var(--mono); font-size: 29px; font-weight: 800;
    letter-spacing: -0.035em; color: #2933c9;
    -webkit-text-stroke: 0.5px #2933c9;
    opacity: 0; transform: translateY(-50%) translateX(-2px) scale(0.82);
    transition: opacity 220ms ease, transform 380ms cubic-bezier(.34, 1.5, .64, 1);
    pointer-events: none; white-space: nowrap;
  }

  /* 1 — the bars wake up */
  .study-mark.eq i:nth-child(1) { animation: mk-eq-a 760ms ease-in-out; }
  .study-mark.eq i:nth-child(2) { animation: mk-eq-b 760ms ease-in-out; }
  .study-mark.eq i:nth-child(3) { animation: mk-eq-c 760ms ease-in-out; }
  @keyframes mk-eq-a { 0%,100% { height: 9px }  25% { height: 18px } 55% { height: 5px }  80% { height: 13px } }
  @keyframes mk-eq-b { 0%,100% { height: 18px } 25% { height: 7px }  55% { height: 17px } 80% { height: 9px } }
  @keyframes mk-eq-c { 0%,100% { height: 12px } 25% { height: 16px } 55% { height: 6px }  80% { height: 17px } }

  /* 2 — they drop away, staggered, and the initials rise */
  .study-mark.yt i { height: 4px; opacity: 0; transform: translateY(7px); }
  .study-mark.yt i:nth-child(1) { transition-delay: 0ms; }
  .study-mark.yt i:nth-child(2) { transition-delay: 55ms; }
  .study-mark.yt i:nth-child(3) { transition-delay: 110ms; }
  .study-mark.yt b { opacity: 1; transform: translateY(-50%) translateX(0) scale(1); transition-delay: 130ms; }

  /* coming back, the bars overshoot slightly before settling */
  .study-mark i:nth-child(1) { transition-delay: 110ms; }
  .study-mark i:nth-child(2) { transition-delay: 55ms; }
  .study-mark i:nth-child(3) { transition-delay: 0ms; }

  @media (prefers-reduced-motion: reduce) {
    .study-mark i, .study-mark b { transition: none; animation: none !important; }
    .study-mark b { display: none; }
  }

  .study-hero .eyebrow {
    position: relative; z-index: 1;
    margin: 0 0 14px; text-align: right;
    font-family: var(--mono); font-size: 10.5px; letter-spacing: 0.2em;
    text-transform: uppercase; color: rgba(255,255,255,0.78);
  }
  .study-hero h3 {
    position: relative; z-index: 1;
    margin: 0; text-align: right;
    font-weight: 500; color: #fff;
    font-size: clamp(24px, 3.2vw, 38px);
    line-height: 1.14; letter-spacing: -0.03em;
    text-shadow: 0 1px 24px rgba(60, 30, 20, 0.18);
  }

  /* a sleek, icon-only dismiss rather than a labelled pill */
  .study-close {
    position: absolute; z-index: 4;
    top: calc(4vh + 20px);
    right: calc(50% - min(420px, 50vw - 20px) + 20px);
    width: 34px; height: 34px; padding: 0;
    display: inline-flex; align-items: center; justify-content: center;
    font: inherit; line-height: 0;
    color: #fff; cursor: pointer;
    /* Dark rather than light: the hero is a gradient but the card under it
       is paper, and a white-on-white button disappears the moment you
       scroll past the hero. */
    background: rgba(16, 16, 15, 0.52);
    border: 1px solid rgba(255,255,255,0.30);
    border-radius: 50%;
    backdrop-filter: blur(10px); -webkit-backdrop-filter: blur(10px);
    transition: background 0.2s ease, transform 0.2s ease;
  }
  .study-close:hover { background: rgba(16, 16, 15, 0.74); transform: scale(1.06); }
  .study-close svg { display: block; }

  /* below the hero: no colour at all */
  .study-inner { padding: 36px 44px 56px; }
  .study .unprompted {
    margin: 0 0 4px; padding: 13px 17px;
    border-left: 2px solid var(--hair);
    font-size: 14.5px; color: var(--soft);
  }
  .study h4 {
    margin: 38px 0 0; font-family: var(--mono); font-size: 10.5px;
    font-weight: 400; letter-spacing: 0.18em; text-transform: uppercase;
    color: var(--faint);
  }
  .study p { margin: 12px 0 0; color: var(--soft); }
  .study p b { font-weight: 500; color: var(--ink); }
  .study ul { margin: 12px 0 0; padding: 0; list-style: none; }
  .study li {
    position: relative; padding-left: 18px; margin-top: 9px;
    color: var(--soft); font-size: 15.5px;
  }
  .study li::before {
    content: ""; position: absolute; left: 2px; top: 0.68em;
    width: 4px; height: 4px; border-radius: 50%; background: var(--faint);
  }
  .study li b { font-weight: 500; color: var(--ink); }
  .study .tiers { margin-top: 12px; }
  .study .tiers div {
    display: grid; grid-template-columns: 10rem 1fr; gap: 16px;
    padding: 11px 0; border-bottom: 1px solid var(--hair); font-size: 15px;
  }
  .study .tiers div:first-child { border-top: 1px solid var(--hair); }
  .study .tiers b { font-weight: 400; color: var(--ink); }
  .study .tiers span { color: var(--soft); }
  .study .stack {
    margin-top: 12px; font-family: var(--mono); font-size: 12px;
    letter-spacing: 0.06em; color: var(--faint);
  }
  body.locked { overflow: hidden; }
  @media (max-width: 620px) {
    .study { padding: 2vh 12px; }
    .study-hero { padding: 26px 22px 28px; min-height: 250px; }
    .study-mark { left: 22px; top: 24px; }
    .study-inner { padding: 26px 22px 44px; }
    .study .tiers div { grid-template-columns: 1fr; gap: 2px; }
  }
  }
  }

  @media (max-width: 620px) {
    .list a { grid-template-columns: 1fr auto; }
    .list .ds { grid-column: 1 / -1; margin-top: 2px; }
  }
  @media (prefers-reduced-motion: reduce) {
    hr { animation: none; transform: none; }
    .list .go { transition: none; }
  }

/* ── case study index: the section that leads the page ── */
.studies-index { margin: 0; padding: 0; list-style: none; }
.studies-index a {
  display: grid;
  grid-template-columns: 8rem 1fr auto;
  gap: 18px;
  align-items: baseline;
  padding: 17px 0;
  border-bottom: 1px solid var(--hair);
  text-decoration: none;
  color: inherit;
}
.studies-index li:first-child a { border-top: 1px solid var(--hair); }
.studies-index .nm { font-weight: 500; letter-spacing: -0.02em; }
.studies-index .ds { color: var(--soft); font-size: 15.5px; }
.studies-index .go { color: var(--faint); font-size: 14px; transition: transform .25s ease, color .25s ease; }
.studies-index a:hover .go { transform: translateX(4px); color: var(--ink); }
.studies-index a:hover .nm { text-decoration: underline; text-underline-offset: 4px; text-decoration-thickness: 1px; }

/* rows in Built that are not links shouldn't pretend to be */
.list .row-static {
  display: grid;
  grid-template-columns: 10.5rem 1fr auto;
  gap: 18px;
  align-items: baseline;
  padding: 17px 0;
}
.list a .go { white-space: nowrap; font-size: 13px; }

/* outbound links inside a case study */
.study .links-out { margin-top: 12px; }
.study .links-out li { padding-left: 0; }
.study .links-out li::before { display: none; }
.study .links-out a {
  color: var(--ink);
  text-decoration-color: var(--hair);
  text-underline-offset: 4px;
}
.study .links-out a:hover { text-decoration-color: var(--ink); }

@media (max-width: 620px) {
  .studies-index a { grid-template-columns: 1fr auto; }
  .studies-index .ds { grid-column: 1 / -1; margin-top: 2px; }
  .list .row-static { grid-template-columns: 1fr; }
}

/* ── writing ── */
.writing { margin: 0; padding: 0; list-style: none; }
.writing a {
  display: grid;
  grid-template-columns: 1fr auto auto;
  gap: 16px;
  align-items: baseline;
  padding: 18px 0;
  text-decoration: none;
  color: inherit;
}
.writing .nm { font-weight: 400; letter-spacing: -0.015em; max-width: 52ch; }
.writing .tg { display: flex; gap: 6px; }
.writing .tg i {
  font-style: normal;
  font-family: var(--mono);
  font-size: 10px;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--faint);
  border: 1px solid var(--hair);
  border-radius: 999px;
  padding: 2px 8px;
  white-space: nowrap;
}
.writing .go { color: var(--faint); font-size: 14px; transition: transform .25s ease, color .25s ease; }
.writing a:hover .go { transform: translateX(4px); color: var(--ink); }
.writing a:hover .nm { text-decoration: underline; text-underline-offset: 4px; text-decoration-thickness: 1px; }

@media (max-width: 620px) {
  .writing a { grid-template-columns: 1fr auto; }
  .writing .tg { grid-column: 1 / -1; margin-top: 4px; }
}

/* ── sticky link bar inside a case study ──
   The card is the scroll container, so top:0 pins this to the top of the card
   as the body scrolls under it. Blurred so text passing beneath stays legible. */
.study-links {
  position: sticky;
  top: 0;
  z-index: 3;
  display: flex;
  align-items: center;
  gap: 8px;
  flex-wrap: wrap;
  padding: 13px 44px;
  background: color-mix(in srgb, var(--paper) 88%, transparent);
  backdrop-filter: blur(14px) saturate(1.5);
  -webkit-backdrop-filter: blur(14px) saturate(1.5);
  border-bottom: 1px solid var(--hair);
}
.study-links .lbl {
  font-family: var(--mono);
  font-size: 10px;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: var(--faint);
  margin-right: 4px;
}
.study-links a {
  font-size: 13px;
  color: var(--ink);
  text-decoration: none;
  border: 1px solid var(--hair);
  border-radius: 999px;
  padding: 6px 13px;
  white-space: nowrap;
  transition: border-color .2s ease, background .2s ease;
}
.study-links a:hover { border-color: var(--ink); background: color-mix(in srgb, var(--ink) 5%, transparent); }
.study-links a span { color: var(--faint); }
.study-links a:hover span { color: var(--ink); }

@media (max-width: 620px) {
  .study-links { padding: 11px 22px; gap: 6px; }
  .study-links .lbl { width: 100%; margin-bottom: 2px; }
}

/* ── company facts strip inside a case study ── */
.facts {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(150px, 1fr));
  gap: 1px;
  margin-top: 26px;
  background: var(--hair);
  border: 1px solid var(--hair);
}
.facts div { background: var(--paper); padding: 13px 15px; }
.facts b {
  display: block;
  font-family: var(--mono);
  font-size: 9.5px;
  font-weight: 400;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: var(--faint);
  margin-bottom: 4px;
}
.facts span { font-size: 14px; color: var(--ink); }
.facts span a { color: var(--ink); text-decoration-color: var(--hair); text-underline-offset: 3px; }
.facts span a:hover { text-decoration-color: var(--ink); }


  /* ══════════════════════════════════════════════════════════════════
     Motion layer.
     The page was built still on purpose, so nothing here moves without
     a reason: it either answers a hover, or it marks arrival. Fast
     curves, small distances, no bounce. All of it off under
     prefers-reduced-motion.
     ══════════════════════════════════════════════════════════════════ */

  :root {
    --tint: rgba(16, 16, 16, 0.034);
    --landmark: 84px;
  }
  @media (prefers-color-scheme: dark) { :root { --tint: rgba(255, 255, 255, 0.05); } }
  :root[data-theme="dark"]  { --tint: rgba(255, 255, 255, 0.05); }
  :root[data-theme="light"] { --tint: rgba(16, 16, 16, 0.034); }

  /* ── SF / NYC: the two cities he actually splits time between ──────
     Hovering either one swaps the pointer for that city's landmark and
     spells the name out underneath. Absolute, so the header never shifts. */
  .where { position: relative; }
  .where .city {
    position: relative;
    font-weight: inherit;
    color: var(--faint);
    transition: color 0.24s ease;
  }
  .where .city:hover { color: var(--ink); }
  /* the real pointer stays put — the landmark rides along beside it */
  .where .city { cursor: default; }

  /* ── the landmark ──────────────────────────────────────────────────
     A floating element rather than a CSS cursor image: a cursor image
     replaces the system pointer instead of joining it, and browsers cap
     it at 128px. This keeps the arrow and has no size limit. */
  .landmark {
    position: fixed;
    top: 0; left: 0;
    z-index: 60;
    pointer-events: none;
    line-height: 1;
    opacity: 0;
    transition: opacity 0.18s ease;
    will-change: transform;
  }
  .landmark.on { opacity: 1; }
  .landmark .mk {
    display: block;
    font-size: var(--landmark);
    transform: scale(0.5);
    transform-origin: 25% 15%;
    transition: transform 0.3s cubic-bezier(0.34, 1.42, 0.64, 1);
  }
  .landmark.on .mk { transform: scale(1); }
  /* the prompt rides with the landmark, written in the same hand as the
     wordmark. It retires the moment a song actually starts. */
  .landmark .cta {
    position: absolute;
    left: 14px; top: -26px;
    font-family: "CHAOS16", monospace;
    font-weight: 400;
    font-size: 20px;
    line-height: 1;
    color: var(--orange);
    white-space: nowrap;
    text-shadow: 0 1px 0 var(--paper), 0 0 7px var(--paper);
    opacity: 0;
    transform: rotate(-7deg) translateY(4px);
    transition: opacity .3s ease .12s, transform .4s cubic-bezier(.34,1.4,.64,1) .12s;
  }
  .landmark.on .cta { opacity: 1; transform: rotate(-7deg); animation: nudge 1.9s ease-in-out .8s infinite; }
  /* it has been read — it pops once and lifts away, rather than blinking out */
  :root.tuned .landmark .cta {
    animation: ctaGone .52s cubic-bezier(.36,0,.66,-0.15) forwards !important;
  }
  @keyframes ctaGone {
    0%   { opacity: 1; transform: rotate(-7deg) scale(1); }
    32%  { opacity: 1; transform: rotate(-9deg) scale(1.16); }
    100% { opacity: 0; transform: rotate(-4deg) scale(.55) translateY(-20px); }
  }
  @keyframes nudge {
    0%, 100% { transform: rotate(-7deg) translateX(0); }
    50%      { transform: rotate(-7deg) translateX(3px); }
  }

  @media (hover: none) { .landmark { display: none; } }
  .where .sep { opacity: 0.45; margin: 0 2px; }

  /* the wordmark answers a hover without making a scene */
  .who { display: inline-block; transition: transform 0.3s cubic-bezier(0.22, 1, 0.36, 1), filter 0.3s ease; }
  .who:hover { transform: translateY(-1px) scale(1.015); filter: saturate(1.25); }

  /* ── roll: the label lifts, its twin arrives from underneath ────────
     Clipped with clip-path rather than overflow: an overflow-hidden
     inline-block reports its bottom edge as its baseline, which drops the
     name below the description in these baseline-aligned rows. */
  .rt { position: relative; display: inline-block; clip-path: inset(-0.02em 0 0 0); }
  .rt i {
    display: block;
    font-style: inherit; font-weight: inherit;
    transition: transform 0.4s cubic-bezier(0.66, 0.02, 0.16, 1);
  }
  .rt i + i { position: absolute; top: 100%; left: 0; white-space: nowrap; }
  a:hover .rt i, a:focus-visible .rt i,
  .role:hover .rt i, .role:focus-visible .rt i { transform: translateY(-100%); }
  /* the title follows the company a beat later rather than moving in lockstep */
  .roles .co span .rt i { transition-delay: .05s; }
  /* the roll replaces the old underline — two signals for one hover is noise */
  .list a:hover .nm { text-decoration: none; }

  /* ── rows answer the whole hover, not just the arrow ─────────────── */
  .list a { position: relative; }
  .list a::before {
    content: "";
    position: absolute;
    inset: 0 -12px;
    border-radius: 4px;
    background: var(--tint);
    opacity: 0;
    transition: opacity 0.22s ease;
  }
  .list a:hover::before { opacity: 1; }
  .list .nm, .list .ds { position: relative; }
  .list .nm { transition: transform 0.3s cubic-bezier(0.22, 1, 0.36, 1); }
  .list a:hover .nm { transform: translateX(2px); }

  /* roles aren't links, but the row should still track the eye */
  .roles li { transition: color 0.22s ease; }
  .roles li:hover .co span { color: var(--ink); }

  /* ── photographs lean in slightly ─────────────────────────────────── */
  .wall figure { overflow: hidden; }
  .wall figure img { transition: transform 0.6s cubic-bezier(0.22, 1, 0.36, 1); }
  .wall figure:hover img { transform: scale(1.035); }

  /* ── arrival: sections settle as you reach them ───────────────────── */
  /* Scoped under .js-reveal so that if the script never runs, the page is
     simply the still version it was before — nothing hidden, nothing lost. */
  .js-reveal .block,
  .js-reveal footer {
    opacity: 0;
    transform: translateY(10px);
    transition: opacity 0.62s cubic-bezier(0.22, 1, 0.36, 1),
                transform 0.62s cubic-bezier(0.22, 1, 0.36, 1);
  }
  .js-reveal .block.in,
  .js-reveal footer.in { opacity: 1; transform: none; }
  /* hold each rule until its section arrives, so the draw is actually seen */
  .js-reveal .block hr,
  .js-reveal footer hr { animation-play-state: paused; }
  .js-reveal .block.in hr,
  .js-reveal footer.in hr { animation-play-state: running; }

  /* the headline sets itself on load, one line after the other */
  .js-reveal h1 em,
  .js-reveal h1 .l2 { display: inline-block; animation: settle 0.72s cubic-bezier(0.22, 1, 0.36, 1) both; }
  .js-reveal h1 em { animation-delay: 0.06s; }
  .js-reveal h1 .l2 { animation-delay: 0.16s; }
  .js-reveal .intro { animation: settle 0.72s cubic-bezier(0.22, 1, 0.36, 1) both 0.26s; }
  @keyframes settle { from { opacity: 0; transform: translateY(9px); } to { opacity: 1; transform: none; } }

  @media (prefers-reduced-motion: reduce) {
    .rt i, .list .nm, .wall figure img, .who, .where .city { transition: none !important; }
    .landmark .cta { animation: none !important; }
    :root.tuned .landmark .cta { display: none !important; }
    .js-reveal .block, .js-reveal footer { opacity: 1 !important; transform: none !important; }
    .js-reveal .block hr, .js-reveal footer hr { animation-play-state: running !important; }
    .js-reveal h1 em, .js-reveal h1 .l2, .js-reveal .intro { animation: none !important; }
  }


  /* ══════════════════════════════════════════════════════════════════
     The conversation.
     One exchange per section, about that section. Glossy iMessage
     bubbles — the loud thing on an otherwise quiet page, so they stay
     small and the loop is slow. Decorative and aria-hidden.
     ══════════════════════════════════════════════════════════════════ */

  .chat { margin: 24px 0 34px; }

  /* On a wide screen the exchange steps out of the column into the margin
     beside its section — right for Writing, left for Around. Below that
     there is no room, so it drops back into the column and reads inline. */
  @media (min-width: 1340px) {
    .block { position: relative; }
    .chat-right, .chat-left {
      position: absolute;
      top: 34px;
      width: 272px;
      margin: 0;
    }
    .chat-right { left: calc(100% + 44px); }
    .chat-left  { right: calc(100% + 44px); }
    /* the reply hugs the outer edge, so the pair leans away from the text */
    .chat-left .slot { display: flex; justify-content: flex-end; }
    .chat-right .bub, .chat-left .bub { font-size: 17.5px; max-width: 100%; padding: 11px 17px; border-radius: 20px; }
    .chat-right .gloss, .chat-left .gloss { border-radius: 14px 14px 40% 40% / 14px 14px 12px 12px; }
  }
  .slot { position: relative; display: block; }
  .slot .dots { position: absolute; top: 0; left: 0; }

  .bub {
    position: relative;
    display: block;
    width: max-content;
    max-width: min(420px, 100%);
    padding: 11px 18px;
    margin-bottom: 14px;
    border-radius: 20px;
    font-size: 18px;
    font-weight: 400;
    line-height: 1.28;
    letter-spacing: -0.012em;
    color: #000;
    opacity: 0;
  }
  /* Glass: near-white at the crown ramping to saturated colour at the
     foot, a thin coloured rim holding the whole shape, and a soft shadow
     under it. The white halo behind the text is what lets black type sit
     on a mid-tone bubble without going muddy. */
  /* The body is deepest right under the specular edge and lightens again
     toward the foot — that bounce is the light coming back up through the
     glass, and it is what stops the whole thing reading as a flat pill. */
  .bub.a {
    background: linear-gradient(180deg,
      #9ea3f1 0%, #6b71e7 46%, #565ce0 52%, #7d83ea 84%, #bcc0f6 100%);
    box-shadow:
      0 0 0 1.4px #4a4fbe,
      inset 0 -9px 11px -7px rgba(255,255,255,.95),
      0 1px 1px rgba(35,55,115,.28),
      0 6px 13px -3px rgba(35,55,115,.42);
  }
  .bub.b {
    background: linear-gradient(180deg,
      #f4a184 0%, #ea7550 46%, #e4643c 52%, #ee8a68 84%, #f8c9b2 100%);
    box-shadow:
      0 0 0 1.4px #c5572f,
      inset 0 -9px 11px -7px rgba(255,255,255,.95),
      0 1px 1px rgba(120,72,26,.26),
      0 6px 13px -3px rgba(120,72,26,.38);
  }
  /* The specular cap. Near-opaque, and it STOPS — the crisp curved edge
     along its bottom is the single thing that reads as glass. Fading it
     out to transparent is what made the earlier version look like a pill. */
  .bub .gloss {
    position: absolute;
    left: 1.5px; right: 1.5px; top: 1.5px;
    height: 49%;
    border-radius: 19px 19px 50% 50% / 17px 17px 11px 11px;
    background: linear-gradient(180deg,
      rgba(255,255,255,.97) 0%, rgba(255,255,255,.86) 62%, rgba(255,255,255,.74) 100%);
    pointer-events: none;
  }
  .bub .txt { text-shadow: 0 0 7px rgba(255,255,255,.95), 0 1px 0 rgba(255,255,255,.7); }

  /* the line types itself in, a character at a time. The bubble is already
     sized for the full string, so nothing reflows as the letters land. */
  .bub .txt i {
    font-style: inherit;
    opacity: 0;
    animation: charIn 11s infinite both;
    animation-delay: calc(var(--start, 0s) + var(--i, 0) * 0.024s);
  }
  .bub.a .txt   { --start: 0.75s; }
  .bub.say .txt { --start: 5.30s; }
  @keyframes charIn {
    0%          { opacity: 0; }
    0.7%, 86%   { opacity: 1; }
    93%, 100%   { opacity: 0; }
  }
  .bub .txt { position: relative; }

  /* the same grain his case-study headers carry, dialled right down so it
     reads as texture rather than noise at this size */
  .bub::after {
    content: "";
    position: absolute;
    inset: 0;
    border-radius: inherit;
    pointer-events: none;
    opacity: .07;
    mix-blend-mode: overlay;
    background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='120' height='120'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='.85' numOctaves='3'/%3E%3C/filter%3E%3Crect width='120' height='120' filter='url(%23n)'/%3E%3C/svg%3E");
  }


  /* tail: a rounded blob, then a paper-coloured cut that carves the hook.
     The cut has to be var(--paper) or it shows as a grey notch in dark mode. */
  .bub .tail, .bub .tail::after { position: absolute; bottom: 0; height: 22px; }
  .bub.a .tail { right: -7px; width: 19px; background: linear-gradient(180deg,#7d83ea,#b2b6f4); border-bottom-left-radius: 22px 20px; }
  .bub.a .tail::after { content: ""; right: -16px; width: 21px; background: var(--paper); border-bottom-left-radius: 19px 21px; }
  .bub.b .tail { left: -7px; width: 19px; background: linear-gradient(180deg,#ee8a68,#f6c2a9); border-bottom-right-radius: 22px 20px; }
  .bub.b .tail::after { content: ""; left: -16px; width: 21px; background: var(--paper); border-bottom-right-radius: 19px 21px; }

  /* typing */
  .bub.dots .txt { display: flex; gap: 5px; padding: 3px 2px; }
  .bub.dots i { width: 7px; height: 7px; border-radius: 50%; background: rgba(0,0,0,.45); animation: dot 1.25s infinite ease-in-out; }
  .bub.dots i:nth-child(2) { animation-delay: .16s; }
  .bub.dots i:nth-child(3) { animation-delay: .32s; }
  @keyframes dot { 0%,60%,100% { opacity:.28; transform: translateY(0); } 30% { opacity:.85; transform: translateY(-3px); } }

  /* the loop: ask → typing → reply → hold → clear */
  .chat .bub { animation-duration: 11s; animation-iteration-count: infinite; animation-timing-function: cubic-bezier(.2,1.5,.4,1); }
  .bub.a    { animation-name: msgAsk; }
  .bub.dots { animation-name: msgDots; }
  .bub.say  { animation-name: msgSay; }
  @keyframes msgAsk {
    0%, 3%    { opacity: 0; transform: translateY(9px) scale(.86); }
    9%, 90%   { opacity: 1; transform: none; }
    96%, 100% { opacity: 0; transform: translateY(-4px) scale(.98); }
  }
  @keyframes msgDots {
    0%, 20%   { opacity: 0; transform: translateY(7px) scale(.86); }
    26%, 42%  { opacity: 1; transform: none; }
    48%, 100% { opacity: 0; transform: scale(.92); }
  }
  @keyframes msgSay {
    0%, 45%   { opacity: 0; transform: translateY(7px) scale(.86); }
    52%, 90%  { opacity: 1; transform: none; }
    96%, 100% { opacity: 0; transform: translateY(-4px) scale(.98); }
  }

  /* nothing runs until its section is actually on screen */
  .js-reveal .chat .bub, .js-reveal .bub.dots i, .js-reveal .bub .txt i { animation-play-state: paused; }
  .js-reveal .block.in .chat .bub,
  .js-reveal .block.in .bub.dots i,
  .js-reveal .block.in .bub .txt i { animation-play-state: running; }

  @media (max-width: 620px) { .bub { font-size: 16px; max-width: 88%; } }

  /* Still version: no loop, no flashing. Show the exchange and leave it. */
  @media (prefers-reduced-motion: reduce) {
    .chat .bub { animation: none !important; opacity: 1 !important; transform: none !important; }
    .bub .txt i { animation: none !important; opacity: 1 !important; }
    .bub.dots { display: none !important; }
    .bub.dots i { animation: none !important; }
  }

  /* ── the city's song ──────────────────────────────────────────────
     A small, real YouTube player that fades in while a city is hovered.
     Kept visible on purpose: YouTube's embed terms don't permit hiding
     the player and treating it as an audio source. */
  .tune {
    position: fixed;
    right: 20px; bottom: 20px;
    z-index: 55;
    width: 220px; height: 124px;
    border-radius: 10px;
    overflow: hidden;
    background: #000;
    box-shadow: 0 2px 6px rgba(16,16,26,.18), 0 16px 36px -12px rgba(16,16,26,.42);
    opacity: 0;
    transform: translateY(10px) scale(.97);
    transition: opacity .3s ease, transform .34s cubic-bezier(.22,1,.36,1);
    pointer-events: none;
  }
  .tune.on { opacity: 1; transform: none; }
  .tune-slot { width: 100%; height: 100%; }
  .tune iframe, .tune #ytp { display: block; width: 100%; height: 100%; border: 0; }

  @media (hover: none), (max-width: 620px) { .tune { display: none; } }

  /* ── say hello ────────────────────────────────────────────────────
     A terminal you can type into, or a voice note. Same hairline and
     mono vocabulary as the rest of the page, so it reads as part of the
     footer rather than a widget dropped into it. */
  .hello {
    margin: 20px 0 0;
    border: 1px solid var(--hair);
    border-radius: 8px;
    background: var(--paper);
    overflow: hidden;
    max-width: 560px;
  }
  .hello[hidden] { display: none; }

  .hello-top {
    display: flex; align-items: center; gap: 14px;
    padding: 10px 12px;
    border-bottom: 1px solid var(--hair);
    font-family: var(--mono); font-size: 11px;
  }
  .hello-prompt { color: var(--faint); letter-spacing: .04em; }
  .hello-modes { margin-left: auto; display: flex; gap: 4px; }
  .hello-modes button,
  .hello-x {
    font-family: var(--mono); font-size: 11px;
    background: none; border: 1px solid transparent; border-radius: 999px;
    padding: 4px 10px; color: var(--faint); cursor: pointer;
    transition: color .2s ease, border-color .2s ease, background .2s ease;
  }
  .hello-modes button:hover, .hello-x:hover { color: var(--ink); }
  .hello-modes button.on { color: var(--ink); border-color: var(--hair); background: #00000006; }
  .hello-x { padding: 2px 8px; font-size: 15px; line-height: 1; }

  .hello-pane { padding: 14px 14px 0; }
  .hello-pane[hidden] { display: none; }

  #helloMsg {
    width: 100%; resize: vertical; min-height: 74px;
    border: 0; outline: 0; background: none; padding: 0;
    font-family: var(--mono); font-size: 13.5px; line-height: 1.6;
    color: var(--ink);
  }
  #helloMsg::placeholder { color: var(--faint); }

  /* voice */
  .rec { display: flex; align-items: center; gap: 12px; }
  #recBtn {
    font-family: var(--mono); font-size: 12px;
    border: 1px solid var(--hair); background: none; color: var(--ink);
    border-radius: 999px; padding: 8px 15px; cursor: pointer;
    transition: border-color .2s ease, color .2s ease;
  }
  #recBtn:hover { border-color: var(--ink); }
  #recBtn.live { color: #c0392b; border-color: #e2a99f; }
  .rec-time { font-family: var(--mono); font-size: 12px; color: var(--faint); font-variant-numeric: tabular-nums; }
  .rec-bars { display: flex; align-items: center; gap: 3px; height: 22px; }
  .rec-bars i {
    width: 3px; height: 3px; border-radius: 2px; background: var(--hair);
    transition: height .09s linear, background .2s ease;
  }
  .rec.live i { background: var(--orange); }
  #recPlay { width: 100%; margin-top: 12px; height: 34px; }
  #recPlay[hidden] { display: none; }

  .hello-foot {
    display: flex; align-items: center; gap: 10px;
    padding: 14px;
  }
  #helloFrom {
    flex: 1; min-width: 0;
    border: 0; border-bottom: 1px solid var(--hair); outline: 0; background: none;
    padding: 6px 0;
    font-family: var(--mono); font-size: 12px; color: var(--ink);
    transition: border-color .2s ease;
  }
  #helloFrom:focus { border-color: var(--ink); }
  #helloFrom::placeholder { color: var(--faint); }
  #helloSend {
    font-family: var(--mono); font-size: 12px;
    border: 1px solid var(--ink); background: var(--ink); color: var(--paper);
    border-radius: 999px; padding: 8px 18px; cursor: pointer;
    transition: opacity .2s ease, transform .15s ease;
  }
  #helloSend:hover { transform: translateY(-1px); }
  #helloSend:disabled { opacity: .4; cursor: default; transform: none; }

  /* the field a human never sees, and a bot always fills */
  .hello-trap { position: absolute; left: -9999px; width: 1px; height: 1px; opacity: 0; }

  .hello-out {
    margin: 0; padding: 0 14px 14px;
    font-family: var(--mono); font-size: 11.5px; color: var(--orange);
    min-height: 1.2em;
  }

  @media (prefers-reduced-motion: reduce) {
    .rec-bars i, #helloSend { transition: none !important; }
  }

  /* ── voice note player ────────────────────────────────────────────
     The native <audio> widget is the browser's own chrome and reads as a
     foreign object on this page, so the element keeps the audio and loses
     the controls. This is built from the same parts as everything else:
     hairline, mono, one orange accent. */
  .pb { display: flex; align-items: center; gap: 12px; margin-top: 14px; }
  .pb[hidden] { display: none; }
  .pb audio { display: none; }
  .pb-play {
    flex: none;
    width: 30px; height: 30px;
    display: grid; place-items: center;
    border: 1px solid var(--hair); border-radius: 50%;
    background: none; color: var(--ink); cursor: pointer; padding: 0;
    transition: border-color .2s ease;
  }
  .pb-play:hover { border-color: var(--ink); }
  .pb-play svg { width: 10px; height: 12px; fill: currentColor; }
  .pb-play .pb-pause { display: none; }
  .pb-play.playing .pb-tri { display: none; }
  .pb-play.playing .pb-pause { display: block; }
  .pb-track {
    position: relative; flex: 1; height: 2px;
    background: var(--hair); border-radius: 2px; cursor: pointer;
  }
  .pb-track::before { content: ""; position: absolute; inset: -9px 0; }
  .pb-track i {
    display: block; height: 100%; width: 0;
    background: var(--orange); border-radius: 2px;
  }
  .pb-time {
    font-family: var(--mono); font-size: 11.5px; color: var(--faint);
    font-variant-numeric: tabular-nums; white-space: nowrap;
  }

  /* one focus style for buttons too, instead of the browser's blue ring */
  button:focus-visible { outline: 1px solid var(--ink); outline-offset: 3px; border-radius: 999px; }
  button:focus:not(:focus-visible) { outline: none; }

  /* ── the face on the cursor ───────────────────────────────────────
     Hovering "say hello" swaps the landmark follower for his photo, a bit
     larger than a landmark since a face needs the room to read. */
  .landmark .mug {
    display: none;
    width: auto;
    height: 132px;
    border-radius: 12px;
    box-shadow: 0 2px 6px rgba(16,16,26,.16), 0 18px 40px -12px rgba(16,16,26,.44);
    transform: rotate(-3deg) scale(.55);
    transform-origin: 20% 10%;
    transition: transform .32s cubic-bezier(.34, 1.4, .64, 1);
  }
  .landmark.photo .mug { display: block; }
  .landmark.photo.on .mug { transform: rotate(-3deg) scale(1); }
  /* a landmark and a face never show at once */
  .landmark.photo .mk,
  .landmark.photo .cta { display: none; }

  @media (prefers-reduced-motion: reduce) {
    .landmark .mug { transition: none !important; transform: rotate(-3deg) !important; }
  }

  /* ── the rail ─────────────────────────────────────────────────────
     Wayfinding lives in the left margin rather than across the top: a
     nav bar would be the one bought-looking object on the page. Ticks
     only; the pixel label arrives on hover, and the section you are in
     stays lit. Sections scroll, the two below the gap leave the page. */
  .rail {
    position: fixed;
    left: 22px; top: 50%;
    transform: translateY(-50%);
    z-index: 40;
    display: none;               /* only where there is margin to spare */
    flex-direction: column;
    gap: 3px;
    align-items: flex-start;
    padding: 10px 14px 10px 0;   /* a forgiving target, not a 1px line */
    transition: gap .34s cubic-bezier(.22,1,.36,1);
  }
  .rail:hover { gap: 7px; }
  @media (min-width: 1180px) { .rail { display: flex; } }

  .rail a {
    display: flex; align-items: center; gap: 10px;
    text-decoration: none;
    padding: 2px 0;
  }
  .rail i {
    display: block; flex: none;
    width: 9px; height: 1px;
    background: var(--faint);
    transition: width .34s cubic-bezier(.22,1,.36,1), background .22s ease;
  }
  .rail:hover i { width: 20px; }
  .rail b {
    font-family: "CHAOS16", monospace;
    font-size: 16px; font-weight: 400; line-height: 1;
    letter-spacing: 1px;
    color: var(--faint);
    white-space: nowrap;
    opacity: 0;
    transform: translateX(-6px);
    transition: opacity .3s ease, transform .34s cubic-bezier(.22,1,.36,1), color .22s ease;
    -webkit-font-smoothing: none;
    -moz-osx-font-smoothing: unset;
    text-rendering: geometricPrecision;
  }
  .rail:hover b { opacity: 1; transform: none; }
  .rail a:hover i { width: 30px; background: var(--ink); }
  .rail a:hover b { color: var(--ink); }

  /* where you are */
  .rail a.here i { width: 22px; background: var(--orange); }
  .rail:hover a.here i { width: 30px; }
  .rail a.here b { color: var(--orange); opacity: 1; transform: none; }

  /* the two that leave the page sit apart from the six that scroll */
  .rail-gap { height: 1px; width: 9px; background: var(--hair); margin: 3px 0;
    transition: width .34s cubic-bezier(.22,1,.36,1); }
  .rail:hover .rail-gap { width: 20px; }
  .rail a.out i { width: 5px; }
  .rail:hover a.out i { width: 12px; }
  .rail a.out:hover i { width: 30px; }

  @media (prefers-reduced-motion: reduce) {
    .rail i, .rail b { transition: none !important; }
  }

  /* ── the arcade ───────────────────────────────────────────────────
     The game used to leave the page. It now opens in a window over it,
     with a way out to the full thing. The iframe is only created when
     the window opens, so a 1.4MB game never loads for someone who is
     just reading. */
  /* Same surface as a case study: dark masked backdrop, soft blur, a big
     rounded card that rises into place. The game is another artifact you
     open, so it should open the way the artifacts do. */
  .arcade {
    position: fixed; inset: 0; z-index: 90;
    display: flex; align-items: center; justify-content: center;
    padding: 4vh 20px;
    background: rgba(16, 16, 15, 0.46);
    backdrop-filter: blur(4px); -webkit-backdrop-filter: blur(4px);
    opacity: 0;
    transition: opacity .3s ease;
  }
  .arcade[hidden] { display: none; }
  .arcade.on { opacity: 1; }

  .arcade-win {
    position: relative;
    width: min(840px, 100%);
    height: min(88vh, 700px);
    display: flex; flex-direction: column;
    /* the same navy the game renders on, so the chrome is not a white
       bar sitting on top of it */
    background: #0a1030;
    border: 0;
    border-radius: 20px;
    overflow: hidden;
    box-shadow: 0 34px 90px rgba(0,0,0,0.30), 0 2px 10px rgba(0,0,0,0.10);
    transform: translateY(14px) scale(.985);
    transition: transform .36s cubic-bezier(0.22, 1, 0.36, 1);
  }
  .arcade.on .arcade-win { transform: none; }

  .arcade-bar {
    display: flex; align-items: center; gap: 14px;
    padding: 10px 12px;
    border-bottom: 1px solid rgba(255,255,255,0.10);
    font-family: var(--mono); font-size: 11px;
  }
  .arcade-name { color: rgba(255,255,255,0.48); letter-spacing: .05em; }
  .arcade-full {
    margin-left: auto;
    font-family: var(--mono); font-size: 11px;
    color: rgba(255,255,255,0.5); text-decoration: none;
    border-bottom: 1px solid rgba(255,255,255,0.22);
    transition: color .2s ease, border-color .2s ease;
  }
  .arcade-full:hover { color: #fff; border-color: #fff; }
  .arcade-x {
    background: none; border: 0; cursor: pointer;
    font-size: 17px; line-height: 1; color: rgba(255,255,255,0.55); padding: 0 4px;
    transition: color .2s ease, border-color .2s ease;
  }
  .arcade-x:hover { color: #fff; }

  .arcade-body { flex: 1; min-height: 0; background: #0a1030; }
  .arcade-body iframe { display: block; width: 100%; height: 100%; border: 0; }

  @media (prefers-reduced-motion: reduce) {
    .arcade, .arcade-win { transition: none !important; }
  }


  /* ── the live record ───────────────────────────────────
     Tickers sit on the case-study gradient so the card belongs to this
     site rather than to Three Axis. Returns resolve on hover; the
     sentence underneath stays readable without one. */
  .book {
    display: block; margin: 18px 0 4px;
    width: min(560px, 100%);
    border: 1px solid var(--hair); border-radius: 16px; overflow: hidden;
    text-decoration: none; color: inherit;
    transition: transform 0.36s cubic-bezier(0.22, 1, 0.36, 1), box-shadow 0.36s ease;
  }
  .book:hover { transform: translateY(-3px); box-shadow: 0 26px 64px rgba(0,0,0,0.14); }
  .book-hero {
    display: flex; flex-direction: column; justify-content: flex-end;
    min-height: 148px; padding: 20px 24px 22px;
    background:
      /* A scrim under the type, so white stays legible wherever the pale
         part of the gradient happens to fall. */
      linear-gradient(to top, rgba(0,0,0,0.34) 0%, rgba(0,0,0,0.14) 32%, rgba(0,0,0,0) 60%),
      radial-gradient(78% 78% at 40% 104%, #de4f26 0%, #e0532b 26%, rgba(224,83,43,0.72) 48%, rgba(224,83,43,0) 76%),
      radial-gradient(86% 84% at 96% -6%, #2f3ed6 0%, #3b49d4 24%, rgba(59,73,212,0.62) 52%, rgba(59,73,212,0) 82%),
      radial-gradient(64% 70% at 108% 74%, #7a63d4 0%, rgba(122,99,212,0.5) 40%, rgba(122,99,212,0) 68%),
      linear-gradient(132deg, #f1f2fc 0%, #e9ecf9 30%, #dfe2f4 58%, #d2d6ec 100%);
  }
  .book-k {
    font-family: var(--mono); font-size: 10px; letter-spacing: 0.2em;
    text-transform: uppercase; color: rgba(255,255,255,0.9);
  }
  .book-foot { display: block; }
  .book-ticks { display: flex; flex-wrap: wrap; gap: 20px; align-items: flex-end; margin-top: 12px; }
  .book-ticks .t { font-family: var(--mono); font-size: 13px; color: #fff; line-height: 1.25; }
  .book-ticks .t i {
    display: block; font-style: normal; font-size: 11.5px; color: rgba(255,255,255,0.9);
    opacity: 0; transform: translateY(-5px);
    transition: opacity 0.34s ease, transform 0.34s cubic-bezier(0.22, 1, 0.36, 1);
  }
  .book:hover .book-ticks .t i { opacity: 1; transform: none; }
  .book-ticks .t:nth-child(2) i { transition-delay: 0.04s; }
  .book-ticks .t:nth-child(3) i { transition-delay: 0.08s; }
  .book-ticks .t:nth-child(4) i { transition-delay: 0.12s; }
  .book-ticks .t:nth-child(5) i { transition-delay: 0.16s; }
  .book-ticks .t:nth-child(6) i { transition-delay: 0.20s; }
  .book-body { display: block; padding: 16px 24px 20px; background: var(--paper); }
  .book-lead { display: block; font-size: 15px; color: var(--soft); }
  .book-lead b { font-weight: 500; color: var(--ink); }
  .book-more {
    display: block; max-height: 0; overflow: hidden;
    transition: max-height 0.44s cubic-bezier(0.22, 1, 0.36, 1);
  }
  .book:hover .book-more { max-height: 140px; }
  .book-in { display: block; padding-top: 12px; }
  .book-p { display: block; font-size: 15px; color: var(--soft); max-width: 46ch; }
  .book-meta {
    display: block; margin-top: 10px;
    font-family: var(--mono); font-size: 11.5px; color: var(--faint);
  }
  @media (prefers-reduced-motion: reduce) {
    .book, .book-ticks .t i, .book-more { transition: none; }
    .book-ticks .t i { opacity: 1; transform: none; }
  }


  /* ── the book, above the rail ──────────────────────────
     Two glyphs at rest so the rail stays furniture. On hover the panel
     opens in Three Axis dress: their black card, their green, their
     numbers. Hidden with the rail below 1180px. */
  .bk { position: relative; display: flex; align-items: center; gap: 8px;
        text-decoration: none; padding: 2px 0 6px; }
  .bk-tip { display: flex; align-items: center; gap: 8px; }
  .bk-mark, .bk-pct {
    font-family: "CHAOS16", monospace; font-size: 16px; line-height: 1.15;
    letter-spacing: 1px; white-space: nowrap;
    -webkit-font-smoothing: none; text-rendering: geometricPrecision;
  }
  .bk-mark { color: var(--orange); }
  .bk-pct { color: #22c528; }

  .bk-pop {
    position: absolute; left: calc(100% + 16px); bottom: -14px; width: 336px;
    display: block; background: #0f0f0f; color: #fff;
    border-radius: 26px; overflow: hidden;
    box-shadow: 0 22px 50px rgba(0,0,0,0.28);
    font-family: var(--sans); letter-spacing: normal;
    opacity: 0; visibility: hidden;
    transform: translateX(-10px) scale(0.94); transform-origin: left center;
    transition: opacity 0.22s ease,
                transform 0.42s cubic-bezier(0.34, 1.4, 0.5, 1),
                visibility 0.22s;
    z-index: 60;
  }
  .bk:hover .bk-pop, .bk:focus-visible .bk-pop { opacity: 1; visibility: visible; transform: none; }
  /* the little tail, so it reads as the rail speaking */
  .bk-pop::before {
    content: ""; position: absolute; left: -6px; bottom: 26px;
    width: 14px; height: 14px; background: #0f0f0f;
    border-radius: 4px; transform: rotate(45deg);
  }
  .bk-say { display: block; padding: 19px 22px 0; font-size: 13.5px;
            line-height: 1.45; color: #fff; }
  .bk-hr { display: block; height: 1px; background: rgba(255,255,255,0.14); margin: 16px 22px 0; }
  .bk-head { display: flex; justify-content: space-between; align-items: flex-start;
             gap: 14px; padding: 16px 22px 0; }
  .bk-val { display: block; font-weight: 500; font-size: 27px; line-height: 1;
            letter-spacing: -0.03em; font-variant-numeric: tabular-nums; }
  .bk-chg { display: block; margin-top: 8px; font-size: 13px;
            font-variant-numeric: tabular-nums; color: rgba(255,255,255,0.82); }
  .bk-right { text-align: right; }
  .bk-brand { display: block; margin-bottom: 7px; font-family: var(--mono);
              font-size: 10px; letter-spacing: 0.2em; text-transform: uppercase;
              color: rgba(255,255,255,0.55); }
  .bk-tag { display: inline-flex; align-items: center; gap: 7px; padding: 7px 13px;
            border-radius: 999px; background: rgba(51,227,58,0.16); color: #33e33a;
            font-family: var(--mono); font-size: 11px; letter-spacing: 0.16em;
            text-transform: uppercase; }
  .bk-dot { width: 5px; height: 5px; border-radius: 50%; background: #33e33a;
            flex: none; position: relative; }
  .bk-dot::after { content: ""; position: absolute; inset: -3px; border-radius: 50%;
                   border: 1px solid #33e33a; opacity: 0.45;
                   animation: bk-pulse 2.6s ease-out infinite; }
  @keyframes bk-pulse { 0% { transform: scale(0.6); opacity: 0.5 }
                        70% { transform: scale(1.8); opacity: 0 }
                        100% { opacity: 0 } }
  .bk-chips { display: flex; flex-wrap: wrap; gap: 7px; padding: 16px 22px 0; }
  .bk-chip { padding: 6px 11px; border-radius: 999px; background: rgba(255,255,255,0.08);
             font-family: var(--mono); font-size: 11px; color: rgba(255,255,255,0.78);
             white-space: nowrap; }
  /* The rail styles every <b> as a pixel-font label at opacity 0, and these
     live inside the rail. Pin everything the chip needs rather than inherit. */
  .bk-chip b {
    font-family: "CHAOS16", monospace; font-weight: 400; font-size: 13px;
    letter-spacing: 1px; color: var(--orange);
    opacity: 1; transform: none;
    -webkit-font-smoothing: none; text-rendering: geometricPrecision;
  }
  /* .rail a:hover b turns every b in a hovered rail link to ink, and the panel
     only exists while that link is hovered. Outrank it explicitly. */
  .rail a:hover .bk-chip b, .rail a:focus-visible .bk-chip b {
    color: var(--orange); opacity: 1; transform: none;
  }
  .bk-foot { display: block; padding: 16px 22px 19px; font-family: var(--mono);
             font-size: 9.5px; letter-spacing: 0.16em; text-transform: uppercase;
             color: rgba(255,255,255,0.45); }
  @media (prefers-reduced-motion: reduce) {
    .bk-pop { transition: opacity 0.2s ease, visibility 0.2s; transform: none; }
    .bk-dot::after { animation: none; }
  }

  /* ── screenshots inside a study ────────────────────────
     Placed after the paragraph that describes them and sized to their own
     shape. Most are white UI on a white card, so they need an edge to sit
     on rather than a shadow. */
  .shots { margin: 30px 0 34px; }
  .shots figure { margin: 0; }
  .shots img {
    display: block; width: 100%; height: auto;
    border: 1px solid var(--hair);
    border-radius: 10px;
    background: var(--hair);
  }
  .shots figcaption {
    margin: 9px 0 0;
    font-family: var(--mono); font-size: 11px;
    line-height: 1.5; letter-spacing: 0.04em;
    color: var(--faint);
    max-width: 52ch;
  }

  /* full column: dashboards and pages */
  .shots.wide figure + figure { margin-top: 24px; }

  /* a squarish panel does not want to be a billboard */
  .shots.half figure { max-width: 440px; }

  /* a 480x1630 sidebar at full width would be absurd */
  .shots.tall figure { max-width: 268px; }

  /* two states of the same screen, read together */
  .shots.pair {
    display: grid; grid-template-columns: 1fr 1fr; gap: 16px;
  }
  .shots.pair figcaption { max-width: none; }

  @media (max-width: 620px) {
    .shots.pair { grid-template-columns: 1fr; gap: 22px; }
    .shots.half figure, .shots.tall figure { max-width: 100%; }
  }
