/* Mirror OS — every colour is a token from tokens.css. No literal colours here. */
* { box-sizing: border-box; }
[hidden] { display: none !important; }
html { background: var(--bg); }
body {
  margin: 0; min-height: 100vh; display: flex; flex-direction: column;
  background: var(--bg); color: var(--text);
  font: 400 15px/1.5 "Schibsted Grotesk", -apple-system, BlinkMacSystemFont, "Segoe UI", system-ui, sans-serif;
  -webkit-font-smoothing: antialiased; overflow-x: hidden;
}
a { color: inherit; text-decoration: none; }
button { font: inherit; }
.visually-hidden { position: absolute; width: 1px; height: 1px; overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; }
.muted { color: var(--text-muted); }
:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; border-radius: 6px; }

/* Top bar — Postpace: mark left, one action right. */
/* Three columns rather than space-between, so the search sits on the page's centre
   line whatever the mark and the signed-in corner happen to weigh. The ground is
   the scrim, not glass: Sangam's cover is nearly white and a translucent bar over
   it disappeared. */
.bar {
  position: sticky; top: 0; z-index: 10;
  display: grid; grid-template-columns: minmax(0, 1fr) auto minmax(0, 1fr); align-items: center; gap: 16px;
  height: var(--bar); padding: 0 clamp(16px, 4vw, 48px);
  background: var(--scrim); backdrop-filter: blur(20px) saturate(180%); -webkit-backdrop-filter: blur(20px) saturate(180%);
  border-bottom: 1px solid var(--border);
}
.bar > .mark { justify-self: start; }
.bar > .you { justify-self: end; }
.mark { display: inline-flex; align-items: center; gap: 10px; font-weight: 650; letter-spacing: -.01em; white-space: nowrap; }
.mark__glyph { display: block; color: var(--text); }
.search { justify-self: center; width: clamp(150px, 40vw, 320px); min-width: 0; position: relative; }
.search kbd { position: absolute; right: 12px; top: 50%; translate: 0 -50%; font: 600 11px/1 inherit; color: var(--text-muted); border: 1px solid var(--glass-border); border-radius: 5px; padding: 3px 6px; }
.search input:focus + kbd, .search input:not(:placeholder-shown) + kbd { opacity: 0; }
.search input {
  width: 100%; border: 1px solid var(--glass-border); background: var(--glass); color: var(--text);
  border-radius: 999px; padding: 8px 14px; font: inherit;
}
.search input::placeholder { color: var(--text-muted); }
.search input:focus { border-color: var(--text-muted); }

/* Pills — white on black is the accent. */
.pill {
  display: inline-flex; align-items: center; gap: 8px;
  border: 0; border-radius: 999px; padding: 10px 18px; font-weight: 650; font-size: 14px;
  background: var(--accent); color: var(--accent-ink); cursor: pointer; white-space: nowrap;
  transition: transform var(--t-press) var(--ease-out), background-color var(--t-fast) ease;
}
.pill:active, .chip:active { transform: scale(.97); }
.chip { transition: transform var(--t-press) var(--ease-out), background-color var(--t-fast) ease, color var(--t-fast) ease; }
.pill--ghost { background: var(--glass); color: var(--text); border: 1px solid var(--glass-border); backdrop-filter: blur(10px); }
.pill:disabled { opacity: .5; cursor: default; }

main { flex: 1; width: 100%; }

/* Hero — full-bleed cover under a scrim; text sits on the scrim, never the photo. */
/* The hero fills the first screen; the dock (chips + Apps shelf) overlaps its lower
   part, Postpace-style, so the cards and the hero they drive share one viewport. */
:root { --bar: 64px; --dock: 300px; --gutter: clamp(16px, 4vw, 48px);
  --ease-out: cubic-bezier(.23, 1, .32, 1); --ease-in-out: cubic-bezier(.77, 0, .175, 1); --ease-drawer: cubic-bezier(.32, .72, 0, 1);
  --t-press: 120ms; --t-fast: 200ms; }
.hero {
  position: relative; min-height: calc(100svh - var(--bar));
  display: flex; flex-direction: column; justify-content: flex-end;
  padding: clamp(24px, 5vw, 64px) clamp(16px, 4vw, 48px) calc(var(--dock) + 36px);
  isolation: isolate; overflow: hidden;
}
.dock { position: relative; z-index: 1; margin-top: calc(-1 * var(--dock)); min-height: var(--dock); display: flex; flex-direction: column; justify-content: flex-end; }
.dock .shelf-wrap { padding-top: 0; }
.hero__bg { position: absolute; inset: 0; width: 100%; height: 100%; object-fit: cover; object-position: center 35%; background-size: cover; background-position: center top; z-index: -2;
  mask-image: linear-gradient(to bottom, black 30%, transparent 88%); -webkit-mask-image: linear-gradient(to bottom, black 30%, transparent 88%); }
.hero::before { content: ""; position: absolute; inset: 0; z-index: -1;
  background: linear-gradient(180deg, transparent 10%, var(--scrim) 62%, var(--bg) 100%), linear-gradient(90deg, var(--scrim) 0%, var(--scrim) 22%, transparent 62%); }
.hero__meta { display: inline-flex; gap: 6px; align-items: center; white-space: nowrap; margin-left: 8px; }
.glass { padding: 4px 10px; border-radius: 999px; background: var(--glass); border: 1px solid var(--glass-border); color: var(--text); font-size: 12px; font-weight: 600; }
.glass--quiet { background: transparent; color: var(--text-muted); }
.hero__title { margin: 0; font: 700 clamp(40px, 5.6vw, 76px)/1.06 "Instrument Sans", "Schibsted Grotesk", -apple-system, system-ui, sans-serif; letter-spacing: -.03em; white-space: nowrap; overflow: clip; overflow-clip-margin: .2em; text-overflow: ellipsis; }
.hero__tag { margin: 12px 0 0; max-width: 56ch; min-height: 3em; color: var(--text-muted); font-size: clamp(15px, 1.4vw, 18px); display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; overflow: hidden; }
.hero__row { display: flex; gap: 10px; margin-top: 22px; flex-wrap: wrap; align-items: center; }

.h2 { font: 700 17px/1.3 "Instrument Sans", "Schibsted Grotesk", system-ui, sans-serif; margin: 0 0 12px; padding: 0 clamp(16px, 4vw, 48px); }
.rail { display: flex; align-items: center; justify-content: space-between; gap: 16px; padding-right: clamp(16px, 4vw, 48px); }
.rail .chips { flex: 1 1 auto; min-width: 0; padding-right: 0; }
.count { margin: 0; flex: none; color: var(--text-muted); font-size: 13px; white-space: nowrap; }
.shelf-wrap { padding: 28px 0 8px; }
.shelf {
  display: flex; gap: 14px; overflow-x: auto; padding: 14px clamp(16px, 4vw, 48px) 20px;
  scroll-snap-type: x mandatory; scroll-padding-inline: clamp(16px, 4vw, 48px); scrollbar-width: none;
}
.shelf::-webkit-scrollbar { display: none; }

/* The second shelf is another row of the same thing, not a separate page: it sits
   close under the first rather than floating in its own field of space. */
#skills-wrap { padding: 0 0 8px; }
#skills-wrap .rail { align-items: baseline; }
#skills { padding-block: 16px 28px; }
@media (max-width: 560px) { #skills { padding-block: 12px 20px; } }

/* An arrow sits over each end of the shelf and exists only while there is something
   left to reach that way; touch devices swipe, so they never see them. Cards slide
   under the gutters as the row moves, so each end fades into the page and the arrow
   always sits on the dark, never on half a cover. */
.shelf-box { position: relative; }
.shelf-box::before, .shelf-box::after {
  content: ""; position: absolute; top: 0; bottom: 0; z-index: 3; width: calc(var(--gutter) + 56px);
  pointer-events: none;
}
.shelf-box::before { left: 0; background: linear-gradient(to right, var(--bg) 45%, transparent); }
.shelf-box::after { right: 0; background: linear-gradient(to left, var(--bg) 45%, transparent); }
.arrow {
  position: absolute; top: 50%; transform: translateY(-50%); z-index: 4;  /* over a hovered card, which rises to 2 */
  width: 44px; height: 44px; display: grid; place-items: center; padding: 0;
  border: 1px solid var(--border); border-radius: 999px;
  background: var(--surface-2); color: var(--text); cursor: pointer;
  transition: background var(--t-fast) var(--ease-out), color var(--t-fast) var(--ease-out),
              transform var(--t-press) var(--ease-out);
}
.arrow:hover { background: var(--accent); color: var(--accent-ink); border-color: var(--accent); }
.arrow svg { width: 22px; height: 22px; }
/* Centred in the shelf's own gutter, so an arrow never sits on a card. */
.arrow--prev { left: max(4px, calc((var(--gutter) - 44px) / 2)); }
.arrow--next { right: max(4px, calc((var(--gutter) - 44px) / 2)); }
.arrow:active { transform: translateY(-50%) scale(.94); }
@media (hover: none) { .arrow { display: none; } }
@media (prefers-reduced-motion: reduce) { .arrow { transition: none; } }
.empty { padding: 0 clamp(16px, 4vw, 48px); color: var(--text-muted); display: flex; gap: 12px; align-items: center; }

.chips { display: flex; gap: 8px; flex-wrap: nowrap; overflow-x: auto; scrollbar-width: none; padding: 0 clamp(16px, 4vw, 48px); }
.chips::-webkit-scrollbar { display: none; }
.chip { flex: none; }
.chip { border: 1px solid var(--border); background: transparent; color: var(--text); border-radius: 999px; padding: 6px 14px; font-size: 13px; cursor: pointer; }
.chip[aria-pressed="true"] { background: var(--accent); color: var(--accent-ink); border-color: var(--accent); font-weight: 650; }

.notfound { padding: 20vh clamp(16px, 4vw, 48px); display: grid; gap: 12px; justify-items: start; }
.notfound h1 { margin: 0; font: 700 32px/1.1 "Instrument Sans", "Schibsted Grotesk", system-ui, sans-serif; letter-spacing: -.02em; }

/* Hero crossfade — two stacked backgrounds; the new one fades in over the old. */
.hero__bg { opacity: 0; transition: opacity 260ms ease; }
.hero__bg.is-in { opacity: 1; }
.hero__bg.is-out { opacity: 0; }
.hero__body { position: relative; transition: opacity 140ms ease, transform 140ms var(--ease-out), filter 140ms ease; }
.hero__body.is-out { opacity: 0; transform: translateY(6px); filter: blur(2px); }

/* Cards — a fixed 16:9 box; hover scales the box, so nothing else moves.
   The glow is the cover itself, blurred and bled out behind the card (no shadow):
   quick to appear, slow to fade — the way Apple's ambient blur behaves. */
.card {
  position: relative; flex: 0 0 clamp(220px, 24vw, 320px); aspect-ratio: 16 / 9;
  scroll-snap-align: start; transform-origin: center center;
  transition: transform var(--t-fast) var(--ease-out); cursor: pointer;
}
.card:active { transform: scale(.98); }
.card[hidden] { display: none; }
.card::before {
  content: ""; position: absolute; inset: -8% -6%; border-radius: 22px; z-index: -1;
  background: var(--cover) center / cover; filter: blur(34px) saturate(1.4);
  opacity: 0; transition: opacity .8s ease;
}
.card__box { position: absolute; inset: 0; border-radius: 14px; overflow: hidden; background: var(--surface); border: 1px solid var(--border); isolation: isolate; transition: border-color .22s ease; }
.card__cover { position: absolute; top: -12%; right: -4%; width: 150%; height: 150%; object-fit: cover; display: block; }
.card--whole .card__cover { inset: 0; width: 100%; height: 100%; }  /* a supplied brand still is framed as-is */
.card__rest, .card__more { position: absolute; inset: 0; padding: 12px 14px; display: flex; }
.card__rest { align-items: flex-end; gap: 10px; font-weight: 650; font-size: 14px;
  background: linear-gradient(180deg, transparent 45%, var(--scrim) 100%); transition: opacity .18s ease; }
.card__icon { width: 28px; height: 28px; border-radius: 7px; display: block; flex: none; }
.card__more { flex-direction: column; justify-content: flex-end; gap: 8px; opacity: 0; pointer-events: none;
  background: linear-gradient(180deg, transparent 5%, var(--scrim) 60%); transition: opacity .18s ease; }
.card__head { display: flex; align-items: center; gap: 10px; }
.card__head b { display: block; font: 700 15px/1.15 "Instrument Sans", "Schibsted Grotesk", system-ui, sans-serif; letter-spacing: -.01em; }
.card__head small { color: var(--text-muted); font-size: 12px; display: flex; gap: 8px; }
.card__head small i { font-style: normal; opacity: .75; }
.card__more p { margin: 0; font-size: 12.5px; line-height: 1.35; color: var(--text); display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; overflow: hidden; }
.card__row { display: flex; gap: 8px; flex-wrap: wrap; }
.card__more .pill { padding: 7px 14px; font-size: 13px; }
.shelf--soon .card__box { opacity: .6; transition: opacity .22s ease; }
.shelf--soon .card:hover .card__box, .shelf--soon .card:focus-within .card__box { opacity: 1; }

/* `body.is-away` drops the hover state while the pointer is somewhere else entirely —
   another tab, another window. Without it a card stays expanded behind the tab that
   Try just opened, because no pointerleave ever arrives. The next pointer move clears it. */
@media (hover: hover) and (pointer: fine) {
  .shelf { padding-block: 36px 48px; }
  body:not(.is-away) .card:hover, .card:focus-within { transform: scale(1.2); z-index: 2; }
  body:not(.is-away) .card:hover::before, .card:focus-within::before { opacity: .55; transition-duration: .3s; }
  .card:active { transform: scale(1.17); }
  body:not(.is-away) .card:hover .card__box, .card:focus-within .card__box { border-color: var(--glass-border); }
  .card.is-first { transform-origin: left center; }
  .card.is-last { transform-origin: right center; }
  body:not(.is-away) .card:hover .card__rest, .card:focus-within .card__rest { opacity: 0; }
  body:not(.is-away) .card:hover .card__more, .card:focus-within .card__more { opacity: 1; pointer-events: auto; }
}
@media (hover: none) {
  :root { --dock: 280px; }
  .hero__bg { background-position: left top; }
  video.hero__bg { object-position: 62% 12%; }
  video.hero__bg.is-in { opacity: .55; }  /* a portrait crop of a 16:9 clip lands under the title; keep the motion, drop the weight */
  .hero::before { background: linear-gradient(180deg, transparent 5%, var(--scrim) 48%, var(--bg) 100%); }
  .hero__bg { mask-image: linear-gradient(to bottom, black 20%, transparent 70%); -webkit-mask-image: linear-gradient(to bottom, black 20%, transparent 70%); }
  .card { scroll-snap-align: center; flex-basis: min(78vw, 360px); }
  .shelf { scroll-padding-inline: 11vw; }
}

/* Dialogs. Native <dialog>: Escape, the focus trap and the backdrop come with it. */
dialog { border: 0; padding: 0; background: transparent; color: var(--text); max-width: none; max-height: none; }
dialog:not([open]) { display: none; }
dialog::backdrop { background: var(--scrim); backdrop-filter: blur(6px); }
dialog::backdrop { transition: opacity var(--t-fast) ease; }
@starting-style { dialog[open]::backdrop { opacity: 0; } }


/* Reduced motion: fades stay, movement goes. */
@media (prefers-reduced-motion: reduce) {
  .card, .card:hover, .card:focus-within, .card:active, .pill:active, .chip:active { transform: none; transition: none; }
  .hero__body.is-out { transform: none; filter: none; }
}
@media (prefers-reduced-transparency: reduce) {
  .bar, .pill--ghost, .glass, .search input { background: var(--surface); backdrop-filter: none; -webkit-backdrop-filter: none; }
}

/* ── The preview ─────────────────────────────────────────────────────────────
   A card is a door handle: clicking one opens this, and everything about an
   application is here — its clip, what its own betadoc says, and the two ways
   forward. A native <dialog>, so Escape, the focus trap and the inert page
   behind it are the platform's job rather than ours. */
.preview {
  width: min(1000px, calc(100vw - 32px)); max-height: min(86svh, 780px); padding: 0;
  border: 1px solid var(--border); border-radius: 24px; background: var(--surface); color: var(--text);
  overflow: hidden; display: flex; flex-direction: column; container-type: inline-size;
}
.preview::backdrop { background: var(--scrim); backdrop-filter: blur(6px); -webkit-backdrop-filter: blur(6px); }
.preview[open] { animation: preview-in 300ms var(--ease-out) both; }
.preview[open]::backdrop { animation: fade-in 300ms var(--ease-out) both; }
body:has(.preview[open]) { overflow: hidden; }

.preview__bar { display: flex; align-items: center; gap: 12px; padding: 16px 18px 14px; flex: none; }
.preview__ico { display: block; flex: none; width: 34px; height: 34px; }
.preview__icon { width: 100%; height: 100%; border-radius: 9px; display: block; }
.preview__who { min-width: 0; }
.preview__who b { display: block; font: 650 15.5px/1.2 "Schibsted Grotesk", system-ui, sans-serif; letter-spacing: -.01em; }
.preview__who small { color: var(--text-muted); font-size: 12.5px; }
.preview__x { margin-left: auto; width: 34px; height: 34px; display: grid; place-items: center; flex: none;
  border: 1px solid var(--border); border-radius: 999px; background: var(--surface-2); color: var(--text);
  cursor: pointer; transition: background var(--t-fast) var(--ease-out), color var(--t-fast) var(--ease-out); }
.preview__x:hover { background: var(--accent); color: var(--accent-ink); border-color: var(--accent); }

.preview__main { display: grid; overflow: auto; }
.preview__media { position: relative; border-radius: 16px; overflow: hidden; aspect-ratio: 16 / 9;
  background: var(--bg); border: 1px solid var(--border); align-self: start; }
.preview__media video, .preview__media img { width: 100%; height: 100%; object-fit: cover; display: block; }

.preview__body { padding: 20px 22px 12px; }
.preview__head { margin: 0 0 12px; font: 700 27px/1.14 "Instrument Sans", "Schibsted Grotesk", system-ui, sans-serif;
  letter-spacing: -.022em; max-width: 21ch; text-wrap: balance; }
.preview__sum { margin: 0 0 6px; color: var(--text-muted); font-size: 15.5px; line-height: 1.6; max-width: 56ch; }

.feat { display: grid; margin: 18px 0 0; padding: 0; list-style: none; }
.feat li { padding: 13px 0; border-top: 1px solid var(--border); }
.feat li:last-child { padding-bottom: 4px; }
.feat b { display: block; font: 650 14px/1.35 "Schibsted Grotesk", system-ui, sans-serif; margin-bottom: 2px; }
.feat span { color: var(--text-muted); font-size: 13.5px; line-height: 1.5; }

/* The two ways forward, side by side under the clip; where the words came from
   sits at the foot of the column so the panel reads as one block, not two. */
.preview__act { display: flex; flex-direction: column; gap: 9px; padding: 14px 0 0; }
.preview__act .pill { width: 100%; justify-content: center; padding: 12px 18px; font-size: 14.5px; }
.preview__from { margin: 4px 0 0; color: var(--text-muted); font-size: 11.5px; line-height: 1.5; }

@container (min-width: 660px) {
  .preview__main { grid-template-columns: 42% 1fr; grid-template-rows: auto 1fr; overflow: hidden;
    padding: 0 0 0 18px; gap: 0 4px; }
  .preview__media { grid-area: 1 / 1; }
  .preview__act { grid-area: 2 / 1; align-self: stretch; padding-bottom: 20px; }
  .preview__from { margin-top: auto; }               /* the column ends where the reading column does */
  .preview__body { grid-area: 1 / 2 / span 2 / 3; overflow: auto; padding: 0 10px 22px 22px;
    min-width: 0; scrollbar-width: thin; scrollbar-color: var(--border) transparent; }
  /* While there is more below, the column fades out rather than being cut. */
  .preview__body.is-more { mask-image: linear-gradient(to bottom, #000 calc(100% - 34px), transparent);
    -webkit-mask-image: linear-gradient(to bottom, #000 calc(100% - 34px), transparent); }
  .preview__head { font-size: 30px; margin-top: -4px; }
}
@container (max-width: 659px) {
  /* Block flow, not grid: a box whose height comes from an aspect ratio contributes
     nothing to an auto grid track, so the clip collapsed onto the headline. */
  .preview__main { display: block; }
  .preview__media { margin: 0 14px; }
  .preview__body { padding: 16px 18px 6px; }
  .preview__head { font-size: 23px; }
  .preview__act { padding: 12px 18px calc(16px + env(safe-area-inset-bottom));
    position: sticky; bottom: 0; background: var(--surface); border-top: 1px solid var(--border); }
  .preview__act .pill { padding: 13px 18px; }
}

@keyframes preview-in { from { opacity: 0; transform: translateY(14px) scale(.985); } to { opacity: 1; transform: none; } }
@keyframes fade-in { from { opacity: 0; } to { opacity: 1; } }
@media (prefers-reduced-motion: reduce) {
  .preview[open], .preview[open]::backdrop { animation: none; }
}

/* ── The door ────────────────────────────────────────────────────────────────
   The whole store is behind this. It names nothing that is behind it: the mark,
   two buttons, and what signing in actually tells us. */
.door { display: grid; place-items: center; min-height: 100svh; padding: var(--gutter);
  background: radial-gradient(120% 80% at 50% -10%, var(--surface), var(--bg) 70%); }
.door__box { width: min(420px, 100%); display: grid; justify-items: center; text-align: center; }
.door__mark { position: relative; width: 66px; height: 82px; margin-bottom: 26px; }
.door__panel { position: absolute; top: 0; height: 100%; width: 28px; border-radius: 7px; background: var(--text); }
.door__panel--l { left: 0; animation: door-l 620ms var(--ease-out) 80ms both; }
.door__panel--r { right: 0; opacity: .45; animation: door-r 620ms var(--ease-out) 80ms both; }
.door__title { margin: 0; font: 700 30px/1.1 "Instrument Sans", "Schibsted Grotesk", system-ui, sans-serif; letter-spacing: -.02em; }
.door__lede { margin: 10px 0 30px; color: var(--text-muted); }
.door__row { display: grid; gap: 10px; width: 100%; }
.door__btn { display: flex; align-items: center; justify-content: center; gap: 10px;
  padding: 13px 18px; border-radius: 999px; font-weight: 650; font-size: 15px;
  background: var(--accent); color: var(--accent-ink); border: 1px solid var(--accent);
  transition: transform var(--t-press) var(--ease-out), background-color var(--t-fast) ease; }
.door__btn--github { background: var(--surface-2); color: var(--text); border-color: var(--border); }
.door__btn--github:hover { background: var(--border); }
.door__btn:active { transform: scale(.985); }
.door__none { margin: 0; padding: 14px 16px; border: 1px solid var(--border); border-radius: 14px;
  color: var(--text-muted); font-size: 14px; background: var(--surface); }
.door__fine { margin: 26px 0 0; color: var(--text-muted); font-size: 12.5px; line-height: 1.6; max-width: 40ch; }
@keyframes door-l { from { transform: translateX(19px); } to { transform: translateX(0); } }
@keyframes door-r { from { transform: translateX(-19px); opacity: 1; } to { transform: translateX(0); opacity: .45; } }
@media (prefers-reduced-motion: reduce) { .door__panel { animation: none; } }

/* Who is signed in, and the way out — the one thing the top bar gains. */
.you { display: flex; align-items: center; gap: 10px; }
.you__face { width: 28px; height: 28px; border-radius: 999px; display: block; background: var(--surface-2); }
.you__name { font-size: 13.5px; font-weight: 600; max-width: 18ch; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.you__out { padding: 7px 13px; font-size: 13px; }
/* At phone width the three columns stop fitting: the word next to the mark and the
   name next to the face are what a small screen can do without. */
@media (max-width: 560px) {
  .you__name { display: none; }
  .bar { gap: 10px; padding: 0 12px; grid-template-columns: auto minmax(0, 1fr) auto; }
  .mark span { display: none; }
  /* Equal side columns would put the signed-in corner on top of the search here,
     since it weighs more than the mark: the search takes what is left instead. */
  .search { width: auto; justify-self: stretch; }
}

/* ── One skill's document, published ─────────────────────────────────────────
   Not a prompt dumped on a page: a paper. A title block, numbered sections, a
   serif reading face, figures for the code, and a contents column that follows
   where you are. Copy still hands over the raw markdown, unchanged. */
.page--doc { background: var(--bg); }
.docwrap { width: min(1320px, 100%); margin: 0 auto; padding: 0 var(--gutter) 140px; }

/* The title page. */
.dochead { padding: 72px 0 40px; margin-bottom: 8px; border-bottom: 1px solid var(--border); }
.dochead__kind { margin: 0 0 20px; color: var(--text-muted);
  font: 600 11.5px/1 "Schibsted Grotesk", system-ui, sans-serif; letter-spacing: .16em; text-transform: uppercase; }
.dochead h1 { margin: 0; font: 700 clamp(40px, 5.4vw, 66px)/1.02 "Instrument Sans", "Schibsted Grotesk", system-ui, sans-serif;
  letter-spacing: -.032em; }
.dochead__tag { margin: 22px 0 0; color: var(--text); font-size: 20px; line-height: 1.5; max-width: 40ch;
  font-family: ui-serif, "New York", Georgia, "Times New Roman", serif; }
.dochead__meta { display: flex; flex-wrap: wrap; gap: 0 26px; margin: 34px 0 0; }
.dochead__meta span { color: var(--text-muted);
  font: 500 12px/1.6 ui-monospace, "SF Mono", Menlo, monospace; }
.dochead__meta a { color: var(--text); text-decoration: underline; text-underline-offset: 3px;
  text-decoration-color: var(--border); }
.dochead__meta a:hover { text-decoration-color: var(--text); }
.dochead__meta span + span { position: relative; padding-left: 26px; }
.dochead__meta span + span::before { content: ""; position: absolute; left: 0; top: 50%;
  width: 1px; height: 12px; background: var(--border); transform: translateY(-50%); }

.docmain { display: grid; gap: 56px; align-items: start; padding-top: 44px; }
@media (min-width: 1060px) { .docmain { grid-template-columns: 236px minmax(0, 1fr); } }

/* Contents: the paper's own sections, and which one you are inside. */
.toc { position: sticky; top: calc(var(--bar) + 30px); align-self: start; }
.toc h2 { margin: 0 0 14px; font: 600 11.5px/1 "Schibsted Grotesk", system-ui, sans-serif;
  letter-spacing: .16em; text-transform: uppercase; color: var(--text-muted); }
.toc ol { margin: 0; padding: 0; list-style: none; counter-reset: toc; }
.toc li { counter-increment: toc; }
.toc a { display: block; padding: 8px 0 8px 28px; position: relative; font-size: 13.5px; line-height: 1.45;
  color: var(--text-muted); transition: color var(--t-fast) ease; }
.toc a::before { content: counter(toc, decimal-leading-zero); position: absolute; left: 0; top: 8px;
  font: 500 11px/1.45 ui-monospace, "SF Mono", Menlo, monospace; color: var(--border); }
.toc a:hover, .toc a[aria-current="true"] { color: var(--text); }
.toc a[aria-current="true"]::before { color: var(--text); }
@media (max-width: 1059px) { .toc { display: none; } }

/* The paper itself. Serif for reading, the house sans for anything structural. */
.doc { max-width: 74ch; font-family: ui-serif, "New York", Georgia, "Times New Roman", serif; }
.doc > h2 { counter-increment: section; }
.doc { counter-reset: section; }
.doc h1 { font: 700 32px/1.14 "Instrument Sans", "Schibsted Grotesk", system-ui, sans-serif; letter-spacing: -.02em; margin: 0 0 22px; }
.doc h2 {
  font: 700 clamp(23px, 2.1vw, 29px)/1.18 "Instrument Sans", "Schibsted Grotesk", system-ui, sans-serif;
  letter-spacing: -.018em; color: var(--text); margin: 68px 0 20px; padding-top: 26px;
  border-top: 1px solid var(--border); scroll-margin-top: calc(var(--bar) + 26px);
}
.doc h2:first-child { margin-top: 0; padding-top: 0; border-top: 0; }
.doc h3 { font: 650 18.5px/1.32 "Schibsted Grotesk", system-ui, sans-serif; letter-spacing: -.008em;
  color: var(--text); margin: 38px 0 12px; }
.doc h4 { font: 650 15px/1.35 "Schibsted Grotesk", system-ui, sans-serif; color: var(--text-muted);
  letter-spacing: .01em; margin: 26px 0 8px; }
.doc p, .doc li { color: var(--text-muted); font-size: 17.5px; line-height: 1.68; }
.doc p { margin: 0 0 18px; }
.doc > p:first-of-type { color: var(--text); font-size: 19.5px; line-height: 1.6; }  /* the opening paragraph leads */
.doc ul, .doc ol { margin: 0 0 22px; padding-left: 26px; }
.doc li { margin: 0 0 9px; padding-left: 4px; }
.doc li::marker { color: var(--border); }
.doc strong { color: var(--text); font-weight: 650; }
.doc em { color: var(--text); }
.doc code { font: 500 14.5px/1.5 ui-monospace, "SF Mono", Menlo, monospace; color: var(--text);
  background: var(--surface); border: 1px solid var(--border); border-radius: 6px; padding: 1px 6px; }
/* Code is a figure: set apart, labelled by its own margin rather than decorated. */
.doc pre { margin: 0 0 26px; padding: 22px 24px; background: var(--surface); border: 1px solid var(--border);
  border-radius: 16px; overflow-x: auto; }
.doc pre code { border: 0; padding: 0; background: none; font-size: 13.5px; line-height: 1.7; white-space: pre; }
/* The document's own opening note — an abstract, set the way a paper sets one. */
.doc blockquote { margin: 0 0 30px; padding: 26px 30px; background: var(--surface);
  border: 1px solid var(--border); border-radius: 18px; }
.doc blockquote p { margin: 0 0 10px; color: var(--text); font-size: 17px; line-height: 1.6; }
.doc blockquote p:last-child { margin: 0; }
.doc hr { border: 0; border-top: 1px solid var(--border); margin: 44px 0; }
.doc a { color: var(--text); text-decoration: underline; text-underline-offset: 3px;
  text-decoration-color: var(--border); }
.doc a:hover { text-decoration-color: var(--text); }
.page--doc .you { gap: 8px; }

/* A table in a paper: rules, not boxes. */
.doc__table { margin: 0 0 26px; overflow-x: auto; }
.doc table { width: 100%; border-collapse: collapse; font-family: "Schibsted Grotesk", system-ui, sans-serif; }
.doc th, .doc td { text-align: left; padding: 11px 16px 11px 0; border-bottom: 1px solid var(--border);
  font-size: 14.5px; line-height: 1.5; color: var(--text-muted); vertical-align: top; }
.doc th { color: var(--text); font-weight: 650; font-size: 12.5px; letter-spacing: .06em;
  text-transform: uppercase; border-bottom-color: var(--text-muted); white-space: nowrap; }
.doc td:first-child { color: var(--text); }

/* ── The third shelf: agents ─────────────────────────────────────────────────
   Same card, same arrows, same keyboard. An agent's cover is a face rather than
   a screenshot, and a face is square — so the card is, and nothing else changes. */
#agents-wrap { padding: 0 0 8px; }
#agents-wrap .rail { align-items: baseline; }
/* The last shelf ends the page — nothing follows it now, so it carries the air
   the footer used to. */
#agents { padding-block: 16px 52px; }
@media (max-width: 560px) { #agents { padding-block: 12px 36px; } }
.card--sq { aspect-ratio: 1; flex-basis: clamp(150px, 15vw, 196px); }
@media (hover: none) { .card--sq { flex-basis: min(46vw, 220px); } }
/* A mascot is line work on its own ground: it fills its card and is never zoomed. */
.card--sq .card__cover { inset: 0; width: 100%; height: 100%; }
/* The foot carries the role as well as the name, and stays put on hover — an
   agent's card has nothing to reveal, because the face is what you came for. */
.card--sq .card__rest span i { display: block; margin-top: 1px; font-style: normal;
  font-weight: 500; font-size: 11.5px; line-height: 1.3; color: var(--text-muted); }
@media (hover: hover) and (pointer: fine) {
  body:not(.is-away) .card--sq:hover .card__rest,
  .card--sq:focus-within .card__rest { opacity: 1; }
}
/* The face carries its own 4px corner; the frame around it must not round it
   further, here or in the preview's bar. */
.card--sq .card__icon, .preview--agent .preview__icon { border-radius: 4px; }

/* ── What an agent does when you look at it ──────────────────────────────────
   Eleven reactions, one each, and none of them the same trick twice. A cover is
   a picture element, so nothing out here can reach inside it to move an eye:
   each agent has a second drawing pulling a different face, stacked over the
   first, and the pair of them moves together.

   A reaction is two variables — which face, and which movement — so every agent
   below is a single line rather than a block of its own. */
.card__react { position: absolute; inset: 0; width: 100%; height: 100%;
  object-fit: cover; display: block; opacity: 0; }
.card--sq { --face: hold; --face-time: 170ms; --face-loop: 1;
            --move: rest; --move-time: 620ms; --move-loop: 1; }
/* An agent that has asked for no movement runs this instead. It has to be a real
   empty animation rather than the keyword: `animation: none 620ms ease 1 both`
   is ambiguous, and the browser reads the `none` as a fill-mode and takes `both`
   for the name. */
@keyframes rest { }

.react--nod    { --move: nod;    --face: flash; --face-time: 2.4s; --face-loop: infinite; }
.react--bounce { --move: bounce; --face: flash; --face-time: 2.4s; --face-loop: infinite; --move-time: 760ms; }
.react--pop    { --move: pop;    --move-time: 520ms; }
.react--shake  { --move: shake;  --move-time: 480ms; }
.react--scan   { --move: scan;   --move-time: 1.8s; --move-loop: infinite; }
.react--lift   { --move: lift;   --move-time: 700ms; }
.react--jolt   { --move: jolt;   --move-time: 620ms; }
.react--buzz   { --move: buzz;   --move-time: 240ms; --move-loop: infinite; }
.react--tick   { --move: tick;   --move-time: 900ms; --move-loop: infinite; }
.react--zoom   { --move: zoom;   --move-time: 1.6s;  --move-loop: infinite; }
.react--tilt   { --move: tilt;   --move-time: 900ms; }

@media (hover: hover) and (pointer: fine) {
  /* Both drawings move as one, or the face would slide off its own head. */
  body:not(.is-away) .card--sq:hover .card__cover,
  .card--sq:focus-within .card__cover {
    animation: var(--move) var(--move-time) var(--ease-out) var(--move-loop) both;
  }
  body:not(.is-away) .card--sq:hover .card__react,
  .card--sq:focus-within .card__react {
    animation: var(--face) var(--face-time) var(--ease-out) var(--face-loop) forwards,
               var(--move) var(--move-time) var(--ease-out) var(--move-loop) both;
  }
}

/* The two ways a second face arrives: it stays, or it flickers past. */
@keyframes hold  { to { opacity: 1; } }
@keyframes flash {
  0%, 6%    { opacity: 0; }
  8%, 13%   { opacity: 1; }
  15%, 20%  { opacity: 0; }   /* the second of the pair, the way a real blink comes */
  22%, 27%  { opacity: 1; }
  29%, 100% { opacity: 0; }
}

/* And the eleven movements. Each stays inside the card, whose ground is the same
   colour as the drawing's, so a tilt or a scale never shows a seam. */
@keyframes nod    { 0%, 100% { transform: none; } 40% { transform: translateY(6px); } }
@keyframes bounce { 0%, 100% { transform: none; } 30% { transform: translateY(-10px); }
                    58% { transform: translateY(0); } 78% { transform: translateY(-4px); } }
@keyframes pop    { 0%, 100% { transform: none; } 45% { transform: scale(1.08); } }
@keyframes shake  { 0%, 100% { transform: none; } 20% { transform: translateX(-5px); }
                    45% { transform: translateX(5px); } 70% { transform: translateX(-3px); } }
@keyframes scan   { 0%, 100% { transform: none; } 30% { transform: translateX(-8px); }
                    70% { transform: translateX(8px); } }
@keyframes lift   { 0%, 100% { transform: none; } 50% { transform: translateY(-9px) scale(1.03); } }
@keyframes jolt   { 0%, 100% { transform: none; } 14% { transform: translateY(-8px) scale(1.06); }
                    38% { transform: translateY(2px) scale(.99); } 64% { transform: none; } }
@keyframes buzz   { 0%, 100% { transform: none; } 25% { transform: translate(-2px, 1px); }
                    50% { transform: translate(2px, -1px); } 75% { transform: translate(-1px, -1px); } }
@keyframes tick   { 0%, 100% { transform: none; } 30% { transform: rotate(-3.5deg); }
                    65% { transform: rotate(3.5deg); } }
@keyframes zoom   { 0%, 100% { transform: none; } 50% { transform: scale(1.07); } }
@keyframes tilt   { 0%, 100% { transform: none; } 40% { transform: rotate(6deg); }
                    75% { transform: rotate(-2.5deg); } }

/* Nobody who asked for less motion gets a face that moves, or one that changes.
   Both animations are named by a variable, so emptying the two variables stops
   them at the source — the page's other reduced-motion rules reach transforms on
   the card, not an animation running on the drawing inside it. */
@media (prefers-reduced-motion: reduce) {
  .card--sq { --move: rest; --face: rest; }
  .card__react { display: none; }
}

/* Installed, said on the card so the shelf can be read without opening anything. */
.tick { position: absolute; top: 9px; right: 9px; width: 24px; height: 24px; z-index: 2;
  display: grid; place-items: center; border-radius: 999px;
  background: var(--accent); color: var(--accent-ink);
  opacity: 0; transform: scale(.7); transition: opacity var(--t-fast) var(--ease-out), transform var(--t-fast) var(--ease-out); }
.tick svg { width: 15px; height: 15px; stroke-width: 2.2; }
.card.is-fitted .tick { opacity: 1; transform: none; }
@media (prefers-reduced-motion: reduce) { .tick { transition: none; } }

/* What an agent leans on, one shelf up. The only place the three rows touch. */
.works { margin: 22px 0 0; }
.works h3 { margin: 0 0 10px; font: 600 11px/1 "Schibsted Grotesk", system-ui, sans-serif;
  letter-spacing: .14em; text-transform: uppercase; color: var(--text-muted); }
.works__chip { display: inline-flex; align-items: center; gap: 9px; padding: 7px 13px 7px 8px;
  border: 1px solid var(--border); border-radius: 999px; background: var(--surface-2);
  transition: border-color var(--t-fast) ease, background var(--t-fast) ease; }
.works__chip:hover { background: var(--border); border-color: var(--glass-border); }
.works__chip img { width: 22px; height: 22px; border-radius: 6px; display: block; }
.works__chip b { font: 650 13px/1 "Schibsted Grotesk", system-ui, sans-serif; }

/* Install — one control, three states. The bar is the whole point of the middle
   one: an install that finishes instantly reads as a button that did nothing. */
.fit { display: grid; gap: 10px; }
.fit .pill { width: 100%; justify-content: center; padding: 12px 18px; font-size: 14.5px; }
.fit .pill:disabled { opacity: 1; cursor: default; }
.fit__bar { height: 3px; border-radius: 999px; background: var(--border); overflow: hidden; }
.fit__bar i { display: block; height: 100%; width: 0; background: var(--accent); }
.fit__state { margin: 0; display: flex; align-items: center; gap: 7px;
  color: var(--text-muted); font-size: 12.5px; }
.fit__state::before { content: ""; width: 6px; height: 6px; border-radius: 999px; background: var(--success); }
.fit__cmd { display: flex; align-items: center; gap: 10px; width: 100%; text-align: left;
  padding: 9px 12px; border: 1px solid var(--border); border-radius: 11px;
  background: var(--bg); color: var(--text-muted); cursor: pointer;
  transition: border-color var(--t-fast) ease, color var(--t-fast) ease; }
.fit__cmd:hover { border-color: var(--glass-border); color: var(--text); }
.fit__cmd code { flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
  font: 500 12.5px/1.4 ui-monospace, "SF Mono", Menlo, monospace; }
.fit__cmd span { flex: none; font: 650 11.5px/1 "Schibsted Grotesk", system-ui, sans-serif;
  letter-spacing: .04em; text-transform: uppercase; color: var(--text-muted); }

/* A mascot is square and has nothing to crop, so it is shown whole rather than
   filling the sixteen-by-nine frame a screenshot wants — and it brings its own
   ground, so the panel around it drops its frame rather than drawing a second one. */
.preview__media--face { aspect-ratio: 1; max-width: 250px; margin-inline: auto;
  background: transparent; border: 0; }
.preview__media--face img { object-fit: contain; }
@container (max-width: 659px) { .preview__media--face { max-width: 190px; } }

/* A skill's features are named, not described: a mark and two or three words. */
.feat--marks { grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 10px; margin-top: 20px; }
.feat--marks li { display: flex; align-items: center; gap: 11px; padding: 13px 14px;
  border: 1px solid var(--border); border-top: 1px solid var(--border); border-radius: 13px;
  background: var(--surface); }
.feat--marks li:last-child { padding-bottom: 13px; }
.feat--marks svg { width: 19px; height: 19px; flex: none; color: var(--text-muted); }
.feat--marks b { font: 650 13.5px/1.3 "Schibsted Grotesk", system-ui, sans-serif; color: var(--text); }
@container (max-width: 659px) { .feat--marks { grid-template-columns: 1fr; } }
