/* Contexter — public site. The only stylesheet.
   Rules: no bundlers, no external domains (fonts, scripts and images are local
   files from /assets only).

   VISUAL IDENTITY. The character is taken from the illustrations and carried
   across three matching axes:
     • its palette — warm coffee ink, creamy paper, solid orange, the gold of
       the glowing thread;
     • its material — every surface is a PANE of glass: translucent, blurring
       what is behind it, edged by the light that catches it and casting a soft
       shadow rather than glowing. The section at the foot of this file is where
       that material is stated;
     • its type pairing — a heavy condensed sans for headings and a monospace
       for the label layer, instead of one sans for everything.
   Orange stays an accent, but never as a block background and never as a frame
   around text: only the primary button, links, bullet dots and the active
   navigation item. Running text takes the darker of the two oranges — the
   brighter one is 4.1:1 on this cream and fails AA as a sentence.

   TWO DEVICES ARE DELIBERATELY ABSENT, and stay absent: a line above a page
   heading, and numbers on the sections. A heading carries its own weight, and
   the order of the sections on a page is not information anybody needs.

   TYPOGRAPHY. Three families, three roles:
     • Instrument Serif — the wordmark and the large headings;
     • Inter — everything else: body text, subheadings, buttons, and the small uppercase
       captions that used to be set in a monospaced third family.
   FONTS ARE SELF-HOSTED rather than linked from a third-party CDN: the page
   promises privacy, and no external domain should get to see its visitors.
   All three are OFL — licenses sit next to the files in assets/fonts/.
   Only the weights actually used were downloaded, latin only (~63 KB total). */

/* TWO FAMILIES, AND ONLY TWO. Instrument Serif carries the headings and the
   wordmark; Inter carries everything else, including the small print that used to be a third
   family in a monospaced disguise. Every file is ours, served from our own host, cut to the
   subsets the product actually shows: latin for the site, cyrillic because contexts and bot
   texts are Russian, latin-ext for the odd accented name.

   ONE WEIGHT OF THE SERIF EXISTS, so only one is declared: asking for a bold the family does not
   have makes the browser draw a fake one, which on a serif shows as smeared stems. */
@font-face {
  font-family: "Instrument Serif";
  font-style: normal;
  font-weight: 400;
  font-display: swap;
  src: url("fonts/instrument-serif-400-latin.woff2") format("woff2");
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304,
    U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}

@font-face {
  font-family: "Instrument Serif";
  font-style: normal;
  font-weight: 400;
  font-display: swap;
  src: url("fonts/instrument-serif-400-latin-ext.woff2") format("woff2");
  unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308,
    U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113,
    U+2C60-2C7F, U+A720-A7FF;
}

/* Inter is variable: 400 through 700 come out of one file, so the interface has its regular, its
   semibold and its bold for the weight of a single request. */
@font-face {
  font-family: "Inter";
  font-style: normal;
  font-weight: 400 700;
  font-display: swap;
  src: url("fonts/inter-var-latin.woff2") format("woff2");
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304,
    U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}

@font-face {
  font-family: "Inter";
  font-style: normal;
  font-weight: 400 700;
  font-display: swap;
  src: url("fonts/inter-var-cyrillic.woff2") format("woff2");
  unicode-range: U+0301, U+0400-045F, U+0490-0491, U+04B0-04B1, U+2116;
}

@font-face {
  font-family: "Inter";
  font-style: normal;
  font-weight: 400 700;
  font-display: swap;
  src: url("fonts/inter-var-latin-ext.woff2") format("woff2");
  unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308,
    U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113,
    U+2C60-2C7F, U+A720-A7FF;
}

/* THEMING. Everything a look is made of lives in these custom properties: the
   palette, the two stroke widths, the stroke colour, the corners, the shadow,
   the heading scale and the three type families. THE DARK PALETTE REASSIGNS
   THEM AND NOTHING ELSE — the layout, the markup and the behaviour below are
   the same page under either light. */
:root {
  color-scheme: light;

  /* Landing palette — teal skin, matched to the mini app (.wx-body). */
  --ground: #fbfaf4; /* soft off-white — page background */
  /* A card is no longer a fill: it is a PANE — mostly the page, held back
     by a film of white. */
  --card: rgba(255, 255, 255, 0.8);
  --sunk: #eef2f0; /* recessed band (technical block) */
  --ink: #091717; /* body text and strokes */
  --ink-2: #46626a; /* muted text */
  --ink-3: #5f7a80; /* small captions */
  --hair: rgba(9, 23, 23, 0.1); /* hairline */
  --accent: #299da9; /* teal */
  --accent-2: #1f818b; /* hover */
  --on-accent: #06251f; /* text sitting on an accent fill */
  --danger: #b8503a; /* deleting, revoking — never the accent */
  --gold: #299da9; /* the connecting thread */
  --link: #17747e;

  /* THE MATERIAL, in four values, so that a chip and a card are the same
     substance at two sizes. See the section at the foot of this file. */
  --glass-blur: saturate(180%) blur(20px);
  --glass-rim: inset 0 0.5px 0 rgba(255, 255, 255, 0.75),
    inset 0 -0.5px 0 rgba(255, 255, 255, 0.16);
  --cast: 16 46 46; /* the colour a shadow is mixed from, per room */
  --glass-cast: 0 1px 2px rgb(var(--cast) / 0.06),
    0 10px 30px -10px rgb(var(--cast) / 0.22);
  --glass-edge: rgba(9, 23, 23, 0.16);
  /* A CONTROL owes more than a pane does: its boundary is what says it can
     be pressed, so it takes the same ink a step further. */
  --edge-strong: rgba(9, 23, 23, 0.26);

  /* THE ROOM'S SURFACES, as values rather than as rules: the dark palette below
     reassigns these and the material section never learns which room it is in. */
  --wash:
    radial-gradient(120% 55% at 100% 0%, rgba(41, 157, 169, 0.06), transparent 55%),
    linear-gradient(168deg, rgba(255, 255, 255, 0.5), rgba(255, 255, 255, 0) 22%);
  --head-bg: var(--ground); /* opaque: see the note on .site-header */
  --film: rgba(255, 255, 255, 0.74); /* a pane that is not a card */
  --film-2: rgba(255, 255, 255, 0.7);
  --film-hover: rgba(255, 255, 255, 0.82);
  --chip: rgba(255, 255, 255, 0.74);
  --chip-2: rgba(255, 255, 255, 0.78);
  --fill: rgba(16, 114, 123, 0.92); /* the primary control */
  --fill-hover: rgba(12, 98, 107, 0.96);
  --sel: rgba(9, 23, 23, 0.9); /* a chosen tab, a person's initial */
  --on-sel: #fbfaf4;
  --tech-bg: rgba(9, 40, 40, 0.06);
  --art-cast: rgba(9, 40, 40, 0.24);
  --opaque: #f5f7f5; /* what a pane becomes with no blur available */


  /* the drawing manner: stroke widths, stroke colour, corners, shadow */
  --bw: 1px; /* the pane's own edge */
  --bw-1: 1px; /* the light outline — chips and small controls */
  --edge: var(--glass-edge); /* colour of a drawn outline */
  --r-lg: 26px; /* the outer corner; inner corners derive from it */
  /* THE SPRING. Controls in this material do not slide to a stop, they settle:
     the curve overshoots a little and comes back, which is what reads as «floating» rather than
     «moving». One curve for every control on both surfaces, so nothing settles on a rhythm of its
     own. Slower than a click and faster than a thought: 280ms out, 120ms back under the finger. */
  --spring: cubic-bezier(0.34, 1.42, 0.52, 1);
  --spring-out: 280ms;
  --spring-in: 120ms;
  --shadow: var(--glass-cast);

  /* One multiplier for every large heading. A condensed face reads smaller at
     the same point size, so the scale is 1 here; a normal-width face asks for
     less and a theme lowers it in one place instead of six. */
  --hs: 1;

  /* ---------- THE HEADING SCALE — FIVE STEPS, AND NOTHING BETWEEN THEM ----------
     There used to be no scale. Every block sized its own heading by eye and wrote the number down
     where it stood: five different display sizes with five hand-tuned clamps, and five more sizes
     for the subheads (21, 18, 17, 16, 18). Each number had a comment explaining how it made the
     line break in that one block — which is exactly how a stylesheet stops being a system: a page
     built from three such blocks shows three typefaces' worth of hierarchy and none of it means
     anything to the reader.
     Five steps, each with a JOB rather than a place:
       display — the one headline that opens the site's front page;
       title   — what names a page or closes one: the head of an inner page, the last call;
       section — what divides a page into parts: a section head, a claim;
       sub     — what heads a card the reader is looking at;
       dense   — what heads a step or a row inside such a card.
     The two smallest are flat sizes on purpose: a card does not get wider with the window, so a
     heading inside one has nothing to scale against. The three largest ride the viewport between a
     phone floor and a desktop ceiling, and all of them go through --hs, so a theme still lowers the
     lot in one place.
     A new block picks the step whose JOB matches. If none matches, that is a question about the
     block, not a licence for a sixth number. */
  --t-display: clamp(calc(40px * var(--hs)), calc(5.7vw * var(--hs)), calc(62px * var(--hs)));
  --t-title: clamp(calc(36px * var(--hs)), calc(5.2vw * var(--hs)), calc(54px * var(--hs)));
  --t-section: clamp(calc(31px * var(--hs)), calc(4.4vw * var(--hs)), calc(45px * var(--hs)));
  --t-sub: 19px;
  --t-dense: 16px;
  /* The heading of a SELF-CONTAINED BLOCK OF CONTENT: a clause in a long document, the title on a
     context card in the workspace. It is its own step and not `section`, because the job differs —
     a marketing page carries three or four section heads, a legal document carries fifteen clause
     heads, and the section size across fifteen of them is a wall rather than a structure.
     The value is the one the legal pages already had, so this step NAMES what the browser was
     choosing for us rather than restyling documents that are in force. The workspace card title
     joined it from 25px: a pixel, and one number fewer. */
  --t-doc: 26px;
  /* The title of an APPLICATION SCREEN — the workspace's sign-in, the mini app's gate. Its own
     step because its job is neither of the two it sits between: it is not a document's clause head,
     and it is not the head of a marketing page that has a whole column to fill. The value is the
     one both screens already carried; what changes is that it is now written ONCE and can be seen
     by the guard, instead of twice in two files as an attribute in the markup. */
  --t-app: 34px;
  /* Not a heading step but the layer beside them: the small upright caps that label a group or
     number a step. It is listed here because it was the same loose number in two places. */
  --t-label: 11px;

  --w: 940px;
  --r: 14px; /* the small corner */

  /* Fallbacks in case the woff2 never arrives: narrow system grotesques for
     display, the system monospace for mono. Metrics will not match exactly, but
     the role of each layer (condensed / regular / monospace) survives. */
  --display: "Instrument Serif", "Iowan Old Style", "Palatino Linotype", Georgia,
    "Times New Roman", serif;
  --sans: "Inter", -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto,
    "Helvetica Neue", Arial, sans-serif;
  /* The label layer keeps its size, its tracking and its caps — it simply stops being a
     third family. One text face for everything that is not a heading. */
  --mono: "Inter", -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif;
}

/* ---------- THE SECOND LIGHT ----------
   Three states, and the difference between them matters.

   NOTHING STAMPED is the default and means «whatever this person is reading
   under»: on the web that is the operating system's own setting, and inside
   Telegram it is the client's, which the Mini App reads and stamps for us.
   data-theme="dark" and data-theme="light" are a DECISION the person made with
   the control in the header, and a decision has to win in both directions —
   which is why the dark values appear twice below. There is no way in plain CSS
   to say a block once and apply it under two different conditions, and a
   preprocessor is not worth adding to a site that has no build step. The two
   copies are identical on purpose; change them together. */

@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) {
  color-scheme: dark;

  /* Landing dark palette — teal skin, matched to the mini app (.wx-body). */
  --ground: #0c1616;
  /* A pane on a dark ground is the reverse film: a little light held over the
     room, rather than a little white held over the paper. */
  --card: #17231f;
  --sunk: #0a1212;
  --ink: #e9f3f1;
  --ink-2: #9fb7b4;
  --ink-3: #7f9996; /* 4.5:1 on a pane, not only on the page */
  --hair: rgba(233, 243, 241, 0.13);
  /* The accent is the one cool light in a dark room: a brighter teal that
     reads as a lamp rather than a fill. */
  --accent: #3fc2cf;
  --accent-2: #63d6e0;
  --on-accent: #052224;
  --danger: #e0876a;
  --gold: #3fc2cf;
  /* On this ground the accent is a light, and a light is legible, so the link
     and the fill are the same value here. */
  --link: #3fc2cf;

  /* The material inverts with the room. The rim is the same idea — light along
     the top edge — at the strength a dark pane can carry before it looks like
     a stroke; the cast is deeper because there is more dark to cast into. */
  --glass-rim: inset 0 0.5px 0 rgba(233, 243, 241, 0.16),
    inset 0 -0.5px 0 rgba(233, 243, 241, 0.04);
  --cast: 0 0 0; /* a shadow in a dark room is plain dark, never tinted */
  --glass-cast: 0 1px 2px rgb(var(--cast) / 0.4),
    0 12px 34px -12px rgb(var(--cast) / 0.75);
  --glass-edge: rgba(233, 243, 241, 0.09);
  --edge-strong: rgba(233, 243, 241, 0.2);

  --wash: radial-gradient(120% 55% at 100% 0%, rgba(63, 194, 207, 0.1), transparent 55%);
  --head-bg: var(--ground); /* opaque: see the note on .site-header */
  --film: rgba(210, 240, 238, 0.2);
  --film-2: rgba(210, 240, 238, 0.17);
  --film-hover: rgba(210, 240, 238, 0.24);
  --chip: rgba(210, 240, 238, 0.2);
  --chip-2: rgba(210, 240, 238, 0.24);
  --fill: rgba(63, 194, 207, 0.92);
  --fill-hover: rgba(99, 214, 224, 0.96);
  --sel: rgba(233, 243, 241, 0.9);
  --on-sel: #052224;
  --tech-bg: rgba(0, 0, 0, 0.28);
  --art-cast: rgba(0, 0, 0, 0.55);
  --opaque: #17231f;

  }
}

:root[data-theme="dark"] {
  color-scheme: dark;

  /* Landing dark palette — teal skin, matched to the mini app (.wx-body). */
  --ground: #0c1616;
  /* A pane on a dark ground is the reverse film: a little light held over the
     room, rather than a little white held over the paper. */
  --card: #17231f;
  --sunk: #0a1212;
  --ink: #e9f3f1;
  --ink-2: #9fb7b4;
  --ink-3: #7f9996; /* 4.5:1 on a pane, not only on the page */
  --hair: rgba(233, 243, 241, 0.13);
  /* The accent is the one cool light in a dark room: a brighter teal that
     reads as a lamp rather than a fill. */
  --accent: #3fc2cf;
  --accent-2: #63d6e0;
  --on-accent: #052224;
  --danger: #e0876a;
  --gold: #3fc2cf;
  /* On this ground the accent is a light, and a light is legible, so the link
     and the fill are the same value here. */
  --link: #3fc2cf;

  /* The material inverts with the room. The rim is the same idea — light along
     the top edge — at the strength a dark pane can carry before it looks like
     a stroke; the cast is deeper because there is more dark to cast into. */
  --glass-rim: inset 0 0.5px 0 rgba(233, 243, 241, 0.16),
    inset 0 -0.5px 0 rgba(233, 243, 241, 0.04);
  --cast: 0 0 0; /* a shadow in a dark room is plain dark, never tinted */
  --glass-cast: 0 1px 2px rgb(var(--cast) / 0.4),
    0 12px 34px -12px rgb(var(--cast) / 0.75);
  --glass-edge: rgba(233, 243, 241, 0.09);
  --edge-strong: rgba(233, 243, 241, 0.2);

  --wash: radial-gradient(120% 55% at 100% 0%, rgba(63, 194, 207, 0.1), transparent 55%);
  --head-bg: var(--ground); /* opaque: see the note on .site-header */
  --film: rgba(210, 240, 238, 0.2);
  --film-2: rgba(210, 240, 238, 0.17);
  --film-hover: rgba(210, 240, 238, 0.24);
  --chip: rgba(210, 240, 238, 0.2);
  --chip-2: rgba(210, 240, 238, 0.24);
  --fill: rgba(63, 194, 207, 0.92);
  --fill-hover: rgba(99, 214, 224, 0.96);
  --sel: rgba(233, 243, 241, 0.9);
  --on-sel: #052224;
  --tech-bg: rgba(0, 0, 0, 0.28);
  --art-cast: rgba(0, 0, 0, 0.55);
  --opaque: #17231f;

}

* {
  box-sizing: border-box;
}

html {
  -webkit-text-size-adjust: 100%;
  scroll-behavior: smooth;
  /* Paint the canvas itself, not just the body: the body background is
     attachment:fixed and does not extend into the top overscroll / rubber-band
     area, which otherwise exposes the browser's default canvas as a strip above
     the pinned header (owner 28.08). --ground is theme-aware, so the fill always
     matches the header at the very top of the page. */
  background-color: var(--ground);
}

body {
  margin: 0;
  background: var(--ground);
  color: var(--ink);
  font-family: var(--sans);
  font-size: 17px;
  line-height: 1.5;
  -webkit-font-smoothing: antialiased;
  overflow-wrap: break-word;
}

img {
  max-width: 100%;
  height: auto;
  display: block;
}

a {
  color: var(--accent);
  text-decoration: none;
}

a:hover {
  text-decoration: underline;
}

p {
  margin: 0;
}

/* Inline literals — a command, a name to type. The monospace layer already
   exists on the page; here it only needs to be a shade smaller so it does not
   grow taller than the sentence around it. */
code {
  font-family: var(--mono);
  font-weight: 500;
  font-size: 0.88em;
  color: var(--ink);
}

/* HEADINGS ARE A SERIF NOW. Instrument Serif has one weight, so nothing here asks
   for a heavier one — a synthesised bold on a serif shows immediately. It is also a wider, quieter
   face than the condensed one it replaces: the line can breathe a little (0.96 was tuned for a
   condensed face and pinched this one) and the tracking closes by a hair at display sizes, which
   is what a serif wants when it is set large. */
h1,
h2 {
  margin: 0;
  font-family: var(--display);
  font-weight: 400;
  letter-spacing: -0.01em;
  line-height: 1.04;
}

/* There is no step down to take: the family has a single weight, and a section heading is told
   apart from a page heading by its size, which is how a serif does it anyway. */

/* Subheadings are the body face at semibold — one family, one step of weight. */
h3 {
  margin: 0;
  font-family: var(--sans);
  font-weight: 600;
  letter-spacing: -0.02em;
  line-height: 1.2;
}

.wrap {
  width: 100%;
  max-width: var(--w);
  margin: 0 auto;
  padding: 0 24px;
}

.muted {
  color: var(--ink-2);
}

/* ---------- header ---------- */

.site-header {
  position: sticky;
  position: -webkit-sticky;
  top: 0;
  z-index: 20;
  background: var(--ground);
  border-bottom: var(--bw) solid var(--edge);
  /* Own compositing layer so the pinned bar does not lag behind inertial
     scrolling in webviews (owner 28.08: header "dragged down" on scroll).
     A transform on the sticky element itself does not break its own sticky,
     and the header has no fixed-position descendants for it to reparent. */
  transform: translateZ(0);
}

.site-header .wrap {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 10px 20px;
  padding-top: 11px;
  padding-bottom: 11px;
}

/* The lockup is a picture and a word, and only the picture is a file. The word
   is set in the condensed face the headings already load, so it is drawn at
   whatever size and in whatever colour the page is in — the two-file trick this
   replaced repainted the letters for the dark room and could only ever be right
   in the rooms it had a file for.
   ONE NUMBER DRIVES THE REST: --brand-h is the height of the spool, and the size of the word and
   the gap beside it are fractions of it. The wordmark is set in the heading face from 16.08, so the fraction is no longer the one measured against the old drawing: a serif of this
   kind reads smaller at the same point size and needs about a tenth more to hold the same optical
   height. Change the height alone and the proportions hold. */
.brand {
  --brand-h: 32px;
  display: flex;
  align-items: center;
  gap: calc(var(--brand-h) * 0.32);
  margin-right: auto;
  /* The lockup sits inside a link, and a link is accent-coloured. The word is not a
     link's word, it is the mark's — while it was a picture the question never came up. */
  color: var(--ink);
}

.brand:hover {
  text-decoration: none;
}

.brand img {
  height: var(--brand-h);
  width: auto;
  display: block;
}

.brand b {
  font-family: var(--display);
  font-weight: 400;
  font-size: calc(var(--brand-h) * 0.88);
  letter-spacing: -0.01em;
  line-height: 1;
  color: currentColor;
}

.nav {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 8px 20px;
  font-family: var(--mono);
  font-weight: 500;
  font-size: 12px;
  letter-spacing: 0.08em;
  text-transform: uppercase;
}

.nav a {
  color: var(--ink-2);
}

.nav a:hover {
  color: var(--ink);
  text-decoration: none;
}

/* .nav a is more specific than .btn-primary — give the button its white text back */
.nav a.btn-primary,
.nav a.btn-primary:hover {
  color: #fff;
}

/* ---------- buttons ---------- */

.btn {
  display: inline-block;
  border: var(--bw) solid var(--edge);
  border-radius: var(--r);
  padding: 11px 18px;
  font-family: var(--sans);
  font-size: 15px;
  font-weight: 600;
  letter-spacing: -0.01em;
  line-height: 1.2;
  text-transform: none;
  cursor: pointer;
}

.btn:hover {
  text-decoration: none;
}

/* the primary button is the only orange surface on the page */
.btn-primary {
  background: var(--accent);
  border-color: var(--accent);
  color: var(--on-accent);
}

.btn-primary:hover {
  background: var(--accent-2);
  border-color: var(--accent-2);
}

.btn-ghost {
  background: transparent;
  color: var(--ink);
}

.btn-ghost:hover {
  background: var(--card);
}

.btn-sm {
  padding: 7px 12px;
  font-size: 13px;
}

/* ---------- side table of contents with a section indicator ----------
   Shown only where there is margin for it; below 1220px it is hidden and
   navigation stays in the header. The active item is highlighted by an
   IntersectionObserver (the only script on the page, inline, ~15 lines, local);
   without JS the list still works as a plain table of contents. */

.rail {
  display: none;
}

/* THE WIDTH IS ARITHMETIC, NOT TASTE. The rail is fixed to the left edge and the
   content is a 940px column in the middle of the window, so the rail is only out
   of the way while (window - 940) / 2 clears it. It stands 18 from the edge and is
   206 wide, so it ends at 224; 1420 leaves 16 between it and the first letter of
   the page. Below that it draws ON the text — at 1260 it covered 64px of it.
   Change the column, the rail's width or its offset, and this number moves with them. */
@media (min-width: 1420px) {
  .rail {
    display: block;
    position: fixed;
    left: 14px;
    top: 50%;
    transform: translateY(-50%);
    width: 130px;
    z-index: 10;
  }
}

.rail ol {
  list-style: none;
  margin: 0;
  padding: 0;
}

.rail a {
  display: grid;
  grid-template-columns: 20px minmax(0, 1fr);
  align-items: center;
  gap: 8px;
  padding: 5px 0;
  font-family: var(--mono);
  font-weight: 500;
  font-size: 11px;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--ink-3);
}

.rail a:hover {
  color: var(--ink);
  text-decoration: none;
}

/* The tick grows by TRANSFORM, not by width: width is a layout property and
   animating it relayouts the rail on every frame. Drawn at its full length and
   scaled down from the left, the resting state is the short mark and the active
   one is the whole rule. */
.rail .tick {
  height: 2px;
  width: 20px;
  background: currentColor;
  transform: scaleX(0.45);
  transform-origin: left center;
  transition: transform 0.14s linear;
}

.rail a.on {
  color: var(--accent);
}

.rail a.on .tick {
  transform: scaleX(1);
}

/* ---------- sections ---------- */

/* The section divider is drawn at CONTENT WIDTH, not across the whole page: the
   left gutter stays clear for the side table of contents, so no line cuts
   through it. */
section {
  padding: 0;
  scroll-margin-top: 58px;
}

section > .wrap {
  border-top: var(--bw) solid var(--edge);
  padding-top: 62px;
  padding-bottom: 62px;
}

.sec-head {
  max-width: 34em;
  margin-bottom: 34px;
}

.sec-head h2 {
  font-size: var(--t-section);
  margin: 9px 0 0;
}

.sec-head p {
  margin-top: 12px;
  color: var(--ink-2);
}

/* section index in monospace — ties the table of contents to the content */
.sec-n {
  font-family: var(--mono);
  font-weight: 500;
  font-size: 11px;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--ink-3);
}

.soon {
  font-family: var(--mono);
  font-size: 11px;
  font-style: normal;
  font-weight: 500;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--ink-3);
  white-space: nowrap;
}

/* ---------- hero ---------- */

.hero {
  padding: 70px 0 62px;
  scroll-margin-top: 58px;
}

.hero .wrap {
  display: grid;
  /* the illustration is a full half of the first screen, not a side icon */
  grid-template-columns: minmax(0, 1.2fr) minmax(0, 0.8fr);
  gap: 40px;
  align-items: center;
}

/* The ceiling of the display step was chosen against this line: at it the second line, "for every
   AI you use", still fits on one line as the <br> intends, and one step larger breaks it into
   three. That is why the number lives on the token rather than here — the constraint belongs to
   the largest heading we set anywhere, not to this block alone. */
.hero h1 {
  font-size: var(--t-display);
  margin: 13px 0 18px;
}

.hero-sub {
  font-size: clamp(17px, 1.9vw, 19px);
  color: var(--ink-2);
  max-width: 34em;
}

.cta-row {
  display: flex;
  flex-wrap: wrap;
  gap: 10px;
  margin: 26px 0 16px;
}

.trust {
  font-family: var(--mono);
  font-weight: 500;
  font-size: 12px;
  line-height: 1.6;
  letter-spacing: 0.03em;
  color: var(--ink-2);
  max-width: 46em;
}

/* The illustration fills its column. The border and the rounded corners are
   drawn into the image file itself, so the layout adds nothing around it —
   otherwise the outline would double up. */
.hero-art picture {
  display: block;
}

.hero-art img {
  display: block;
  width: 100%;
  height: auto;
  margin-left: auto;
}

/* ---------- STORY DECK — the hero carousel ----------
   Four stacked cards; a tap sends the front one down and under the deck while
   the next rises to the front, and the caption above cross-fades. Positions are
   the classes p0..p3 (front to back); the transition does the sliding. */
.deck {
  display: flex;
  flex-direction: column;
  gap: 14px;
  width: 100%;
  max-width: 330px;
  margin-left: auto;
  /* Own stacking context so the cards' z-index (10..40) stays LOCAL to the deck
     and never rises above the sticky header (z-index 20). */
  position: relative;
  z-index: 0;
  isolation: isolate;
}
.deck-bar {
  display: flex;
  align-items: center;
  gap: 11px;
  min-height: 34px;
  padding: 0 2px;
}
.deck-app {
  display: inline-flex;
  width: 30px;
  height: 30px;
  flex: none;
}
.deck-app svg { width: 100%; height: 100%; display: block; }
.deck-cap {
  font-weight: 650;
  font-size: 20px;
  color: var(--ink);
  letter-spacing: -0.015em;
  transition: opacity 0.2s ease;
  /* Its own layer, and a whole-word wrap. Fading the text in place left the
     previous caption ghosting behind the new one on iOS — the compositor kept
     the old raster of a partially transparent text node (owner 28.08). */
  transform: translateZ(0);
  backface-visibility: hidden;
  min-width: 0;
}
.deck-cap.swap { opacity: 0; }
.deck-dots {
  margin-left: auto;
  display: inline-flex;
  gap: 6px;
  align-items: center;
}
.deck-dots i {
  width: 6px;
  height: 6px;
  border-radius: 999px;
  background: var(--edge-strong);
  transition: width 0.3s ease, background 0.3s ease;
}
.deck-dots i.on { width: 18px; background: var(--accent); }

.deck-stack {
  position: relative;
  width: 100%;
  aspect-ratio: 1024 / 1536;
  padding: 0;
  border: 0;
  background: none;
  cursor: pointer;
  -webkit-tap-highlight-color: transparent;
}
.deck-stack:focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: 5px;
  border-radius: 24px;
}
.card {
  position: absolute;
  inset: 0;
  margin: 0;
  border-radius: 20px;
  overflow: hidden;
  background: var(--opaque);
  box-shadow: 0 12px 34px -12px rgb(var(--cast) / 0.4), inset 0 0 0 1px var(--glass-edge);
  transform: translateY(var(--y, 0)) scale(var(--s, 1));
  opacity: var(--o, 1);
  z-index: var(--z, 1);
  transition: transform 0.46s cubic-bezier(0.36, 1.18, 0.5, 1), opacity 0.46s ease;
  will-change: transform, opacity;
}
.card img { width: 100%; height: 100%; object-fit: cover; display: block; }
.card.p0 { --y: 0; --s: 1; --o: 1; --z: 40; }
.card.p1 { --y: 16px; --s: 0.95; --o: 1; --z: 30; }
.card.p2 { --y: 31px; --s: 0.905; --o: 0.92; --z: 20; }
.card.p3 { --y: 45px; --s: 0.86; --o: 0.8; --z: 10; }
@media (prefers-reduced-motion: reduce) {
  .card { transition: opacity 0.3s ease; }
}

/* The relocated START widget now lives in "Getting started": centred, smaller,
   with a short prompt above it. */
.gs-art {
  margin: 18px 0 34px;
  max-width: 400px;
  text-align: left;
}
.gs-art-h {
  margin: 0 0 14px;
  font-weight: 650;
  font-size: 18px;
  color: var(--ink);
}
.gs-art .flip { margin-inline: 0; }

/* ---------- overview diagram: inputs -> one context ---------- */

.surfaces {
  display: grid;
  grid-template-columns: repeat(4, minmax(0, 1fr));
  gap: 10px;
}

.surface {
  background: var(--card);
  border: var(--bw) solid var(--edge);
  border-radius: var(--r);
  box-shadow: var(--shadow);
  padding: 13px 12px;
  text-align: center;
}

.surface b {
  display: block;
  font-size: 15px;
  font-weight: 600;
  letter-spacing: -0.02em;
}

.surface span {
  display: block;
  font-family: var(--mono);
  font-weight: 500;
  font-size: 11px;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--ink-3);
  margin-top: 4px;
}

/* threads converging into a single context — the mascot's gold */
.threads {
  display: block;
  width: 100%;
  height: 54px;
}

.threads path {
  fill: none;
  stroke: var(--gold);
  stroke-width: 1.5;
  vector-effect: non-scaling-stroke;
}

.threads circle {
  fill: var(--accent);
}

.threads-narrow {
  display: none;
  width: 2px;
  height: 30px;
  margin: 0 auto;
  background: var(--gold);
}

.hub {
  border: var(--bw) solid var(--edge);
  border-radius: var(--r-lg);
  background: var(--card);
  box-shadow: var(--shadow);
  padding: 22px;
  text-align: center;
}

.hub h3 {
  font-size: var(--t-sub);
}

.hub p {
  margin-top: 9px;
  color: var(--ink-2);
  font-size: 15px;
  max-width: 40em;
  margin-left: auto;
  margin-right: auto;
}

/* ---------- context cards ---------- */

.ctxs {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(330px, 1fr));
  gap: 18px;
  margin-top: 42px;
}

.ctx {
  background: var(--card);
  border: var(--bw) solid var(--edge);
  border-radius: var(--r-lg);
  box-shadow: var(--shadow);
  overflow: hidden;
}

.ctx-head {
  padding: 16px 18px 14px;
}

.ctx-top {
  display: flex;
  align-items: flex-start;
  gap: 11px;
}

.ctx-emoji {
  font-size: 24px;
  line-height: 1.1;
  flex: 0 0 auto;
}

.ctx-name {
  flex: 1;
  min-width: 0;
}

.ctx-name h3 {
  font-size: var(--t-sub);
  margin-bottom: 4px;
}

.ctx-name p {
  font-size: 13.5px;
  color: var(--ink-2);
  line-height: 1.4;
}

.pill {
  flex: 0 0 auto;
  font-family: var(--mono);
  font-weight: 500;
  font-size: 11px;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  border: var(--bw-1) solid var(--edge);
  border-radius: 3px;
  padding: 3px 7px;
  white-space: nowrap;
}

.ctx-meta {
  display: flex;
  flex-wrap: wrap;
  gap: 4px 14px;
  margin-top: 13px;
  font-family: var(--mono);
  font-weight: 500;
  font-size: 11px;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--ink-3);
}

.ctx-meta b {
  color: var(--ink);
  font-weight: 500;
}

.ctx-aud {
  margin-top: 13px;
  padding-top: 12px;
  border-top: 1px solid var(--hair);
  font-family: var(--mono);
  font-weight: 500;
  font-size: 11px;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--ink-3);
}

.who {
  display: flex;
  flex-wrap: wrap;
  gap: 6px;
  margin-top: 7px;
}

.chip {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  border: 1px solid var(--hair);
  border-radius: 99px;
  /* The avatar sets the height, but the NAME still needs an inset of its own:
     at 2px the text was flush against the top and bottom of the pill. */
  padding: 4px 10px 4px 3px;
  font-family: var(--sans);
  font-size: 12.5px;
  letter-spacing: 0;
  text-transform: none;
  color: var(--ink);
}

.av {
  width: 19px;
  height: 19px;
  border-radius: 50%;
  display: grid;
  place-items: center;
  font-family: var(--mono);
  font-weight: 500;
  font-size: 11px;
  color: var(--on-sel);
  background: var(--sel);
}

.chip i {
  font-family: var(--mono);
  font-weight: 500;
  font-size: 11px;
  font-style: normal;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--ink-3);
}

.ctx-src {
  margin-top: 12px;
  font-family: var(--mono);
  font-weight: 500;
  font-size: 11px;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--ink-3);
}

.tag {
  display: inline-block;
  border: 1px solid var(--hair);
  border-radius: 3px;
  padding: 2px 6px;
  margin: 4px 4px 0 0;
  color: var(--ink-2);
}

/* A source that is on its way rather than in place: the marker rides inside the
   tag, in the same form the surface tiles use. */
.tag .soon {
  margin-left: 6px;
}

.ctx-items {
  list-style: none;
  margin: 0;
  padding: 0;
  border-top: var(--bw) solid var(--edge);
}

.ctx-items > li {
  display: flex;
  gap: 11px;
  padding: 12px 18px;
  border-top: 1px solid var(--hair);
}

.ctx-items > li:first-child {
  border-top: 0;
}

.ico {
  flex: 0 0 auto;
  width: 27px;
  height: 27px;
  border: var(--bw-1) solid var(--edge);
  border-radius: var(--r);
  display: grid;
  place-items: center;
  font-family: var(--mono);
  font-size: 11px;
  font-weight: 500;
}

.it-b {
  min-width: 0;
}

.it-t {
  font-size: 13.5px;
  font-weight: 600;
  letter-spacing: -0.01em;
}

.it-p {
  font-size: 13px;
  color: var(--ink-2);
  margin-top: 2px;
  line-height: 1.4;
}

.it-q {
  border-left: var(--bw) solid var(--hair);
  padding-left: 9px;
  margin-top: 4px;
}

.it-f {
  font-family: var(--mono);
  font-weight: 500;
  font-size: 11px;
  letter-spacing: 0.07em;
  text-transform: uppercase;
  color: var(--ink-3);
  margin-top: 6px;
}

/* ---------- scenario: animation (pure CSS) ----------
   The invariant this must preserve: there is exactly ONE context card in the
   markup, only the inputs change; the surfaces hold no copies of the context.
   The active beat is outlined in dark, in the manner of the mascot, rather than
   filled with an orange background. */

.scene {
  display: grid;
  grid-template-columns: minmax(0, 1fr) 70px minmax(0, 1.1fr);
  align-items: center;
  border: var(--bw) solid var(--edge);
  border-radius: var(--r-lg);
  background: var(--card);
  padding: 24px;
  box-shadow: var(--shadow);
}

.lanes {
  display: grid;
  gap: 9px;
  border-right: var(--bw) solid var(--hair);
  padding-right: 16px;
}

.lane {
  position: relative;
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  gap: 4px 8px;
  border: 1px solid var(--hair);
  border-radius: var(--r);
  padding: 9px 12px;
  font-size: 14px;
  color: var(--ink-2);
  animation: laneOn 18s linear infinite both;
}

.lane b {
  font-weight: 600;
  letter-spacing: -0.01em;
}

.lane::after {
  content: "";
  position: absolute;
  right: -17px;
  top: 50%;
  width: 16px;
  height: 1px;
  background: var(--hair);
}

.lane:nth-child(2) {
  animation-delay: 4.5s;
}
.lane:nth-child(3) {
  animation-delay: 9s;
}
.lane:nth-child(4) {
  animation-delay: 13.5s;
}

@keyframes laneOn {
  0%,
  2% {
    border-color: var(--hair);
    color: var(--ink-2);
  }
  5%,
  21% {
    border-color: var(--ink);
    color: var(--ink);
  }
  24%,
  100% {
    border-color: var(--hair);
    color: var(--ink-2);
  }
}

.trunk {
  position: relative;
  height: 2px;
  background: var(--gold);
}

.trunk .pulse {
  position: absolute;
  top: -3px;
  left: 0;
  width: 8px;
  height: 8px;
  margin-left: -4px;
  border-radius: 999px;
  background: var(--accent);
  animation: pulseTravel 4.5s linear infinite both;
}

@keyframes pulseTravel {
  0% {
    left: 0;
    opacity: 0;
  }
  12% {
    opacity: 1;
  }
  85% {
    opacity: 1;
  }
  100% {
    left: 100%;
    opacity: 0;
  }
}

.scene-ctx {
  border: var(--bw) solid var(--edge);
  border-radius: var(--r);
  background: var(--ground);
  padding: 15px 17px 17px;
}

.scene-ctx header {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 8px 10px;
  padding-bottom: 11px;
  border-bottom: 1px solid var(--hair);
}

.scene-ctx h3 {
  font-size: var(--t-dense);
}

.scene-recs {
  list-style: none;
  margin: 11px 0 0;
  padding: 0;
  display: grid;
  gap: 8px;
  font-size: 13.5px;
  color: var(--ink-2);
}

.scene-recs li {
  display: flex;
  gap: 9px;
  align-items: baseline;
  animation: recIn 18s linear infinite both;
}

.scene-recs li::before {
  content: "";
  flex: 0 0 auto;
  width: 6px;
  height: 6px;
  border-radius: 999px;
  background: var(--accent);
}

.scene-recs li:nth-child(2) {
  animation-delay: 4.5s;
}
.scene-recs li:nth-child(3) {
  animation-delay: 9s;
}
.scene-recs li:nth-child(4) {
  animation-delay: 13.5s;
}

@keyframes recIn {
  0%,
  4% {
    opacity: 0;
    transform: translateY(5px);
  }
  10%,
  93% {
    opacity: 1;
    transform: none;
  }
  99%,
  100% {
    opacity: 0;
    transform: translateY(5px);
  }
}

.captions {
  list-style: none;
  counter-reset: cap;
  margin: 20px 0 0;
  padding: 0;
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(195px, 1fr));
  gap: 14px;
}

.captions li {
  counter-increment: cap;
  font-size: 14px;
  color: var(--ink-3);
  padding-top: 9px;
  border-top: var(--bw) solid var(--hair);
  animation: capOn 18s linear infinite both;
}

.captions li::before {
  content: counter(cap, decimal-leading-zero);
  display: block;
  font-family: var(--mono);
  font-weight: 500;
  font-size: 11px;
  letter-spacing: 0.1em;
  margin-bottom: 5px;
}

.captions li:nth-child(2) {
  animation-delay: 4.5s;
}
.captions li:nth-child(3) {
  animation-delay: 9s;
}
.captions li:nth-child(4) {
  animation-delay: 13.5s;
}

@keyframes capOn {
  0%,
  2% {
    border-top-color: var(--hair);
    color: var(--ink-3);
  }
  5%,
  21% {
    border-top-color: var(--ink);
    color: var(--ink);
  }
  24%,
  100% {
    border-top-color: var(--hair);
    color: var(--ink-3);
  }
}

/* ---------- connecting a surface: steps with schematic illustrations ---------- */

.flow {
  display: grid;
  /* Explicit column counts, never auto-fit: with auto-fit a tablet-width
     viewport fits three columns and the fourth step wraps alone under them.
     Four steps read as 4 across, 2×2, or a single column — nothing between. */
  grid-template-columns: repeat(4, minmax(0, 1fr));
  gap: 16px;
  counter-reset: f;
  list-style: none;
  margin: 0;
  padding: 0;
}

@media (max-width: 900px) {
  .flow { grid-template-columns: repeat(2, minmax(0, 1fr)); }
}

@media (max-width: 480px) {
  .flow { grid-template-columns: 1fr; }
  /* One column has no row to align with — let each mockup take its natural
     height instead of padding short ones out to the equal-height floor. */
  .flow .ui { min-height: 0; }
}

.flow > li {
  border: var(--bw) solid var(--edge);
  border-radius: var(--r-lg);
  background: var(--card);
  box-shadow: var(--shadow);
  padding: 16px;
  counter-increment: f;
}

.flow h3 {
  font-size: var(--t-dense);
  margin: 13px 0 5px;
}

.flow h3::before {
  content: counter(f, decimal-leading-zero) " ";
  font-family: var(--mono);
  font-size: var(--t-label);
  font-weight: 500;
  letter-spacing: 0.1em;
  color: var(--accent);
}

.flow p {
  font-size: 14px;
  color: var(--ink-2);
  line-height: 1.45;
}

/* Schematic interface elements — diagrams rather than screenshots: they are
   drawn in markup, so they never go stale and pull in no external image.
   THE TYPE IN HERE IS BELOW THE PAGE'S 11px FLOOR ON PURPOSE. That floor is for
   text a person reads or acts on; this is a picture of an interface, sitting in
   a figure marked aria-hidden, and nothing in it is reachable. Raised to the
   floor, the drawings simply burst their boxes. */
.ui {
  margin: 0;
  border: 1px solid var(--hair);
  border-radius: var(--r);
  background: var(--ground);
  overflow: hidden;
  /* equal height across all four — the step headings line up in a row.
     Must clear the TALLEST mockup (step 2: bar + two lines + button ≈ 99px),
     otherwise the shorter ones stretch to the floor and the tallest still
     overshoots it, which is exactly the jump this rule exists to prevent. */
  min-height: 100px;
  display: flex;
  flex-direction: column;
  justify-content: flex-start;
}

.ui-bar {
  display: flex;
  align-items: center;
  gap: 6px;
  padding: 5px 8px;
  border-bottom: 1px solid var(--hair);
  font-family: var(--mono);
  font-weight: 500;
  font-size: 9px;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--ink-3);
}

.ui-body {
  padding: 9px;
  display: grid;
  gap: 7px;
}

.ui-row {
  display: flex;
  align-items: center;
  gap: 7px;
}

.ui-sq {
  flex: 0 0 auto;
  width: 16px;
  height: 16px;
  border: var(--bw-1) solid var(--edge);
  border-radius: 3px;
  background: var(--accent);
}

.ui-txt {
  flex: 1;
  min-width: 0;
  font-size: 11.5px;
  font-weight: 600;
  letter-spacing: -0.01em;
}

.ui-b {
  flex: 0 0 auto;
  border: var(--bw-1) solid var(--edge);
  border-radius: 3px;
  padding: 3px 8px;
  font-family: var(--mono);
  font-weight: 500;
  font-size: 9px;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  white-space: nowrap;
}

.ui-b.on {
  background: var(--accent);
  border-color: var(--accent);
  color: var(--on-accent);
}

.ui-line {
  height: 5px;
  border-radius: 3px;
  background: var(--hair);
}

.ui-url {
  flex: 1;
  min-width: 0;
  border: 1px solid var(--hair);
  border-radius: 99px;
  background: var(--card);
  padding: 3px 9px;
  font-family: var(--mono);
  font-weight: 500;
  font-size: 9.5px;
  color: var(--ink-2);
  overflow: hidden;
  white-space: nowrap;
  text-overflow: ellipsis;
}

.ui-kbd {
  display: flex;
  gap: 5px;
}

.ui-kbd .ui-b {
  flex: 1;
  text-align: center;
}

.ui-check {
  flex: 0 0 auto;
  width: 16px;
  height: 16px;
  border: var(--bw-1) solid var(--edge);
  border-radius: 3px;
  display: grid;
  place-items: center;
  font-size: 11px;
  line-height: 1;
}

/* ---------- security ---------- */

.claim {
  max-width: 31em;
}

.claim h2 {
  font-size: var(--t-section);
  margin: 9px 0 14px;
}

.claim p {
  color: var(--ink-2);
}

/* ---------- server address ----------
   The one string a person needs in order to connect anything, so it is built as
   a block that can be dropped anywhere and still read as the most important
   thing on the screen: heavy outline, the address in the monospace layer, the
   copy button next to it rather than under it. */

.endpoint {
  border: var(--bw) solid var(--edge);
  border-radius: var(--r-lg);
  background: var(--card);
  box-shadow: var(--shadow);
  padding: 16px 18px;
  margin: 0 0 30px;
  max-width: 44em;
}

.endpoint-l {
  font-family: var(--mono);
  font-weight: 500;
  font-size: 11px;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--ink-3);
  margin: 0 0 9px;
}

.endpoint-row {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 10px;
}

.endpoint-url {
  flex: 1 1 260px;
  min-width: 0;
  font-family: var(--mono);
  font-weight: 500;
  font-size: clamp(14px, 3.4vw, 19px);
  color: var(--ink);
  /* the address is one unbroken token, and at 375px it is wider than the
     column — breaking anywhere beats a horizontal scrollbar on the page */
  overflow-wrap: anywhere;
  user-select: all;
}

.endpoint-row .btn {
  flex: 0 0 auto;
}

.endpoint-n {
  font-size: 14px;
  color: var(--ink-2);
  margin: 11px 0 0;
}

.endpoint-n a {
  font-weight: 600;
}

/* ---------- MCP ---------- */

.tech {
  background: var(--sunk);
}

.tech .endpoint {
  background: var(--card);
}

.scopes {
  display: flex;
  flex-wrap: wrap;
  gap: 7px;
  margin-bottom: 28px;
  font-family: var(--mono);
  font-weight: 500;
  font-size: 12px;
}

.scopes span {
  border: var(--bw-1) solid var(--edge);
  border-radius: 3px;
  padding: 3px 8px;
}

/* ---------- tools grouped by purpose ----------
   Without JS every group is simply printed one after another under its own
   heading; the script turns the same markup into tabs. That is why the tab
   strip is hidden in the source and the group headings are hidden only once
   the container reports the script is running. */

.tabs-list[hidden] {
  display: none;
}

.tabs-list {
  display: flex;
  flex-wrap: wrap;
  gap: 6px;
  margin-bottom: 20px;
}

.tab {
  border: var(--bw) solid var(--edge);
  border-radius: var(--r);
  background: transparent;
  color: var(--ink);
  font-family: var(--mono);
  font-weight: 500;
  font-size: 12px;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  padding: 8px 13px;
  cursor: pointer;
}

.tab:hover {
  background: var(--card);
}

.tab[aria-selected="true"] {
  background: var(--sel);
  border-color: var(--sel);
  color: var(--on-sel);
}

/* The focus ring is the only thing that tells a keyboard user which tab the
   arrow keys are on, so it is drawn outside the solid selected fill. */
.tab:focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: 2px;
}

.tab-lead {
  font-size: 15px;
  color: var(--ink-2);
  margin: 0 0 14px;
}

[data-tabs="on"] .mgroup > h3 {
  display: none;
}

[data-tabs="on"] .mgroup {
  margin-bottom: 0;
}

.mgroup {
  margin-bottom: 26px;
}

.mgroup > h3 {
  font-family: var(--mono);
  font-size: var(--t-label);
  font-weight: 500;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--ink-3);
  margin-bottom: 10px;
}

.methods {
  display: grid;
  grid-template-columns: minmax(180px, auto) minmax(0, 1fr);
  gap: 0 20px;
  font-size: 14px;
  border-top: var(--bw) solid var(--edge);
}

.methods dt {
  font-family: var(--mono);
  font-weight: 500;
  font-size: 13px;
  padding: 9px 0 11px;
  border-top: 1px solid var(--hair);
}

.methods dd {
  margin: 0;
  color: var(--ink-2);
  padding: 9px 0 11px;
  border-top: 1px solid var(--hair);
}

.methods dt:first-of-type,
.methods dd:first-of-type {
  border-top: 0;
}

.rule {
  max-width: 44em;
  font-size: 15px;
  color: var(--ink-2);
}

.rule b {
  color: var(--ink);
  font-weight: 600;
}

/* Lead-in line above a .flow list: the list itself has no outer margin, so the
   gap has to come from here — including when the line wraps on narrow screens. */

/* ---------- ways of connecting ----------
   Two labelled parts that must read as ONE table: the outer rules belong to the
   wrapper, so the parts are divided by a hairline rather than by a second heavy
   border. Everything here is scoped to .ways — the .methods list in the
   developer section keeps its own look untouched. */

.ways {
  border-top: var(--bw) solid var(--edge);
  border-bottom: var(--bw) solid var(--edge);
}

.ways-part {
  margin-bottom: 0;
}

.ways-part > h3 {
  margin: 0;
  padding: 13px 0 3px;
}

.ways-part + .ways-part > h3 {
  border-top: 1px solid var(--hair);
}

.ways .methods {
  border-top: 0;
}

/* A cell that carries an action. The cell is a stack of blocks; the control is
   never one of them on its own. It is paired with the thing it acts on inside
   .way-run — the sentence for a link that installs, the command for a button
   that copies it — and the pair is what the row is built from. */
.way-act {
  display: flex;
  flex-direction: column;
  gap: 12px;
}

.way-act > p {
  margin: 0;
}

/* Subject and its control, side by side with one gap between them. The subject
   is sized to its own text rather than to the column, so the control follows
   the words instead of standing off at the far edge of a half-empty row. When
   the column is too narrow to hold the pair, the control takes the next line —
   still directly under the subject it belongs to. */
.way-run {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  gap: 8px 12px;
}

.way-run > p,
.way-run > code {
  flex: 0 1 auto;
  min-width: 0;
  margin: 0;
}

/* The command is one long unbroken run of characters: it may break anywhere
   rather than push the row wider than the column. */
.way-run > code {
  overflow-wrap: anywhere;
}

.way-run > .btn {
  flex: 0 0 auto;
}

/* A BUTTON IN A DENSE LIST CASTS A SHORT SHADOW. The site's hover shadow falls fourteen pixels
   with a twenty-eight pixel spread — right for a control with air around it, wrong inside a table
   whose rows are separated by a hairline: the shadow reaches the next rule and the two merge into
   a smudge under the button. Same reasoning as the workspace cards, one scale down. */
.way-run > .btn:hover {
  box-shadow: var(--glass-rim), 0 1px 2px rgb(var(--cast) / 0.1),
    0 6px 12px -8px rgb(var(--cast) / 0.28);
}

/* ---------- good to know + /help ---------- */

.bullets {
  list-style: none;
  margin: 0;
  padding: 0;
  display: grid;
  gap: 13px;
  max-width: 50em;
}

.bullets > li {
  padding-left: 19px;
  position: relative;
  color: var(--ink-2);
  font-size: 16px;
}

.bullets > li::before {
  content: "";
  position: absolute;
  left: 0;
  top: 9px;
  width: 7px;
  height: 7px;
  border-radius: 999px;
  background: var(--accent);
}

.bullets b {
  color: var(--ink);
  font-weight: 600;
}

.qa {
  display: grid;
  gap: 22px;
  max-width: 44em;
}

.qa h3 {
  font-size: var(--t-sub);
  margin-bottom: 7px;
}

.qa p {
  color: var(--ink-2);
}

.page-head {
  padding: 52px 0 10px;
}

.page-head h1 {
  font-size: var(--t-title);
  margin: 11px 0 16px;
}

/* The title of an application screen. It was written inline on the element, in two files, as the
   same literal — the least visible place a number can hide: the guard reads stylesheets and the
   pages' own <style> blocks, and an attribute on the tag is neither. Same look, one declaration. */
.app-title {
  font-family: var(--display);
  font-size: var(--t-app);
  margin: 0 0 6px;
  text-transform: uppercase;
  letter-spacing: 0.02em;
}

/* ---------- long-form documents (terms, privacy, subprocessors) ----------
   THE WHOLE LAYOUT OF THESE PAGES LIVED INSIDE THE PAGES: five inline <style> blocks, four of them
   byte-identical. Only the headings had been rescued earlier; the rest stayed because the fifth
   block differed and «what is deliberate here» was not yet known.
   IT IS KNOWN NOW, AND THE ANSWER WAS A NAME COLLISION. `.legal-meta` was doing two different jobs
   under one name: on the contracts it is the boxed panel carrying the effective date and version;
   on the subprocessors page it is a single quiet line saying when the list was last updated. Two
   intentions behind one class is exactly why the blocks «could not» be merged — merging them would
   have put a box around a sentence. Named apart, everything else merges without argument.
   The one number reconciled by hand is the table cell: 9×12 on one page against 10×14 on the
   other, which was drift and not a decision. Both pages carry exactly one table; the roomier value
   wins, and the privacy table gains a pixel or two of air. */
.legal { max-width: 780px; }
.legal-lang {
  font-family: var(--mono);
  font-size: 12px;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  margin: 0 0 22px;
}
/* The boxed panel: what is in force, from when, in which version. */
.legal-meta {
  font-family: var(--mono);
  font-size: 13px;
  line-height: 1.7;
  border: 2px solid var(--ink);
  border-radius: 8px;
  background: var(--card);
  padding: 14px 18px;
  margin: 0 0 28px;
}
/* The quiet line: when a list was last touched. Not a panel — a note. */
.legal-updated {
  font-family: var(--mono);
  font-size: 13px;
  line-height: 1.6;
  color: var(--ink-3);
  margin: 0 0 24px;
}
.legal h2 {
  font-family: var(--display);
  font-size: var(--t-doc);
  text-transform: uppercase;
  letter-spacing: 0.02em;
  margin: 40px 0 12px;
}
.legal h3 {
  font-size: var(--t-dense);
  margin: 26px 0 8px;
}
.legal p,
.legal li { color: var(--ink-2); line-height: 1.6; }
.legal ul { padding-left: 22px; margin: 10px 0 16px; }
.legal li { margin: 5px 0; }
.legal b,
.legal strong { color: var(--ink); }
.legal .tblwrap { overflow-x: auto; margin: 14px 0 18px; }
.legal table { border-collapse: collapse; width: 100%; min-width: 560px; font-size: 14px; }
.legal th,
.legal td {
  border: 1px solid var(--ink);
  padding: 10px 14px;
  text-align: left;
  vertical-align: top;
  color: var(--ink-2);
  line-height: 1.5;
}
.legal th {
  font-family: var(--mono);
  font-size: var(--t-label);
  letter-spacing: 0.07em;
  text-transform: uppercase;
  color: var(--ink);
  background: var(--card);
}

/* ---------- final ---------- */

.final {
  text-align: center;
}

.final h2 {
  font-size: var(--t-title);
  /* a condensed face fits more characters into the same width, so the em
     measure is trimmed to keep the line breaking in the same place */
  max-width: 13.5em;
  margin: 9px auto 12px;
}

.final p {
  color: var(--ink-2);
  max-width: 32em;
  margin: 0 auto 26px;
}

/* ---------- footer ---------- */

.site-footer {
  border-top: var(--bw) solid var(--edge);
  padding: 24px 0 42px;
  font-family: var(--mono);
  font-weight: 500;
  font-size: 11px;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--ink-3);
}

.site-footer .wrap {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 10px 18px;
}

/* The bot handle is a name to be copied, and the footer's uppercase turns it into a label. */
.site-footer code {
  text-transform: none;
  letter-spacing: normal;
  font-size: 12px;
}

/* The fallback line is a sentence under the button, not a caption glued to it. */
.final .trust {
  margin-top: 18px;
}

/* Smaller down here, and the word follows the height on its own — see .brand. */
.site-footer .brand {
  --brand-h: 24px;
  margin-right: auto;
}

/* The one SENTENCE down here reads as a sentence. Everything else in the footer
   is a label, and labels keep the caps of the label layer. */
.site-footer > .wrap > span:not(.brand) {
  text-transform: none;
  letter-spacing: 0.01em;
  font-size: 12px;
}

.site-footer a {
  color: var(--ink-2);
}

/* ---------- adaptive ---------- */

@media (max-width: 900px) {
  .hero {
    padding: 48px 0 44px;
  }

  .hero .wrap {
    grid-template-columns: minmax(0, 1fr);
    gap: 28px;
  }

  .hero-art {
    /* On mobile the slider sits BELOW the headline block, not above it. */
    order: 0;
  }

  .hero-art .deck {
    margin-left: auto;
    margin-right: auto;
  }

  .hero-art img {
    max-width: 420px;
    margin-left: 0;
    margin-right: auto;
  }
}

@media (max-width: 760px) {
  /* The scene folds into a column: inputs on top, one thread running down, the
     context below it. The meaning holds — many inputs, still one context. */
  .scene {
    grid-template-columns: minmax(0, 1fr);
    padding: 16px;
  }

  .lanes {
    border-right: 0;
    border-bottom: var(--bw) solid var(--hair);
    padding-right: 0;
    padding-bottom: 14px;
  }

  .lane::after {
    display: none;
  }

  .trunk {
    width: 2px;
    height: 28px;
    margin: 0 auto;
  }

  .trunk .pulse {
    top: 0;
    left: 50%;
    margin-left: -3px;
    animation-name: pulseDown;
  }

  @keyframes pulseDown {
    0% {
      top: 0;
      opacity: 0;
    }
    12% {
      opacity: 1;
    }
    85% {
      opacity: 1;
    }
    100% {
      top: 100%;
      opacity: 0;
    }
  }

  .scene-ctx {
    margin-top: 14px;
  }

  .methods {
    grid-template-columns: minmax(0, 1fr);
    gap: 0;
  }

  .methods dt {
    padding-bottom: 1px;
  }

  .methods dd {
    border-top: 0;
    padding-top: 1px;
  }

  .methods dt:first-of-type {
    border-top: 0;
  }
}

@media (max-width: 700px) {
  body {
    font-size: 16px;
  }

  .wrap {
    padding: 0 18px;
  }

  section > .wrap {
    padding-top: 46px;
    padding-bottom: 46px;
  }

  .surfaces {
    grid-template-columns: repeat(2, minmax(0, 1fr));
  }

  .threads {
    display: none;
  }

  .threads-narrow {
    display: block;
  }

  .ctxs {
    grid-template-columns: minmax(0, 1fr);
    margin-top: 30px;
  }

  /* One card is enough on a phone: two stacked example contexts is a long scroll
     past the same idea. The shared one stays — it is the one the carousel tells
     the story about (owner 28.08). */
  .ctxs .ctx-secondary {
    display: none;
  }

  .nav {
    gap: 8px 14px;
    font-size: 11px;
  }
}

/* Respecting prefers-reduced-motion: no animation, the sequence is shown
   statically — every input, every record and every caption at once. */
@media (prefers-reduced-motion: reduce) {
  html {
    scroll-behavior: auto;
  }

  .lane,
  .scene-recs li,
  .captions li,
  .trunk .pulse,
  .rail .tick {
    animation: none !important;
    transition: none !important;
  }

  .trunk .pulse {
    display: none;
  }

  .captions li {
    color: var(--ink-2);
  }
}

/* ---------- who it's for ---------- */

.aud-grid {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: 18px;
}
.aud-card {
  border: var(--bw) solid var(--edge);
  border-radius: 18px;
  background: var(--card);
  box-shadow: var(--shadow);
  padding: 14px;
}
.aud-card p { font-size: 14px; }
/* THE ROUNDING IS THE LAYOUT'S JOB.
   The illustrations arrived as rounded cards drawn onto square canvases: a light stroke, a dark
   line and a ~30px curve, all baked into the file, with the corners outside the curve filled
   dark. Anything baked can only be right at one size — the image scales with the column, and a
   radius frozen into pixels stopped matching the moment the art changed, showing the dark fill
   as a square notch. So the frame and the curve were cut out of the files (34px a side), and
   the corner is rounded here, where it can simply follow the card. */
.aud-card img {
  width: 100%;
  height: auto;
  border-radius: 12px;
  display: block;
}
.aud-card h3 {
  font-family: var(--display);
  font-size: var(--t-sub);
  text-transform: uppercase;
  letter-spacing: 0.02em;
  margin: 14px 0 6px;
}
.aud-card p {
  color: var(--ink-2);
  margin: 0;
  line-height: 1.5;
}
@media (max-width: 860px) {
  .aud-grid { grid-template-columns: 1fr; }
}

/* ---------- hero flip card ---------- */

.flip { perspective: 1200px; }
.flip-inner {
  position: relative;
  transition: transform 0.6s;
  transform-style: preserve-3d;
}
.flip.is-open .flip-inner { transform: rotateY(180deg); }
.flip-front,
.flip-back {
  -webkit-backface-visibility: hidden;
  backface-visibility: hidden;
  border-radius: 18px;
}
/* THE FRONT IS SWITCHED OFF BY HAND, not left to `backface-visibility` alone: mobile Safari does not apply an ancestor's backface rule to a child that carries its
   own transform, and START — which is animated — kept showing, mirrored, over the turned card.
   The badge goes on the tap that starts the turn, and the whole front at its halfway point. */
.flip-front { transition: opacity 0s linear 0.3s; }
.flip.is-open .flip-front { opacity: 0; }
.flip-front {
  display: block;
  width: 100%;
  border: 0;
  padding: 0;
  background: none;
  cursor: pointer;
  position: relative;
}
/* Same story as .aud-card img: the baked frame and curve were cut out of the file, so the
   corner is rounded here and matches the panel it sits in. */
.flip-front img { display: block; width: 100%; height: auto; border-radius: 18px; }
/* START floats over the picture: translucent, pulsing gently, with a soft shadow. */
.flip-start {
  position: absolute;
  left: 50%;
  top: 50%;
  transform: translate(-50%, -50%);
  font-family: var(--display);
  font-size: 40px;
  letter-spacing: 0.14em;
  /* White and its own dark scrim, not palette tokens: this word sits ON the
     illustration, and it has to hold there whatever the page around it is. */
  color: #fff;
  background: rgba(43, 26, 13, 0.45);
  border: 2px solid rgba(255, 255, 255, 0.85);
  border-radius: 10px;
  padding: 10px 30px;
  box-shadow: 0 6px 24px rgba(43, 26, 13, 0.35);
  animation: flip-pulse 2.2s ease-in-out infinite;
  pointer-events: none;
  -webkit-backface-visibility: hidden;
  backface-visibility: hidden;
  transition: opacity 0.15s linear;
}
.flip.is-open .flip-start { opacity: 0; animation: none; }
@keyframes flip-pulse {
  0%, 100% { transform: translate(-50%, -50%) scale(1); }
  50% { transform: translate(-50%, -50%) scale(1.045); }
}
/* The back keeps the front's colour — we saw for the same background. */
.flip-back {
  position: absolute;
  inset: 0;
  transform: rotateY(180deg);
  background: var(--card);
  border: var(--bw) solid var(--edge);
  color: var(--ink);
  cursor: pointer;
  padding: 20px;
  display: flex;
  flex-direction: column;
  /* `safe`, not plain `center`: plain centring also centres content that overflows, and the top
     of a long list of steps then sits above the panel with no way to scroll to it. */
  justify-content: safe center;
  gap: 8px;
  overflow: auto;
}
.flip-choice { display: flex; flex-direction: column; gap: 10px; }
/* An answer is a row: drawing left in a fixed box, caption right. Stacked, three answers were
   taller than the card and only fitted at a caption size too small to read. */
.flip-choice .btn {
  position: relative;
  /* CLIP-PATH, NOT OVERFLOW, because this button now grows a little when it is pressed or hovered.
     `overflow: hidden` clips in the element's own untransformed box: the moment the button scales,
     the clip stays behind — the corners square off and the growth is sliced against an edge that
     is no longer where the button is. A clip-path is part of the element and scales with it. */
  clip-path: inset(0 round 999px);
  background: var(--ground);
  border-color: var(--edge);
  color: var(--ink);
  width: 100%;
  display: flex;
  flex-direction: row;
  align-items: center;
  gap: 12px;
  padding: 10px 14px;
  font-size: 16px;
  text-align: left;
  white-space: normal;
  line-height: 1.28;
  transition: transform var(--spring-out) var(--spring), background-color 180ms ease;
}
/* Two lines per answer, and the ACCENT IS THE OPEN QUESTION, not the part already done: what is
   connected is stated quietly above, what is still missing is what the visitor is picking. */
.flip-choice .ans {
  position: relative;
  z-index: 1;
  flex: 1;
  padding-right: 30%;
  display: flex;
  flex-direction: column;
  gap: 2px;
  min-width: 0;
}
.flip-choice .ans-state {
  font-family: var(--mono);
  font-weight: 500;
  font-size: 11px;
  letter-spacing: 0.02em;
  line-height: 1.2;
  color: var(--ink-3);
}
.flip-choice .ans-open {
  font-size: 17px;
  font-weight: 600;
  line-height: 1.2;
}
/* The tick belongs to the quiet line and is quiet with it. */
.flip-choice .ans-tick { color: var(--ink-3); font-weight: 700; }
/* An answer has to look answerable. */
/* One motion for every control: the answers settle like the buttons everywhere else
   rather than nudging upwards on their own curve. */
.flip-choice .btn:hover,
.flip-choice .btn:focus-visible {
  background: var(--sunk);
  transform: scale(1.02);
}
.flip-choice .btn:active {
  transform: scale(0.985);
  transition-duration: var(--spring-in);
}
@media (prefers-reduced-motion: reduce) {
  .flip-choice .btn { transition: none; }
  .flip-choice .btn:hover { transform: none; }
}

/* The head belongs to the CARD, not to the line beside it: it is pinned to the card's own corner
   and never shares a container with the text, so nothing it does moves a word. The line keeps its
   own space clear of it. */
.flip-back .dlg-cat {
  position: absolute;
  left: 14px;
  top: 14px;
  width: 96px;
  height: 96px;
  object-fit: contain;
  pointer-events: none;
}
.dlg-say {
  font-family: var(--display);
  font-weight: 800;
  font-size: 40px;
  line-height: 1.02;
  padding-left: 100px;
  min-height: 96px;
  display: flex;
  align-items: center;
  flex-shrink: 0;
}
/* Opening screen: the whole block sits in the middle of the card. Over a list of steps the column
   starts at the top instead, because there the list needs the height. */
.flip-back.replying { justify-content: flex-start; }
/* The head is centred on the same middle as the line, minus half the row the prompt to answer
   occupies below it — that row is what the line's own centre is offset by. */
.flip-back:not(.replying) .flip-steps:empty { display: none; }
.flip-back:not(.replying) .dlg-cat { top: calc(50% - 12px); transform: translateY(-50%); }
/* The prompt keeps its space before it appears, so finishing the typing does not jog the line. */
.flip-back:not(.noface):not(.replying) .dlg-hint.hidden { display: block; visibility: hidden; }
/* While the options are up the head steps out and the question takes the full width. */
.flip-back.noface .dlg-cat { display: none; }
.flip-back.noface .dlg-say { padding-left: 0; min-height: 0; font-size: 26px; }
/* The prompt to answer belongs under the line it is prompting about, not at the card's edge. */
.dlg-hint { padding-left: 100px; }
.flip-back.noface .dlg-hint { padding-left: 0; }
/* Over a list of steps the reply gives the list its room back. */
.flip-back.replying .dlg-say { font-size: 24px; }


.dlg-say.typing::after {
  content: "▌";
  margin-left: 2px;
  animation: flip-blink 0.8s steps(1) infinite;
}
.dlg-hint {
  font-family: var(--mono);
  font-size: 11px;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--ink-3);
}
/* currentColor throughout, so the drawing follows the button's own ink — including hover and
   any future dark treatment — instead of pinning a hex that would then drift from the palette. */
/* The drawing is part of the button's ground, along the right edge. Each file is cropped to its
   own content, so right-aligning them lines every one up on the same edge and they grow leftwards
   with what they carry: one figure, a spool and a plane, a robot as well. */
.flip-choice .bico {
  position: absolute;
  /* CLEAR OF THE CURVE. At 10px the drawing sat under the pill's own rounding and its right edge
     was shaved by it — visible on the first answer, where the figure reaches furthest right. The
     radius here is half the height, so the last ~18px of the box are curve, not straight edge. */
  right: 20px;
  top: 50%;
  transform: translateY(-50%);
  height: 62%;
  width: auto;
  max-width: 46%;
  opacity: 0.85;
  object-fit: contain;
  object-position: right center;
  pointer-events: none;
}
/* The steps type themselves in, top to bottom, in a vintage mono voice with a block cursor. */
/* While the choice is on screen the steps area is EMPTY and must not grow: `flex: 1` made it
   swallow every spare pixel, which is why the buttons sat pinned to the top and the auto margins
   that centre them had nothing left to distribute. It goes back to growing the moment it has
   something to show. */
.flip-steps:empty { flex: 0 0 auto; }
.flip-steps {
  /* The list sits in the middle of what is left between the head above and Back below, rather
     than starting right under the head and leaving a hole at the bottom. */
  display: flex;
  flex-direction: column;
  justify-content: center;
  font-family: var(--mono);
  font-size: 13px;
  line-height: 1.65;
  flex: 1;
  min-height: 0;
}
/* THE HERO CARD HAS A FIXED HEIGHT — the picture on its front sets it — and a branch can be
   taller than that. Centred, the surplus spilled BOTH ways at once: the first step climbed over
   the heading and the last one sat on top of the Back button. `safe` centres only while there is
   room and falls back to the top when there is not, and the list then scrolls in the space it
   has. Scoped to the hero: the product's own card grows with its contents and needs neither. */
.flip-back .flip-steps {
  justify-content: safe center;
  overflow-y: auto;
  overscroll-behavior-y: contain;
}
/* The gap AFTER the last step is space between it and nothing: it cost the card a scrollbar it
   did not need. Back keeps its own distance from the list. */
.flip-back .flip-steps li:last-child { margin-bottom: 0; }
/* Numbered steps wrap NATURALLY with a hanging indent — no manual line breaks (review p.1-2).
   THE NUMBERS START WHERE EVERYTHING ELSE STARTS. Reserving the marker's room with padding on the
   list pushed the whole list in by 2.2em, so a card ended up with three left edges: the Back
   button at the card's own edge, the steps a couple of characters in, and the heading further
   still because it keeps room for the cat. Three edges inside something the width of a phone read
   as a mistake, and the owner reported it as one.
   The indent is carried by the ITEM instead, and taken back off its first line: the marker lands on
   the card's edge, the words hang at 2.2em, and a wrapped line lands under the words rather than
   under the number. Same hanging indent, one edge fewer. */
.flip-steps ol {
  margin: 0;
  padding: 0;
}
.flip-steps li {
  /* Room to spare for the marker: at 1.6em it hit the edge of the card and was clipped. */
  padding-left: 2.2em;
  text-indent: -2.2em;
  /* The marker has to sit INSIDE the first line box for the negative indent to move it at all —
     left outside, it is positioned against the padding edge and the indent slides only the text,
     which puts the number and its own sentence on two different edges. */
  list-style-position: inside;
}
/* A block inside a step — the address row is one — is not part of that first line and must not
   inherit its pull-back, or it starts 2.2em to the LEFT of the step it belongs to. */
.flip-steps li > * { text-indent: 0; }
/* THE MCP FLOW — picking an assistant and its steps. The head goes entirely here:
   this branch carries three answers, or three steps with an address inside one of them, and even
   at half size the drawing was pushing the last step into the Back button. Everywhere else it
   stays: there the card has room, and the head is what makes it a conversation. */
.flip-back.mcp .dlg-cat { display: none; }
.flip-back.mcp .dlg-say { padding-left: 0; min-height: 0; font-size: 22px; }
.flip-back.mcp .dlg-hint { padding-left: 0; }
/* The longest branch there is, in the shortest card there is: a notch of type buys back the line
   the desktop card was short of, and the phone keeps the scroll above as its safety net. */
.flip-back.mcp .flip-steps { font-size: 12px; line-height: 1.55; }
/* The client choice inside the hero card: the same three answers the product's card offers. */
/* THE SAME ANSWER, THE SAME SHAPE. This step and the one before it both ask a question and offer
   a column of answers, so they are built alike: same gap, same padding, same clip, same motion.
   They were not: these were narrower, tighter and unclipped, so under the pointer they grew past
   the card's own padding and slid under its edge — «укатываются под отступы» in the report. */
.flip-clients { display: flex; flex-direction: column; gap: 10px; }
.flip-clients .btn {
  position: relative;
  /* Clipped by shape, not by box, so the corners survive being scaled — see .flip-choice .btn. */
  clip-path: inset(0 round 999px);
  width: 100%;
  text-align: left;
  background: var(--ground);
  border-color: var(--edge);
  color: var(--ink);
  padding: 11px 18px;
  font-size: 15px;
}
@media (hover: hover) {
  .flip-clients .btn:hover,
  .flip-clients .btn:focus-visible {
    background: var(--sunk);
    transform: scale(1.02);
  }
}
.flip-clients .btn:active {
  transform: scale(0.985);
  transition-duration: var(--spring-in);
}
/* The address is a thing to copy by hand here — the landing has no clipboard button inside the
   card, and one is not worth the room it would take beside the steps. */
.flip-addr { display: block; margin-top: 2px; word-break: break-all; color: var(--ink); }
.flip-steps li::marker { font-variant-numeric: tabular-nums; }
.flip-steps li { margin: 0 0 10px; }
.flip-steps a {
  color: var(--accent);
  text-decoration: underline;
  text-underline-offset: 3px;
}
.flip-steps .cursor {
  display: inline-block;
  width: 0.6em;
  height: 1em;
  background: var(--ink);
  vertical-align: text-bottom;
  animation: flip-blink 0.8s steps(1) infinite;
}
@keyframes flip-blink { 50% { opacity: 0; } }
.flip-backbtn {
  align-self: flex-start;
  /* Pinned to the bottom, clear of the steps above it. */
  margin-top: auto;
  flex-shrink: 0;
  font-family: var(--sans);
  font-weight: 600;
  border: var(--bw) solid var(--edge);
  border-radius: var(--r);
  background: var(--ground);
  color: var(--ink);
  padding: 8px 14px;
  cursor: pointer;
}
.flip-backbtn.hidden, .flip .hidden { display: none; }

/* ---------- getting started ---------- */

.gs-steps { counter-reset: gs; list-style: none; padding: 0; margin: 0; max-width: 46em; }
.gs-steps li {
  counter-increment: gs;
  position: relative;
  padding: 0 0 18px 54px;
  line-height: 1.55;
}
.gs-steps li::before {
  content: counter(gs);
  position: absolute;
  left: 0;
  top: -2px;
  width: 34px;
  height: 34px;
  border: var(--bw) solid var(--edge);
  border-radius: var(--r-lg);
  display: flex;
  align-items: center;
  justify-content: center;
  font-family: var(--display);
  font-size: 18px;
  background: var(--card);
}
.gs-steps code {
  font-family: var(--mono);
  font-size: 13px;
  background: var(--sunk);
  border: 1px solid var(--hair);
  border-radius: 4px;
  padding: 1px 6px;
  /* A LINE THAT WRAPS KEEPS ITS BOX ON BOTH HALVES. By default the browser draws one box around
     the whole run and cuts it at the line break: the left half loses its right edge and the right
     half loses its left, which reads as a torn chip rather than a wrapped one. */
  -webkit-box-decoration-break: clone;
  box-decoration-break: clone;
}
.gs-tips { margin-top: 26px; }

/* ---------- footer language switch ---------- */

.lang-switch { font-family: var(--mono); font-size: 12px; letter-spacing: 0.06em; }

/* ---------- what is a MARKER and what is a SENTENCE ----------
   The label layer is set in capitals, and capitals are for a marker: a word or
   two the eye takes as a sign rather than reads. The lines below are not that.
   «created in ChatGPT · updated 18 min ago · 5 records» is a sentence about a
   context; «you, in Telegram · shared into this context on purpose» is a
   sentence about a record; «everyone who sees this context» is a sentence about
   who is in it. Set in capitals they were a texture the reader had to decode.
   They keep the layer's size, tracking and colour — only the shouting goes.
   The short markers around them, the nav, the pills, the source chips and the
   section names, stay in capitals, because that is what capitals are for. */
.ctx-meta,
.ctx-aud,
.it-f,
.ctx-src,
.wx-facts,
.wx-headrows > .wx-meta {
  text-transform: none;
  letter-spacing: 0.02em;
  /* Lower case costs a step of size: capitals hold at 11px because the eye
     takes them as a marker, and the same line read as a sentence is simply
     small. This is the one price of the change above, and it is paid. */
  font-size: 12px;
}

/* The source chips inside that line are markers again, and go back to caps. */
.ctx-src .tag {
  text-transform: uppercase;
  letter-spacing: 0.06em;
}

/* ============================================================================
   THE MATERIAL
   ============================================================================
   Everything above is the page's structure, its type and its palette, and all
   of it predates this material: the layout does not know it is made of glass.
   Everything below is what the surfaces are MADE of.

   WHAT GLASS IS, so that this stays a material and not a filter:
     • it is TRANSLUCENT and BLURS what is behind it, so the page must have
       something behind it worth blurring — a pane over a flat ground reads as
       milk. The two colours the brand already owns are laid down as slow
       washes and every panel refracts them;
     • it has a SPECULAR EDGE: a bright rim where light catches the top of the
       sheet and a fainter bounce along the bottom, drawn as inset highlights
       rather than as a border;
     • it CASTS, it does not glow: a shadow with a real offset, so a panel sits
       above the page instead of radiating from it;
     • it is CONCENTRIC: an inner corner is the outer one minus its inset, and
       GLASS IS NEVER LAID ON GLASS — a pane inside a pane is a smudge, so the
       rows of a card stay plain and let the card do the refracting.

   A FILM COSTS CONTRAST, and that is paid for rather than argued with: the
   primary control's tint had to move a step darker to keep white on it above
   4.5:1 once the page shows through.

   The material is OPTIONAL. Where a browser cannot blur, and where a person
   has asked their system for less transparency, every pane below turns into
   the opaque card it was filming and the washes come off.
   ============================================================================ */

body {
  /* The ground colour lives on <html> (it paints the canvas and the top
     overscroll). The body stays colour-transparent so the fixed doodle
     ::before, which sits at z-index -1 BELOW the body's own background box,
     is not hidden by an opaque body fill — it shows over the html ground.
     The glossy --wash still rides the body.

     NOT attachment:fixed. On iOS a fixed background under a sticky header
     repaints in strips: the owner saw a band of stale page content sitting
     above the header in Safari (28.08). The doodle layer is already a fixed
     ::before, so the wash can scroll without anyone noticing. */
  background-color: transparent;
  background-image: var(--wash);
  background-repeat: no-repeat;
}

/* Landing background pattern — the mini-app doodle tile, over the glossy --wash
   already on the body. Scoped to the plain landing body; the workspace carries
   its own on .wx-body::before, so it is excluded here to avoid a double layer. */
body:not(.wx-body)::before {
  content: "";
  position: fixed;
  inset: 0;
  z-index: -1;
  pointer-events: none;
  background-image: url(/assets/wx-doodles.png?v=86);
  background-size: 760px 760px;
  background-repeat: repeat;
  background-position: center;
  opacity: 0.1;
}
@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) body:not(.wx-body)::before {
    opacity: 0.14;
  }
}
:root[data-theme="dark"] body:not(.wx-body)::before {
  opacity: 0.14;
}

/* The workspace paints its own ground with a shorthand, which wipes the washes
   off the element they were set on — so the same field is restated for it. Its
   cards have nothing to refract otherwise. */
.wx-body {
  /* A STACK NEEDS A SHORTER SHADOW THAN A SINGLE CARD. The site's cast falls 12px with a wide
     spread — right for one card lying on a page, wrong for a column of them 18px apart: it lands
     on the top of the card below, and since that card is glass, it shows THROUGH it as a dark
     band across its head. We saw it on every card but the first, which is the tell — the
     first one has nothing above it.
     The token is redefined for this surface rather than the shadow being restated on the card:
     the material stays one thing, and the card goes on asking the design system for it. */
  --glass-cast: 0 1px 2px rgb(var(--cast) / 0.3), 0 4px 12px -10px rgb(var(--cast) / 0.45);
  /* Colour-transparent for the same reason as <body>: <html> paints the ground,
     so the .wx-body::before doodle layer (z-index -1) is not hidden by an opaque
     fill. The --wash stays on the surface. */
  background-color: transparent;
  background-image: var(--wash);
  background-attachment: fixed;
  background-repeat: no-repeat;
}

.wx-head,
.wx-actions,
.wx-panel,
.wx-rows {
  background: transparent;
}

.wx-top {
  border-bottom-color: var(--hair);
}

/* ---------- WORKSPACE SKIN ----------
   A teal palette and a doodle-pattern background, scoped to the workspace
   surface only (.wx-body); the landing keeps the brand palette. The three
   blocks mirror the theme structure at the top of this file: base = light,
   then the two dark guards (system-dark-unforced, and forced dark). The
   pattern rides a fixed ::before behind the content so the glass refracts it. */
.wx-body {
  --ground: #fbfaf4;
  --ink: #091717;
  --ink-2: #46626a;
  --ink-3: #5f7a80;
  --hair: rgba(9, 23, 23, 0.1);
  --accent: #299da9;
  --accent-2: #1f818b;
  --on-accent: #06251f;
  --gold: #299da9;
  --danger: #b8503a;
  --link: #17747e;
  --fill: rgba(16, 114, 123, 0.92);
  --fill-hover: rgba(12, 98, 107, 0.96);
  --sel: rgba(9, 23, 23, 0.9);
  --on-sel: #fbfaf4;
  --sunk: #eef2f0;
  --cast: 16 46 46;
  --glass-edge: rgba(9, 23, 23, 0.16);
  --edge-strong: rgba(9, 23, 23, 0.26);
  --tech-bg: rgba(9, 40, 40, 0.06);
  --art-cast: rgba(9, 40, 40, 0.24);
  --opaque: #f5f7f5;
  --wash:
    radial-gradient(120% 55% at 100% 0%, rgba(41, 157, 169, 0.06), transparent 55%),
    linear-gradient(168deg, rgba(255, 255, 255, 0.5), rgba(255, 255, 255, 0) 22%);
}
.wx-body { -webkit-tap-highlight-color: transparent; }
.wx-body::before {
  content: "";
  position: fixed;
  inset: 0;
  z-index: -1;
  pointer-events: none;
  background-image: url(/assets/wx-doodles.png?v=86);
  background-size: 760px 760px;
  background-repeat: repeat;
  background-position: center;
  opacity: 0.17;
}
@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) .wx-body {
    --ground: #0c1616;
    --ink: #e9f3f1;
    --ink-2: #9fb7b4;
    --ink-3: #7f9996;
    --hair: rgba(233, 243, 241, 0.13);
    --accent: #3fc2cf;
    --accent-2: #63d6e0;
    --on-accent: #052224;
    --gold: #3fc2cf;
    --danger: #e0876a;
    --link: #3fc2cf;
    --fill: rgba(63, 194, 207, 0.92);
    --fill-hover: rgba(99, 214, 224, 0.96);
    --sel: rgba(233, 243, 241, 0.9);
    --on-sel: #052224;
    --sunk: #0a1212;
    --cast: 0 0 0;
    --glass-edge: rgba(233, 243, 241, 0.09);
    --edge-strong: rgba(233, 243, 241, 0.2);
    --tech-bg: rgba(0, 0, 0, 0.28);
    --art-cast: rgba(0, 0, 0, 0.55);
    --opaque: #17231f;
    --wash: radial-gradient(120% 55% at 100% 0%, rgba(63, 194, 207, 0.1), transparent 55%);
  }
  :root:not([data-theme="light"]) .wx-body::before {
    opacity: 0.22;
  }
}
:root[data-theme="dark"] .wx-body {
  --ground: #0c1616;
  --ink: #e9f3f1;
  --ink-2: #9fb7b4;
  --ink-3: #7f9996;
  --hair: rgba(233, 243, 241, 0.13);
  --accent: #3fc2cf;
  --accent-2: #63d6e0;
  --on-accent: #052224;
  --gold: #3fc2cf;
  --danger: #e0876a;
  --link: #3fc2cf;
  --fill: rgba(63, 194, 207, 0.92);
  --fill-hover: rgba(99, 214, 224, 0.96);
  --sel: rgba(233, 243, 241, 0.9);
  --on-sel: #052224;
  --sunk: #0a1212;
  --cast: 0 0 0;
  --glass-edge: rgba(233, 243, 241, 0.09);
  --edge-strong: rgba(233, 243, 241, 0.2);
  --tech-bg: rgba(0, 0, 0, 0.28);
  --art-cast: rgba(0, 0, 0, 0.55);
  --opaque: #17231f;
  --wash: radial-gradient(120% 55% at 100% 0%, rgba(63, 194, 207, 0.1), transparent 55%);
}
:root[data-theme="dark"] .wx-body::before {
  opacity: 0.22;
}

/* Cards a touch less see-through than the site default, so the pattern reads
   as texture behind them rather than through them (owner 26.08). */
.wx-body {
  --card: rgba(255, 255, 255, 0.8);
  --film: rgba(255, 255, 255, 0.74);
  --film-2: rgba(255, 255, 255, 0.7);
  --chip: rgba(255, 255, 255, 0.74);
  --chip-2: rgba(255, 255, 255, 0.78);
}
@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) .wx-body {
    --card: #17231f;
    --film: rgba(210, 240, 238, 0.2);
    --film-2: rgba(210, 240, 238, 0.17);
    --chip: rgba(210, 240, 238, 0.2);
    --chip-2: rgba(210, 240, 238, 0.24);
  }
}
:root[data-theme="dark"] .wx-body {
  --card: #17231f;
  --film: rgba(210, 240, 238, 0.2);
  --film-2: rgba(210, 240, 238, 0.17);
  --chip: rgba(210, 240, 238, 0.2);
  --chip-2: rgba(210, 240, 238, 0.24);
}

.wx-b,
.wx-who,
.wx-tag,
.wx-badge {
  border-radius: 999px;
  border-color: var(--glass-edge);
  background: var(--chip);
  box-shadow: inset 0 0.5px 0 rgba(255, 255, 255, 0.5);
}

.wx-b.wx-primary {
  background: rgba(196, 73, 26, 0.86);
  border-color: rgba(255, 255, 255, 0.22);
}

.wx-b.wx-on {
  background: var(--sel);
  border-color: rgba(255, 255, 255, 0.18);
  color: var(--on-sel);
}

.wx-full,
.wx-rbody pre {
  background: var(--film);
  border-color: var(--glass-edge);
  border-radius: var(--r);
}

a {
  color: var(--link);
}

.bullets > li::before,
.flow h3::before,
.rail a.on {
  color: var(--link);
}

/* ---------- the panes ----------
   One rule makes every surface on the page out of the same material. */

.surface,
.hub,
.ctx,
.scene,
.flow > li,
.endpoint,
.aud-card,
.ui,
.flip-back,
.tab,
.btn,
.pill,
.tag,
.chip,
.ico,
.scopes span,
.gs-steps li::before,
.ui-b,
/* THE WORKSPACE CARDS DO NOT FILTER THEIR BACKDROP, and this is the answer to «why does hovering
   one card put a dark band on the one below it».

   A backdrop filter is not paint, it is a live read of everything behind the element, and the
   browser keeps that read in a compositing layer. A column of cards is therefore a stack of such
   layers, each looking through to the page. The moment anything in that stack changes — a button
   inside one card grows under the pointer, a shadow deepens — the compositor re-does the layers,
   and at the seam between two of them it shows what it has, not what it will have: a band along
   the edge of a neighbour. Nothing in our CSS reaches across cards; the compositor does.

   On the landing page the filter earns its cost, because illustrations pass behind the cards. Here
   nothing does: behind these is the page's own ground with a soft wash. So the material keeps its
   film, its rim and its cast, and gives up the one property that had nothing to look at. */
.wx-ctx,
.wx-skel {
  -webkit-backdrop-filter: none;
  backdrop-filter: none;
}

/* THE SMALL PARTS INSIDE A CARD DO NOT FILTER.
   A backdrop filter is a compositing layer, and the workspace list had one per button, per person
   pill, per label, per opened record — dozens of them, stacked inside a scrolling container, in a
   web view. iOS drew them as pale rectangles where the controls should be: the layers were there,
   their contents were not.
   Nothing is lost by taking it away: these sit ON the card, and the card is already glass. What
   they filtered was its film, not the page. The card and the placeholder keep the material,
   because they are what lies on the page itself. */
.wx-b,
.wx-who,
.wx-tag,
.wx-full {
  -webkit-backdrop-filter: none;
  backdrop-filter: none;
}

.surface,
.hub,
.ctx,
.scene,
.flow > li,
.endpoint,
.aud-card,
.ui,
.flip-back,
.wx-ctx,
.wx-skel {
  background: var(--card);
  border: 1px solid var(--glass-edge);
  border-radius: var(--r-lg);
  box-shadow: var(--glass-rim), var(--glass-cast);
}

/* CONCENTRIC CORNERS. An inner corner is the outer one minus the inset it sits
   at; equal radii inside and out is the tell of a shape that was never thought
   about. The small panes take the small corner. */
.surface,
.ui {
  border-radius: var(--r);
}

.aud-card img,
.flip-front img,
.flip-front {
  border-radius: calc(var(--r-lg) - 6px);
}

/* GLASS IS NEVER LAID ON GLASS. The rows of a card, the lanes of the diagram
   and the mockups' innards stay plain: the pane they sit in is what refracts,
   and a second film over the first is a smudge, not depth. */
.ctx-items,
.ctx-items > li,
.lane,
.scene-ctx,
.ui-bar,
.ui-body,
.ui-line,
.methods,
.methods dt,
.methods dd,
.ways,
.captions li {
  -webkit-backdrop-filter: none;
  backdrop-filter: none;
  box-shadow: none;
}

.ctx-items {
  border-top: 1px solid var(--hair);
}

.ctx-items > li {
  border-top-color: var(--hair);
}

/* Not a pane of its own: it is the far half of the one it already sits in, so
   it is divided from the lanes by a rule and by nothing else. */
.scene-ctx {
  background: transparent;
  border: 0;
  border-radius: 0;
  box-shadow: none;
  padding-left: 22px;
}

.lane {
  border-color: var(--hair);
  border-radius: 10px;
}

/* ---------- the toolbar ----------
   THE ONE PANE THAT IS PAINTED ON THE PAGE RATHER THAN LAID OVER IT (owner 17.08).
   It used to be the thinnest and most transparent surface of all, on the reasoning
   that a bar always on screen should not weigh on it. The reasoning held only while
   the blur held with it: the fill is 30% see-through, and glass without a blur is
   not glass, it is a window. On a narrow screen the header also carries the open
   menu — so the page ran straight through the words of the menu, and the owner read
   his own sections over a picture of a cat.

   The blur was not something we could rely on: it is already off under
   `prefers-reduced-transparency`, off where the browser lacks the filter, and it was
   observed off on a live iPhone with neither of those in play. A surface whose
   legibility depends on an effect that three separate conditions can switch off is
   not a material, it is a bet. So the header is opaque, and the menu inside it needs
   nothing of its own — one fill, one answer to "what colour is this bar".

   The rest of the glass — cards, rail, chips — is untouched: nothing is laid over
   text there. */

.site-header {
  background: var(--head-bg);
  border-bottom: 1px solid var(--glass-edge);
  box-shadow: 0 0.5px 0 rgba(255, 255, 255, 0.42) inset,
    0 6px 20px -12px rgb(var(--cast) / 0.35);
}

/* ---------- controls ----------
   A control in this material is a capsule: the shape says «press me» before the
   colour does, and the pill is the form the whole system uses at every size. */

.btn {
  border-radius: 999px;
  border: 1px solid var(--glass-edge);
  padding: 12px 22px;
  box-shadow: var(--glass-rim), 0 1px 2px rgb(var(--cast) / 0.08),
    0 8px 20px -10px rgb(var(--cast) / 0.3);
  transition: transform 0.18s cubic-bezier(0.2, 0.8, 0.2, 1),
    box-shadow 0.18s cubic-bezier(0.2, 0.8, 0.2, 1),
    background-color 0.18s cubic-bezier(0.2, 0.8, 0.2, 1);
}

/* The accent is still the accent — it is simply lit from behind now instead of
   printed flat, so the colour of the page shows through its own button. */
/* A FILM COSTS CONTRAST, and the primary control cannot pay it. White on the
   bright orange is 4.75:1 while the fill is solid; at 86% over the cream it
   drops to 3.9:1 and fails AA. The film stays — it is the whole point — and the
   tint underneath becomes the palette's darker orange, which holds 5.1:1
   through the same film. */
.btn-primary,
.nav a.btn-primary {
  background: var(--fill);
  border-color: rgba(255, 255, 255, 0.22);
  color: var(--on-accent);
}

.btn-primary:hover,
.nav a.btn-primary:hover {
  background: var(--fill-hover);
  color: var(--on-accent);
}

.btn-ghost {
  background: var(--film);
  color: var(--ink);
}

.btn-ghost:hover {
  background: var(--film-hover);
}

/* Glass under a finger gives: the pane lifts and its cast deepens. One
   authored moment, on the thing a person actually presses. */
.btn:hover {
  transform: translateY(-1px);
  box-shadow: var(--glass-rim), 0 2px 4px rgb(var(--cast) / 0.1),
    0 14px 28px -10px rgb(var(--cast) / 0.34);
}

.btn:active {
  transform: translateY(0) scale(0.985);
  box-shadow: var(--glass-rim), 0 1px 2px rgb(var(--cast) / 0.12);
}

.btn:focus-visible,
.tab:focus-visible,
a:focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: 3px;
}

/* ---------- the small panes ----------
   Every chip is the same material at the smallest size it still reads at. */

/* NO BLUR ON AN INLINE CHIP THAT CAN WRAP. `backdrop-filter` on a box that
   fragments across two line boxes takes the whole line with it: the inline
   command in the steps below washed out the sentence around it. These keep the
   tint and the rim, which is all a chip this small has room to say anyway. */
.pill,
.tag,
.chip,
.ico,
.scopes span,
.ui-b,
.gs-steps code {
  background: var(--chip);
  border-color: var(--glass-edge);
  border-radius: 999px;
  box-shadow: inset 0 0.5px 0 rgba(255, 255, 255, 0.5);
}

.ico,
.gs-steps li::before {
  background: var(--chip-2);
  border-color: var(--glass-edge);
  border-radius: 50%;
  box-shadow: inset 0 0.5px 0 rgba(255, 255, 255, 0.55),
    0 2px 6px -2px rgb(var(--cast) / 0.2);
}

.ui-b.on {
  background: var(--fill);
  border-color: rgba(255, 255, 255, 0.22);
}

/* The avatar keeps a solid fill: it is a token of a person, and a translucent
   letter on a translucent disc is illegible at 19px. */
.av {
  background: var(--ink);
  color: var(--on-sel);
  box-shadow: inset 0 0.5px 0 rgba(255, 255, 255, 0.18);
}

/* ---------- tabs ---------- */

.tab {
  background: var(--film-2);
  border: 1px solid var(--glass-edge);
  border-radius: 999px;
  box-shadow: inset 0 0.5px 0 rgba(255, 255, 255, 0.45);
}

.tab[aria-selected="true"] {
  background: var(--sel);
  border-color: rgba(255, 255, 255, 0.18);
  color: var(--on-sel);
}

/* ---------- the rail ----------
   It floats over the page, so in this material it becomes a pane of its own
   rather than a list of marks lying on the background. */

@media (min-width: 1420px) {
  .rail {
    /* The pane needs the room its padding takes AND the room the magnified item
       grows into, or the labels it exists to show start wrapping two words to
       a line. 206 = the labels' own width, plus 28 of padding, plus the 24 the
       hover reserves. */
    width: 206px;
    left: 18px;
    padding: 14px 14px;
    background: var(--film-2);
    border: 1px solid var(--glass-edge);
    border-radius: 20px;
    box-shadow: var(--glass-rim), var(--glass-cast);
    -webkit-backdrop-filter: var(--glass-blur);
    backdrop-filter: var(--glass-blur);
  }
}

/* ---------- the sunk band ----------
   A recess in glass is not a darker fill, it is a pane with LESS light on its
   rim: the same material, turned away from the window. */

.tech {
  background: var(--tech-bg);
  -webkit-backdrop-filter: var(--glass-blur);
  backdrop-filter: var(--glass-blur);
  box-shadow: inset 0 0.5px 0 rgba(255, 255, 255, 0.34),
    inset 0 -0.5px 0 rgba(255, 255, 255, 0.2);
}

.tech .endpoint {
  background: var(--chip-2);
}

/* ---------- section rules ---------- */

section > .wrap {
  border-top: 1px solid var(--hair);
}

.site-footer {
  border-top: 1px solid var(--hair);
}

.methods,
.ways {
  border-top-color: var(--hair);
  border-bottom-color: var(--hair);
}

.methods,
.ways {
  border-top-width: 1px;
  border-bottom-width: 1px;
}

.captions li {
  border-top-width: 1px;
  border-top-color: var(--hair);
}

.lanes {
  border-right-width: 1px;
  border-right-color: var(--hair);
}

/* ---------- the illustrations ----------
   Untouched, and given the one thing glass gives a photograph: a cast, so the
   plate sits on the page rather than in it. */
.hero-art picture {
  filter: drop-shadow(0 18px 32px var(--art-cast));
}

/* ---------- when the material is unavailable or unwanted ----------
   Two cases, one answer: every pane becomes an opaque card of the same colour
   it was filming. Nothing below depends on blur to stay legible. */

@supports not ((backdrop-filter: blur(1px)) or (-webkit-backdrop-filter: blur(1px))) {
  .site-header {
    background: var(--ground);
  }

  .surface,
  .hub,
  .ctx,
  .scene,
  .flow > li,
  .endpoint,
  .aud-card,
  .ui,
  .flip-back,
  .scene-ctx,
  .btn-ghost,
  .tab,
  .pill,
  .tag,
  .chip,
  .ico,
  .scopes span,
  .ui-b,
  .gs-steps code,
  .gs-steps li::before,
  .wx-ctx,
  .wx-skel {
    background: var(--opaque);
  }

  .btn-primary,
  .nav a.btn-primary {
    background: var(--accent);
  }

  .tech {
    background: var(--sunk);
  }

  .tab[aria-selected="true"] {
    background: var(--ink);
  }
}

@media (prefers-reduced-transparency: reduce) {
  .site-header {
    background: var(--ground);
    -webkit-backdrop-filter: none;
    backdrop-filter: none;
  }

  .surface,
  .hub,
  .ctx,
  .scene,
  .flow > li,
  .endpoint,
  .aud-card,
  .ui,
  .flip-back,
  .scene-ctx,
  .btn-ghost,
  .tab,
  .pill,
  .tag,
  .chip,
  .ico,
  .scopes span,
  .ui-b,
  .gs-steps code,
  .gs-steps li::before,
  .rail,
  .wx-ctx,
  .wx-skel {
    background: var(--opaque);
    -webkit-backdrop-filter: none;
    backdrop-filter: none;
  }

  .btn-primary,
  .nav a.btn-primary {
    background: var(--accent);
    -webkit-backdrop-filter: none;
    backdrop-filter: none;
  }

  .tech {
    background: var(--sunk);
    -webkit-backdrop-filter: none;
    backdrop-filter: none;
  }

  .tab[aria-selected="true"] {
    background: var(--ink);
  }

  body {
    background-image: none;
  }
}

@media (prefers-reduced-motion: reduce) {
  .btn,
  .btn:hover,
  .btn:active {
    transform: none;
    transition: none;
  }
}

/* ---------- spacing the material exposed ----------
   The table of connection paths sits between two heavy rules with its rows
   flush against them. Flat, that was tight; on a pane, where the rule is the
   pane's own edge, it reads as text touching the glass. */
.ways {
  padding: 4px 0 8px;
}

.ways-part > h3 {
  padding-top: 16px;
}

/* This cell is a `dd` and already carries the table's own 9px inset; the rule
   below it needs more than that, not less. Selected as `.methods dd.way-act`
   and not as `.way-act`: the table's own rule is the more specific of the two,
   so the bare class never reached the element at all. */
.methods dd.way-act {
  padding-top: 16px;
  padding-bottom: 16px;
}

/* ---------- what glass does under a pointer ----------
   A pane is a physical thing, so it answers a pointer the way one does: it
   catches a little more light along its rim, its cast deepens, and the ones
   that are pressable rise by a pixel. Nothing changes hue on hover — a surface
   that turns a different colour under the cursor stopped being a material and
   became a state machine. Everything here is one transition curve, the same
   ease-out the buttons already use, so the page moves as one thing. */

.surface,
.hub,
.ctx,
.scene,
.flow > li,
.endpoint,
.aud-card,
.tab,
.pill,
.tag,
.chip,
.ico,
.scopes span,
.wx-ctx,
.wx-b,
.wx-who {
  transition: background-color 0.18s cubic-bezier(0.2, 0.8, 0.2, 1),
    box-shadow 0.18s cubic-bezier(0.2, 0.8, 0.2, 1),
    border-color 0.18s cubic-bezier(0.2, 0.8, 0.2, 1),
    transform 0.18s cubic-bezier(0.2, 0.8, 0.2, 1);
}

/* A card the whole of which is not a control still answers: more light on the
   rim, a deeper cast, and no movement — it is not offering to be pressed. */
.ctx:hover,
.scene:hover,
.flow > li:hover,
.endpoint:hover,
.aud-card:hover,
.hub:hover {
  background: var(--film-hover);
  box-shadow: var(--glass-rim), 0 2px 4px rgba(0, 0, 0, 0.06),
    0 18px 40px -14px rgba(0, 0, 0, 0.28);
}
/* The workspace card is NOT one of the landing cards above: it must not go to
   --film-hover (light in dark → a white card) and must not react on touch, where
   the hover sticks after a tap on the card head / Records. Its own material and
   any pointer hover live in the page's @media (hover: hover) and (pointer: fine). */

/* The ones that ARE a control lift, the way the buttons do. (.wx-b is left out:
   the workspace button's hover lives in the page's own @media (hover: hover) and
   (pointer: fine) block, so it never sticks after a tap on a touch screen.) */
.surface:hover,
.tab:hover {
  background: var(--film-hover);
  transform: translateY(-1px);
  box-shadow: var(--glass-rim), 0 2px 4px rgba(0, 0, 0, 0.08),
    0 12px 24px -10px rgba(0, 0, 0, 0.3);
}

.tab[aria-selected="true"]:hover {
  background: var(--sel);
  color: var(--on-sel);
}

/* The small panes only brighten: a chip that moved would jitter the line of
   text it sits in. */
.pill:hover,
.tag:hover,
.chip:hover,
.ico:hover,
.scopes span:hover {
  background: var(--chip-2);
  border-color: var(--glass-edge);
}

/* A nav item picks up its own small pane rather than only changing colour —
   the same capsule every other control on the page is made of. */
.nav a:not(.btn) {
  padding: 6px 10px;
  border-radius: 999px;
  transition: background-color 0.18s cubic-bezier(0.2, 0.8, 0.2, 1),
    color 0.18s cubic-bezier(0.2, 0.8, 0.2, 1);
}

.nav a:not(.btn):hover {
  background: var(--film-2);
  color: var(--ink);
}

/* A row inside a card is not a pane, so it warms rather than lifts. */
.ctx-items > li:hover,
.methods dt:hover,
.methods dd:hover {
  background: var(--film-2);
}

/* A record row and its description are one target: hovering either warms both,
   or the pair flickers as the pointer crosses the gap between them. */
.methods dt:hover + dd,
.lane:hover {
  background: var(--film-2);
}

/* And the rail: its marks are links, so they take the same capsule. */
.rail a {
  padding-left: 6px;
  padding-right: 6px;
  border-radius: 999px;
  transition: background-color 0.18s cubic-bezier(0.2, 0.8, 0.2, 1),
    color 0.18s cubic-bezier(0.2, 0.8, 0.2, 1);
}

.rail a:hover {
  background: var(--film-2);
}

@media (prefers-reduced-motion: reduce) {
  .surface,
  .hub,
  .ctx,
  .scene,
  .flow > li,
  .endpoint,
  .aud-card,
  .tab,
  .pill,
  .tag,
  .chip,
  .ico,
  .scopes span,
  .nav a,
  .rail a,
  .wx-ctx,
  .wx-b,
  .wx-who {
    transition: none;
  }

  .surface:hover,
  .tab:hover,
  .wx-b:hover {
    transform: none;
  }
}

/* The wordmark needs no rule for the dark room: it is text, and text is already
   painted with the ink of whichever palette is in force. */

/* ---------- the theme control ----------
   A pane the size of its mark, and the mark is the whole of it: the state is
   said in the accessible name, not printed beside the icon, because a row of
   navigation has no room for a word that changes length in three ways. */
.theme-b {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 34px;
  height: 34px;
  padding: 0;
  flex: 0 0 auto;
  color: var(--ink-2);
}

.theme-b:hover {
  color: var(--ink);
}

/* The one state worth marking visually: a choice that overrides the system. */
.theme-b[data-choice="light"],
.theme-b[data-choice="dark"] {
  color: var(--accent);
  border-color: var(--accent);
}

.theme-b svg {
  display: block;
}

/* A disc is a token, not a hole: on the dark ground it carries a rim of its own
   and the letter inside it sits at full ink, so «where this record came from»
   reads at a glance instead of being guessed at. */
@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) .ico,
  :root:not([data-theme="light"]) .gs-steps li::before {
    border-color: rgba(242, 232, 218, 0.26);
    color: var(--ink);
  }
}

:root[data-theme="dark"] .ico,
:root[data-theme="dark"] .gs-steps li::before {
  border-color: rgba(242, 232, 218, 0.26);
  color: var(--ink);
}

/* ---------- the rail magnifies ----------
   The marks down the left edge are set at ten pixels, which is a size you read
   by recognising a shape rather than by reading a word. Under the pointer the
   one being pointed at grows to a size you actually read, from its own left
   edge so the column does not shift, and its tick runs out to full length. The
   section the page is on is already a step up, so hovering it changes less —
   which is correct: it is the one you did not need help finding. */
.rail a {
  transform-origin: left center;
  /* THE ROOM TO GROW IS RESERVED, not borrowed. The item is as wide as its
     column, so scaling it would have pushed the capsule out through the side of
     the pane; it keeps a margin the size of its own growth instead, and the
     magnified item lands exactly inside the glass. */
  margin-right: 24px;
  transition: background-color 0.18s cubic-bezier(0.2, 0.8, 0.2, 1),
    color 0.18s cubic-bezier(0.2, 0.8, 0.2, 1),
    transform 0.22s cubic-bezier(0.2, 0.8, 0.2, 1);
}

.rail a:hover {
  transform: scale(1.16);
  color: var(--ink);
}

.rail a.on {
  transform: scale(1.08);
}

.rail a.on:hover {
  transform: scale(1.16);
}

/* The tick is drawn at full length and scaled from its left end, so its growth
   is the same kind of movement as the label's and costs no layout. */
.rail a:hover .tick {
  transform: scaleX(1);
}

@media (prefers-reduced-motion: reduce) {
  .rail a,
  .rail a:hover,
  .rail a.on,
  .rail a.on:hover {
    transform: none;
    transition: none;
  }
}

/* ---------- a control on a light ground ----------
   Its own film is 1.08:1 against the cream — the fill can never be what
   separates it, because both are light. The boundary and the cast are, so the
   controls take the stronger ink edge and sit on a shadow that is theirs rather
   than the page's. */
.btn-ghost,
.tab,
.theme-b,
.wx-b {
  border-color: var(--edge-strong);
}

.btn-ghost:hover,
.tab:hover,
.theme-b:hover {
  border-color: var(--edge-strong);
}

/* THE WORKSPACE'S CONTROLS HAD NO CAST AT ALL. On the site a button is
   separated from the page as much by the shadow under it as by its edge; in the
   workspace the same shape was drawn with an edge and nothing else, so on a
   phone — light chip, light card, small — Records, the gear and Read merged
   into the card they sit on. They get the button's own cast. */
.wx-b {
  box-shadow: var(--glass-rim), 0 1px 2px rgb(var(--cast) / 0.08),
    0 6px 14px -8px rgb(var(--cast) / 0.28);
}

/* The workspace button's hover (shadow, tone and lift) is defined once, in the
   page's own @media (hover: hover) and (pointer: fine) block, so it never sticks
   after a tap. No loose :hover for it here. */

/* The chosen filter is a filled chip and needs neither. */
.wx-b.wx-on,
.wx-b.wx-primary {
  box-shadow: 0 1px 2px rgb(var(--cast) / 0.18);
}

/* ---------- the header on a narrow screen ----------
   Past this width the header can no longer hold the sections, the two actions
   and the theme control on one line, and it wraps; at phone size that is three
   lines of header standing in front of the page. Everything but the primary
   action goes behind the button, and the script keeps those items out of the
   document while the panel is closed — what is left here is the button, the
   panel's shape, and the two rules that let `hidden` beat the display these
   classes bring with them. */

/* The open row is three things now instead of seven, and they have to sit on
   ONE line — a header that wraps is the whole of what the button is for. The
   gap between them is the only slack there is at 375, so it comes in. */
@media (max-width: 900px) {
  .site-header .wrap {
    column-gap: 12px;
  }

  .site-header .nav a.btn-primary {
    padding-left: 16px;
    padding-right: 16px;
  }
}

/* PRIMARY CTA — WHITE LABEL (owner 28.08). White fails AA on the bright brand
   teal and on the glass fill, so the filled control drops the film and rides a
   solid deeper teal that clears it: #0f7681 gives white 5.1:1 in light,
   #147c88 gives 4.95:1 on the dark ground. Placed after every earlier button
   rule (glass, and the opaque / reduced-transparency fallbacks) so it governs
   the label and the fill in all modes. The bright --accent stays for labels
   and links; only this one control goes deep. */
.btn-primary,
.nav a.btn-primary {
  background: #0f7681;
  border-color: rgba(255, 255, 255, 0.22);
  color: #fff;
}
.btn-primary:hover,
.nav a.btn-primary:hover {
  background: #0b5e67;
  color: #fff;
}
@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) .btn-primary,
  :root:not([data-theme="light"]) .nav a.btn-primary {
    background: #147c88;
  }
  :root:not([data-theme="light"]) .btn-primary:hover,
  :root:not([data-theme="light"]) .nav a.btn-primary:hover {
    background: #106b76;
  }
}
:root[data-theme="dark"] .btn-primary,
:root[data-theme="dark"] .nav a.btn-primary {
  background: #147c88;
}
:root[data-theme="dark"] .btn-primary:hover,
:root[data-theme="dark"] .nav a.btn-primary:hover {
  background: #106b76;
}

.nav-burger {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 34px;
  height: 34px;
  padding: 0;
  flex: 0 0 auto;
  color: var(--ink-2);
}

.nav-burger:hover {
  color: var(--ink);
}

.nav-burger[hidden] {
  display: none;
}

/* Three strokes drawn rather than typed, for the reason the theme control's
   marks are drawn: a glyph is at the mercy of whatever the system substitutes,
   and these have to read at sixteen pixels. */
.nav-burger-mark {
  position: relative;
  display: block;
  width: 16px;
  height: 1.5px;
  border-radius: 1px;
  background: currentColor;
}

.nav-burger-mark::before,
.nav-burger-mark::after {
  content: "";
  position: absolute;
  left: 0;
  width: 16px;
  height: 1.5px;
  border-radius: 1px;
  background: currentColor;
  transition: transform 0.18s cubic-bezier(0.2, 0.8, 0.2, 1),
    top 0.18s cubic-bezier(0.2, 0.8, 0.2, 1);
}

.nav-burger-mark::before {
  top: -5px;
}

.nav-burger-mark::after {
  top: 5px;
}

/* Open, the same three strokes are a cross: the control says what pressing it
   does now, not what it did a moment ago. */
.menu-open .nav-burger-mark {
  background: transparent;
}

.menu-open .nav-burger-mark::before,
.menu-open .nav-burger-mark::after {
  top: 0;
}

.menu-open .nav-burger-mark::before {
  transform: rotate(45deg);
}

.menu-open .nav-burger-mark::after {
  transform: rotate(-45deg);
}

/* The panel is a line of the header's own row, so it takes the header's padding
   and its glass instead of carrying a second copy of both. It also carries the
   class `nav`, which is where its type and its links come from — one set of
   rules for the row and the panel, laid out two different ways. */
.nav-menu {
  flex: 1 0 100%;
  align-items: center;
  gap: 6px 12px;
  padding-top: 4px;
  padding-bottom: 6px;
  border-top: 1px solid var(--glass-edge);
}

.nav-menu[hidden] {
  display: none;
}

/* A section in the panel is a row to press, not a word in a line of words. The
   same is true of the way in: a button that keeps its own width leaves a stub of
   a line beside it, and next to a control of another shape the pair reads as two
   unrelated things rather than one row. Everything in the panel is a full line. */
.nav-menu a:not(.btn),
.nav-menu .btn {
  flex: 1 0 100%;
  padding: 11px 10px;
}

/* A list is read as a list when its rows are divided. The first row is divided from
   the header by the panel's own top border, so it takes no line of its own.

   THE ROW DROPS THE PILL IT INHERITED (owner 17.08). A link in the wide header is a
   capsule, and `.nav a` gives it a 999px radius; in the panel the same link is a
   full-width row, and the divider drawn on a rounded box curves away at both ends —
   on screen it reads as fragments of capsules rather than as a list. A row is a row:
   the radius belongs to the shape, and here the shape changed. */
.nav-menu a:not(.btn) {
  border-radius: 0;
}

.nav-menu a:not(.btn) + a:not(.btn) {
  border-top: 1px solid var(--hair);
}

.nav-menu .btn {
  text-align: center;
}

/* THE CHOICE OF THEME IS A PREFERENCE, NOT AN ACTION, so it is said the way a
   setting is said: its name on the left, its three states on the right, the
   current one filled. The round button of the wide header cannot do this — a
   circle has no room for a word — which is why the panel builds its own. */
.nav-theme {
  flex: 1 0 100%;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
  padding: 10px;
  border-top: 1px solid var(--hair);
}

.nav-theme > span {
  font-family: var(--mono);
  font-size: 11px;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--ink-2);
}

.nav-seg {
  display: flex;
  border: var(--bw-1) solid var(--edge);
  border-radius: 999px;
  overflow: hidden;
}

.nav-seg button {
  font-family: var(--mono);
  font-size: 11px;
  letter-spacing: 0.05em;
  text-transform: uppercase;
  padding: 7px 11px;
  border: 0;
  background: none;
  color: var(--ink);
  cursor: pointer;
}

.nav-seg button.on {
  background: var(--ink);
  color: var(--ground);
}

@media (prefers-reduced-motion: reduce) {
  .nav-burger-mark::before,
  .nav-burger-mark::after {
    transition: none;
  }
}

/* ============================================================================
   THE GLASS SETTLES
   ============================================================================ */

.btn,
.nav-seg button,
.pill,
.tab,
.chip {
  transition:
    transform var(--spring-out) var(--spring),
    box-shadow var(--spring-out) var(--spring),
    background-color 180ms ease;
}

/* Pointer only: on a touch screen there is nothing to leave, and a hovered look
   that stays behind is the defect we removed this morning. */
@media (hover: hover) {
  .btn:hover,
  .nav-seg button:hover,
  .tab:hover,
  .chip:hover {
    transform: scale(1.035);
  }
}

/* The press is felt everywhere: it shrinks a touch, fast, and springs back. */
.btn:active,
.nav-seg button:active,
.tab:active,
.chip:active {
  transform: scale(0.985);
  transition-duration: var(--spring-in);
}

@media (prefers-reduced-motion: reduce) {
  .btn,
  .nav-seg button,
  .pill,
  .tab,
  .chip {
    transition: background-color 120ms linear;
  }

  .btn:hover,
  .btn:active,
  .nav-seg button:hover,
  .nav-seg button:active,
  .tab:hover,
  .tab:active,
  .chip:hover,
  .chip:active {
    transform: none;
  }
}

/* ============================================================================
   THE TAP DOES NOT DRAW ITS OWN BOX
   ----------------------------------------------------------------------------
   Mobile WebKit paints a grey rectangle over whatever was tapped, and that
   rectangle is a rectangle: it ignores the border radius, so a pill answers a
   touch by flashing square corners. Reported twice as clipping at the edges while the search
   went through our own CSS — there was nothing there; the box belongs to the browser.
   Turning it off costs nothing, because every control here already answers a
   press in tone and in scale.
   ============================================================================ */
a,
button,
.btn,
.flip-choice .btn,
[role="button"] {
  -webkit-tap-highlight-color: transparent;
}
