@font-face {
  font-family: Inter;
  src: url("/inter.woff2") format("woff2");
  font-weight: 100 900;
  font-display: swap;
}
/* TITLES INSIDE THE DRAWN SCENES ARE NOT HEADINGS (2026-10-02). A sample
   note's title ("The outbreak spread through the water...") was an h4 or an
   h5, which made it a section of THIS page to a search engine and a screen
   reader. They are divs with these classes now. The two rules below are the
   browser's own defaults for those tags, kept so that nothing moved: every
   rule further down that sets a margin, size or weight still wins, as it did
   over the browser's. Do not put a real heading tag back in a scene. */
.h4 {
  margin-block: 1.33em;
  font-weight: bold;
}
.h5 {
  margin-block: 1.67em;
  font-size: 0.83em;
  font-weight: bold;
}

/* Bodhi's dark ramp at the Sand dial (the app's `Sand` preset in
   src/lib/theme/dials.ts: warm near-black surfaces with an amber accent), copied from src/index.css (the dials on
   .theme-linear, the ramp on .dark.theme-linear). The formulas are kept, not
   resolved colours, so the site re-tints from the same three numbers the app
   does. The ramp is itself measured off Linear, so each level here plays the
   part its Linear counterpart plays on linear.app: page, content surface,
   raised surface, and four tiers of text. A token added here keeps its band's
   chroma line: surfaces 0.0022, borders 0.0039, grey text 0.0032. */
:root {
  /* THE PAGE'S OWN THEME IS MOONLIGHT (user, 2026-10-02: "can you use the
     moonlight theme for the website?"): the app's own preset, the four
     numbers in `lib/theme/dials.ts` (tint 255 at chroma 5, accent 258 at
     chroma 0.11). It is what the bar, the footer and the policy pages wear,
     and what `theme-color` in index.html is. Every surface, edge and grey on
     the site is a formula on these four. */
  --tint-hue: 255;
  --tint-chroma: 5;
  --brand-l: 0.5674;
  --brand-c: 0.11;
  --brand-hue: 258;
}
/* EACH MAJOR SECTION OF THE HOME PAGE WEARS A DIFFERENT THEME (user,
   2026-10-05: "each major section uses a different theme throughout the
   website. So the top section will use moonlight, this second one [Discover]
   will use sand, and keep changing going down the page"). A section names
   its theme with `data-palette` in index.html, and the six below are the
   app's six presets, in the app's order (`DIAL_PRESETS`): the top is
   Moonlight, then Sand, Moss, Lagoon, Twilight, Rose garden, and round
   again. The same attribute picks the colours of a Hairline drawing inside
   the section (scripts/product-site-hairline.mjs).
   HOW IT WORKS: a token such as --background is a formula on the dials, but
   a custom property is worked out where it is DECLARED and handed down
   finished, so new dials on a section would change nothing by themselves.
   The ramp is therefore declared on `:root` AND on every `[data-palette]`
   (the next rule, its light twin, and the two `--art-*` rules further
   down), so it is worked out again inside each themed section. A themed
   section also paints its own ground and takes its own text colour, since
   those two are inherited finished as well. A NEW TOKEN THAT USES A DIAL
   GOES IN ONE OF THOSE SHARED RULES, or it will be Moonlight everywhere.
   THE TINTS ARE THE APP'S OWN STRENGTH, so in dark mode a section's theme is
   a faint cast. Three times the tint was built the same day and taken back
   ("i take it back, no increasing of the tint"): do not strengthen it. */
[data-palette="moonlight"] {
  --tint-hue: 255;
  --tint-chroma: 5;
  --brand-c: 0.11;
  --brand-hue: 258;
}
[data-palette="sand"] {
  --tint-hue: 75;
  --tint-chroma: 3;
  --brand-c: 0.16;
  --brand-hue: 60;
}
[data-palette="moss"] {
  --tint-hue: 155;
  --tint-chroma: 4;
  --brand-c: 0.16;
  --brand-hue: 160;
}
[data-palette="lagoon"] {
  --tint-hue: 205;
  --tint-chroma: 4;
  --brand-c: 0.16;
  --brand-hue: 200;
}
[data-palette="twilight"] {
  --tint-hue: 273;
  --tint-chroma: 1;
  --brand-c: 0.16;
  --brand-hue: 275;
}
[data-palette="rose-garden"] {
  --tint-hue: 320;
  --tint-chroma: 4;
  --brand-c: 0.16;
  --brand-hue: 320;
}
[data-palette] {
  background: var(--background);
  color: var(--muted-foreground);
}
:root,
[data-palette] {
  color-scheme: dark;

  --background: oklch(0.1423 calc(0.0022 * var(--tint-chroma)) var(--tint-hue));
  --panel: oklch(0.1829 calc(0.0022 * var(--tint-chroma)) var(--tint-hue));
  --surface-2: oklch(0.209 calc(0.0022 * var(--tint-chroma)) var(--tint-hue));
  --foreground: oklch(1 0 0);
  --foreground-secondary: oklch(
    0.9178 calc(0.0032 * var(--tint-chroma)) var(--tint-hue)
  );
  --muted-foreground: oklch(
    0.6709 calc(0.0032 * var(--tint-chroma)) var(--tint-hue)
  );
  --foreground-dim: oklch(
    0.4568 calc(0.0032 * var(--tint-chroma)) var(--tint-hue)
  );
  --border: oklch(0.2509 calc(0.0039 * var(--tint-chroma)) var(--tint-hue));
  --primary: oklch(var(--brand-l) var(--brand-c) var(--brand-hue));
  /* THE FILLED BUTTON IS NOT THE ACCENT (the app's own rule since
     2026-10-02, and the user's ask here the same day: "the primary button no
     matter what colour theme is just the lightest colour from our palette...
     make that change throughout the website, both the mock and the
     functional"). These three are the app's dark values, as formulas
     (`--action` in src/index.css): the theme's lightest shade, the panel's
     colour for the words on it, and a hover that moves toward the page.
     `--primary` stays the accent for what the app keeps it for: the focus
     ring, the selection and links. */
  --action: oklch(0.93 calc(0.0032 * var(--tint-chroma)) var(--tint-hue));
  --action-foreground: oklch(
    0.1829 calc(0.0022 * var(--tint-chroma)) var(--tint-hue)
  );
  --action-hover: color-mix(in oklab, var(--action), black 12%);
  --radius: 0.5rem;

  /* Linear draws its edges as white at three strengths, not as greys. */
  --hairline-faint: oklch(1 0 0 / 0.05);
  --hairline: oklch(1 0 0 / 0.08);
  --hairline-strong: oklch(1 0 0 / 0.12);

  --radius-card: calc(var(--radius) * 1.5);
  --radius-bezel: calc(var(--radius) * 2.75);

  /* Two shadows: the one under anything that floats over a picture, and the
     stack of short ones that seats the light button on the dark page. */
  --shadow-float: 0 0 2rem
    color-mix(in oklab, var(--background) 80%, transparent);
  --shadow-button:
    0 3px 2px oklch(0 0 0 / 0.04), 0 1px 1px oklch(0 0 0 / 0.07),
    0 0 1px oklch(0 0 0 / 0.08);

  --wash: oklch(1 0 0 / 0.05);
  --wash-strong: oklch(1 0 0 / 0.08);
  /* THE LIGHT IN A MOCKUP (the rules are under "THE LIGHT" further down):
     the glow in a window's lit corner; what lifts a card off its panel, and
     a little more for the card in focus; a card's light hairline, the
     brighter stretch of it at its lit corner, and the dark ring it is
     seated in; the two ends of a field's lit edge. */
  --glow: oklch(1 0 0 / 0.04);
  --lift: color-mix(in oklab, var(--foreground) 3%, transparent);
  --lift-2: color-mix(in oklab, var(--foreground) 6%, transparent);
  --edge: oklch(1 0 0 / 0.08);
  --edge-lit: oklch(1 0 0 / 0.12);
  --seat: 0 0 0 1px oklch(0 0 0 / 0.2);
  --lit-from: oklch(1 0 0 / 0.15);
  --lit-to: oklch(1 0 0 / 0.05);
  /* A text link: the brand's hue, light enough to read on the dark page. */
  --link: oklch(0.8 calc(var(--brand-c) * 0.75) var(--brand-hue));

  --mono: ui-monospace, "SF Mono", SFMono-Regular, Menlo, Consolas, monospace;
  --gutter: 2.5rem;
  /* Linear's page is 1344 wide at most with its text 32px inside that: a
     text column of 1280. */
  --content: min(100% - 2 * var(--gutter), 80rem);
  --header: 5.5rem;
}
/* THE PAGE FOLLOWS THE SYSTEM'S LIGHT OR DARK SETTING (user, 2026-10-05:
   "make it so that the website is dark mode or light mode based on system
   preference"). There is no switch and no script for it: every light rule in
   this file sits in `@media (prefers-color-scheme: light)`, right after the
   dark rule it answers, so the right mode is on the page at first paint.
   (From 2026-10-02 to 10-05 the site was dark only, and before that the mode
   was a switch in the bar that set `data-theme`; both are gone.) Only the
   tree needs a script, because its light is set when it is mounted
   (hero-tree.js).
   The light ramp is the app's (.theme-linear:not(.dark) in src/index.css),
   copied the way the dark one was: its lightness values, and its own chroma
   lines, which are NOT the dark ramp's (surfaces and borders 0.0013, grey
   text 0.0033, primary text untinted). Edges turn from white at an alpha to
   black at an alpha, and shadows thin out: a shadow that seats a dark card
   on a dark page is a smudge on a light one. The filled button is the app's
   light one: the theme's darkest shade with white words.
   ANYTHING NEW THAT HARDCODES WHITE OR BLACK AT AN ALPHA (an edge, a wash, a
   shadow, a highlight) NEEDS A LIGHT TWIN. Prefer `--hairline*`, `--wash*`,
   `--mark*` and `--shadow-*`, which already have one. */
@media (prefers-color-scheme: light) {
  :root,
  [data-palette] {
    color-scheme: light;
    --background: oklch(
      0.9522 calc(0.0013 * var(--tint-chroma)) var(--tint-hue)
    );
    --panel: oklch(0.9823 calc(0.0013 * var(--tint-chroma)) var(--tint-hue));
    --surface-2: oklch(0.99 calc(0.0013 * var(--tint-chroma)) var(--tint-hue));
    --foreground: oklch(0.2224 0 0);
    --foreground-secondary: oklch(
      0.307 calc(0.0033 * var(--tint-chroma)) var(--tint-hue)
    );
    --muted-foreground: oklch(
      0.4759 calc(0.0033 * var(--tint-chroma)) var(--tint-hue)
    );
    --foreground-dim: oklch(
      0.6954 calc(0.0033 * var(--tint-chroma)) var(--tint-hue)
    );
    --border: oklch(0.9124 calc(0.0013 * var(--tint-chroma)) var(--tint-hue));
    --hairline-faint: oklch(0 0 0 / 0.05);
    --hairline: oklch(0 0 0 / 0.08);
    --hairline-strong: oklch(0 0 0 / 0.12);
    --wash: oklch(0 0 0 / 0.035);
    --wash-strong: oklch(0 0 0 / 0.06);
    /* On a light page there is no glow and no bright corner to see: an edge
       is a darker hairline, and the seat under a card is barely there. */
    --glow: transparent;
    --edge: oklch(0 0 0 / 0.08);
    --edge-lit: transparent;
    --seat: 0 0 0 1px oklch(0 0 0 / 0.03);
    --lit-from: oklch(0 0 0 / 0.14);
    --lit-to: oklch(0 0 0 / 0.08);
    --link: oklch(0.48 var(--brand-c) var(--brand-hue));
    --action: oklch(0.2224 calc(0.0033 * var(--tint-chroma)) var(--tint-hue));
    --action-foreground: oklch(1 0 0);
    --action-hover: color-mix(in oklab, var(--action), white 14%);
  }
}

/* TYPE. Two systems share this sheet. The bar, the hero, the footer and the
   policy pages keep the sizes measured on linear.app (15 for prose, 13 for
   the bar and footer, weight 510 for titles). The BODY of the home page uses
   the scale measured on apple.com/iphone-duo, set further down with the
   sections it belongs to. */
*,
*::before,
*::after {
  box-sizing: border-box;
}
html {
  background: var(--background);
  scroll-behavior: smooth;
  -webkit-text-size-adjust: 100%;
}
body {
  margin: 0;
  background: var(--background);
  color: var(--muted-foreground);
  font-family:
    Inter,
    -apple-system,
    BlinkMacSystemFont,
    "Segoe UI",
    sans-serif;
  font-size: 0.9375rem;
  font-weight: 400;
  letter-spacing: -0.011em;
  line-height: 1.6;
  -webkit-font-smoothing: antialiased;
}
h1,
h2,
h3,
p,
ul,
dl,
dd,
blockquote {
  margin: 0;
}
h1,
h2 {
  color: var(--foreground);
  font-weight: 510;
  letter-spacing: -0.022em;
  line-height: 1;
  text-wrap: balance;
}
h2 {
  font-size: clamp(2rem, 1.25rem + 2.4vw, 3rem);
}
h3 {
  color: var(--foreground-secondary);
  font-size: 0.9375rem;
  font-weight: 510;
  line-height: 1.6;
}
.small {
  font-size: 0.8125rem;
  letter-spacing: 0;
  line-height: 1.5;
}
.caption {
  font-family: var(--mono);
  font-size: 0.75rem;
  letter-spacing: 0;
  line-height: 1.4;
}
a {
  color: inherit;
}
::selection {
  background: oklch(var(--brand-l) var(--brand-c) var(--brand-hue) / 0.4);
}
:focus-visible {
  outline: 2px solid var(--primary);
  outline-offset: 2px;
}
.wrap {
  width: var(--content);
  margin-inline: auto;
}
.skip {
  position: absolute;
  left: 1rem;
  top: -4rem;
  z-index: 20;
  padding: 0.5rem 1rem;
  border-radius: 9999px;
  background: var(--action);
  color: var(--action-foreground);
  text-decoration: none;
}
.skip:focus {
  top: 0.75rem;
}

/* The mark's ink is forced, white on the dark page and black on the light
   one, so it is the same pure ink as the name beside it in both. */
.brand img {
  filter: brightness(0) invert(1);
}
@media (prefers-color-scheme: light) {
  :root .brand img {
    filter: brightness(0);
  }
}
/* THE NAME BESIDE THE MARK, IN LINEAR'S PROPORTIONS (user, 2026-10-02,
   pointing at Linear's own lockup: "size the text and the weight so it's
   similar to the size difference between the logo and the text for
   Linear"). Read off Linear's logo file (a 400 by 100 drawing): the capitals
   are 0.70 of the mark's height, the letters' stems are a sixth of the
   capitals' height (Inter at about 600), and the gap is 0.46 of the mark's
   height. The tree's ink is 21.6px tall in its 24px box, so the capitals are
   15px, which is Inter at 21px. It was 17px at weight 510. */
.brand {
  display: inline-flex;
  align-items: center;
  gap: 0.625rem;
  min-height: 2rem;
  color: var(--foreground);
  font-size: 1.3125rem;
  font-weight: 600;
  letter-spacing: -0.022em;
  text-decoration: none;
}

/* Linear's two buttons. The light one sits ON the page (a stack of short
   shadows); the dark one is cut INTO it (a faint top highlight inside, a
   black ring outside). */
.button {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  height: 2.75rem;
  padding-inline: 1.25rem;
  border-radius: 9999px;
  background: var(--action);
  box-shadow: var(--shadow-button);
  color: var(--action-foreground);
  font-size: 1rem;
  font-weight: 510;
  letter-spacing: -0.011em;
  text-decoration: none;
  white-space: nowrap;
  transition: background-color 150ms;
}
.button:hover {
  background: var(--action-hover);
}
.button.secondary {
  background: var(--wash);
  box-shadow:
    inset 0 0 0 1px oklch(1 0 0 / 0.03),
    inset 0 1px 0 oklch(1 0 0 / 0.04),
    0 0 0 1px oklch(0 0 0 / 0.6),
    0 4px 4px oklch(0 0 0 / 0.1);
  color: var(--foreground);
}
.button.secondary:hover {
  background: var(--wash-strong);
}
/* On a light page the same button is a plain ring, with no black outline. */
@media (prefers-color-scheme: light) {
  :root .button.secondary {
    box-shadow:
      0 0 0 1px var(--hairline-strong),
      0 1px 2px oklch(0 0 0 / 0.05);
  }
}

/* THE BAR IS LINEAR'S: a strip across the top that stays there (user,
   2026-10-05, with Linear's bar beside this page's two floating boxes: "use
   sticky top, just like Linear does. I don't want to use what we have
   anymore. The color of that nav, basically the theme, should change based
   on the section that it touches. I want it to be very seamless"). It
   replaces Dia's two rounded boxes (2026-10-01).
   Measured on linear.app that day: fixed to the top, 72 tall over a 1px rule
   of white at 8%, NO FILL, the page behind it blurred by 20px; its row as
   wide as the page's text; the mark at the left, the links at the right at
   13 in the third tier of text, each a 32px pill with 12 at its sides, then
   the button: a 32px pill in the lightest shade, 13 at weight 510.
   ITS FILL IS A VEIL, NOT A COLOUR OF ITS OWN. Each section wears its own
   theme, and a solid bar would have to switch colour at every seam. With
   no fill at all (Linear's own, tried first) the bar was always the colour
   of what was under it, but a white headline or a drawing passing under it
   glowed straight through the links. So it is the ground of the section
   under it at three quarters strength, over the blur: enough to quiet what
   passes, thin enough that a seam is seen sliding through rather than the
   bar changing. The veil, the links and the button take the theme of the
   section under the bar's middle line (nav.js gives the bar that section's
   `data-palette`) and ease to it over 400ms.
   The items are the user's own list and order: the mark and name, Features,
   Log in, Free Download with the Apple mark. */
.site-header {
  position: fixed;
  inset: 0 0 auto;
  z-index: 10;
  height: 4.5rem;
  border-bottom: 1px solid var(--hairline);
  background: color-mix(in oklab, var(--background) 76%, transparent);
  backdrop-filter: blur(20px);
  -webkit-backdrop-filter: blur(20px);
  transition: background-color 400ms;
}
.nav-inner {
  display: flex;
  align-items: center;
  width: var(--content);
  height: 100%;
  margin-inline: auto;
}
.site-header .brand {
  margin-right: auto;
}
.nav-links {
  display: flex;
  align-items: center;
}
.nav-link {
  display: inline-flex;
  align-items: center;
  height: 2rem;
  padding-inline: 0.75rem;
  border-radius: 9999px;
  color: var(--muted-foreground);
  font-size: 0.8125rem;
  letter-spacing: 0;
  text-decoration: none;
  transition: color 400ms;
}
.nav-link:hover {
  color: var(--foreground);
  transition-duration: 150ms;
}
.nav-download {
  display: inline-flex;
  align-items: center;
  gap: 0.5rem;
  height: 2rem;
  margin-left: 0.5rem;
  padding-inline: 0.75rem;
  border-radius: 9999px;
  background: var(--action);
  box-shadow: var(--shadow-button);
  color: var(--action-foreground);
  font-size: 0.8125rem;
  font-weight: 510;
  letter-spacing: 0;
  text-decoration: none;
  white-space: nowrap;
  transition:
    background-color 400ms,
    color 400ms;
}
.nav-download:hover {
  background: var(--action-hover);
  transition-duration: 150ms;
}
.nav-download svg {
  width: 0.875rem;
  height: 0.875rem;
  margin-top: -0.125rem;
  fill: currentColor;
}
/* THE BODY OF THE PAGE, after linear.app (user direction, 2026-10-02: "move
   my website to be more like linear, since my design system for my app is
   heavily influenced by linear"; the hero, the bar and the stack of papers
   are NOT part of this and keep their own notes). It replaced a body modelled
   on apple.com/iphone-duo (bands of alternating colour, centred lockups,
   borderless 28px tiles). Measured on linear.app at a 1440 window the same
   day; each figure below is theirs unless it says otherwise:
     - ONE flat page colour from top to bottom. Sections are ruled off with a
       full-width hairline (white at 8%), 128 above and below each.
     - A section opens with a HEAD IN TWO COLUMNS: the headline at the left
       (48 on 48), one paragraph at the right starting at the page's centre
       (24 on 32, in the second text tier), an action under the paragraph.
     - A picture sits on a WELL one step lighter than the page (theirs is
       15,16,17 on 8,9,10: here `--panel` on `--background`), with a 1px
       ring and a 12 to 16px corner, 96 under the head.
     - Things side by side are OPEN COLUMNS with a faint vertical hairline
       between them (white at 5%), not filled cards: their three "FIG"
       columns, their changelog, their pricing plans. A column is a picture
       or a mark, a name (15, weight 510), a sentence (15 on 24, grey).
     - A statement is one large sentence, set left, its claim in the text
       colour and the rest in grey.
     - The closing line is centred.
   Kept from before, by the user's own earlier calls: the label over a
   headline, the words. (The titles were in EB Garamond until they moved to
   Inter; editorial.css is still in the folder, unpublished, and its font
   files were deleted 2026-10-02: they are in git history at 00da5c42.) */
section[id] {
  scroll-margin-top: var(--header);
}
.band {
  --tile: var(--panel);
  /* Linear: a rule, then 136 to the headline (an 8px spacer and the
     section's own 128), and 128 under the section's last row. */
  padding-block: 8.5rem 8rem;
  border-top: 1px solid var(--hairline);
}
/* The statement stands alone under the hero, with no rule above it: 160
   from it to the rule, then the usual 136 to the first chapter's headline.
   (Two columns stood under it until 2026-10-02, Zotero and privacy; see
   `.also` for where Zotero went and why privacy went altogether.) */
.hero + .band {
  padding-block: 8rem 10rem;
  border-top: 0;
}
/* Another app's own icon, at the size the app shows it in a row of ways in.
   Its artwork carries its own rounded shape. */
.app-icon {
  display: block;
  width: 3rem;
  height: 3rem;
  margin-bottom: 1.5rem;
}
/* ZOTERO, UNDER THE BROWSER EXTENSION (user, 2026-10-02, on the pair of
   columns that stood under the statement: "something about the place of
   this doesn't feel right, from an information harmony perspective", then
   "yes, do the split"). The pair answered two questions before anyone had
   seen the app, and its halves had nothing to do with each other. So:
     - "Private by default." is GONE as a block. Every sentence of it is
       said where the question comes up: the AI section's head (it starts
       off, nothing is sent to a model) and the free plan (no account, stays
       on your computer, works offline).
     - Zotero is the last row of the section about getting your sources in,
       under the extension's scene and its four kinds of page: the mark and
       the name at the left, the two sentences in the right half, on the
       same two columns as a section's head and its Features row.
   That section moved up to stand right after Discover (find new papers,
   then bring in what you already have, then read). */
.also {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  column-gap: var(--column-gap);
  align-items: center;
  margin-top: 3.5rem;
  padding-top: 3.5rem;
  border-top: 1px solid var(--hairline);
  scroll-margin-top: var(--header);
}
.also-name {
  display: flex;
  align-items: center;
  gap: 1rem;
}
.also .app-icon {
  flex: none;
  margin-bottom: 0;
}
.also h3 {
  color: var(--foreground);
  font-size: 1.25rem;
  font-weight: 590;
  letter-spacing: -0.012em;
  line-height: 1.33;
}
.also p {
  max-width: 28rem;
  color: var(--muted-foreground);
  font-size: 0.9375rem;
  letter-spacing: -0.011em;
  line-height: 1.6;
}
.column {
  position: relative;
}
/* THE RULE BETWEEN COLUMNS. The columns keep equal widths and an ordinary
   gap; the rule is drawn in the middle of that gap by every column after
   the first. (Padding the columns instead makes the outer ones wider than
   the inner ones, and pictures in them come out different sizes.) */
:root {
  --column-gap: 4rem;
}
.column + .column::before,
.plan:not(.choice) + .plan:not(.choice)::before {
  position: absolute;
  top: 0;
  bottom: 0;
  left: calc(var(--column-gap) / -2);
  width: 1px;
  background: var(--hairline);
  content: "";
}
/* An icon of ours in the place of an app's: a filled tile of the same size
   and corner, with a line glyph in it. */
.app-icon.glyph {
  display: grid;
  place-items: center;
  border: 1px solid var(--hairline);
  border-radius: 0.6875rem;
  background: var(--panel);
  color: var(--foreground);
}
.app-icon.glyph svg {
  width: 1.375rem;
  height: 1.375rem;
  fill: none;
  stroke: currentColor;
  stroke-linecap: round;
  stroke-linejoin: round;
  stroke-width: 1.75;
}
/* A SECTION'S HEAD, AFTER ROBINHOOD'S (user, 2026-10-05, selecting a
   section of robinhood.com beside Discover's head: "rearrange the formatting
   of the title text, the description text, and the illustration throughout
   the website so that it matches the style of Robinhood's sections... the
   title and the description in the same column now on the left, and the
   illustration on the right side... decrease the size of the title and the
   description by a little bit. Match Robinhood's sizing and properly change
   the size of the illustration so it matches the text on the left").
   Two halves, level at their middles: the words at the left (`.lockup-text`:
   headline, paragraph, and an action if there is one) and one of the app's
   Hairline drawings at the right (`.lockup-art`, hairline.js).
   MEASURED ON ROBINHOOD'S SECTION the same day (its "Trade crypto 24/7"):
     40 on 48, tracking -1px    the title (theirs is weight 400 in their own
                                face; this one keeps the page's 510 Inter)
     16 on 24, tracking -0.25   the description, in a column 400 wide
     16                         title to description
     32                         description to the button, which is 44 tall
   Their picture fills its half, far larger than the words. Here the drawing
   is sized so its INK is about a third taller than the words beside it (350
   by 250 at a wide window), which is what lets the two read as a pair.
   Until this the head was Linear's: the headline at the left at 48, the
   paragraph at the right at 24, level at the bottom. That is in git. */
.lockup {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  column-gap: var(--column-gap);
  align-items: center;
  justify-items: start;
}
.lockup-text {
  grid-column: 1;
  grid-row: 1;
}
/* THE DRAWING. Sized by its width (`--w`, never wider than its column); it
   is 5:4, and the box is stated here so the head does not move when the
   script arrives.
   EVERY DRAWING HAS AIR INSIDE ITS OWN FRAME, and each a different amount,
   so each names where its ink is, measured at rest in the preview (the
   first <g>'s getBBox, in the frame's own 400 by 320): the air above and
   below as a share of the WIDTH, and how far its ink's middle is from the
   frame's. The margins take the air above and below back, so the head is as
   tall as the ink, and the shift puts the ink's middle on the column's.
   A new drawing: measure it and add its three numbers below. */
.lockup-art {
  --size: 36rem;
  --w: min(var(--size), 100%);
  --air-top: 0.2;
  --air-bottom: 0.1;
  --off: 0;
  --ink-w: 0.65;
  grid-column: 2;
  grid-row: 1;
  justify-self: center;
  position: relative;
  width: var(--w);
  aspect-ratio: 5 / 4;
  margin-block: calc(var(--w) * var(--air-top) * -1)
    calc(var(--w) * var(--air-bottom) * -1);
  translate: calc(var(--w) * var(--off) * -1) 0;
}
/* Each drawing's frame is sized so its INK is about the same weight on the
   page as the others' (near 80,000 square px at a wide window): a tall
   drawing gets a narrower frame, a low wide one the whole column. Its ink,
   as a share of the frame's width, is in the comment beside it. */
/* ink 0.61 wide, 0.43 tall */
[data-hairline="paper-stack"] {
  --ink-w: 0.61;
  --size: 36rem;
  --air-top: 0.243;
  --air-bottom: 0.127;
}
/* ink 0.63 wide, 0.69 tall */
[data-hairline="web-clip"] {
  --ink-w: 0.63;
  --size: 26rem;
  --air-top: 0.065;
  --air-bottom: 0.041;
  --off: 0.013;
}
/* ink 0.63 wide, 0.33 tall */
[data-hairline="paper-notes"] {
  --ink-w: 0.63;
  --size: 38rem;
  --air-top: 0.296;
  --air-bottom: 0.177;
}
/* ink 0.76 wide, 0.39 tall */
[data-hairline="canvas"] {
  --ink-w: 0.76;
  --size: 34rem;
  --air-top: 0.212;
  --air-bottom: 0.195;
}
/* ink 0.62 wide, 0.63 tall */
[data-hairline="typewriter"] {
  --ink-w: 0.62;
  --size: 28rem;
  --air-top: 0.099;
  --air-bottom: 0.068;
  --off: -0.018;
}
/* ink 0.51 wide, 0.52 tall */
[data-hairline="neural"] {
  --ink-w: 0.51;
  --size: 34rem;
  --air-top: 0.121;
  --air-bottom: 0.159;
}
/* ink 0.80 wide, 0.62 tall: two cards side by side, filling their frame.
   A narrower frame than the rest for that reason, and because the head it
   stands in (pricing) is a headline alone, with no paragraph. */
[data-hairline="library-plans"] {
  --ink-w: 0.8;
  --size: 25rem;
  --air-top: 0.145;
  --air-bottom: 0.039;
  --off: -0.009;
}
/* ink 0.73 wide, 0.52 tall, measured with the book lying open; a page in
   the air stands a little higher than that, into the room above it. */
[data-hairline="folio"] {
  --ink-w: 0.73;
  --size: 28rem;
  --air-top: 0.123;
  --air-bottom: 0.157;
}
/* EVERY DRAWING STANDS ON A PLATE, LIKE A FIGURE IN A PAPER (user,
   2026-10-06, with a picture of a plotted surface on a field of fine dots
   and "FIG_003" beside it: "add a very light type background and have a
   little figure number at the bottom right of that dotted box so that it
   sort of looks like a scientific figure. Make sure the color matches each
   section's color"). Their reference had the number turned on its side at
   the top left; they asked for it at the bottom right, reading left to
   right.
   THE PLATE is a rectangle of fine dots a little larger than the drawing's
   ink: the ink's own box (the air numbers above, and `--ink-w`, its width
   as a share of the frame's, from the same measuring) and `--plate` of room
   all round. So each plate is its drawing's shape, as a figure's is. The
   dots are the section's accent, thinned: each section's own colour, with
   no list of colours here. THEY ARE FAINT ON PURPOSE: at 70% of the accent
   the user said at once that they "need to be more muted", so they are 30%
   now. The plate is to be felt behind the drawing, not read.
   THE NUMBER counts the drawings down the page (a CSS counter, so adding or
   moving a drawing renumbers the rest). It is an ornament: it is given an
   empty spoken name where the browser allows one.
   Both are painted by the frame itself and take no pointer, so a drawing
   still answers only when the pointer is over the frame. A drawing that
   lifts under the pointer may rise past its plate's edge; that is meant. */
body {
  counter-reset: figure;
}
/* Every length here is a share of the frame's own box, never `--w`: that is
   `100%` where the column is narrower than the drawing's size, and a
   percentage means the frame's HEIGHT in `top` and `bottom`. The frame is
   5:4, so a share of its width is 1.25 times that share of its height. */
.lockup-art {
  --plate: 1.75rem;
  --plate-x: calc(50% + (var(--off) - var(--ink-w) / 2) * 100%);
  isolation: isolate;
  counter-increment: figure;
}
.lockup-art::before {
  position: absolute;
  top: calc(var(--air-top) * 125% - var(--plate));
  bottom: calc(var(--air-bottom) * 125% - var(--plate));
  left: calc(var(--plate-x) - var(--plate));
  z-index: -1;
  width: calc(var(--ink-w) * 100% + 2 * var(--plate));
  background: radial-gradient(
      circle,
      color-mix(in oklab, var(--primary) 30%, transparent) 0.5px,
      transparent 0.75px
    )
    0 0 / 0.375rem 0.375rem;
  content: "";
  pointer-events: none;
}
.lockup-art::after {
  position: absolute;
  right: calc(
    100% - var(--plate-x) - var(--ink-w) * 100% - var(--plate) + 0.5rem
  );
  bottom: calc(var(--air-bottom) * 125% - var(--plate) + 0.375rem);
  z-index: -1;
  padding: 0 0.25rem;
  background: var(--background);
  color: color-mix(in oklab, var(--primary) 30%, var(--foreground-dim));
  font-family: var(--mono);
  font-size: 0.6875rem;
  letter-spacing: 0.04em;
  line-height: 1rem;
  content: "FIG_00" counter(figure);
  content: "FIG_00" counter(figure) / "";
  pointer-events: none;
}
/* The closing line alone is centred, as Linear's is. */
.lockup.center {
  grid-template-columns: minmax(0, 1fr);
  justify-items: center;
  text-align: center;
}
.lockup.center > * {
  grid-column: 1;
  grid-row: auto;
}
.kicker {
  color: var(--foreground);
  font-size: 1.125rem;
  font-weight: 560;
  letter-spacing: -0.014em;
  line-height: 1.2;
}
.kicker + h2 {
  margin-top: 1.25rem;
}
.lockup h2 {
  font-size: clamp(2.5rem, 5.6vw, 4.5rem);
  font-weight: 560;
  letter-spacing: -0.035em;
  line-height: 1.04;
}
.copy {
  max-width: 36rem;
  margin-top: 1.625rem;
  font-size: 1.1875rem;
  letter-spacing: -0.014em;
  line-height: 1.45;
  text-wrap: pretty;
}
.copy strong {
  color: var(--foreground);
  font-weight: 510;
}
.text-link {
  color: var(--link);
  font-size: 1rem;
  text-decoration: none;
  white-space: nowrap;
}
.text-link:hover {
  text-decoration: underline;
}

/* THE HERO, after Dia's: everything centred. The line that says what Bodhi
   is, a small sentence under it, then the name set very large, with the
   product picture laid over the bottom of the letters so they read as
   standing behind it. `--wordmark` is the size of the name; the picture's
   overlap is worked out from it, so the two stay in step at every width. */
.hero {
  /* THE BIG NAME IS GONE (user, 2026-10-02: "get rid of this big bodhi
     text, and add the word bodhi after the logo in the top navigation"). The
     hero is now the line, the tree and the window. The tree still stands on
     the window's top edge, and `--tree-room` is the room left for it
     between the line and the window, where the name used to be. The notes
     below about the name's size and overlap are history. */
  --tree-room: clamp(6.5rem, 14vw, 11.5rem);
  --tree-h: clamp(15rem, 30vw, 24rem);
  /* Dia's layout, opened up a little at the user's ask. At a 1280 window:
     the line starts 136px down and is 2.7% of the width tall; the name's
     capitals start 56px under it; the picture is 77% of the width, about 1.4
     to 1, and covers the bottom 18% of the name, which puts its top edge
     near where Dia's sits. */
  padding-top: var(--hero-top);
  text-align: center;
}
/* Behind the name stands the Bodhi tree from the app's welcome screen, drawn
   by /hero-tree.js. There is NO sky behind it: a moon, then a sun, with
   graded light and paper grain were built here and taken out again (user,
   2026-10-01: "let's simplify. just the tree"). The tree carries its own
   light instead, set in hero-tree.js for each mode, so it reads on the dark
   page and on the light one. Without the script the hero simply has no
   tree. */
.hero {
  --hero-top: 8.5rem;
  position: relative;
  overflow-x: clip;
}
/* In daylight the line is dark words over green leaves. A soft halo smeared
   there, so it has a crisp white outline instead (user call): the same hard
   shadow set in twelve directions, which every browser draws alike and which
   leaves the letters' own weight alone (a text stroke eats into them). */
@media (prefers-color-scheme: light) {
  :root .hero h1 {
    --o: 0.09375rem;
    --d: 0.066rem;
    text-shadow:
      var(--o) 0 #fff,
      calc(-1 * var(--o)) 0 #fff,
      0 var(--o) #fff,
      0 calc(-1 * var(--o)) #fff,
      var(--d) var(--d) #fff,
      calc(-1 * var(--d)) var(--d) #fff,
      var(--d) calc(-1 * var(--d)) #fff,
      calc(-1 * var(--d)) calc(-1 * var(--d)) #fff,
      0 0 0.125rem #fff;
  }
}
/* NO LIGHT IN THE PAGE'S CORNER. A soft white glow stood in the hero's top
   left corner from 2026-10-02 (the user's ask then, brightened twice) until
   2026-10-05, when they had it taken out: "remove the gradient made to look
   like there's light coming from top left". The ground is one flat colour.
   The window under the line still has its own light (`.app-win`), which was
   not part of this. A sky was built here twice before (a moon, then a sun,
   with grain) and taken out too. */
.hero > .wrap {
  position: relative;
}
/* THE LINE STANDS RIGHT ABOVE THE WINDOW, AND THE TREE'S TRUNK IS THE SPACE
   BETWEEN ITS TWO HALVES (user, 2026-10-02: "make it so that the text shows
   up right above the screenshot with a bit of padding below it. Show 'Your
   research workspace' on the left of the tree trunk and 'for deep thinking'
   on the right... Make the text larger so it matches the width of the
   screenshot", then "I want the text to get very close to the tree trunk so
   it feels like it's just the tree trunk separating the sentence").
   The line's box is as wide as the window. The first half starts at the
   window's left edge, the second ends at its right edge, and the two stop
   either side of a gap that is the trunk's width and a little air. The two
   halves are not the same length, so the gap is not at the middle: THE TREE
   IS MOVED to stand in it (about a tenth of the width right of centre).
   Three measured numbers hold this together, and each has to be measured
   again if what it depends on changes:
     11.579  the first half's width, in font sizes (Inter 400 at this
             tracking). Change the words, the face or the tracking: measure
             (a Range over each half's words, after `document.fonts.ready`).
     19.487  both halves together, the same way.
     0.255   the gap, as a share of the tree's height. The trunk is the
             Bodhi's thick one now (hero-tree.js: trunkGirth, rootFlare):
             0.19 of the tree's height wide at the words' baseline and 0.125
             at their cap height, and bare for the first quarter of the
             height above the ground, which is the room the line has under
             the limbs. (It was 0.16 for the thinner trunk.) Change the
             trunk and measure again: read the tree's canvas for opaque
             pixels along the baseline.
   The size comes from the box's width (`cqw`: `.hero-line` is a size
   container, and --size is read by its children, where cqw means that box).
   The crown is above the line now, so the room for it is above too: the
   block is as tall as the tree, less what the hero's top padding gives. */
.hero-text {
  --line-foot: 1.125rem;
  position: relative;
  display: flex;
  flex-direction: column;
  justify-content: flex-end;
  min-height: calc(var(--tree-h) - 5rem);
  padding-bottom: var(--line-foot);
}
.hero-line {
  --gap: calc(var(--tree-h) * 0.255);
  --size: calc((100cqw - var(--gap)) / 19.487);
  position: relative;
  left: 50%;
  width: min(84rem, 100vw - 2 * var(--gutter));
  container-type: inline-size;
  translate: -50% 0;
}
.hero .hero-line h1 {
  position: relative;
  display: flex;
  justify-content: space-between;
  font-size: var(--size);
  text-shadow: none;
  white-space: nowrap;
}
/* The tree stands on the picture's top edge: the engine reports how far above
   the box's bottom its ground is (--bodhi-tree-base, the middle of the trunk's
   base; the app's welcome screen uses --bodhi-tree-floor, the trunk's lowest
   point, which would lift this tree off the measured headline), and the box is dropped
   by that much and by the line's foot (its parent is the line's box). Its
   trunk is put in the gap between the line's two halves; the last term is
   the trunk's own lean off its box's middle. */
.hero-tree {
  --bodhi-bark-color: #6b5646;
  position: absolute;
  bottom: calc(0px - var(--line-foot) - var(--bodhi-tree-base, 0px));
  left: calc(11.579 * var(--size) + var(--gap) / 2 - var(--tree-h) * 0.006);
  width: min(92vw, 46rem);
  height: var(--tree-h);
  opacity: 0;
  translate: -50% 0;
  transition: opacity 1.2s;
}
.hero-tree[data-live] {
  opacity: 1;
}
/* A SLIGHT GLOW WHERE THE LIGHT LANDS (user, 2026-10-06: "add a slight glow
   to the tree where the light is hitting the leaves the strongest at the
   top"). The tree's own drawing is untouched: the browser takes the picture
   the engine drew, keeps only its lightest leaves (the two palest tones,
   which the engine puts at the crown's top left, where its light falls),
   blurs them and lays that back over the tree, so those leaves bloom a
   little and a little of it spills past the crown's edge. The filter is in
   the page (index.html, #tree-glow) and its three numbers are there: the
   last of the matrix's rows is the cut (lightness times six, less 4.44, so
   leaves under about three quarters light give nothing), the blur's 14 is
   how far it spreads in px, and the slope's 0.85 is how strong it is. They
   were 10 and 0.55 at first: the user asked for "a bit stronger", then "a
   little wider", the same day.
   SLIGHT IS THE BRIEF: the same user had a "glowy, shimmery" tree taken out
   of the app on 2026-10-05, so do not turn this up unasked. The app's tree
   has no glow; this is the site's only. */
.hero-tree canvas {
  filter: url(#tree-glow);
}
/* The line has to be read over the tree. */
.hero h1 {
  text-shadow:
    0 0 1.5rem var(--background),
    0 0 0.5rem var(--background);
}
/* The one size on the page outside the scale: Dia sets this line at about a
   seventh of its wordmark, and neither 24 nor 40 does that here.
   THE LINE IS INTER, like every other title. It was EB Garamond for part of
   2026-10-05 (the user's ask, "that serif font we used to use... for this
   top title only") and went back the same day ("make this top hero section
   text sans-serif again"). The font file is gone again; it is in git
   history (00da5c42) if a serif is ever wanted back. */
.hero h1 {
  font-size: clamp(1.5rem, 2.7vw, 2.375rem);
  font-weight: 400;
  line-height: 1.1;
}

/* A picture is a filled shape with a 28px corner and no edge. Until the real
   pictures exist it is a labelled placeholder. */
.shot {
  display: grid;
  place-items: center;
  padding: 1.5rem;
  border-radius: 1.75rem;
  background: var(--tile, var(--panel));
  color: var(--foreground-dim);
  font-family: var(--mono);
  font-size: 0.75rem;
  letter-spacing: 0;
  text-align: center;
}
/* THE PICTURES ARE DRAWINGS (user, 2026-10-01: "zoom in on things, abstract
   things... to make whatever the screenshot is for stand out. I want clean
   simple design", after real captures had looked soft on a big screen). Each
   is an inline SVG that shows ONE thing close up, in the citation film's
   vocabulary: a page, lines for words, the amber highlight, a card, a chip.
   Inline, so they take the page's own colours and follow light and dark, and
   they are sharp at any size. Real words appear only where they are true (a
   paper in the sample library, a control's name).
   Short class names, because every shape carries one:
     s, s2  a raised surface, and one raised further (a popover)
     k      a hairline round a shape; n  no fill; ka  the amber edge
     b, b2  a line of words, faint and strong
     m      the highlight; a  amber, filled; al  an amber line; l  a line
     c      a citation chip
     text   td dim, tb semibold, to on amber, ta amber
   The picture fills its shape and is cut to it (`slice`), so what matters is
   kept in the middle of each drawing. */
:root,
[data-palette] {
  --art-surface: oklch(0.235 calc(0.0022 * var(--tint-chroma)) var(--tint-hue));
  --art-surface-2: oklch(
    0.285 calc(0.0022 * var(--tint-chroma)) var(--tint-hue)
  );
  --art-ink: color-mix(in oklab, var(--foreground) 13%, transparent);
  --art-ink-strong: color-mix(in oklab, var(--foreground) 46%, transparent);
  --art-line: color-mix(in oklab, var(--foreground) 24%, transparent);
  --art-accent: oklch(0.78 0.135 72);
  --art-on-accent: oklch(0.2 0.03 70);
  --art-mark: oklch(0.8 0.14 78 / 0.38);
  --art-chip: oklch(0.78 0.135 72 / 0.16);
}
@media (prefers-color-scheme: light) {
  :root,
  [data-palette] {
    --art-surface: oklch(1 0 0);
    --art-surface-2: oklch(1 0 0);
    --art-ink: color-mix(in oklab, var(--foreground) 14%, transparent);
    --art-ink-strong: color-mix(in oklab, var(--foreground) 52%, transparent);
    --art-accent: oklch(0.7 0.15 66);
    --art-on-accent: oklch(1 0 0);
    --art-mark: oklch(0.86 0.16 92 / 0.75);
    --art-chip: oklch(0.7 0.15 66 / 0.14);
  }
}
/* Words inside a drawing are the APP's words, so they are in the app's face
   (Inter), whatever face the page around them is set in. */
.art {
  overflow: hidden;
  padding: 0;
  font-family:
    Inter,
    -apple-system,
    BlinkMacSystemFont,
    "Segoe UI",
    sans-serif;
  letter-spacing: 0;
}
.art > svg {
  display: block;
  width: 100%;
  height: 100%;
}
.art .s {
  fill: var(--art-surface);
}
.art .s2 {
  fill: var(--art-surface-2);
}
.art .n {
  fill: none;
}
.art .k {
  stroke: var(--hairline-strong);
  stroke-width: 1;
}
.art .ka {
  stroke: var(--art-accent);
  stroke-width: 1.5;
}
.art .b {
  fill: var(--art-ink);
}
.art .b2 {
  fill: var(--art-ink-strong);
}
.art .m {
  fill: var(--art-mark);
}
.art .a {
  fill: var(--art-accent);
}
.art .l,
.art .al {
  fill: none;
  stroke: var(--art-line);
  stroke-linecap: round;
  stroke-linejoin: round;
  stroke-width: 1.25;
}
.art .al {
  stroke: var(--art-accent);
  stroke-width: 1.5;
}
.art .c {
  fill: var(--art-chip);
  stroke: var(--art-accent);
  stroke-width: 1;
}
.art text {
  fill: var(--foreground);
  font-family: inherit;
  letter-spacing: 0;
}
.art .td {
  fill: var(--muted-foreground);
}
.art .tb {
  font-weight: 600;
}
.art .to {
  fill: var(--art-on-accent);
}
.art .ta {
  fill: var(--art-accent);
}
@media (prefers-color-scheme: light) {
  :root .art .ta {
    fill: oklch(0.5 0.13 62);
  }
}
/* THE HERO'S PICTURE IS THE WHOLE APP, IN CODE (user, 2026-10-02: "I want
   that screenshot to take up full width and I want the most sensible screens
   for it... this is the hero section and it needs to wow", then, shown the
   idea, "yes build it"). One Mac window as wide as Linear's page (1344 at
   most), all four panels open, and ONE passage followed across them: it is
   highlighted on the paper, quoted in the note made from it, the lit card on
   the canvas, and the opened citation in the draft. The same amber marks it
   in all four, and when the page loads it is drawn across them in turn,
   once. That is the product in one look: read, mark, think, write, and
   nothing loses its source.
   This is the one place with a window's own chrome (the three buttons, the
   project, the four panel names): it is the whole app, where the scenes
   below are parts of it.
   EVERY SIZE INSIDE IS IN EM, and the window's font size is the frame's
   width over 84 (`cqw`: the frame is a size container). So the whole window
   scales with the page as a picture does, and is 1:1 at 1344. Below 756
   wide it stops shrinking (9px) and is cut off at the right instead, where
   it runs on. Do not put a rem or a px length inside `.app`.
   It dissolves at the bottom, as the scenes do. */
.hero-frame {
  position: relative;
  left: 50%;
  width: min(84rem, 100vw - 2 * var(--gutter));
  margin: 0;
  container-type: inline-size;
  translate: -50% 0;
}
.app {
  --t-claim: oklch(0.66 0.17 282);
  --t-group: oklch(0.72 0.1 185);
  /* Four tiers of text, spent as Linear spends its own (see "EASY ON THE
     EYES" below). */
  --t1: var(--foreground);
  --t2: var(--foreground-secondary);
  --t3: var(--muted-foreground);
  --t4: color-mix(in oklab, var(--muted-foreground) 70%, var(--background));
  /* The passage's wash, at rest and at the top of its pulse. */
  --rest: var(--wash-strong);
  --peak: oklch(1 0 0 / 0.17);
  --halo: oklch(1 0 0 / 0.08);
  overflow: hidden;
  height: 46em;
  border-radius: 0.875em 0.875em 0 0;
  font-size: max(100cqw / 84, 0.5625rem);
  mask-image: linear-gradient(#000 0%, #000 70%, transparent 100%);
  /* The hero centres its words; a window's are set left. */
  text-align: left;
}
/* On the light page the passage's wash is ink, not light, and the type
   marks take the deeper colours the scenes use there. */
@media (prefers-color-scheme: light) {
  .app {
    --peak: oklch(0 0 0 / 0.13);
    --halo: oklch(0 0 0 / 0.06);
    --t-claim: oklch(0.5 0.19 282);
    --t-group: oklch(0.52 0.1 185);
  }
}
.app-win {
  width: 84em;
  height: 100%;
  border: 1px solid var(--border);
  border-bottom: 0;
  border-radius: inherit;
  background: var(--background);
  color: var(--foreground-secondary);
  letter-spacing: -0.003em;
  line-height: 1.5;
}
/* An icon takes its size from the words beside it (an <svg> does not
   inherit a font size here by itself, and its em would be a fixed 13px). */
.app .ic {
  width: 0.875em;
  height: 0.875em;
  font-size: inherit;
}
.app .ic.small {
  width: 1.1em;
  height: 1.1em;
  margin-right: 0.35em;
  vertical-align: -0.12em;
}
.app-bar {
  position: relative;
  display: flex;
  align-items: center;
  gap: 0.625em;
  height: 3.25em;
  padding-inline: 1.25em;
}
.app-bar b {
  color: var(--t2);
  font-size: 0.8125em;
  font-weight: 510;
}
.app-lights {
  display: flex;
  gap: 0.5em;
  margin-right: 0.5em;
}
.app-lights i {
  width: 0.75em;
  height: 0.75em;
  border-radius: 50%;
  background: #ff5f57;
}
.app-lights i:nth-child(2) {
  background: #febc2e;
}
.app-lights i:nth-child(3) {
  background: #28c840;
}
.app-mark {
  width: 1em;
  height: 1em;
  filter: brightness(0) invert(1);
}
@media (prefers-color-scheme: light) {
  .app-mark {
    filter: brightness(0);
  }
}
.app-chips {
  position: absolute;
  left: 50%;
  display: flex;
  gap: 0.25em;
  translate: -50% 0;
}
.app-chips i {
  padding: 0.3em 0.85em;
  border-radius: 9999px;
  background: var(--wash);
  color: var(--t3);
  font-size: 0.75em;
  font-style: normal;
  font-weight: 510;
}
.app-panels {
  display: grid;
  grid-template-columns: 28fr 21fr 28fr 23fr;
  gap: 0.5em;
  height: calc(100% - 3.25em);
  padding-inline: 0.5em;
}
.app-pn {
  position: relative;
  overflow: hidden;
  border: 1px solid var(--border);
  border-bottom: 0;
  border-radius: 0.875em 0.875em 0 0;
  background: var(--panel);
}
.app-tab {
  display: flex;
  align-items: center;
  gap: 0.5em;
  height: 2.75em;
  padding-inline: 1em;
  border-bottom: 1px solid var(--border);
  color: var(--t3);
}
/* A view's name stands alone: the icon beside each was removed (user,
   2026-10-04: "also remove these icons"). */
.app-tab b {
  overflow: hidden;
  font-size: 0.75em;
  font-weight: 510;
  text-overflow: ellipsis;
  white-space: nowrap;
}
/* EASY ON THE EYES (user, 2026-10-02, beside Linear's window: "our hero UI
   looks extremely wordy and stuffy... any text shade and opacity
   improvements? The Linear one looks very nice and easy to look at... I
   don't love the highlighting and colour of the orange/amber items, I
   wonder if a soft, very minimal pulsing animation would be better").
   Counted the same day, the words on screen in each window: Linear's 202,
   this one 537, and Linear sets seven words in ten in its two GREYS, where
   this had half its words in near-white. So:
     - FEWER WORDS. The page of the paper is two paragraphs. Only the note
       made from the passage is open; the others are a title and where they
       come from. A card on the canvas is its title; only the passage's
       card holds a quote. The draft is its title, one paragraph and the
       opened citation.
     - THE GREYS DO THE WORK. Running text is the third tier (--t3), what
       tells you where something comes from the fourth (--t4), titles the
       second (--t2). White (--t1) is kept for ONE thing: the passage, in
       each of the four panels. So the thread is the four bright places in
       a grey window.
     - ONE LARGER TITLE, the draft's, as Linear's window has one.
     - NO AMBER. The passage is white on a wash of white, the note's and the
       card's quote boxes are plain, and the citation is a plain chip.
       Colour is left to the small type marks.
     - THEN EVERYTHING BUT THE PASSAGE WAS MUTED A STEP FURTHER (user, the
       same day: "mute all text that isn't part of the quote that's being
       connected so users can follow it very easily"). Titles are the third
       tier now and running text the fourth; nothing is in the second. The
       citation chip is muted too: in the draft the white place is the
       quote in the opened citation, and that is what pulses.
     - A PANEL IS NAMED AS ITS BUTTON IN THE BAR IS (also asked for: "so
       it's easier to understand"): Sources, Notes, Canvas, Drafts, not the
       paper's, the canvas's and the draft's own names.
     - A SLOW PULSE instead: every six seconds the wash under the passage
       lifts and settles, in the paper, then the note, the card and the
       opened citation, half a second apart. It is the only thing that says the
       four are one passage, so keep the order. Not run with reduced motion
       on: the four simply stand a little brighter than the rest.
   The words are still the paper's and the sample library's (the draft's one
   paragraph is the one written for this page; see CLAUDE.md). */
/* Sources: the paper, scrolled to the passage. */
.app .leaf {
  width: auto;
  margin: 1.231em 1.231em 0;
  padding: 1.846em 1.846em 3em;
  border-width: 1px;
  border-radius: 0.5em 0.5em 0 0;
  color: var(--t3);
  font-size: 0.8125em;
  line-height: 1.7;
}
.app .leaf p + p {
  margin-top: 1.1em;
}
.app .leaf p b {
  color: inherit;
  font-weight: 510;
}
.app .leaf mark.trace {
  padding-block: 0.1em;
  border: 0;
  border-radius: 0.2em;
  background: var(--rest);
  box-decoration-break: clone;
  -webkit-box-decoration-break: clone;
  color: var(--t1);
}
/* Notes. */
.an {
  padding: 1em;
}
.an + .an {
  border-top: 1px solid var(--border);
}
.an .h4,
.ac .h4 {
  display: flex;
  align-items: start;
  gap: 0.6em;
  color: var(--t2);
  font-size: 0.8125em;
  font-weight: 510;
  letter-spacing: inherit;
  line-height: 1.45;
}
/* A heading's default margin (1.33em above and below) doubled the gap over
   each note's title and over the draft's (user, 2026-10-02, on the same gap
   in the scenes: "too much padding above this... the same in other areas
   too"). The canvas cards lost theirs on 2026-10-04 (user, on a card: "the
   top and bottom padding here is unnecessary, I want nice crisp design"),
   and the cards and arrows were laid out again to their new heights. */
.an .h4,
.ac .h4,
.adr .h4 {
  margin: 0;
}
.app .mark {
  flex: none;
  width: 1.5em;
  height: 1.5em;
}
.app .mark .ic {
  width: 0.9em;
  height: 0.9em;
}
/* A PASSAGE, AS THE APP DRAWS ONE SINCE 2026-10-04 (`components/
   passageStyle`, `PassageMark`, `SourceQuoteBox`; user, the same day: "we
   changed the UI a bit, especially for the quotes... update this first hero
   section"). One filled card with NO border: a first column holding a quote
   mark, then the words, then who and where in the source's SHORT name
   ("Shiode et al. 2015 · p. 1"), with no file icon. It was a bordered box
   with a file icon and the paper's full title. The words of the passage
   being followed stay in the text colour (this page's one-bright-thing
   rule); in the app a passage is a tier quieter than the writing round it. */
.an blockquote,
.ac blockquote,
.apop .q {
  display: grid;
  grid-template-columns: 1.1em minmax(0, 1fr);
  column-gap: 0.5em;
  margin: 0.667em 0 0;
  padding: 0.667em 0.833em 0.667em 0.667em;
  border-radius: 0.833em;
  background: var(--rest);
  color: var(--t1);
  font-size: 0.75em;
  line-height: 1.5;
}
/* The mark is one line of the passage tall and centred in it, so it sits on
   the first line. */
.app .qm {
  width: 1.1em;
}
.an blockquote span,
.an cite,
.ac blockquote span,
.apop .q span {
  grid-column: 2;
}
.an blockquote {
  line-height: 1.625;
}
.an cite {
  display: block;
  overflow: hidden;
  margin-top: 0.15em;
  color: var(--t4);
  font-size: 0.92em;
  font-style: normal;
  text-overflow: ellipsis;
  white-space: nowrap;
}
/* A note further down the list: its title and where it is from. */
.an .src {
  margin-top: 0.364em;
  padding-left: 3.05em;
  color: var(--t4);
  font-size: 0.6875em;
}
.an p {
  margin-top: 0.667em;
  color: var(--t3);
  font-size: 0.75em;
  line-height: 1.5;
}
/* Canvas. */
.acv {
  position: relative;
  height: calc(100% - 2.75em);
}
.acv-links {
  position: absolute;
  top: 0;
  left: 0;
  width: 23em;
  height: 40em;
}
.acv-links .arc {
  fill: none;
  stroke: var(--foreground-dim);
  stroke-linecap: round;
  stroke-width: 1.25;
}
.acv-links .ah {
  fill: none;
  stroke: var(--foreground-dim);
  stroke-linecap: round;
  stroke-linejoin: round;
  stroke-width: 1.5;
}
.acv-links .lb {
  fill: var(--t3);
  font-family: inherit;
  font-size: 10px;
  letter-spacing: 0;
  paint-order: stroke;
  stroke: var(--background);
  stroke-linejoin: round;
  stroke-width: 7px;
  text-anchor: middle;
}
.ac {
  --fill: var(--panel);
  position: absolute;
  padding: 0.75em;
  border: 1px solid transparent;
  border-radius: 0.75em;
}
.ac .h4 {
  font-size: 0.75em;
}
.ac blockquote span {
  display: -webkit-box;
  overflow: hidden;
  -webkit-box-orient: vertical;
  -webkit-line-clamp: 3;
}
.ac.lit {
  --fill: var(--surface-2);
}
.ac.k1 {
  top: 1.5em;
  left: 6.5em;
  width: 13em;
}
.ac.k2 {
  top: 11em;
  left: 1.5em;
  width: 15em;
}
.ac.k3 {
  top: 24.5em;
  left: 8.5em;
  width: 12.5em;
}
/* Drafts. */
.adr {
  padding: 1.5em 1.5em 0;
}
.adr .h4 {
  color: var(--t2);
  font-size: 1.1875em;
  font-weight: 590;
  letter-spacing: -0.012em;
  line-height: 1.3;
}
.adr p {
  margin-top: 0.923em;
  color: var(--t3);
  font-size: 0.8125em;
  line-height: 1.65;
}
.adr .ref {
  padding: 0.1em 0.35em;
  border-radius: 0.35em;
  background: var(--wash);
  color: var(--t2);
  white-space: nowrap;
}
.apop {
  margin-top: 1em;
  padding: 1em;
  border: 1px solid transparent;
  border-radius: 0.75em;
  box-shadow: 0 0 2em color-mix(in oklab, var(--panel) 80%, transparent);
  font-size: 0.75em;
  line-height: 1.5;
}
.apop > span {
  display: block;
  color: var(--t4);
}
.apop .q {
  margin-top: 0.667em;
  font-size: 1em;
}
.apop .go {
  display: inline-flex;
  align-items: center;
  height: 2.2em;
  margin-top: 0.667em;
  padding-inline: 0.75em;
  border: 1px solid var(--border);
  border-radius: 0.6em;
  background: var(--wash);
  color: var(--t3);
  font-weight: 510;
}
/* THE WINDOW IS LIT FROM ITS TOP LEFT, AS LINEAR LIGHTS ITS OWN HERO WINDOW
   (user, 2026-10-02, pointing at Linear's: "that beautiful gradient through
   its UI... gives it that premium look... use that kind of selective
   gradient as well, but stay within our color tokens"). Nothing in Linear's
   window is one flat fill. Read off linear.app the same day, and each piece
   here is one of theirs, in this page's tokens and in em:
     - the frame is a step lighter than the page (16,17,18 on 8,9,10), with
       a soft blot of white at 3% in its top left corner (240px, blurred
       80): here `--panel` spreading from that corner and dying into
       `--background`, with `--wash` over the corner;
     - a SHINE on the frame's edge: a second 1px border of white at 8%, cut
       by a 200px radial mask at 8% 0%, so the edge is brightest at the top
       left and ordinary everywhere else;
     - (Linear also lights each view and each card inside the window: a
       glow in its corner and an edge that runs from bright to faint. Those
       were built here and taken out on 2026-10-04; see below.)
     - the window sinks into the page at its right (360px, to 80% there; to
       a quarter here, because this window ends in the draft, which has to
       be read, and its text is grey already).
   Not taken: Linear's paper grain (the user removed grain from this site),
   the shine's slow travel round the edge, and the pale light on the ground
   under Linear's window (this one dissolves into the page at its foot).
   An edge with a gradient is drawn as BACKGROUND LAYERS, not as a second
   element: the fill is clipped to the padding box and the edge's colours
   lie under it in the border box, behind a transparent border. So the fill
   has to be opaque, which is why the quote boxes (a wash) keep plain
   edges. */
.app-win {
  position: relative;
  background:
    radial-gradient(26em 18em at 4% 0%, var(--wash) 0%, transparent 100%),
    radial-gradient(70em 44em at 0% 0%, var(--panel) 0%, transparent 100%),
    var(--background);
}
.app-win::before,
.app-win::after {
  position: absolute;
  z-index: 2;
  border-radius: inherit;
  content: "";
  pointer-events: none;
}
.app-win::before {
  inset: 0;
  background: linear-gradient(
    90deg,
    transparent calc(100% - 20em),
    color-mix(in oklab, var(--background) 25%, transparent) 100%
  );
}
/* Where the shine sits on the window's edge. Registered so it can be moved
   smoothly (see WHAT MOVES); it rests at 8% along the top. */
@property --shine-x {
  syntax: "<percentage>";
  inherits: false;
  initial-value: 8%;
}
@property --shine-y {
  syntax: "<percentage>";
  inherits: false;
  initial-value: 0%;
}
.app-win::after {
  inset: -1px;
  border: 1px solid oklch(1 0 0 / 0.08);
  border-bottom: 0;
  mask-image: radial-gradient(
    14em 14em at var(--shine-x, 8%) var(--shine-y, 0%),
    #000 0%,
    rgb(0 0 0 / 0.6) 30%,
    rgb(0 0 0 / 0.2) 50%,
    transparent 70%
  );
}
/* On the light page the window's light is its paler top left corner alone:
   a wash there is a smudge (it is black at an alpha in this mode), and a
   white shine on a pale edge shows nothing. */
@media (prefers-color-scheme: light) {
  .app-win {
    background:
      radial-gradient(70em 44em at 0% 0%, var(--panel) 0%, transparent 100%),
      var(--background);
  }
  .app-win::after {
    display: none;
  }
}
/* INSIDE THE WINDOW EVERYTHING IS FLAT, WITH A CRISP HAIRLINE EDGE (user,
   2026-10-04: "remove the gradient type effect from the panels themselves
   and only keep it for the main window container. I want crisp hairline
   borders for the UI itself"). The four views, the canvas, its cards, the
   paper's sheet and the citation's card each had a glow in their top left
   corner and an edge that ran from bright to faint. Only the window keeps
   its light now: the fill above, the shine on its edge, and the fall-off at
   its right.
   THE EDGES AND FILLS ARE THE SCENES' OWN VALUES (user, the same day: "use
   the same values for the background color and border color/border px so
   it's like a thin hairline and crisp UI"). The hero drew 1px edges at 8%
   while every scene below drew 0.5px at 12%, so the hero looked heavier.
   Now, as in `.win`: a window, a view, a card and a floating card are
   0.5px `--hairline-strong`; the paper's sheet, a rule between notes, the
   line under a view's name and a button are 0.5px `--hairline`. Fills are
   the same three steps: `--background` (canvas), `--panel` (a view, a
   card), `--surface-2` (the sheet, the lit card, what floats). */
.app-pn {
  background: var(--panel);
}
.acv {
  background: var(--background);
}
.ac {
  border-color: var(--border);
  background: var(--fill);
}
.app .leaf {
  border-color: var(--border);
  background: var(--fill);
}
.app .leaf {
  --fill: var(--surface-2);
}
.apop {
  border-color: var(--border);
  background: var(--surface-2);
}
/* WHAT MOVES.
   WHEN THE PAGE LOADS, ONCE, AS LINEAR'S HERO COMES IN (user, 2026-10-02:
   "Linear has a really nice smooth fade in animation you see at the top
   when you first visit the site. Can you add that to my website as well?").
   Read out of linear.app's own hero code that day, and these are its
   numbers:
     - each half of the headline comes up out of a blur: from clear, blurred
       by 10px and 20% of its height low, over about a second, the first
       half starting at 0.4s and the second at 0.5s;
     - the window's empty frame fades in over half a second from 0.4s;
     - what is IN the window fades in over 1.5s from 1.3s, eased in and out
       (cubic-bezier(0.455, 0.03, 0.515, 0.955)).
     - THE SHINE TRAVELS INTO ITS CORNER (added when the user said "yes, add
       the travelling shine too"): it starts on the window's left edge a
       quarter of the way down, runs up it and round the corner to its
       resting place 8% along the top, in half a second from 0.4s on the
       same ease, and fades in as it goes (from 0.6s). Linear moves it by
       animating the mask's position; here the two numbers of that position
       are registered properties (--shine-x, --shine-y) so CSS can do the
       same.
   The corner of light fades in with the frame, and the tree fades in when
   its script has drawn it (`.hero-tree`).
   THEN the pulse under the passage, for as long as the window is there:
   the paper, the note, the card, the citation, half a second apart, every
   six seconds, starting once the window has arrived.
   Each rule above is the resting state and the keyframes only give the
   start, so with reduced motion on the hero simply stands finished. */
.hero h1 > span {
  display: inline-block;
}
@media (prefers-reduced-motion: no-preference) {
  .hero h1 > span {
    animation: hero-word 1s cubic-bezier(0.25, 0.1, 0.25, 1) 0.4s both;
  }
  .hero h1 > span + span {
    animation-delay: 0.5s;
  }
  .app-win {
    animation: hero-fade 0.5s ease 0.4s both;
  }
  .app-win::after {
    animation:
      shine-travel 0.5s cubic-bezier(0.455, 0.03, 0.515, 0.955) 0.4s both,
      hero-fade 0.75s ease 0.6s both;
  }
  .app-bar,
  .app-panels {
    animation: hero-fade 1.5s cubic-bezier(0.455, 0.03, 0.515, 0.955) 1.3s both;
  }
  .app .leaf mark.trace,
  .an.lit blockquote,
  .ac.lit blockquote,
  .apop .q {
    animation: thread 6s ease-in-out calc(3.4s + var(--nth, 0) * 0.5s) infinite
      both;
  }
  .an.lit blockquote {
    --nth: 1;
  }
  .ac.lit blockquote {
    --nth: 2;
  }
  .apop .q {
    --nth: 3;
  }
}
/* THE HERO'S LOOP (a mock for the user to judge, 2026-10-06; hero-loop.js
   has the ask and keeps the time). The window follows one passage through
   its four panels. Every rule here hangs on `data-step`, which only the
   script sets: with no step the window is the finished picture, as before.
     6  the panels fade out (the turn of the loop)
     0  the paper alone, nothing marked yet
     1  the passage is highlighted, a line at a time
     2  it becomes a note, which opens at the top of the list
     3  the note is laid on the canvas and its arrow is drawn
     4  the draft cites it, and the citation's card opens
     5  "Go to passage" is pressed: the paper is lit alone again and the
        passage in it is found
   One panel is lit at a time (1 to 4) and comes a little forward; the rest
   stand back at a third. The chip for that panel in the bar is lit. (A line
   of words naming each step stood in the bar too; the user had it out the
   same day, "get rid of these". The loop says it without words.) A thing that has not happened yet
   is simply absent, with no transition, so going back to the start is
   instant behind the fade.
   THE WINDOW ARRIVES AT STEP 0, not at the finished picture (hero-loop.js
   has why): the script puts the step on before the panels' fade-in shows
   anything, and the loop begins when that fade-in ends. */
/* A step takes the window's fade-in off its bar and panels, so their
   opacity is free for the turn of the loop. Not while the window is still
   arriving: it arrives at the first step, and that fade-in is how. */
.app-win[data-step]:not(.is-arriving) :is(.app-bar, .app-panels) {
  animation: none;
}
.app-win[data-step] .app-panels {
  transition: opacity 450ms ease;
}
.app-win[data-step="6"] .app-panels {
  opacity: 0;
}
.app-win[data-step] .app-pn {
  opacity: 0.34;
  transition:
    opacity 600ms ease,
    scale 600ms cubic-bezier(0.2, 0.8, 0.2, 1),
    box-shadow 600ms ease;
}
.app-win:is([data-step="0"], [data-step="1"], [data-step="5"], [data-step="6"])
  .p-src,
.app-win[data-step="2"] .p-notes,
.app-win[data-step="3"] .p-canvas,
.app-win[data-step="4"] .p-draft {
  opacity: 1;
}
.app-win[data-step="1"] .p-src,
.app-win[data-step="2"] .p-notes,
.app-win[data-step="3"] .p-canvas,
.app-win[data-step="4"] .p-draft {
  z-index: 1;
  box-shadow: 0 0 2.5em var(--background);
  scale: 1.045;
}
.app-pn {
  transform-origin: 50% 0;
}
.p-src {
  transform-origin: 0 0;
}
.p-draft {
  transform-origin: 100% 0;
}
.app-win[data-step] .app-chips i {
  transition:
    background 400ms,
    color 400ms;
}
.app-win[data-step="1"] .app-chips i:nth-child(1),
.app-win[data-step="2"] .app-chips i:nth-child(2),
.app-win[data-step="3"] .app-chips i:nth-child(3),
.app-win[data-step="4"] .app-chips i:nth-child(4) {
  background: var(--wash-strong);
  color: var(--t1);
}
/* The pulse that ran every six seconds belongs to the still picture; a
   step takes it off.
   5: "GO TO PASSAGE" GOES THERE (user, 2026-10-06, on the ending as first
   built: "the effect after the cursor presses 'go to passage', that
   animation could be a bit better"; of four ideas they chose this one).
   Until then all four panels lit and the four copies of the passage pulsed
   softly one after another, draft to paper: a small change in four places,
   with nothing to say the button had gone anywhere. Now the press does what
   the button does in the app. The other three panels stand back, the paper
   is lit alone, and the passage in it is found: its wash brightens and a
   ring in the theme's accent opens out from each of its lines, twice. One
   thing to look at, and the round ends where it began. It waits for the
   window to slide back to the paper, where the window slides. */
.app-win[data-step]
  :is(.leaf mark.trace, .an.lit blockquote, .ac.lit blockquote, .apop .q) {
  animation: none;
}
.app-win[data-step="5"] .leaf mark.trace .tw-done {
  animation: passage-found 0.95s ease-out 0.7s 2 both;
}
@keyframes passage-found {
  0% {
    background-color: var(--rest);
    box-shadow: 0 0 0 0 color-mix(in oklab, var(--primary) 75%, transparent);
  }
  30% {
    background-color: var(--peak);
  }
  100% {
    background-color: var(--rest);
    box-shadow: 0 0 0 0.6em transparent;
  }
}
/* A HAND DOES IT (user, 2026-10-06: "add a smoothly moving cursor that
   highlights the text and sources, types in the text for the notes, just
   like a blinking cursor typing, ... clicks and drags the box over to the
   canvas and does thinking, maybe connecting one note to the other note ...
   types and cites. So that users can sort of see this smooth interaction
   between all of them"). Until then each thing simply appeared when its
   step came. Now a pointer makes it: it drags across the passage, writes
   the note's own sentence, carries the note to the canvas, draws its arrow
   and names it, writes the draft's sentence and opens its citation.
   hero-loop.js moves the pointer and keeps the time; it says what has
   happened so far with a class on the window, and these rules show it:
     has-note   the note is in the list
     has-card   the note is on the canvas
     has-arc    its arrow is being drawn (the script draws it)
     has-arrow  the arrow is finished, and has its head
     has-ref    the draft has its citation
     has-pop    the citation's card is open
   A thing that has not happened yet is absent, with no transition, so the
   turn of the loop is instant behind the fade.
   WORDS THAT ARE WRITTEN ARE ALREADY THERE, UNSEEN (`.tw-rest`): the line
   breaks never move as a word is typed, and nothing under it is pushed
   down a line at a time. The script moves letters from the unseen part to
   the seen part (`.tw-done`). The passage is the same two parts, with the
   unseen one plain text: the wash is on the part the pointer has passed. */
.app-cursor,
.hand {
  position: absolute;
  top: 0;
  left: 0;
  z-index: 6;
  width: 1.5rem;
  height: 1.5rem;
  filter: drop-shadow(0 1px 1.5px oklch(0 0 0 / 0.5));
  opacity: 0;
  pointer-events: none;
  will-change: translate;
}
.app-cursor svg,
.hand svg {
  display: block;
  overflow: visible;
  width: 100%;
  height: 100%;
  transform-origin: 21% 12.5%;
  transition: scale 110ms ease;
}
/* FIGMA'S POINTER, NOT THE COMPUTER'S (user, 2026-10-06: "can you use
   figma's cursor style instead of the standard cursor?"). Looked at on
   figma.com that day: an arrowhead with no tail, filled with a colour, a
   white edge round it and a soft shadow. Here the colour is the theme's
   accent, the same as the line under the bar. Figma's also carries a name
   on a tag; this one does not, because a name says a second person is in
   the project, and sharing is not something the site can promise yet. */
.app-cursor path,
.hand path {
  fill: var(--primary);
  stroke: oklch(1 0 0);
  stroke-linejoin: round;
  stroke-width: 1.5;
}
.app-cursor.down svg,
.hand.down svg {
  scale: 0.84;
}
.app-win[data-step]:not(.is-arriving) .app-cursor {
  opacity: 1;
  transition: opacity 300ms ease;
}
/* It goes when it has pressed "Go to passage" (user, 2026-10-06: "fade the
   pointer out on the press"): what follows is in the paper, at the other
   side of the window, and a pointer left on the button held the eye there. */
.app-win:is([data-step="5"], [data-step="6"]):not(.is-arriving) .app-cursor {
  opacity: 0;
}
/* The note in the hand, between the list and the canvas: a copy of the card
   it becomes, so letting go of it changes nothing that is seen. */
.app.stage .app-ghost {
  top: 0;
  left: 0;
  z-index: 5;
  width: 15em;
  box-shadow:
    var(--seat),
    0 0.75rem 2rem oklch(0 0 0 / 0.35);
  font-size: 1rem;
  opacity: 0;
  pointer-events: none;
  will-change: translate;
}
.app-win .typed .tw-rest {
  visibility: hidden;
}
.app-win .lb .tw-rest {
  fill: transparent;
  stroke: transparent;
}
.tw-caret {
  position: relative;
}
.tw-caret::after {
  position: absolute;
  inset-block: 0;
  left: 0;
  width: 1.5px;
  border-radius: 1px;
  background: var(--foreground);
  content: "";
  opacity: 0;
}
.lb-caret {
  fill: var(--foreground);
  opacity: 0;
}
.tw-caret.on::after,
.lb-caret.on {
  opacity: 1;
  animation: caret 1.06s steps(1, end) infinite;
}
/* It stands still while letters are being written, as a real one does, and
   blinks when the writing stops. */
.tw-caret.busy::after,
.lb-caret.busy {
  animation: none;
}
/* 1: the passage. */
.app-win[data-step] .leaf mark.trace {
  background: none;
  color: inherit;
}
.app-win[data-step] .leaf mark.trace .tw-done {
  padding-block: 0.1em;
  border-radius: 0.2em;
  background: var(--rest);
  box-decoration-break: clone;
  -webkit-box-decoration-break: clone;
  color: var(--t1);
}
/* 2: the note opens at the top of the list and the others move down. */
.app-win[data-step] .an.lit {
  overflow: hidden;
  max-height: 16em;
  transition:
    max-height 700ms cubic-bezier(0.2, 0.8, 0.2, 1),
    padding 700ms cubic-bezier(0.2, 0.8, 0.2, 1),
    opacity 500ms ease 200ms;
}
.app-win[data-step]:not(.has-note) .an.lit {
  max-height: 0;
  padding-block: 0;
  opacity: 0;
  transition: none;
}
.app-win[data-step]:not(.has-note) .an.lit + .an {
  border-top-color: transparent;
}
/* 3: the card is put down, and its arrow is drawn to the claim it supports.
   The head waits for the line to arrive. */
.app-win[data-step]:not(.has-card) .ac.k2,
.app-win[data-step]:not(.has-arc) .acv-links .arc.new {
  opacity: 0;
}
.app-win[data-step]:not(.has-arrow) .acv-links .arc.new {
  marker-end: none;
}
/* 4: the citation is written, and its card opens under the pointer. */
.app-win[data-step] .adr .ref {
  transition:
    opacity 250ms ease,
    background 400ms ease,
    color 400ms ease;
}
.app-win[data-step] .apop {
  transition:
    opacity 350ms ease,
    translate 350ms ease;
}
.app-win[data-step="4"] .adr .ref {
  background: var(--wash-strong);
  color: var(--t1);
}
.app-win[data-step]:not(.has-ref) .adr .ref,
.app-win[data-step]:not(.has-pop) .apop {
  opacity: 0;
  transition: none;
}
.app-win[data-step]:not(.has-pop) .apop {
  translate: 0 0.5em;
}
.app-win .apop .go.down {
  background: var(--wash-strong);
}
/* THE HERO WINDOW AT ITS REAL SIZE, ON A STAGE (a mock for the user to
   judge, 2026-10-06). They pointed at cursor.com's hero: "I really like the
   mockup aesthetic of Cursor's website. It just feels very clean and crisp.
   Can you please study it and learn from it". Measured there that day:
     THE WINDOW IS NEVER SHRUNK. It is 1080 by 620 at every width, and a
       narrow screen is shown part of it. Its words are a real app's: 12 and
       13px, 11 for the small ones. Ours was scaled to fit, so its words
       were about 10px at a laptop's width and smaller in places.
     IT STANDS ON SOMETHING: a framed picture behind it, with a real shadow
       (two soft layers) and a 1px ring at 10% white. Ours stood on the bare
       page, in the page's colour, and faded out at its foot.
     ITS CHROME IS NEARLY NOTHING: a 28px bar, three grey dots, a title at
       70%. Ours had coloured lights, the mark, the name and four chips.
     INSIDE IT IS FLAT: columns parted by 1px lines. Ours was cards inside
       panels inside the window, three boxes deep.
   So, on `.app.stage`: the window is set at one size (its em is fixed, not
   a share of the width), and the stage shows as much of it as fits. (The
   stage was a framed field of fine dots at first; the note over
   `.app.stage` below says what it is now.) THE
   LOOP MOVES THE WINDOW, NOT A SPOTLIGHT: at each step the window slides so
   the panel the passage is in is in the middle of the stage (or as near as
   the window's ends allow), which is also what makes the loop work on a
   phone. At the end it slides back to the paper as the passage pulses back.
   This is laid over the older rules by the one class, so taking the class
   off the element gives the old window back while this is being judged. */
/* THE WINDOW STANDS IN A BAND OF LIGHT THAT RISES FROM ITS FOOT, NOT IN A
   BOX (user, 2026-10-06, on the first build of this, which had a framed
   stage of fine dots, and pointing at linear.app's hero: "Instead of that
   box container, I think I want there to just be a gradient going upwards,
   like how Linear has it"). Measured on linear.app that day:
     the band is the whole width of the screen and 748 tall, cut off hard at
       its foot, with the next section's dark page straight under it;
     its ground is the page's colour for the first tenth, then runs evenly
       to a light grey (208, 214, 224) at the foot, under a radial wash that
       is clear at the bottom's middle and half the page's colour at the
       corners (52.53% by 57.5%, centred on the foot);
     the window is 1320 by 720, a step lighter than the page (16, 17, 18),
       with a 1px edge of white at 8% and 12px corners, and no shadow of its
       own. It starts 32 in from the left and 28 of the band shows under it.
   Here the band's far colour is the theme's second text tier on the dark
   page (which is that same light grey), and a mid grey on the light one,
   where light rising from the foot would be invisible. */
.app.stage {
  /* The window is as wide as the page's column (the headline's width), and
     never narrower than 68rem: under that the band shows part of it and the
     loop slides it. */
  --win-w: max(68rem, 100cqw);
  --band: 0rem;
  --foot: 1.75rem;
  --left: max(var(--gutter), (100vw - 84rem) / 2);
  --room: min(0px, 100vw - var(--win-w) - 2 * var(--left));
  --stage-far: var(--foreground-secondary);
  position: relative;
  width: 100vw;
  height: 37rem;
  margin-left: calc((100cqw - 100vw) / 2);
  border-radius: 0;
  background:
    radial-gradient(
      52.53% 57.5% at 50% 100%,
      transparent 0%,
      color-mix(in oklab, var(--background) 50%, transparent) 100%
    ),
    linear-gradient(var(--background) 10%, var(--stage-far) 100%);
  font-size: 1rem;
  mask-image: none;
}
@media (prefers-color-scheme: light) {
  .app.stage {
    --stage-far: color-mix(in oklab, var(--foreground) 34%, var(--background));
  }
}
.app.stage .app-win {
  position: absolute;
  top: var(--band);
  left: var(--left);
  overflow: hidden;
  width: var(--win-w);
  height: calc(100% - var(--band) - var(--foot));
  /* A border, not a ring outside the box: the window starts at the band's
     very top, and the band cut the ring's top line off (user: "the top of
     the mock is cut off, you can't see the border at all"). */
  border: 1px solid var(--border);
  border-radius: 0.75rem;
  background: var(--panel);
  box-shadow: none;
  translate: var(--pan, 0px) 0;
  transition: translate 1000ms cubic-bezier(0.45, 0, 0.2, 1);
}
.app.stage .app-win::before,
.app.stage .app-win::after {
  display: none;
}
/* The name stands after the dots, not in the middle as Cursor's does: this
   window is wider than most screens, and its middle is often out of sight. */
.app.stage .app-bar {
  gap: 0.75rem;
  height: 1.75rem;
  padding: 0 0.625rem;
  border-bottom: 1px solid var(--border);
}
.app.stage .app-lights {
  gap: 0.375rem;
  margin: 0;
}
.app.stage .app-lights i {
  width: 0.625rem;
  height: 0.625rem;
  background: var(--border);
}
.app.stage .app-mark,
.app.stage .app-chips {
  display: none;
}
.app.stage .app-bar b {
  color: var(--t2);
  font-size: 0.75rem;
  font-weight: 400;
  opacity: 0.7;
}
.app.stage .app-panels {
  gap: 0;
  height: calc(100% - 1.75rem);
  padding: 0;
  mask-image: linear-gradient(#000 calc(100% - 3rem), transparent);
}
.app.stage .app-pn {
  border: 0;
  border-radius: 0;
  background: none;
  box-shadow: none;
}
.app.stage .app-pn + .app-pn {
  border-left: 1px solid var(--border);
}
.app.stage .app-tab {
  border-bottom: 0;
}
.app.stage .leaf {
  margin: 0;
  padding: 0.25em 1.25em 3em;
  border: 0;
  border-radius: 0;
  background: none;
  box-shadow: none;
}
.app.stage .acv {
  background: none;
}
/* A REAL APP'S SMALL TYPE, SO THE WHOLE WINDOW FITS A LAPTOP (user,
   2026-10-06, on the first build of this, which was a fixed 1428px wide in
   13 and 14px and slid from panel to panel at every width: "Can we use the
   smallness of the type, similar to what Cursor has, so that everything
   mainly just fits within a normal screen width without this scrolling
   effect needing to take place? Let's not get rid of this animated
   scrolling yet... Cursor is very well designed. The UI itself is very
   nicely laid out, compact"). With that the window is the page's column
   wide and all four panels are in it at once from about 1170px up, where
   the window no longer slides at all; narrower than that it keeps 68rem
   and the loop still slides it.

   ONE SCALE, AND FEW RULES (the same day: "Are there any ways we could
   improve the typographic hierarchy, color, and weights here so that
   there's more harmony in general? Look at Cursor as an example"). Counted
   in Cursor's window: three sizes, two weights, one line height to a size,
   one colour at three strengths. Counted here before this: four sizes, three
   weights (one used once), 12px words on four different line heights, four
   kinds of title, and the same note set a size smaller on the canvas than
   in the list beside it. Now:
     SIZES   11 on 16  a label, a source line, an arrow's words
             12 on 18  everything that is read
             14 on 20  the two documents' titles (the paper's, the draft's)
     WEIGHTS 510 for a title, a label and a lead word; 400 for the rest
     COLOURS --t1 the passage being followed, and nothing else
             --t2 titles and the window's name
             --t3 what is read; labels
             --t4 where something is from
     and one letter spacing throughout (the window's own).
   A note's title and its quote are the same on the canvas as in the list
   (the canvas is drawn at the size that makes them so, which is why its
   column is the widest: it needs 21rem). A note's own sentence is reading,
   so it is 12 like the draft's, not 11. The citation in the draft is a
   title's tier at rest and is lit only in the step where it is written.
   The sizes are in rem, on each part, because the older rules under them
   are in em and were meant to shrink together. Add a part: give it one of
   the three sizes and one of the four colours, and no third weight. */
.app.stage .app-tab {
  height: 2rem;
  padding-inline: 0.875rem;
}
.app.stage .app-tab b {
  font-size: 0.6875rem;
  line-height: 1rem;
}
.app.stage .leaf {
  padding: 0.125rem 0.875rem 2rem;
  font-size: 0.75rem;
  letter-spacing: inherit;
  line-height: 1.125rem;
}
/* The paper's name, and under it the name its citations use. "Shiode et al.
   2015" is on the paper, in the note and in the draft, word for word: that
   is the thread the eye follows from panel to panel. */
.app .leaf .h5 {
  margin: 0;
  color: var(--t2);
  font-size: 1.25em;
  font-weight: 510;
  letter-spacing: -0.01em;
  line-height: 1.3;
}
.app .leaf .authors {
  margin: 0.4em 0 1em;
  color: var(--t4);
  font-size: 0.92em;
}
.app.stage .leaf .h5,
.app.stage .adr .h4 {
  font-size: 0.875rem;
  font-weight: 510;
  letter-spacing: -0.01em;
  line-height: 1.25rem;
}
.app.stage .leaf .authors {
  margin: 0.25rem 0 0.75rem;
  font-size: 0.6875rem;
  line-height: 1rem;
}
.app.stage .leaf p + p {
  margin-top: 0.625rem;
}
/* THE SOURCE IS SET AS A PAPER (user, 2026-10-06, pointing at the panel when
   it was a title, "Shiode et al. 2015" and one paragraph: "can you give
   this source the look of a legit scientific paper?"). It has what the
   first page of this paper has: the title, all five authors with the marks
   to their affiliations, and a structured abstract, "Background" then
   "Results". Every word is the paper's own (it is in the sample library,
   CC BY 4.0). The Background is its first sentence only: the passage the
   window follows is in Results and has to stay clear of the fade at the
   window's foot (measured at 1100, 1180, 1344 and 1440 wide). No new size,
   weight or colour.
   NO RUNNING HEAD AND NO "Research / Open Access" LINE. The first build had
   both over the title (the journal, year and article number over a rule,
   then the two labels, as the publisher sets them); the user pointed at the
   two the same day: "remove these". The paper starts at its title. */
.app.stage .leaf .authors sup {
  font-size: 0.75em;
  line-height: 0;
}
.app.stage .leaf p.p-abs {
  margin: 0;
  color: var(--t2);
  font-weight: 510;
}
.app.stage .leaf p.p-bg {
  margin-top: 0.125rem;
}
/* The canvas is a drawing: its cards and arrows are placed in its own em.
   It is drawn at 1rem to the em, the size at which a card's title and its
   quote come to the 12px they are in the notes. It needs 21rem across at
   that size, which is why its column is the widest. The arrows' words are
   set in the drawing's own units, which are pixels at this size. */
.app.stage .app-panels {
  grid-template-columns: 27fr 20fr 31.5fr 21.5fr;
}
.app.stage .acv {
  font-size: 1rem;
}
.app.stage .acv-links .lb {
  font-size: 11px;
}
/* The lower arrow's words stood too near the column's right edge at this
   size and lost their last letter there; they are moved in a little. */
.app.stage .acv-links .lb:not(.new) {
  translate: -1.125rem 0;
}
.app.stage .adr {
  padding: 0.875rem 1rem 0;
}
.app.stage .adr p {
  margin-top: 0.625rem;
}
.app.stage .adr .ref {
  color: var(--t2);
}
.app.stage .app-win[data-step="4"] .adr .ref {
  color: var(--t1);
}
.app.stage .apop {
  margin-top: 0.75rem;
  padding: 0.75rem;
}
/* A LINE THAT RUNS ALONG THE FOOT OF THE WINDOW'S BAR AS THE LOOP PLAYS
   (user, 2026-10-06: "show a progress bar-type effect as the bottom border
   of this top bar... slowly moving from left to right as the animation
   takes place", so the eye can follow the source panel, then notes, canvas
   and drafts). Its leading end is always over the panel whose turn it is:
   it crosses that panel in the time the step lasts and reaches the panel's
   right edge as the step ends. The four stops are the panels' right edges
   (the columns' shares, 27, 20, 31.5 and 21.5, added up), so change them
   with the columns. The time is the step's own, handed over by the script
   as `--hold`, so the two cannot drift apart. The paper has two steps (the
   page alone, then the highlight): the line crosses it at one speed through
   both. At the end it stays whole while the passage pulses back, and goes
   out with the panels at the turn of the loop. It is the theme's accent, on
   the bar's own hairline (2px at first; the note below). It moves with the
   window where the window slides. */
/* IT IS A PLAIN 1px LINE. It was 2px, then (after a picture of the loading
   line under Dia's address field) 1px with a glow: bright at its head and a
   soft light rising into the bar. The user had the glow out the same day:
   "remove the glow, it's taking up too much attention". Do not give it one
   again. */
.app.stage .app-bar::after {
  position: absolute;
  bottom: -1px;
  left: 0;
  width: 0;
  height: 1px;
  background: var(--primary);
  content: "";
  opacity: 0;
  pointer-events: none;
}
.app.stage .app-win[data-step]:not([data-step="6"]) .app-bar::after {
  opacity: 1;
  transition:
    width var(--hold, 2s) linear,
    opacity 300ms ease;
}
/* The turn: it fades where it stands, whole, and only then is emptied, so
   the next round starts from nothing and it is never seen running back. */
.app.stage .app-win[data-step="6"] .app-bar::after {
  transition:
    opacity 400ms ease,
    width 0s linear 400ms;
}
.app.stage .app-win[data-step="0"] .app-bar::after {
  width: 8.4%;
}
.app.stage .app-win[data-step="1"] .app-bar::after {
  width: 27%;
}
.app.stage .app-win[data-step="2"] .app-bar::after {
  width: 47%;
}
.app.stage .app-win[data-step="3"] .app-bar::after {
  width: 78.5%;
}
.app.stage .app-win:is([data-step="4"], [data-step="5"]) .app-bar::after {
  width: 100%;
}
/* The loop here: no panel is pushed forward or shadowed, the rest stand back
   a little less (they are legible now), and the window slides. `--pan` puts
   the middle of the step's panel (a share of the window's width) at the
   middle of the stage, held between the window's two ends. */
.app.stage .app-win[data-step] .app-pn {
  opacity: 0.45;
  box-shadow: none;
  scale: 1;
}
.app.stage
  .app-win:is(
    [data-step="0"],
    [data-step="1"],
    [data-step="5"],
    [data-step="6"]
  )
  .p-src,
.app.stage .app-win[data-step="2"] .p-notes,
.app.stage .app-win[data-step="3"] .p-canvas,
.app.stage .app-win[data-step="4"] .p-draft {
  opacity: 1;
}
.app.stage .app-win[data-step="2"] {
  --pan: clamp(var(--room), 50vw - var(--left) - 0.37 * var(--win-w), 0px);
}
.app.stage .app-win[data-step="3"] {
  --pan: clamp(var(--room), 50vw - var(--left) - 0.6275 * var(--win-w), 0px);
}
.app.stage .app-win[data-step="4"] {
  --pan: clamp(var(--room), 50vw - var(--left) - 0.8925 * var(--win-w), 0px);
}
@media (prefers-reduced-motion: reduce) {
  .app.stage .app-win {
    transition: none;
  }
}

@keyframes hero-word {
  from {
    opacity: 0;
    filter: blur(10px);
    translate: 0 20%;
  }
}
@keyframes hero-fade {
  from {
    opacity: 0;
  }
}
@keyframes shine-travel {
  0% {
    --shine-x: 0%;
    --shine-y: 25%;
  }
  50% {
    --shine-x: 3%;
    --shine-y: 4%;
  }
  100% {
    --shine-x: 8%;
    --shine-y: 0%;
  }
}
@keyframes thread {
  0%,
  30%,
  100% {
    background-color: var(--rest);
    box-shadow: 0 0 0 0 transparent;
  }
  9% {
    background-color: var(--peak);
    box-shadow: 0 0 1.25em 0 var(--halo);
  }
}
/* A SCENE IN CODE: the app's own windows rebuilt in HTML, as linear.app
   builds its product shots (user, 2026-10-02: "these UIs are actual code, not
   screenshots... I want to follow this beautiful product shot convention as
   much as possible"). The first one is the AI row. Read off Linear's own row
   of agent windows the same day, and each figure here is theirs:
     - four windows of 400 by 560 at a 12px corner, 12px apart, in one row
       centred on the page, so two are in focus and the outer two run off;
     - the row sits in a stage as wide as the page (1344 at most), 72px
       above the windows for the glow, and the stage is cut by THREE masks at
       once: clear to solid over 400px from the left, the same from the
       right, and clear to solid over 300px from the bottom;
     - a window is the well's fill (15,16,17 on 8,9,10 there: `--panel` on
       `--background` here), a half-pixel edge of white at 12%, a shadow of
       0 2px 32px at 25% black, and a brighter edge at the corner nearest the
       middle of the row (a second border under a 200px radial mask);
     - inside: a 48px bar (a 16px mark, the name at 13 weight 500, a small
       outlined chip at 11), the question as a bubble at the right (13 on 20,
       10px padding, 8px corner), then the reply at 13 on 20 in the second text tier, and lists in a
       box with a half-pixel edge at a 6px corner.
   What is inside each window is BODHI'S: the Ask window's own parts
   (components/workspace: the question bubble, the
   steps line, numbered citation chips in the accent at 15%, the sources
   list, the agent's contract line and plan card), in the site's tokens, so
   the whole row follows light and dark. The words are the sample library's
   own; the contract line is the app's own sentence (lib/agent/contract).
   Icons are the app's (Lucide, ISC), drawn once in a sprite and reused.
   Left out on purpose (user, 2026-10-02: "remove noise that doesn't need to
   be there"): the window's dock and close buttons, and the line under a
   question naming the project it went with. Do not add window chrome or
   meta lines back. */
.sprite {
  position: absolute;
  width: 0;
  height: 0;
}
.ic {
  flex: none;
  width: 0.875rem;
  height: 0.875rem;
  fill: none;
  stroke: currentColor;
  stroke-linecap: round;
  stroke-linejoin: round;
  stroke-width: 2;
}
.ic.small {
  width: 0.75rem;
  height: 0.75rem;
}
.scene {
  --win: 25rem;
  /* Linear's side fade is 400px on a 1344 stage, which leaves the outer edge
     of each window in focus at about two thirds strength. The same share is
     kept at every width: the fade is 0.7576 of the room beside the two. */
  --fade: max(2rem, (100% - 2 * var(--win) - 0.75rem) * 0.7576);
  position: relative;
  overflow: hidden;
  width: min(100%, 84rem);
  height: 39.5rem;
  /* Linear: 96 from the head to the stage, whose windows start 72 lower. */
  margin: 6rem auto 0;
  background: radial-gradient(
    34rem 11rem at 50% 12rem,
    oklch(1 0 0 / 0.05),
    transparent 70%
  );
  mask-image:
    linear-gradient(to right, transparent 0, #000 var(--fade)),
    linear-gradient(to left, transparent 0, #000 var(--fade)),
    linear-gradient(to top, transparent 0, #000 18.75rem);
  mask-composite: intersect;
}
.scene-track {
  position: absolute;
  top: 4.5rem;
  left: 50%;
  display: flex;
  gap: 0.75rem;
  translate: -50% 0;
}
.win {
  position: relative;
  flex: none;
  overflow: hidden;
  width: var(--win);
  height: 35rem;
  border: 1px solid var(--border);
  border-radius: 0.75rem;
  background: var(--panel);
  color: var(--foreground-secondary);
  font-size: 0.8125rem;
  letter-spacing: -0.003em;
  line-height: 1.25rem;
}
/* Light catching the corner nearest the middle of the row. */
.win.lit-right::after,
.win.lit-left::after {
  position: absolute;
  inset: -1px;
  border: 1px solid oklch(1 0 0 / 0.08);
  border-radius: inherit;
  content: "";
  mask-image: radial-gradient(
    200px 200px at var(--lit, 100%) 0%,
    #000 0%,
    rgb(0 0 0 / 0.6) 30%,
    rgb(0 0 0 / 0.2) 50%,
    transparent 70%
  );
  pointer-events: none;
}
.win.lit-left::after {
  --lit: 0%;
}
@media (prefers-color-scheme: light) {
  :root .win::after {
    display: none;
  }
}
.win-bar {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 0.625rem;
  height: 3.5rem;
  padding: 0 1.25rem;
  border-bottom: 1px solid var(--border);
}
.win-who {
  display: inline-flex;
  align-items: center;
}
.win-who {
  gap: 0.375rem;
  min-width: 0;
}
.win-who .ic {
  width: 1rem;
  height: 1rem;
  stroke-width: 1.75;
}
.win-who b {
  font-weight: 510;
}
.win-who i {
  color: var(--muted-foreground);
  font-size: 0.75rem;
  font-style: normal;
  line-height: 1.25rem;
}
.win-body {
  padding: 0.375rem;
}
/* The question: a bubble at the right. */
.win .say {
  width: fit-content;
  max-width: calc(100% - 2.75rem);
  margin: 0.25rem 0.25rem 0 auto;
  padding: 0.625rem;
  border-radius: 0.5rem;
  background: var(--wash);
  box-shadow: 0 0 0 1px var(--hairline-faint);
  color: var(--foreground);
}
.win .step {
  display: flex;
  align-items: center;
  gap: 0.25rem;
  padding: 1rem 0.875rem 0.5rem;
  color: var(--muted-foreground);
  font-size: 0.75rem;
  line-height: 1rem;
}
.win .reply {
  padding: 0 0.875rem 0.75rem;
}
/* NO AMBER IN ANY SCENE (user, 2026-10-02, after the hero lost its own:
   "make the amber neutral in the other scenes too"). A citation, a
   highlight, the quote a note was made from and the step in hand were all
   the app's amber accent. Each is now white at an alpha on the window, as
   in the hero: a wash for a highlight or a chip, a stronger wash and the
   text colour for the one that is being pointed at. The type marks keep
   their colours, and one highlight on the paper keeps its violet. */
.win {
  --mark: var(--wash-strong);
  --mark-strong: oklch(1 0 0 / 0.17);
  /* The four tiers of text a scene spends, as the hero window does: white
     for the ONE thing the scene is about, then titles, running text, and
     what only says where something comes from. */
  --t1: var(--foreground);
  --t2: var(--foreground-secondary);
  --t3: var(--muted-foreground);
  --t4: color-mix(in oklab, var(--muted-foreground) 70%, var(--background));
  --t-claim: oklch(0.66 0.17 282);
  --t-event: oklch(0.8 0.14 85);
  --t-person: oklch(0.66 0.16 255);
  --t-group: oklch(0.72 0.1 185);
  --t-concept: oklch(0.68 0.2 0);
  --t-place: oklch(0.72 0.16 150);
  --t-org: oklch(0.64 0.2 300);
  --t-note: oklch(0.72 0.15 55);
}
@media (prefers-color-scheme: light) {
  :root .win {
    --mark-strong: oklch(0 0 0 / 0.12);
    --t-claim: oklch(0.5 0.19 282);
    --t-event: oklch(0.62 0.14 80);
    --t-person: oklch(0.52 0.17 255);
    --t-group: oklch(0.52 0.1 185);
    --t-concept: oklch(0.56 0.2 0);
    --t-place: oklch(0.52 0.15 150);
    --t-org: oklch(0.5 0.2 300);
    --t-note: oklch(0.6 0.15 55);
  }
}
.win .cite {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-width: 1.125rem;
  height: 1.125rem;
  margin-inline: 0.1875rem 0;
  padding-inline: 0.25rem;
  border-radius: 0.25rem;
  background: var(--mark);
  color: var(--foreground-secondary);
  font-size: 0.75rem;
  font-weight: 510;
  letter-spacing: 0;
  line-height: 1;
  vertical-align: 0.0625rem;
}
.win .group {
  padding: 0.75rem 0.875rem 0.5rem;
  font-weight: 510;
}
.win .box {
  margin: 0 0.1875rem;
  padding: 0.6875rem;
  border: 1px solid var(--border);
  border-radius: 0.375rem;
  list-style: none;
}
.win .sources {
  display: grid;
  gap: 0.75rem;
}
.win .sources li {
  display: flex;
  align-items: start;
  gap: 0.5rem;
}
.win .sources .cite {
  flex: none;
  margin: 0.125rem 0 0;
}
.win .sources span {
  font-weight: 510;
}
.win .sources i {
  color: var(--muted-foreground);
  font-style: normal;
  font-weight: 400;
  white-space: nowrap;
}
.win .contract {
  display: flex;
  align-items: start;
  gap: 0.375rem;
  padding: 1rem 0.875rem 0.75rem;
  color: var(--muted-foreground);
  font-size: 0.75rem;
  line-height: 1.125rem;
}
.win .contract .ic {
  margin-top: 0.125rem;
}
.win .steps {
  margin-top: 0.25rem;
}
.win .say + .steps {
  margin-top: 1rem;
}
.win .steps p {
  display: flex;
  align-items: center;
  gap: 0.375rem;
  color: var(--muted-foreground);
  font-size: 0.75rem;
  line-height: 1rem;
}
.win .steps p span {
  margin-left: auto;
  font-variant-numeric: tabular-nums;
}
.win .steps ul {
  display: grid;
  gap: 0.5rem;
  margin: 0.625rem 0 0;
  padding: 0;
  list-style: none;
}
.win .steps li {
  display: flex;
  align-items: center;
  gap: 0.5rem;
}
.win .steps li .ic {
  color: var(--muted-foreground);
}
.win .steps .doing {
  color: var(--foreground);
}
.win .steps .doing .ic {
  color: var(--foreground-secondary);
}
.win .steps .todo,
.win .steps .todo .ic {
  color: var(--foreground-dim);
}
/* The reply on its way: the app's one status line in words. */
.win .writing {
  padding: 1rem 0.875rem 0;
  color: var(--muted-foreground);
  font-weight: 510;
}
/* THE LINE THAT SAYS THE AGENT IS BUSY SHIMMERS (user, 2026-10-06, pointing
   at "Writing notes…": "add shimmering effect here to make it seem like ai
   is working"). A band of the brightest text colour crosses the words from
   left to right, the way a chat shows that a reply is on its way. The words
   are painted with a picture two and a half times their width whose two
   ends are the resting colour, so each pass starts and ends with nothing
   lit and the loop has no seam. Both such lines have it ("Thinking…" too).
   With reduced motion on they are the plain resting colour. */
@media (prefers-reduced-motion: no-preference) {
  .win .writing {
    width: fit-content;
    background: linear-gradient(
        100deg,
        var(--muted-foreground) 40%,
        var(--foreground) 50%,
        var(--muted-foreground) 60%
      )
      100% 0 / 250% 100% no-repeat;
    -webkit-background-clip: text;
    background-clip: text;
    color: transparent;
    animation: working 2.2s linear infinite;
  }
}
@keyframes working {
  to {
    background-position: 0 0;
  }
}
/* THE DISCOVER SCENE: two windows in code, as Linear sets its "Intake"
   shot. Measured there: ONE card in full focus at the left (480 wide, a 16px
   corner, 24px out past the text's left edge, 28px above the stage), and
   behind it a wide view that starts under the card, runs off the right edge
   of the page and dissolves: solid to 40% then clear by 90% downward, and
   clear over its last stretch at the right. (Here it stays solid to 66%:
   the card that names the paper sits two thirds of the way down the map.)
   Here the card is the paper search and the view behind is the citation
   map. The map is drawn from the app's own (the user's screenshot of it,
   2026-10-02): the years across the top, three rows (your papers, papers
   both of yours cite, papers one of them cites), a page-shaped mark for each
   paper sized by its citations, the arrows from your papers back to the one
   both cite, and the card the app shows for the paper under the pointer.
   Every word and figure in it is from that screenshot. It is an SVG with a
   viewBox, so it keeps its proportions at any width, and its colours are
   tokens, so it follows light and dark.
   `--edge` is where the page's text column begins. */
.scene-duo {
  --edge: max(var(--gutter), (100% - 80rem) / 2);
  /* The same edge for anything deeper inside, where a percentage would mean
     something else: the scene is a size container, and `cqw` is its width. */
  --edge-c: max(var(--gutter), (100cqw - 80rem) / 2);
  container-type: inline-size;
  position: relative;
  overflow: hidden;
  height: 35rem;
  margin-top: 6rem;
}
/* No glow behind the window in front (user, 2026-10-02, pointing at it in
   five scenes: "this weird gradient thing... feels unnecessary"). It was a
   25rem disc of white at 5%, after Linear's, and the scene's top edge cut
   it off in a straight line. Do not put it back. */
.back {
  position: absolute;
  top: 2.75rem;
  right: 0;
  bottom: 0;
  left: calc(var(--edge) + 24.5rem);
  /* The stretch at the right over which the view fades: Linear's share of
     the stage, within limits. */
  --tail: clamp(5rem, 14%, 12rem);
  mask-image:
    linear-gradient(#000 0%, #000 66%, transparent 100%),
    linear-gradient(to left, transparent 0, #000 var(--tail));
  mask-composite: intersect;
}
.win.pane {
  width: calc(100% + 3rem);
  height: 34rem;
}
.pane-tools {
  display: flex;
  align-items: center;
  gap: 0.375rem;
  padding: 0 0.875rem 0.75rem 1.125rem;
}
.pill {
  display: inline-flex;
  align-items: center;
  gap: 0.25rem;
  height: 1.625rem;
  padding-inline: 0.625rem;
  border-radius: 9999px;
  color: var(--muted-foreground);
  font-size: 0.75rem;
  white-space: nowrap;
}
.pill.on {
  background: var(--wash-strong);
  color: var(--foreground);
  font-weight: 510;
}
.pill.menu {
  margin-left: auto;
  border: 1px solid var(--border);
}
/* The chart keeps clear of the stretch at the right where the view fades. */
.map {
  display: block;
  width: min(47.5rem, 100% - var(--tail) - 3.5rem);
  height: auto;
  margin-left: 0.5rem;
  --you: oklch(0.62 0.19 262);
  --both: oklch(0.5 0.1 62);
  --one: oklch(0.42 0.065 162);
}
@media (prefers-color-scheme: light) {
  :root .map {
    --you: oklch(0.55 0.2 262);
    --both: oklch(0.68 0.13 62);
    --one: oklch(0.72 0.08 162);
  }
}
.map .g {
  stroke: var(--hairline-faint);
  stroke-width: 1;
}
.map text {
  fill: var(--muted-foreground);
  font-family: inherit;
  font-size: 12px;
  letter-spacing: 0;
}
.map .yr {
  font-size: 10px;
  text-anchor: middle;
}
.map .ln {
  fill: var(--foreground);
  font-size: 11px;
  font-weight: 600;
}
.map .ct {
  fill: var(--muted-foreground);
  font-weight: 400;
}
.map .you {
  fill: var(--you);
  stroke: oklch(1 0 0 / 0.85);
  stroke-width: 1;
}
@media (prefers-color-scheme: light) {
  .map .you {
    stroke: oklch(0 0 0 / 0.75);
  }
}
.map .both {
  fill: var(--both);
}
.map .one {
  fill: var(--one);
}
.map .arc {
  fill: none;
  stroke: var(--muted-foreground);
  stroke-width: 1;
}
.map .tip {
  fill: var(--surface-2);
  stroke: var(--hairline-strong);
  stroke-width: 1;
}
.map .btn {
  fill: var(--wash);
  stroke: var(--hairline-strong);
  stroke-width: 0.5;
}
.map .t-btn {
  fill: var(--foreground);
  font-weight: 510;
  text-anchor: middle;
}
.map .t-title {
  fill: var(--foreground);
  font-size: 12.5px;
  font-weight: 510;
}
/* The window in front: the paper search. One step lighter than the window
   behind it, with a deeper shadow, as Linear's front card is. */
.win.front {
  position: absolute;
  top: 1rem;
  left: calc(var(--edge) - 1.5rem);
  z-index: 1;
  width: 27rem;
  height: auto;
  border-radius: 0.75rem;
  background: var(--panel);
  box-shadow: var(--shadow-float);
}
.front .win-body {
  padding: 0 0.75rem 0.5rem;
}
/* The paper search has no bar of its own: its field is its first row. */
.scene-find .front .win-body {
  padding-top: 0.75rem;
}
/* The search field and the Add buttons are rounded boxes, not pills (user,
   2026-10-02, after the extension's panel: "yes, change the paper search
   pills too"). The small button takes a smaller corner than the field. */
.win .search-field {
  display: flex;
  align-items: center;
  height: 2.5rem;
  padding-inline: 0.875rem;
  border: 1px solid var(--border);
  border-radius: var(--radius);
  background: var(--wash);
  color: var(--foreground);
}
.win .results {
  margin: 0.375rem 0 0;
  padding: 0;
  list-style: none;
}
.win .results li {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 1rem;
  padding: 0.875rem 0.5rem;
}
.win .results li + li {
  border-top: 1px solid var(--border);
}
.win .results li > span:first-child {
  min-width: 0;
}
.win .results b,
.win .results i {
  display: block;
}
.win .results b {
  color: var(--foreground);
  font-weight: 510;
}
.win .results i {
  margin-top: 0.125rem;
  color: var(--muted-foreground);
  font-size: 0.75rem;
  font-style: normal;
  line-height: 1.125rem;
}
.win .add {
  display: grid;
  flex: none;
  place-items: center;
  height: 1.625rem;
  padding-inline: 0.6875rem;
  border: 1px solid var(--border);
  border-radius: 0.375rem;
  color: var(--muted-foreground);
  font-size: 0.75rem;
  font-weight: 510;
}
.win .add.on {
  border-color: transparent;
  background: var(--action);
  color: var(--action-foreground);
}
/* DISCOVER SAYS ONE THING: the paper you did not know to look for (user,
   2026-10-02, going through the scenes after the hero: "what part of the
   screenshots are important to show? which can be faded and given less
   importance so that the user knows what to look at easily?"). That paper
   is Snow's, from 1856, which both of your papers cite. It is in both
   windows, and it is the only thing in white in either: the first result
   in the search, with the one Add button that is lit, and the card on the
   map, with its mark and the arrows that lead to it from your two papers.
   Everything else is a step or two down: the other two results, the query,
   the windows' names, the years, the row names, the tools. The papers only
   one of yours cites are fainter still: they are the crowd it was found
   in. "Cited by both" is neutral now (it was an amber brown), so the found
   paper's mark can be the brightest thing on the chart. */
.scene-find .win-who b,
.scene-find .search-field {
  color: var(--t2);
}
.scene-find .win-who .ic {
  color: var(--t4);
}
.scene-find .win-who i {
  color: var(--t3);
}
.scene-find .pill,
.scene-find .pill.on {
  color: var(--t3);
}
.scene-find .pill.on {
  background: var(--wash);
  color: var(--t2);
}
.scene-find .pill.menu {
  border-color: var(--hairline);
}
.scene-find .results b {
  color: var(--t2);
}
.scene-find .results i,
.scene-find .add {
  color: var(--t3);
}
.scene-find .add {
  border-color: var(--hairline);
}
.scene-find .results .hit b {
  color: var(--t1);
}
.scene-find .results .hit i {
  color: var(--t3);
}
.scene-find .add.on {
  border-color: transparent;
  color: var(--action-foreground);
}
.scene-find .map {
  --both: var(--foreground-dim);
}
.scene-find .map text {
  fill: var(--t3);
}
.scene-find .map .ln {
  fill: var(--t2);
  font-weight: 510;
}
.scene-find .map .ct {
  fill: var(--t4);
}
.scene-find .map .one {
  fill-opacity: 0.55;
}
.scene-find .map .found {
  fill: var(--foreground-secondary);
  stroke: oklch(1 0 0 / 0.2);
  stroke-width: 5;
  paint-order: stroke;
}
@media (prefers-color-scheme: light) {
  .scene-find .map .found {
    stroke: oklch(0 0 0 / 0.2);
  }
}
.scene-find .map .arc {
  stroke: var(--foreground-secondary);
  stroke-opacity: 0.7;
}
.scene-find .map .t-btn {
  fill: var(--t3);
}
.scene-find .map .btn {
  stroke: var(--hairline);
}
.scene-find .map .t-title {
  fill: var(--t1);
}
/* Too narrow for the two side by side: the map takes the width, dissolving
   downward only, and the search sits over its lower part. */
/* DISCOVER'S SCENE AS ONE TIGHT WINDOW (a trial for the user to judge,
   2026-10-06, after the hero: "I want to see what using similar principles
   from Cursor's UI mockups might look like for these other mockups... I
   like how it's a very tight UI that makes it feel like it's the actual
   product UI itself... but I like the really nice gradients that are in
   this linear style that we have today"). So both:
     FROM CURSOR (measured for the hero; the note over `.app.stage`): one
       window, not two floating ones; a 28px bar with three grey dots and
       the project's name; flat columns parted by a hairline; a real app's
       type, 12 to read and 11 under it; rows packed close, the chosen one
       on a wash, with no rules between them.
     FROM LINEAR, KEPT: the window runs off the right of the page and
       washes out there, and washes out at its foot.
   The search is the window's left column and the map its main area.
   All of it hangs on the class `tight` and on its own parts (`.twin`), so
   none of the other scenes' rules are touched. The two-window scene this
   replaces is in git (the commit before this one). */
.scene-find.tight {
  --side: 19.5rem;
  --tail: clamp(2.5rem, 6vw, 5rem);
  overflow: hidden;
  height: 32rem;
  padding: 0;
  mask-image:
    linear-gradient(#000 0%, #000 58%, transparent 100%),
    linear-gradient(to left, transparent 0, #000 var(--tail));
  mask-composite: intersect;
}
.twin {
  --t1: var(--foreground);
  --t2: var(--foreground-secondary);
  --t3: var(--muted-foreground);
  --t4: color-mix(in oklab, var(--muted-foreground) 70%, var(--background));
  position: absolute;
  inset: 0.5rem -3rem 0 var(--edge);
  overflow: hidden;
  border: 1px solid var(--border);
  border-bottom: 0;
  border-radius: 0.75rem 0.75rem 0 0;
  background: var(--panel);
  color: var(--t2);
  font-size: 0.75rem;
  letter-spacing: -0.003em;
  line-height: 1rem;
  text-align: left;
}
.twin-bar {
  display: flex;
  align-items: center;
  gap: 0.75rem;
  height: 1.75rem;
  padding: 0 0.625rem;
  border-bottom: 1px solid var(--border);
}
.twin-bar .app-lights {
  gap: 0.375rem;
  margin: 0;
}
.twin-bar .app-lights i {
  width: 0.625rem;
  height: 0.625rem;
  background: var(--border);
}
.twin-bar b {
  color: var(--t2);
  font-weight: 400;
  opacity: 0.7;
}
.twin-cols {
  display: grid;
  grid-template-columns: var(--side) minmax(0, 1fr);
  height: calc(100% - 1.75rem);
}
.twin-side {
  border-right: 1px solid var(--border);
}
.twin-tab {
  display: flex;
  align-items: center;
  gap: 0.375rem;
  height: 2rem;
  margin: 0;
  padding-inline: 0.875rem;
  color: var(--t3);
  font-size: 0.6875rem;
  font-weight: 510;
}
.twin-tab .ic {
  color: var(--t4);
}
.twin .search-field {
  display: flex;
  align-items: center;
  gap: 0.4375rem;
  height: 1.75rem;
  margin: 0 0.625rem;
  padding-inline: 0.5rem;
  border-radius: 0.375rem;
  background: var(--wash);
  box-shadow: inset 0 0 0 1px var(--border);
  color: var(--t1);
}
.twin .search-field .ic {
  flex: none;
  color: var(--t4);
}
.twin .results {
  margin: 0.5rem 0.375rem 0;
  padding: 0;
  list-style: none;
}
.twin .results li {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 0.75rem;
  padding: 0.5rem;
  border-radius: 0.375rem;
}
.twin .results li.hit {
  background: var(--wash);
}
.twin .results li > span:first-child {
  min-width: 0;
}
.twin .results b,
.twin .results i {
  display: block;
}
.twin .results b {
  color: var(--t2);
  font-weight: 510;
}
.twin .results .hit b {
  color: var(--t1);
}
.twin .results i {
  margin-top: 0.125rem;
  color: var(--t3);
  font-size: 0.6875rem;
  font-style: normal;
  line-height: 0.9375rem;
}
.twin .add {
  display: grid;
  flex: none;
  place-items: center;
  height: 1.375rem;
  padding-inline: 0.5rem;
  border-radius: 0.3125rem;
  box-shadow: inset 0 0 0 1px var(--border);
  color: var(--t3);
  font-size: 0.6875rem;
  font-weight: 510;
}
.twin .add.on {
  background: var(--action);
  box-shadow: none;
  color: var(--action-foreground);
}
.twin-main {
  min-width: 0;
}
/* The map is its own size where there is room (from about 1280px), and
   under that it is fitted to what can be seen: your two papers stand at its
   far right, and a map whose right end ran off the screen had lost the
   point of it. It always stops short of the wash at the window's right. */
.scene-find.tight .map {
  width: min(
    47.5rem,
    100cqw - var(--edge-c) - var(--side) - var(--tail) - 1rem
  );
  max-width: none;
  margin-left: 0.375rem;
}
@media (max-width: 64rem) {
  .scene-duo {
    overflow: visible;
    height: auto;
    padding-inline: var(--gutter);
  }
  .back {
    position: static;
    overflow: hidden;
    mask-image: linear-gradient(#000 0%, #000 62%, transparent 100%);
  }
  /* As tall as the map in it, which shrinks with the page. */
  .win.pane {
    width: 100%;
    height: auto;
    padding-bottom: 1rem;
  }
  .map {
    width: calc(100% - 1rem);
  }
  .pill.menu {
    display: none;
  }
  .win.front {
    position: relative;
    top: auto;
    left: auto;
    width: auto;
    max-width: 27rem;
    /* A share of the width, so it covers the same part of the map at any
       size: the papers at the bottom, never the card that names one. */
    margin: -9% auto 0;
  }
}
/* READ, THINK AND WRITE, each its own section (user, 2026-10-02: "it might
   be better to give read, think and write their own sections, instead of
   doing a slider. I don't think the user will wait for the slider to
   finish"). Each is a head, ONE scene, and three names under a hairline,
   which is how Linear sets a chapter. What each has to say:
     Read    a note is made from a passage and can always get back to it;
     Think   the notes can be laid out and seen whole, four ways;
     Write   the draft is written from the notes, and every citation opens
             its passage.
   The NOTES window is in all three, in focus: the notes are what the three
   share. Behind it is that chapter's view. The side alternates down the page
   (Discover has its window at the left, Read at the right, Think at the
   left, Write at the right), so the view behind runs off the left edge of
   the page in Read and Write and off the right in Think.
   Every view is built from the app's own parts and the sample library's own
   words (the user's screenshots, 2026-10-02): the note card (a type mark, a
   title, the quoted passage with its source and page, a sentence of your
   own); a page of Shiode et al. 2015 with its three highlights (the paper
   is CC BY 4.0 and credited in the footer); the canvas with its labelled
   arrows; the graph; the timeline; the draft with a citation opened. The map
   was a picture cut from a screenshot first; it is a drawing now (`.soho`). */
.scene-work {
  height: 37rem;
}
/* ONE FADE FOR THE SCENE, AND BOTH PANELS RUN OFF ITS FOOT (Linear's way;
   the note over `.win` has the study). The view behind and the notes in
   front have no bottom edge: one mask over the scene takes its last 10rem.
   The view behind still dissolves at its far side by itself. */
.scene-work {
  mask-image: linear-gradient(to top, transparent 0, #000 10rem);
}
.scene-work .back {
  left: calc(var(--edge) + 21.5rem);
  mask-image: linear-gradient(to left, transparent 0, #000 var(--tail));
}
.scene-work .win.front {
  bottom: 0;
  height: auto;
  border-bottom: 0;
  border-radius: 0.75rem 0.75rem 0 0;
}
.scene-work .win.pane {
  border-bottom: 0;
  border-radius: 0.75rem 0.75rem 0 0;
}
.scene-work .win.pane {
  height: 35rem;
}
/* FLIPPED: the notes at the right, and the view behind running off the LEFT
   edge of the page. The window starts past the edge, so its own left side is
   never seen, and what is in it starts where the page's text does. It fades
   over the margin only, never over its words. */
.scene-work.flip .back {
  right: calc(var(--edge) + 21.5rem);
  left: 0;
  mask-image: linear-gradient(
    to right,
    transparent 0,
    #000 max(1rem, var(--edge-c) - 1.5rem)
  );
}
.flip .win.pane {
  width: calc(100% + 1rem);
  margin-left: -1rem;
}
.flip .pane .win-bar,
.flip .reader,
.flip .draft {
  padding-left: calc(var(--edge-c) + 1rem);
}
.flip .win.front {
  right: calc(var(--edge) - 1.5rem);
  left: auto;
}
.flip .draft {
  max-width: calc(var(--edge-c) + 47rem);
}
/* The passage the first note was made from sits four fifths of the way down
   the page (81% to 85% of the view, measured), so Read's scene is taller
   than the others and its view stays whole to 90%. The page starts at the
   paper's title: the journal's running line and banner above it were taken
   out when the passage became the one thing to look at, because they pushed
   it into the fade. Add a line above the passage and measure again. */
.scene-read .win.pane {
  height: calc(28rem + var(--player-h));
}
/* READ, DRAWN THE WAY LINEAR DRAWS A MOCKUP (user, 2026-10-05, with a
   picture of Linear's issue list and diff beside this scene: "the UI
   mockups from our website don't feel as crisp and clean as they do in
   Linear. Can you study it in detail, in extremely close detail, and improve
   one of the mockups first, the one I've highlighted"). A TRIAL ON THIS
   SCENE ONLY; the others still wear `.win`'s older look.
   Read off linear.app's stage that day (its list panel and diff panel),
   beside this scene as it was:
     TEXT IS NEVER SMALL OR FAINT. Linear's smallest is 12; names are 13 at
       weight 510 in its second tier (208,214,224), running text and meta 13
       or 12 in its third (138,143,152), and its dimmest (98,102,109) is for
       an icon and one file path. Here the paper was set at 11 in that
       dimmest grey (about 3:1 on its sheet) and the titles in the third
       tier: a whole tier too dim, and smaller. Now a title is the second
       tier, running text the third at 13 on 21, and nothing is under 12.
       The fourth tier is for an icon, the quote mark and a source line.
     AN EDGE IS ONE SOLID PIXEL. A panel there is a flat fill (15,16,17 on
       a page of 8,9,10) with an inset ring of 1px in a solid colour
       (35,37,42). Here an edge was half a pixel of white at 12%. Now it is
       1px of `--border`, the ramp's own solid edge, drawn as an inset ring,
       and a rule inside a panel is 1px of the same.
     WHAT FLOATS HAS A HALO OF THE PAGE, NOT A SHADOW: `0 0 32px` of the
       page's colour at 80%. Here it was two black shadows, 64 and 32 deep.
     ONE FILL. Both their panels are the same colour, a step over the page.
       Here the sheet and the notes were two steps up. Now both are
       `--panel`, and a quote's card is a 3% wash (8% for the one in focus).
     ROOM. Their header is 60 tall with 32 at its sides and a rule under it;
       code is padded 32. Here: 56 with 20, a rule under it, and 32 to 40
       round the paper.
     THE STAGE FADES, NOT THE THINGS IN IT. One mask over the whole stage
       dissolves its last 300px, and both panels run off its foot with no
       bottom edge. Here each window faded by itself and the notes were a
       whole card with words dissolving inside it. Now the sheet and the
       notes both run to the foot and one mask takes the last 10rem.
     A HIGHLIGHT IS A SOFT FILL with a 2px corner and no line under it.
   Not taken: their grain and corner glow (both were removed from this site
   at the user's ask), and the 22px frame round their back panel.
   ONE THING IS STILL THE POINT: the passage and its quote in the first note
   are the only pure white, and the passage is the only strong fill. But the
   rest is no longer pushed down to make it so; the fill does that work.
   The paper is ONE CARD with no window behind it and no bar over it (user,
   earlier the same day: "remove this text... I just want it to be one card
   that's faded out from the bottom"). Its Methods paragraph is left out so
   the passage stands above the fade at the larger size: the passage ends at
   most 34rem down the scene (in the narrowest window that still sets the
   two side by side) and the fade starts at 35. Add a line above it and
   measure again. */
/* ONE SCENE, TWO SOURCES: the sheet turns from the paper to a video and
   back (the note over `.views` below). Both have to stand in one height, and
   the video needs the more: its player is the width of the sheet, so its
   height follows the window's. That height is worked out here from the
   window (`vw`, because the scene cannot measure itself; the same sums as
   the sheet's width and side padding below, so change them together), and
   stops growing at 22rem, past which the player is simply wider than 16:9.
   The scene is the back panel's top, the sheet's top padding, the player,
   and room for the two paragraphs under it, then the fade. The paper's
   passage ends 34rem down at most and the fade starts at 37 or lower. */
.scene-read {
  --edge-v: max(var(--gutter), (100vw - 80rem) / 2);
  --pad-v: clamp(2.5rem, (100vw - 64rem) * 0.3 + 2.5rem, 5.5rem);
  --player-h: min(
    22rem,
    (100vw - 2 * var(--edge-v) - 21.25rem - 2 * var(--pad-v)) * 0.5625
  );
  height: calc(28rem + var(--player-h));
}
.scene-read .back {
  top: 3.25rem;
}
.scene-read .win.pane {
  border: 0;
  background: none;
  box-shadow: none;
}
.scene-read .win.pane::after {
  display: none;
}
.scene-read .reader {
  height: 100%;
  padding-top: 0;
}
.scene-read.flip .back {
  mask-image: none;
}
/* THE TWO PANELS ARE ONE PICTURE AT ANY WIDTH (user, 2026-10-05, on a wide
   window: "the two cards just row apart. Linear doesn't really have that").
   The sheet was 44rem at most and started at the page's left, the notes
   hung from the page's right, and on a wide window a hole opened between
   them. Linear's back panel is as wide as its stage and its front panel
   lies over it, so nothing can part. So the sheet runs from where the
   page's text starts to a little way under the notes' left edge, whatever
   the width: it never stops short of them, and its lines never run on
   behind them. (The notes are 24rem wide and stand 1.5rem into the page's
   margin.) As it widens, the sheet's own margins widen with it, so its
   lines stay a readable length. */
.scene-read .leaf {
  width: calc(100cqw - 2 * var(--edge-c) - 21.25rem);
  /* Past the scene's foot whatever its words come to, so it has no bottom
     edge to show through the fade. */
  min-height: calc(29.5rem + var(--player-h));
  padding: 2rem 2.5rem 10rem;
  padding-inline: clamp(2.5rem, (100cqw - 64rem) * 0.3 + 2.5rem, 5.5rem);
  border: 0;
  border-radius: 0.75rem 0.75rem 0 0;
  background: var(--panel);
  box-shadow: inset 0 0 0 1px var(--border);
  font-size: 0.8125rem;
  letter-spacing: -0.01em;
  line-height: 1.3125rem;
}
.scene-read .leaf .h5 {
  margin-top: 0;
  font-size: 1.25rem;
  letter-spacing: -0.015em;
  line-height: 1.625rem;
}
.scene-read .leaf .authors {
  margin-top: 0.5rem;
  font-size: 0.75rem;
  line-height: 1.125rem;
}
.scene-read .leaf .abstract {
  margin-top: 1.25rem;
  padding-top: 1.25rem;
  border-top: 1px solid var(--border);
}
.scene-read .leaf .abstract p + p {
  margin-top: 0.75rem;
}
.scene-read .leaf p b {
  font-weight: 510;
}
/* THINK: four views in one window, and their names in its bar. Pressing a
   name shows that view (showcase.js). Until the script runs the names are
   not shown: a name that did nothing would be worse than none. */
.views {
  display: none;
  gap: 0.125rem;
  margin-right: auto;
  margin-left: 0.5rem;
}
.is-live .views {
  display: inline-flex;
}
button.pill {
  border: 0;
  background: none;
  cursor: pointer;
  font: inherit;
  font-size: 0.75rem;
  letter-spacing: inherit;
  transition:
    background 150ms,
    color 150ms;
}
button.pill:hover {
  color: var(--foreground);
}
button.pill.on {
  background: var(--wash-strong);
}
button.pill:focus-visible {
  outline: 1px solid var(--primary);
  outline-offset: 1px;
}
.stack {
  position: relative;
  height: calc(100% - 3.5rem);
}
.stack .body {
  position: absolute;
  inset: 0;
  visibility: hidden;
  opacity: 0;
  transition:
    opacity 250ms,
    visibility 0s 250ms;
}
.stack .body.is-on {
  visibility: visible;
  opacity: 1;
  transition: opacity 250ms;
}
.stack .canvas {
  height: 100%;
}
/* WHAT ELSE A CHAPTER COVERS, as Linear sets it under each of its scenes
   (user, 2026-10-02, pointing at Linear's row: "follow linear's design
   convention"). Measured there: the row starts 36px under the scene; the
   word "Features" stands at the left in the dimmest grey; the names are in
   the RIGHT half, where the head's paragraph is, in two columns with a
   hairline between them, each name 15 on a 28px row, in grey. No rule above
   and no boxes.
   Linear's names end in a "+" because each opens a panel about that
   feature. These open nothing, so they have no "+": a mark that promises a
   control would be a lie. */
.features {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  column-gap: var(--column-gap);
  margin-top: 2.25rem;
}
.features p {
  color: var(--foreground-dim);
}
.features ul {
  position: relative;
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  margin: 0;
  padding: 0;
  list-style: none;
}
.features ul::before {
  position: absolute;
  top: -0.5rem;
  bottom: -0.5rem;
  left: 50%;
  width: 1px;
  background: var(--hairline);
  content: "";
}
.features li {
  color: var(--muted-foreground);
}
.features li:nth-child(even) {
  padding-left: 2rem;
}
/* A name never wraps: where the two columns are too narrow to hold one on a
   line, the names stand in one column and the hairline goes. */
@media (max-width: 76rem) {
  .features ul {
    grid-template-columns: minmax(0, 1fr);
  }
  .features ul::before {
    display: none;
  }
  .features li:nth-child(even) {
    padding-left: 0;
  }
}
/* The notes. Their list runs past the window and fades, as a long list
   does. */
.win.notes {
  width: 24rem;
  height: 34rem;
}
.notes .win-body {
  height: calc(100% - 3.5rem);
  padding: 0 1.25rem;
}
/* margin 0: a heading's own default margin (1.33em above and below) put a
   gap over the first note's title twice the size of the gap under it (user,
   2026-10-02: "too much padding above this"). */
.note .h4 {
  display: flex;
  align-items: start;
  gap: 0.5rem;
  margin: 0;
  color: var(--foreground);
  font-size: inherit;
  font-weight: 510;
  letter-spacing: inherit;
  line-height: inherit;
}
.notes .note {
  padding-block: 1rem 1.125rem;
}
.notes .note + .note {
  border-top: 1px solid var(--border);
}
/* A note's type: a disc in the type's colour with its glyph, as the app
   marks it. */
.mark {
  display: grid;
  flex: none;
  place-items: center;
  width: 1.25rem;
  height: 1.25rem;
  border-radius: 50%;
  background: color-mix(in oklab, var(--t) 22%, transparent);
  color: var(--t);
}
.mark .ic {
  width: 0.75rem;
  height: 0.75rem;
}
/* A TYPE'S MARK IS FILLED (user, 2026-10-06, pointing at the quote glyph in
   a note's disc: "Use a filled version of all these icons in the notes
   panel. Make that change across all the mocks"). The glyph in the disc
   beside a note's title, wherever a note is drawn (the hero, Read, Think's
   notes and its canvas cards, Write), was a 2px outline; it is a solid
   shape now, from its own symbols (`i-…-fill` in index.html). The outline
   glyphs of the same names are untouched: they are still the icons used
   outside a disc. The concept's mark is an asterisk, which has no inside to
   fill, so its strokes are heavy instead (set on the symbol). */
.mark .ic {
  fill: currentColor;
  stroke: none;
}

.m-claim {
  --t: var(--t-claim);
}
.m-event {
  --t: var(--t-event);
}
.m-person {
  --t: var(--t-person);
}
.m-group {
  --t: var(--t-group);
}
.m-concept {
  --t: var(--t-concept);
}
/* A PASSAGE, AS THE APP DRAWS ONE SINCE 2026-10-04 (see the same note on
   the hero window's, `.an blockquote`): a filled card with no border, a
   first column with a FILLED quote mark, the words at 12 on a relaxed line,
   then who and where in the source's short name at 11, with no file icon. */
.note blockquote,
.pop .q {
  display: grid;
  grid-template-columns: 0.75rem minmax(0, 1fr);
  column-gap: 0.5rem;
  margin: 0.5rem 0 0;
  padding: 0.5rem 0.625rem 0.5rem 0.5rem;
  border-radius: 0.5rem;
  background: color-mix(in oklab, var(--foreground) 3%, transparent);
  color: var(--foreground-secondary);
  font-size: 0.8125rem;
  line-height: 1.25rem;
}
/* The mark: one line of the passage tall, so it sits on the first line. It
   is FILLED (user, 2026-10-04, as in the app: at this size the outline read
   as two loose hooks). */
.qm {
  width: 0.75rem;
  height: 1lh;
  fill: currentColor;
  color: var(--t4);
}
.note blockquote span,
.note cite,
.pop .q span {
  display: block;
  grid-column: 2;
}
.note.on blockquote {
  background: var(--mark);
  color: var(--foreground);
}
.note cite {
  overflow: hidden;
  margin-top: 0.25rem;
  color: var(--muted-foreground);
  font-size: 0.75rem;
  line-height: 1.125rem;
  font-style: normal;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.note p {
  margin-top: 0.5rem;
  color: var(--muted-foreground);
}
/* A PAGE OF THE PAPER, in the page's own design (user, 2026-10-02: "the pdf
   looks kind of crappy, please use your own design for it so it looks nicer
   and make it dark mode"). It was a copy of the journal's page first: pale
   paper, a maroon banner, a boxed abstract. Now it is a dark sheet one step
   lighter than the window, with the paper's own words set quietly: a small
   line for the journal, the title, the authors, and the abstract under a
   hairline with its lead words in the text colour. (The app can show a paper
   this way too: "Read papers in dark mode".) The words are still the
   paper's (Shiode et al. 2015, CC BY 4.0, credited in the footer). */
.reader {
  height: calc(100% - 3.5rem);
  padding: 0.25rem 2rem 0;
}
.leaf {
  width: 44rem;
  padding: 2rem 3rem 3rem;
  border: 1px solid var(--border);
  border-radius: 0.375rem 0.375rem 0 0;
  background: var(--surface-2);
  color: var(--foreground-secondary);
  font-size: 0.6875rem;
  letter-spacing: 0;
  line-height: 1.6;
}
.leaf p {
  color: inherit;
}
.leaf p b {
  color: var(--foreground);
  font-weight: 600;
}
.leaf .run {
  color: var(--muted-foreground);
  font-size: 0.625rem;
  line-height: 1.5;
}
.leaf .banner {
  display: flex;
  justify-content: space-between;
  margin-top: 1rem;
  padding-top: 0.75rem;
  border-top: 1px solid var(--hairline-strong);
  color: var(--muted-foreground);
  font-size: 0.625rem;
}
.leaf .banner b {
  color: var(--foreground-secondary);
  font-weight: 510;
}
.leaf .h5 {
  margin: 0.875rem 0 0;
  color: var(--foreground);
  font-size: 1.375rem;
  font-weight: 510;
  letter-spacing: -0.018em;
  line-height: 1.2;
}
.leaf .authors {
  margin-top: 0.625rem;
  color: var(--muted-foreground);
}
.leaf .abstract {
  margin-top: 1.125rem;
  padding-top: 0.875rem;
  border-top: 1px solid var(--hairline);
}
.leaf .abstract p + p {
  margin-top: 0.5rem;
}
.leaf mark {
  border-bottom: 1px solid var(--hairline-strong);
  background: var(--mark);
  color: var(--foreground);
}
.leaf mark.v {
  border-color: oklch(0.66 0.17 290 / 0.8);
  background: oklch(0.62 0.17 290 / 0.26);
}
/* The passage a note quotes: a stronger wash than a highlight's, as the app
   marks a passage you have just landed on. (The hero's passage is not this
   one: it is `mark.trace`, with the hero window's rules.) */
.leaf mark.land {
  border-bottom-color: color-mix(in oklab, var(--foreground) 45%, transparent);
  border-radius: 0.125rem;
  background: var(--mark-strong);
}
/* READ SAYS ONE THING: a note keeps its passage. The passage on the page
   and the same words quoted in the first note are the only pure white in
   the scene, and the passage is its one strong fill. The tiers are Linear's
   since 2026-10-05 (the note over `.scene-read` above): a title, a lead
   word and a note's title are the SECOND tier; running text, the other
   notes' quotes and the authors the third; an icon, the quote mark and a
   source line the fourth. (From 2026-10-02 until then everything but the
   passage sat a tier lower than this, which is what made the scene murky.)
   The other highlight on the page is a quiet violet fill: it says that
   marking is something you do often, without asking to be read. */
.scene-read .win-who b,
.scene-read .leaf .h5,
.scene-read .leaf p b,
.scene-read .note .h4 {
  color: var(--t2);
}
.scene-read .win-who i,
.scene-read .leaf,
.scene-read .leaf .authors,
.scene-read .note p,
.scene-read .note blockquote {
  color: var(--t3);
}
.scene-read .win-who .ic,
.scene-read .note cite {
  color: var(--t4);
}
.scene-read .note.on blockquote {
  color: var(--t1);
}
.scene-read .leaf mark {
  padding-block: 0.125rem;
  border: 0;
  border-radius: 0.1875rem;
  box-decoration-break: clone;
  -webkit-box-decoration-break: clone;
}
.scene-read .leaf mark.v {
  background: oklch(0.62 0.17 290 / 0.2);
  color: var(--t2);
}
.scene-read .leaf mark.land {
  background: var(--mark-strong);
  color: var(--t1);
}
/* THE SHEET TURNS FROM A PAPER TO A VIDEO AND BACK (user, 2026-10-06).
   They wanted to advertise "YouTube transcript highlighting and treating it
   as another material that users can bring in". It was built first as a
   second scene under this one with a heading of its own ("Watch. Mark what
   was said."), and they said: "we're just adding more things for users to
   scroll through... I wonder if there's a way to incorporate the video into
   that mockup above it". So Read is ONE scene and its sheet turns: the
   paper, then the video, every six seconds (showcase.js, the script that
   turns Think's four views; `data-every` on the scene is the pace). The two
   names stand over the sheet, and pressing one shows it and stops the
   turning. THE NOTES DO NOT TURN: one list holds notes from both, and the
   one that is lit is the one made from the source on show (`data-with`).
   That is the point of the scene: one list of notes, whatever the source.
   Without the script the paper stands alone and the names are not shown.
   The video's sheet is the transcript as the app sets it (`VideoReader`):
   paragraphs, each with its time in the margin, the paragraph being spoken
   a tier brighter, the word being spoken softly underlined, and highlights
   painted as on a paper. Its note's source line ends in a time where a
   paper's ends in a page.
   THE LECTURE IS INVENTED AND ITS WORDS ARE OURS. The sample library has no
   video because a real lecture's transcript is not ours to print
   (sample/broad-street/README.md), and a real video's still is someone
   else's picture (and the header allows no outside image). The still is a
   drawing made here: a lecturer's slide of the map.
   THE PLAYER IS THE WIDTH OF THE SHEET, with no title beside it (user, on a
   first build that had it 24rem wide with the lecture's name at its right:
   "I'd rather this be full screen and not have that text on the right"). */
.scene-read .views {
  position: absolute;
  top: 0.5rem;
  left: var(--edge);
  z-index: 1;
  margin: 0;
}
.scene-read button.pill {
  color: var(--t3);
}
.scene-read button.pill:hover,
.scene-read button.pill.on {
  color: var(--t2);
}
/* One over the other. The one coming in fades in over the one going out,
   which stays whole underneath until it is covered: two sheets fading
   through each other would let the page show through both half way. */
.turn {
  display: grid;
}
.turn > .view {
  grid-area: 1 / 1;
  visibility: hidden;
  opacity: 0;
  transition:
    opacity 0s 500ms,
    visibility 0s 500ms;
}
.turn > .view.is-on {
  z-index: 1;
  visibility: visible;
  opacity: 1;
  transition: opacity 500ms;
}
/* The lit note changes hands as the sheet turns. */
.scene-read .note blockquote {
  transition:
    background 500ms,
    color 500ms;
}
@media (prefers-reduced-motion: reduce) {
  .turn > .view,
  .turn > .view.is-on,
  .scene-read .note blockquote {
    transition: none;
  }
}
/* THE STILL IS SNOW'S OWN MAP, IN THE THEME'S COLOURS (user, 2026-10-06).
   Two calls made it this. First, on a cream slide with black bars and a red
   pump: "change the coloring of the video still so that it feels harmonious
   with the rest of the mockup". Then, on the drawing made in the section's
   tokens that answered it: "the video thumbnail is really basic. Is there a
   way we can make it look really, really nice?", and from three ways they
   chose the real map, "with the caption strip and moving word".
   The map is Map 1 of Snow's 1855 edition, public domain: a crop round the
   Broad Street pump of the scan the sample library carries
   (sample/broad-street/raw/snow-cholera-map-1854.jpg), 1440 by 810, saved
   as ink in white on black in eight greys (69 KB, lossless). IT IS A MASK,
   NOT A PICTURE: the page paints a rectangle in a text tier and the map
   lets it through where the ink is, so the map is light lines on the dark
   page and dark lines on the light one, in the section's own tint, with no
   second file. The ring round the pump is the theme's accent, in the same
   svg, so it stays on the pump however the player crops the map (the
   player is wider than 16:9 on a wide window and the map fills it).
   The file is fetched when Read comes near the window, not with the page
   (showcase.js sets its address): without the script the video is never
   shown at all.
   To make the file again: crop (1319, 1196, 3019, 2153) of the scan, resize
   to 1440 by 810, invert, push greys under 40 to black and over 200 to
   white, eight greys, lossless WebP. */
.player {
  position: relative;
  overflow: hidden;
  height: var(--player-h);
  border-radius: 0.5rem;
  background: var(--background);
  box-shadow: inset 0 0 0 1px var(--border);
}
.player .slide {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
}
.slide .s-ground {
  fill: var(--background);
}
.slide .s-ink {
  fill: var(--t3);
}
.slide .s-ring {
  fill: color-mix(in oklab, var(--primary) 24%, transparent);
  stroke: var(--primary);
  stroke-width: 3;
}
/* THE WORDS BEING SPOKEN, as a caption over the map: the same words the
   transcript underlines below, so the two halves of the sheet are seen to
   be one thing. showcase.js moves both along while the video is on show. */
.said {
  position: absolute;
  inset: auto 0 3.5rem;
  display: flex;
  justify-content: center;
  margin: 0;
  padding-inline: 1.25rem;
}
.said span {
  padding: 0.25rem 0.625rem;
  border-radius: 0.375rem;
  background: color-mix(in oklab, var(--background) 88%, transparent);
  color: var(--t1);
  font-size: 0.875rem;
  line-height: 1.25rem;
  text-align: center;
}
.player-bar {
  position: absolute;
  inset: auto 0 0;
  display: flex;
  align-items: center;
  gap: 0.625rem;
  padding: 2.5rem 1.25rem 1rem;
  background: linear-gradient(
    to top,
    color-mix(in oklab, var(--background) 92%, transparent),
    transparent
  );
  color: var(--t2);
  font-size: 0.75rem;
  font-variant-numeric: tabular-nums;
  line-height: 1rem;
}
.player-bar .ic {
  fill: currentColor;
  stroke: none;
}
/* Paused until the script plays it: the mark is the one you would press. */
.player-bar .i-pause,
.player.is-playing .i-play {
  display: none;
}
.player.is-playing .i-pause {
  display: block;
}
.player-bar i {
  flex: 1;
  height: 0.1875rem;
  margin-left: 0.25rem;
  border-radius: 1rem;
  background: color-mix(in oklab, var(--foreground) 16%, transparent);
}
.player-bar b {
  display: block;
  width: 26.4%;
  height: 100%;
  border-radius: inherit;
  background: var(--t1);
}
.scene-read .tape .transcript {
  margin-top: 1.5rem;
  padding-top: 1.25rem;
  border-top: 1px solid var(--border);
}
.transcript p {
  display: grid;
  grid-template-columns: 3.25rem minmax(0, 1fr);
}
.transcript p + p {
  margin-top: 0.875rem;
}
.transcript .ts {
  color: var(--t4);
  font-family: var(--mono);
  font-size: 0.75rem;
  font-variant-numeric: tabular-nums;
}
.transcript .at,
.transcript .at .ts {
  color: var(--t2);
}
.transcript .now {
  text-decoration: underline;
  text-decoration-color: color-mix(in oklab, var(--t1) 60%, transparent);
  text-decoration-thickness: 0.125rem;
  text-underline-offset: 0.25rem;
}
/* THINK: the canvas. An argument laid out: one claim in the middle, and
   around it the person who made it, the idea it argued against, and the
   evidence that carried it, joined by curved, labelled arrows. It was drawn
   closer to the app's own canvas first (seven full cards, straight arrows)
   and read as cluttered (user, 2026-10-02: "looks a bit cluttered, you can
   improve the UI and make it up if needed. Use bendy arrows... we are trying
   to sell the product here"). So: six cards with room between them, each a
   title and ONE thing under it (the quoted passage, or a sentence), and the
   claim in the middle a size larger. The notes and their words are still
   the sample library's; the arrangement is made for this page. */
.canvas {
  position: relative;
  overflow: hidden;
  height: calc(100% - 3.5rem);
  background: var(--background);
}
.links {
  position: absolute;
  top: 0;
  left: 0;
  width: 61.25rem;
  height: 32.5rem;
}
.win svg text {
  fill: var(--muted-foreground);
  font-family: inherit;
  font-size: 12px;
  letter-spacing: 0;
}
.win svg .arc {
  fill: none;
  stroke: var(--muted-foreground);
  stroke-width: 1;
}
.links .arc {
  stroke: var(--foreground-dim);
  stroke-linecap: round;
  stroke-width: 1.25;
}
.win svg .ah {
  fill: var(--muted-foreground);
}
.links .ah {
  fill: none;
  stroke: var(--foreground-dim);
  stroke-linecap: round;
  stroke-linejoin: round;
  stroke-width: 1.5;
}
.win svg .g {
  stroke: var(--hairline-faint);
  stroke-width: 1;
}
.win svg .lb {
  fill: var(--foreground-secondary);
  font-size: 10px;
  paint-order: stroke;
  stroke: var(--panel);
  stroke-linejoin: round;
  stroke-width: 5px;
  text-anchor: middle;
}
.links .lb {
  font-size: 11px;
  stroke: var(--background);
  stroke-width: 8px;
}
.win svg .lb.dim {
  fill: var(--muted-foreground);
  text-anchor: start;
}
.card {
  position: absolute;
  width: 15rem;
  padding: 0.75rem 0.875rem 0.875rem;
  border: 1px solid var(--border);
  border-radius: 0.75rem;
  background: var(--panel);
  font-size: 0.75rem;
  line-height: 1.0625rem;
}
.card .h4 {
  gap: 0.5rem;
  font-size: 0.8125rem;
  line-height: 1.125rem;
}
.card .mark {
  width: 1.125rem;
  height: 1.125rem;
}
.card .mark .ic {
  width: 0.625rem;
  height: 0.625rem;
}
.card blockquote {
  grid-template-columns: 0.625rem minmax(0, 1fr);
  column-gap: 0.375rem;
  margin-top: 0.5rem;
  padding: 0.5rem 0.625rem 0.5rem 0.5rem;
  border-radius: 0.5rem;
  font-size: inherit;
  line-height: inherit;
}
.card .qm {
  width: 0.625rem;
}
.card blockquote span,
.card p {
  display: -webkit-box;
  overflow: hidden;
  -webkit-box-orient: vertical;
  -webkit-line-clamp: 2;
}
.card p {
  margin-top: 0.375rem;
  padding-left: 1.625rem;
}
.card cite {
  margin-top: 0.25rem;
  font-size: 0.75rem;
}
/* The claim in the middle: a size larger, and one step lighter. */
.card.k-outbreak,
.card.k-well {
  width: 19rem;
}
.card.k-outbreak {
  background: linear-gradient(var(--lift-2), var(--lift-2)), var(--panel);
}
.k-snow {
  top: 2.5rem;
  left: 1.5rem;
}
.k-outbreak {
  top: 2.75rem;
  left: 20.75rem;
}
.k-miasma {
  top: 3.5rem;
  left: 44.5rem;
}
.k-infant {
  top: 18.75rem;
  left: 1.5rem;
}
.k-well {
  top: 18.25rem;
  left: 20.75rem;
}
.k-mortality {
  top: 17.75rem;
  left: 44.5rem;
}
/* THINK: the graph and the timeline, each one SVG. A node is a disc in its
   type's colour. */
.graph,
.timeline {
  display: block;
  height: auto;
  margin: 0.25rem 0 0 0.5rem;
}
.graph {
  width: 53.75rem;
}
.timeline {
  width: 56.25rem;
}
.win svg .e {
  fill: none;
  stroke: var(--hairline-strong);
  stroke-width: 1;
}
.win svg .n {
  fill: color-mix(in oklab, var(--t) 30%, var(--panel));
  stroke: var(--t);
  stroke-width: 1.5;
}
.n-claim {
  --t: var(--t-claim);
}
.n-event {
  --t: var(--t-event);
}
.n-person {
  --t: var(--t-person);
}
.n-group {
  --t: var(--t-group);
}
.n-concept {
  --t: var(--t-concept);
}
.n-place {
  --t: var(--t-place);
}
.n-org {
  --t: var(--t-org);
}
.n-note {
  --t: var(--t-note);
}
.win svg .gl {
  fill: var(--foreground);
  font-size: 11px;
}
.win svg .gl.dim {
  fill: var(--muted-foreground);
  font-size: 12px;
}
.win svg .gl.end {
  text-anchor: end;
}
.win svg .yr {
  font-size: 10px;
  text-anchor: middle;
}
.win svg .ln {
  fill: var(--foreground);
  font-size: 11px;
  font-weight: 600;
}
.win svg .ct {
  fill: var(--muted-foreground);
  font-weight: 400;
}
.win svg .bar {
  stroke: none;
}
.win svg .b-event {
  fill: var(--t-event);
}
.win svg .b-person {
  fill: var(--t-person);
}
.win svg .bl {
  fill: oklch(0.25 0.03 80);
  font-size: 11px;
  font-weight: 510;
}
.win svg .num {
  fill: var(--foreground);
  font-size: 10px;
  font-weight: 600;
  text-anchor: middle;
}
/* THINK: the map, DRAWN (user, 2026-10-02: "I think you might be using a
   screenshot, can you make up the map UI? Maybe use vector, whatever is
   going to be packaged as a small size but looks really nice. This current
   map looks very pixelated. And show individual points instead of the
   grouped blob"). It was a crop of the user's screenshot of the app's map
   (Mapbox tiles), enlarged past its size. Now it is one SVG of about three
   kilobytes: a street plan of Soho round Broad Street, turned a few degrees
   as the real streets lie, with each note as its own dot in its type's
   colour and its name beside it.
   It is a SKETCH, drawn for this page from the streets' names and order
   (Broad, Poland, Marshall, Cambridge, Berwick, Wardour...), not traced
   from anyone's map and not to scale. So it carries no map provider's
   credit, and must not be replaced with traced tiles without one. The five
   notes are where the sample library's sources put them: the pump at Broad
   Street and Cambridge Street, No. 40 beside it, the percussion-cap
   manufactory and the brewery in Broad Street, the workhouse in Poland
   Street.
   The streets are an OPAQUE mix of the text colour into the page (not
   white at an alpha: two crossing streets would add up to a bright square
   at every corner). It is 1600 wide at 1:1 so it reaches the view's right
   edge on a very wide screen; the notes are all within its first 740. */
.soho {
  --street: color-mix(in oklab, var(--foreground) 7%, var(--background));
  display: block;
  width: 100rem;
  height: 30rem;
  background: var(--background);
}
.soho .st {
  fill: none;
  stroke: var(--street);
  stroke-width: 7;
}
.soho .st.major {
  stroke-width: 13;
}
.soho .st.lane {
  stroke-width: 4;
}
.soho .sq {
  fill: color-mix(in oklab, var(--foreground) 3%, var(--background));
  stroke: var(--street);
  stroke-width: 4;
}
.win .soho .sn {
  fill: var(--foreground-dim);
  font-size: 8.5px;
  letter-spacing: 0.06em;
}
.win .soho .gl {
  paint-order: stroke;
  stroke: var(--background);
  stroke-linejoin: round;
  stroke-width: 4px;
}
.soho .halo {
  fill: oklch(1 0 0 / 0.07);
}
@media (prefers-color-scheme: light) {
  :root .soho .halo {
    fill: oklch(0 0 0 / 0.06);
  }
}
/* THINK SAYS ONE THING: see what connects (the focus pass; DISCOVER above
   has the method). The thing everything connects to is one claim, "The
   outbreak spread through the water of the Broad Street pump". Its title
   is the only white: on its card in the middle of the canvas, on its dot in
   the middle of the graph, and on its note in the window in front. What
   joins the others to it (the canvas's arrows and their labels, the graph's
   lines into that dot) is a step down; every other card, dot, label and
   note is two. The view's name in the bar is a step down too, so the white
   stays the claim's while the views turn.
   The timeline holds no title for that claim, so nothing in it is white.
   Its bars were solid yellow and blue with dark words; they are tinted
   washes with grey words now, like the dots. On the map the claim is the
   dot at the pump, with a white ring and its name in white. */
.scene-think .win-who b,
.scene-think .card .h4,
.scene-think .notes .note .h4,
.scene-think .notes .note:first-child blockquote {
  color: var(--t2);
}
.scene-think .card p,
.scene-think .card blockquote,
.scene-think .notes .note p,
.scene-think .notes .note blockquote {
  color: var(--t3);
}
.scene-think .win-who .ic,
.scene-think .card cite,
.scene-think .notes .note cite {
  color: var(--t4);
}
.scene-think .card.k-outbreak .h4,
.scene-think .notes .note:first-child .h4 {
  color: var(--t1);
}
.scene-think .card.k-outbreak blockquote,
.scene-think .notes .note:first-child blockquote {
  color: var(--t2);
}
.scene-think .card.k-outbreak {
  border-color: color-mix(in oklab, var(--foreground) 26%, transparent);
}
.scene-think .links .arc,
.scene-think .links .ah {
  stroke: var(--t3);
}
.scene-think .links .lb {
  fill: var(--t3);
}
.scene-think button.pill {
  color: var(--t3);
}
.scene-think button.pill:hover,
.scene-think button.pill.on {
  color: var(--t2);
}
.scene-think svg text,
.scene-think svg .gl,
.scene-think svg .ct {
  fill: var(--t3);
}
.scene-think svg .ln {
  fill: var(--t2);
  font-weight: 510;
}
.scene-think svg .gl.hot {
  fill: var(--t1);
  font-weight: 510;
}
/* THE GRAPH, REDRAWN TO BE READ AT A GLANCE (user, 2026-10-02: "can you
   improve the graph view too so it's easy to follow"). It was the app's own
   graph of the whole project: some fifty dots, seventeen of them named, the
   names cut short and lying over one another. Now it is the one claim in
   the middle with its whole name under it, and eleven notes round it, each
   with its whole name beside it and room of its own. They sit in the groups
   the argument has: who and what it argued against at the left, how the
   well was fouled above, the three exceptions at the right, and what was
   done about it below. Lines into the claim are a step brighter than the
   three that join one note to another. The notes and what joins them are
   the sample library's; where each dot sits is made for this page. */
.scene-think .graph .n {
  fill-opacity: 0.85;
  stroke-opacity: 0.85;
}
.scene-think .graph .n.hot {
  fill-opacity: 1;
  stroke: var(--foreground);
  stroke-opacity: 1;
}
.scene-think .graph .e {
  stroke: var(--hairline-strong);
}
.scene-think .graph .e.to {
  stroke: color-mix(in oklab, var(--foreground) 28%, transparent);
}
/* ONE GROUND FOR ALL FOUR VIEWS (user, 2026-10-06, pointing at the views'
   names and the graph: "improve the background color of all of these
   different views so that there's consistency and harmony among all of
   them"). The canvas and the map each stood on the page's dark colour and
   the graph and the timeline on nothing, so they showed the window's
   lighter panel: two grounds, changing as the views turned. Now the place
   the views stand in is one surface, the darker one (the same desk the
   draft's page stands on in Write), and the names over it and the notes
   beside it are the lighter panel, with a hairline between. The knock-out
   round a label in the graph and the timeline is that ground's colour, so
   a line still stops short of the words on it. */
.scene-think .stack {
  background: var(--background);
}
.scene-think .timeline .lb {
  stroke: var(--background);
}
.scene-think .graph .gl {
  fill: var(--t3);
  paint-order: stroke;
  stroke: var(--background);
  stroke-linejoin: round;
  stroke-width: 4px;
}
.scene-think .graph .gl.hot {
  fill: var(--t1);
}
.win svg .gl.mid {
  text-anchor: middle;
}
.graph .halo {
  fill: oklch(1 0 0 / 0.07);
}
@media (prefers-color-scheme: light) {
  :root .graph .halo {
    fill: oklch(0 0 0 / 0.06);
  }
}
.scene-think svg .lb {
  fill: var(--t3);
}
.scene-think svg .bar {
  fill: color-mix(in oklab, var(--t) 22%, var(--panel));
  stroke: color-mix(in oklab, var(--t) 55%, transparent);
  stroke-width: 1;
}
.scene-think svg .b-event {
  --t: var(--t-event);
}
.scene-think svg .b-person {
  --t: var(--t-person);
}
.scene-think svg .bl {
  fill: var(--t3);
}
.scene-think svg .num {
  fill: var(--t2);
}
/* THE TIMELINE, REDRAWN TO BE READ AT A GLANCE (user, 2026-10-02: "improve
   the timeline UI so it's very easy to follow. No need to show grouped
   events, it just makes it harder to immediately grasp what I'm seeing.
   Also the grouping of the items could be something better, can you think
   of a sensible one that the user could make for this topic?"). It was the
   app's own rows by TYPE (claim, event, person, organization), with two
   items folded into a "2 items" dot. Now:
     - three rows a reader of this subject would make for themselves: what
       happened (the outbreak), what was done about it (the inquiry), and
       what it shows (the argument). Read down the eighth of September and
       the page's point is there: the handle came off after the attacks had
       fallen.
     - every note is its own bar or dot with its whole name beside it, one
       to a line, and nothing is folded together.
     - two links only: the handle came off "the next morning" after Snow
       spoke, and the claim that says "the table says otherwise" points up
       at it.
     - the days are short ("Sep 8"), every two days.
   The names and dates are the sample library's; the three rows and which
   row a note is in are made for this page. The claim the scene is about is
   in the argument's row, in white. */
.scene-think .timeline .n {
  fill-opacity: 0.8;
  stroke-opacity: 0.8;
}
.scene-think .timeline .n.hot {
  fill-opacity: 1;
  stroke: var(--foreground);
  stroke-opacity: 1;
}
.scene-think .timeline .gl {
  fill: var(--t3);
}
.scene-think .timeline .gl.hot {
  fill: var(--t1);
}
.timeline .halo {
  fill: oklch(1 0 0 / 0.07);
}
@media (prefers-color-scheme: light) {
  :root .timeline .halo {
    fill: oklch(0 0 0 / 0.06);
  }
}
.win .timeline .lb.dim {
  fill: var(--t3);
}
.scene-think .soho .n {
  fill-opacity: 0.8;
  stroke-opacity: 0.8;
}
.scene-think .soho .n.hot {
  fill-opacity: 1;
  stroke: var(--foreground);
  stroke-opacity: 1;
}
/* WRITE: the draft, with one citation opened to its passage. */
.draft {
  position: relative;
  max-width: 44rem;
  padding: 0.25rem 2.5rem 0 2rem;
  color: var(--foreground);
  font-size: 0.875rem;
  letter-spacing: -0.006em;
  line-height: 1.5rem;
}
.draft .h5 {
  margin: 1.375rem 0 0.25rem;
  color: inherit;
  font-size: 1.0625rem;
  font-weight: 600;
  letter-spacing: -0.012em;
  line-height: 1.5rem;
}
/* The document's own title, at the head of its page, and the cursor where
   the writing has got to (user, 2026-10-06: "make it look more like a
   document that you're personally writing yourself. Maybe also have a
   blinking cursor at the last character of the document"). The cursor is a
   text cursor's: a thin upright line the height of a line of words, on and
   off about once a second. With reduced motion it stands still. */
.draft .doc-title {
  margin: 0.5rem 0 0.75rem;
  color: var(--foreground);
  font-size: 1.375rem;
  font-weight: 600;
  letter-spacing: -0.018em;
  line-height: 1.75rem;
}
.draft .caret {
  display: inline-block;
  width: 0.09375rem;
  height: 1.15em;
  margin-left: 0.125rem;
  border-radius: 1px;
  background: var(--foreground);
  vertical-align: -0.2em;
  animation: caret 1.06s steps(1, end) infinite;
}
@keyframes caret {
  50% {
    opacity: 0;
  }
}
@media (prefers-reduced-motion: reduce) {
  .draft .caret {
    animation: none;
  }
}
.draft .h5:first-child {
  margin-top: 0.5rem;
}
.draft p {
  margin-top: 0.375rem;
  color: inherit;
}
/* A citation is quieter than the sentence it follows; the opened one is a
   chip in the text colour. */
.draft .ref {
  color: var(--muted-foreground);
}
.draft .ref.on {
  border-radius: 0.1875rem;
  background: var(--mark-strong);
  box-shadow: 0 0 0 2px var(--mark-strong);
  color: var(--foreground);
}
/* The opened citation's card hangs from the citation itself (it is inside
   it in the markup), so it is always right under those words (user,
   2026-10-02: "arrange this better so that it's showing right below the
   citation text"). It used to be placed by hand over the paragraph, and
   the citation had broken across two lines above it. A citation never
   breaks across lines now. */
.draft .ref {
  white-space: nowrap;
}
.draft .ref.on {
  position: relative;
}
.pop {
  position: absolute;
  top: calc(100% + 0.5rem);
  left: -0.125rem;
  z-index: 1;
  display: block;
  width: 23.5rem;
  font-weight: 400;
  letter-spacing: 0;
  white-space: normal;
  padding: 0.875rem 1rem 1rem;
  border: 1px solid var(--border);
  border-radius: 0.75rem;
  background: var(--surface-2);
  box-shadow: var(--shadow-float);
  color: var(--foreground);
  font-size: 0.8125rem;
  line-height: 1.25rem;
}
.pop b {
  display: block;
  font-weight: 510;
}
.pop > span {
  display: block;
  color: var(--muted-foreground);
  font-size: 0.75rem;
}
.pop .q {
  margin-top: 0.625rem;
}
.pop .go {
  display: inline-flex;
  align-items: center;
  gap: 0.375rem;
  height: 1.75rem;
  margin-top: 0.75rem;
  padding-inline: 0.625rem;
  border: 1px solid var(--border);
  border-radius: 0.5rem;
  background: var(--wash);
  color: var(--foreground);
  font-size: inherit;
  font-weight: 510;
}
/* WRITE SAYS ONE THING: a citation opens its passage (the focus pass;
   DISCOVER above has the method). The passage in the opened citation's card
   is the only white. The citation it was opened from is a chip a step
   under it. The draft's headings and its other citations are a step under
   that, so the eye can still count the citations down the page ("cite as
   you type") without reading the prose, which is two steps down with the
   notes. None of the notes beside the draft is the opened citation's, so
   none of them is lit. */
.scene-write .win-who b,
.scene-write .draft .h5,
.scene-write .draft .ref,
.scene-write .pop b,
.scene-write .pop .go,
.scene-write .note .h4 {
  color: var(--t2);
}
.scene-write .win-who i,
.scene-write .draft,
.scene-write .note p,
.scene-write .note blockquote {
  color: var(--t3);
}
.scene-write .win-who .ic,
.scene-write .pop > span,
.scene-write .note cite {
  color: var(--t4);
}
.scene-write .pop .go {
  border-color: var(--hairline);
}
/* The citation that is open. It was a wash of white at 8%, the same as any
   highlight, and could hardly be told from the plain citations beside it
   (user, 2026-10-06: "it's hard to see that this is highlighted, please
   make it stand out more"). Now it is filled with the section's accent,
   which nothing else on the page wears, so it is the one coloured thing in
   the draft and the card under it plainly hangs from it. */
.scene-write .draft .ref.on {
  --open: color-mix(in oklab, var(--primary) 42%, transparent);
  background: var(--open);
  box-shadow: 0 0 0 2px var(--open);
  color: var(--t1);
}
.scene-write .pop .q {
  background: var(--mark);
  color: var(--t1);
}
/* Too narrow for the two side by side: the view takes the width (cut off
   at the right, where it runs on) and the notes sit over its lower edge. */
@media (max-width: 64rem) {
  .scene-work {
    height: auto;
  }
  .scene-work .back,
  .scene-work.flip .back {
    position: relative;
    top: auto;
    right: auto;
    left: auto;
    height: 27rem;
    mask-image:
      linear-gradient(#000 0%, #000 62%, transparent 100%),
      linear-gradient(to left, transparent 0, #000 3rem);
    mask-composite: intersect;
  }
  .scene-work .win.pane {
    width: 60rem;
    height: 35rem;
    padding-bottom: 0;
  }
  .flip .win.pane {
    margin-left: 0;
  }
  .flip .pane .win-bar {
    padding-left: 1.25rem;
  }
  .flip .reader,
  .flip .draft {
    padding-left: 2rem;
  }
  .flip .win.front {
    right: auto;
  }
  .features {
    grid-template-columns: minmax(0, 1fr);
  }
  .scene-work .win.notes {
    width: auto;
    height: 22rem;
    margin-top: -5rem;
  }
  /* READ, STACKED. The notes sit over the view's foot, and the view is one
     height for both sheets, so nothing below moves when the sheet turns.
     THE VIDEO SETS THAT HEIGHT, whatever the width: it takes the view's own
     width (a player cut off at the right would not say "video"), with the
     player whole at its top and the paragraph being spoken under it (and
     the next one where there is room for it), then the room the notes
     cover.
     THE PAPER IS LAID OVER THAT BOX and keeps a paper's width (44rem), so
     it is cut off at the right, and is shown scrolled to its passage: the
     passage ends `--passage` down the sheet (measured here, where the
     sheet's width is fixed) and is put 2.5rem above where the notes begin.
     Where the box is tall enough to need no scrolling, the sheet's top is
     whole, and its fade there is nothing.
     Each sheet fades by itself, so the view behind them has no mask. */
  .scene-read {
    --player-h: min(18rem, (100vw - 2 * var(--gutter) - 2.5rem) * 0.5625);
    --passage: 29.25rem;
    --passage-at: calc(100% - 7.5rem);
    height: auto;
    mask-image: linear-gradient(to top, transparent 0, #000 7rem);
  }
  .scene-read .views,
  .scene-read.is-live .views {
    position: static;
    margin: 0 0 0.75rem;
  }
  .scene-read.flip .back {
    top: auto;
    height: auto;
    mask-image: none;
  }
  .scene-read .win.pane {
    width: auto;
    height: auto;
  }
  .scene-read .reader {
    height: auto;
    padding-inline: 0;
  }
  .scene-read .turn {
    position: relative;
  }
  .scene-read [data-pane="paper"] {
    position: absolute;
    inset: 0;
    overflow: hidden;
    mask-image:
      linear-gradient(
        transparent 0,
        #000 clamp(0rem, var(--passage) + 7.5rem - 100%, 3rem),
        #000 calc(100% - 7rem),
        transparent calc(100% - 4.5rem)
      ),
      linear-gradient(to left, transparent 0, #000 3rem);
    mask-composite: intersect;
  }
  .scene-read .leaf {
    position: absolute;
    top: min(0rem, var(--passage-at) - var(--passage));
    left: 0;
    width: 44rem;
    min-height: 0;
    padding-inline: 2.5rem;
  }
  .scene-read .leaf.tape {
    position: static;
    width: auto;
    padding: 1.25rem 1.25rem 6.5rem;
  }
  /* The notes are narrower than the sheet on a tablet, so its foot would
     show either side of them: it fades out under them instead. */
  .scene-read [data-pane="video"] {
    mask-image: linear-gradient(
      #000 0,
      #000 calc(100% - 6rem),
      transparent calc(100% - 2rem)
    );
  }
  .transcript p:nth-child(n + 3) {
    display: none;
  }
  /* Only the top of the notes shows here, so the note made from the source
     on show is moved to the top of the list. */
  .scene-read .notes .win-body {
    display: flex;
    flex-direction: column;
  }
  .scene-read .notes .note {
    border-top: 1px solid var(--border);
  }
  .scene-read .notes .note.on {
    order: -1;
    border-top: 0;
  }
}
@media (max-width: 40rem) {
  .transcript p:nth-child(n + 2) {
    display: none;
  }
}
/* Discover's tight window on a narrow screen: it keeps its height and its
   place (the rule above undoes both for the two-window scenes), and on a
   phone the search stands over the map instead of beside it. */
@media (max-width: 64rem) {
  .scene-find.tight {
    --side: 16.5rem;
    overflow: hidden;
    height: 32rem;
    padding: 0;
  }
}
@media (max-width: 40rem) {
  .scene-find.tight {
    height: 36rem;
  }
  .scene-find.tight .map {
    width: 34rem;
  }
  .twin-cols {
    grid-template-columns: minmax(0, 1fr);
    grid-template-rows: auto minmax(0, 1fr);
  }
  .twin-side {
    padding-bottom: 0.5rem;
    border-right: 0;
    border-bottom: 1px solid var(--border);
  }
}
/* THE TIGHT LOOK ON THE OTHER SCENES (user, 2026-10-06, on Discover's
   trial above: "apply the same look to the other mockups too"). The same
   two halves: Cursor's tight product window, Linear's wash at the foot.
   A scene of the two-window kind (a view behind, a panel floating in
   front) becomes ONE WINDOW with the class `tight`, with no change to what
   is in it: a frame and a 28px bar are drawn by the scene itself (three
   grey dots and the project's name), the view is its main column and the
   panel its side column, parted by a hairline, flat, with nothing
   floating. The parts' own bars become small labels. Type is a real app's:
   12 to read, 11 under it.
   From 48rem up. Under that the scenes keep their stacked layouts (a phone
   has no room for two columns), and every rule those layouts set between
   48 and 64rem is set back here, which is why this block is long. */
@media (min-width: 48.0625rem) {
  .scene-work.tight {
    --side: 21rem;
    --top: 0.5rem;
    --bar: 1.75rem;
    /* where the columns start: under the frame's edge and the bar */
    --in: calc(var(--top) + var(--bar) + 2px);
    overflow: hidden;
    height: 34rem;
    padding: 0;
    mask-image: linear-gradient(to top, transparent 0, #000 9rem);
  }
  .scene-work.tight::before {
    position: absolute;
    inset: var(--top) var(--edge) 0;
    border: 1px solid var(--border);
    border-bottom: 0;
    border-radius: 0.75rem 0.75rem 0 0;
    background: var(--panel);
    content: "";
  }
  /* The bar: the name is the stylesheet's (an ornament, not content), and
     the three dots are painted behind it. */
  .scene-work.tight::after {
    position: absolute;
    top: calc(var(--top) + 1px);
    right: calc(var(--edge) + 1px);
    left: calc(var(--edge) + 1px);
    height: var(--bar);
    padding-left: 3.75rem;
    border-bottom: 1px solid var(--border);
    background:
      radial-gradient(circle, var(--border) 0.3125rem, transparent 0.34rem)
        0.625rem 50% / 0.625rem 0.625rem no-repeat,
      radial-gradient(circle, var(--border) 0.3125rem, transparent 0.34rem)
        1.625rem 50% / 0.625rem 0.625rem no-repeat,
      radial-gradient(circle, var(--border) 0.3125rem, transparent 0.34rem)
        2.625rem 50% / 0.625rem 0.625rem no-repeat;
    color: color-mix(in oklab, var(--foreground-secondary) 70%, transparent);
    font-size: 0.75rem;
    line-height: var(--bar);
    content: "Cholera in Soho, 1854";
  }
  /* The view: the main column. */
  .scene-work.tight .back,
  .scene-work.tight.flip .back {
    position: absolute;
    top: var(--in);
    right: calc(var(--edge) + 1px);
    bottom: 0;
    left: calc(var(--edge) + var(--side) + 1px);
    height: auto;
    mask-image: none;
  }
  .scene-work.tight.flip .back {
    right: calc(var(--edge) + var(--side) + 1px);
    left: calc(var(--edge) + 1px);
  }
  .scene-work.tight .win.pane {
    width: 100%;
    height: 100%;
    margin: 0;
    padding: 0;
    border: 0;
    border-radius: 0;
    background: none;
    box-shadow: none;
  }
  /* The panel: the side column. */
  .scene-work.tight .win.front {
    position: absolute;
    top: var(--in);
    right: auto;
    bottom: 0;
    left: calc(var(--edge) + 1px);
    width: var(--side);
    height: auto;
    margin: 0;
    border: 0;
    border-right: 1px solid var(--border);
    border-radius: 0;
    background: none;
    box-shadow: none;
  }
  .scene-work.tight.flip .win.front {
    right: calc(var(--edge) + 1px);
    left: auto;
    border-right: 0;
    border-left: 1px solid var(--border);
  }
  .scene-work.tight .win::before,
  .scene-work.tight .win::after {
    display: none;
  }
  /* A part's own bar is a label now. */
  .scene-work.tight .win-bar {
    height: 2rem;
    padding: 0 0.875rem;
    border-bottom: 0;
  }
  .scene-work.tight .win-who {
    gap: 0.375rem;
  }
  .scene-work.tight .win-who b {
    color: var(--t3);
    font-size: 0.6875rem;
    font-weight: 510;
  }
  .scene-work.tight .win-who .ic {
    color: var(--t4);
  }
  /* The project's name is in the window's bar; a part does not say it
     again. */
  .scene-work.tight .notes .win-who i {
    display: none;
  }
  /* Notes, packed as the hero's are. */
  .scene-work.tight .notes .win-body {
    display: block;
    height: calc(100% - 2rem);
    padding: 0;
  }
  .scene-work.tight .notes .note {
    order: 0;
    padding: 0.75rem 0.875rem;
  }
  .scene-work.tight .notes .note + .note {
    border-top: 1px solid var(--border);
  }
  .scene-work.tight .note .h4 {
    font-size: 0.75rem;
    line-height: 1rem;
  }
  .scene-work.tight .note blockquote {
    font-size: 0.75rem;
    line-height: 1.0625rem;
  }
  .scene-work.tight .note cite,
  .scene-work.tight .note p {
    font-size: 0.6875rem;
    line-height: 1rem;
  }

  /* READ. The sheet is the main column, flat; "Paper" and "Video" are its
     label row. The player is the column's width, so its height is worked
     out from the window again (the same kind of sum as before, with this
     layout's numbers). */
  .scene-read.tight {
    --edge-v: max(var(--gutter), (100vw - 80rem) / 2);
    --player-h: min(
      19rem,
      (100vw - 2 * var(--edge-v) - var(--side) - 2.5rem) * 0.5625
    );
    height: calc(21.5rem + var(--player-h));
    mask-image: linear-gradient(to top, transparent 0, #000 8rem);
  }
  /* The two names stand in the MIDDLE of the reader's column, over the
     page (user, 2026-10-06, on a first build that had them at its left:
     "show this tab in the center of the page instead of on the left"). */
  .scene-read.tight .views,
  .scene-read.tight.is-live .views {
    position: absolute;
    top: calc(var(--in) + 0.25rem);
    left: calc(var(--edge) + (100% - 2 * var(--edge) - var(--side)) / 2);
    z-index: 2;
    margin: 0;
    translate: -50% 0;
  }
  .scene-read.tight .views .pill {
    height: 1.5rem;
    padding-inline: 0.5rem;
    font-size: 0.6875rem;
  }
  .scene-read.tight .reader {
    overflow: hidden;
    height: 100%;
    padding: 0;
  }
  .scene-read.tight .turn {
    position: static;
    height: 100%;
  }
  .scene-read.tight .turn > .view,
  .scene-read.tight [data-pane] {
    position: static;
    inset: auto;
    overflow: visible;
    mask-image: none;
  }
  /* THE TURN IS A TRUE CROSSFADE HERE (user, 2026-10-06: "There's this
     weird lagginess when it switches between paper and video. Can you
     please make it smoother?"). The turn was written for sheets that were
     opaque cards: the one coming in faded in OVER the one going out, which
     stayed whole underneath for the half second and was then switched off.
     In this layout the sheets have no ground of their own (the window's
     column is the ground), so that showed the new words through the old
     ones for half a second and then the old ones vanished at a stroke:
     the lag. Now the one going out fades away while the one coming in
     fades up just behind it, so nothing is ever doubled and nothing snaps.
     The two sheets and the map are each kept as their own painted layer,
     so the fade is a matter of blending two pictures and the map is not
     drawn again each time the caption or the clock over it changes. */
  .scene-read.tight .turn > .view {
    transition:
      opacity 260ms ease,
      visibility 0s 260ms;
    will-change: opacity;
  }
  .scene-read.tight .turn > .view.is-on {
    transition: opacity 380ms ease 140ms;
  }
  .scene-read.tight .player .slide {
    will-change: transform;
  }
  .scene-read.tight .leaf,
  .scene-read.tight .leaf.tape {
    position: static;
    width: auto;
    min-height: 100%;
    margin: 0;
    padding: 2.5rem 1.25rem 8rem;
    border: 0;
    border-radius: 0;
    background: none;
    box-shadow: none;
    font-size: 0.75rem;
    line-height: 1.1875rem;
  }
  /* A paper's lines are a paper's length: the page stands in the middle of
     its column, 40rem wide at most. */
  .scene-read.tight .leaf:not(.tape) {
    padding-inline: max(1.25rem, (100% - 40rem) / 2);
  }
  .scene-read.tight .leaf .h5 {
    font-size: 0.9375rem;
    line-height: 1.25rem;
  }
  .scene-read.tight .leaf .authors {
    margin-top: 0.25rem;
    font-size: 0.6875rem;
    line-height: 1rem;
  }
  .scene-read.tight .leaf .abstract {
    margin-top: 0.75rem;
    padding-top: 0.75rem;
  }
  .scene-read.tight .leaf .abstract p + p {
    margin-top: 0.625rem;
  }
  .scene-read.tight .said span {
    font-size: 0.75rem;
    line-height: 1.0625rem;
  }
  .scene-read.tight .tape .transcript {
    margin-top: 1rem;
    padding-top: 0.875rem;
  }
  .scene-read.tight .transcript p {
    display: grid;
    grid-template-columns: 2.75rem minmax(0, 1fr);
  }
  .scene-read.tight .transcript p + p {
    margin-top: 0.625rem;
  }
  .scene-read.tight .transcript .ts {
    font-size: 0.6875rem;
  }

  /* THINK. The notes are the side column and the four views the main one,
     their names its label row (the project's name beside them went: the
     window's bar says it). The canvas is a drawing wider than the column,
     as it always was, so this window runs off the right of the page and
     washes out there, as Discover's does. */
  .scene-think.tight {
    height: 36rem;
    mask-image:
      linear-gradient(to top, transparent 0, #000 9rem),
      linear-gradient(to left, transparent 0, #000 clamp(2.5rem, 6vw, 5rem));
    mask-composite: intersect;
  }
  .scene-think.tight::before,
  .scene-think.tight::after {
    right: -3rem;
  }
  .scene-think.tight .back {
    right: 0;
  }
  .scene-think.tight .pane .win-who {
    display: none;
  }
  .scene-think.tight .views {
    margin-left: 0;
  }
  .scene-think.tight .views .pill {
    height: 1.5rem;
    padding-inline: 0.5rem;
    font-size: 0.6875rem;
  }
  .scene-think.tight .stack {
    height: calc(100% - 2rem);
  }
  .scene-think.tight .pane .win-bar {
    border-bottom: 1px solid var(--border);
  }

  /* SAVE. This one is not Bodhi's window: it is a browser, with Bodhi's
     button in its toolbar and Bodhi's panel hanging under that button. So
     the frame is the browser's (its bar holds the dots and the address,
     and no project name), the page is its whole width, and the panel is
     the one thing that floats, over the page's top right, as a browser's
     own popover does and as Cursor's second window does. The selectors
     carry `scene-work` as well so they beat the scene's own rules, which
     come later in this file. */
  .scene-work.scene-save.tight {
    height: 31.5rem;
  }
  .scene-work.scene-save.tight::after {
    border-bottom: 0;
    content: "";
    pointer-events: none;
  }
  .scene-work.scene-save.tight .back {
    top: calc(var(--top) + 1px);
    right: calc(var(--edge) + 1px);
    left: calc(var(--edge) + 1px);
  }
  .scene-work.scene-save.tight .win.pane {
    height: 100%;
  }
  .scene-work.scene-save.tight .br-bar {
    gap: 0.5rem;
    height: var(--bar);
    padding: 0 0.5rem 0 4rem;
  }
  .scene-work.scene-save.tight .br-url {
    height: 1.25rem;
    padding-inline: 0.625rem;
    font-size: 0.6875rem;
    line-height: 1.25rem;
  }
  .scene-work.scene-save.tight .br-ext {
    width: 1.25rem;
    height: 1.25rem;
  }
  .scene-work.scene-save.tight .br-ext img {
    width: 0.75rem;
    height: 0.75rem;
  }
  .scene-work.scene-save.tight .br-page {
    max-width: none;
    padding: 0;
  }
  /* The article and its rail stop short of Bodhi's panel, which hangs over
     the page's right side. Under 64rem the rail goes, to leave the article
     a readable width. */
  .scene-work.scene-save.tight .br-cols {
    max-width: calc(100% - 20.5rem);
  }
  @media (max-width: 64rem) {
    .scene-work.scene-save.tight .br-cols {
      grid-template-columns: minmax(0, 1fr);
    }
    .scene-work.scene-save.tight .br-rail {
      display: none;
    }
  }
  .scene-work.scene-save.tight .br-run {
    font-size: 0.6875rem;
  }
  .scene-work.scene-save.tight .br-page .h5 {
    margin-top: 0.5rem;
    font-size: 1.25rem;
    line-height: 1.5rem;
  }
  .scene-work.scene-save.tight .br-by {
    margin-top: 0.375rem;
    font-size: 0.75rem;
  }
  .scene-work.scene-save.tight .br-acts {
    gap: 0.375rem;
    margin-top: 0.75rem;
  }
  .scene-work.scene-save.tight .br-acts i {
    padding: 0.0625rem 0.5rem;
    font-size: 0.6875rem;
  }
  .scene-work.scene-save.tight .sk-h {
    margin-top: 1.25rem;
    font-size: 0.75rem;
  }
  .scene-work.scene-save.tight .sk {
    gap: 0.5rem;
    margin-top: 0.625rem;
  }
  .scene-work.scene-save.tight .sk i {
    height: 0.375rem;
  }
  .scene-work.scene-save.tight .win.front.ext {
    top: calc(var(--in) + 0.375rem);
    right: calc(var(--edge) + 0.625rem);
    bottom: auto;
    left: auto;
    width: 19rem;
    max-width: none;
    height: auto;
    margin: 0;
    border: 1px solid var(--border);
    border-radius: 0.625rem;
    background: var(--surface-2);
    box-shadow: var(--shadow-float);
  }
  .scene-work.scene-save.tight .ext .win-bar {
    padding: 0 0.75rem;
  }
  .scene-work.scene-save.tight .ext .win-who b {
    color: var(--t2);
    font-size: 0.75rem;
  }
  .scene-work.scene-save.tight .ext .win-who i {
    font-size: 0.6875rem;
  }
  .scene-work.scene-save.tight .ext-mark {
    width: 0.875rem;
    height: 0.875rem;
  }
  .scene-work.scene-save.tight .ext-body {
    padding: 0 0.75rem 0.75rem;
    font-size: 0.75rem;
    line-height: 1.0625rem;
  }
  .scene-work.scene-save.tight .ext-field {
    height: 1.75rem;
    padding-inline: 0.625rem;
    border-radius: 0.375rem;
    line-height: 1.75rem;
  }
  .scene-work.scene-save.tight .ext-field.area {
    height: 3.25rem;
  }
  .scene-work.scene-save.tight .ext-field + .ext-field {
    margin-top: 0.5rem;
  }
  .scene-work.scene-save.tight .ext-meta {
    margin-top: 0.375rem;
    font-size: 0.6875rem;
    line-height: 1rem;
  }
  .scene-work.scene-save.tight .ext-meta + .ext-meta {
    margin-top: 0;
  }
  .scene-work.scene-save.tight .ext-row {
    margin-top: 0.625rem;
    margin-bottom: 0.5rem;
  }
  .scene-work.scene-save.tight .ext-save {
    height: 1.75rem;
    margin-top: 0.625rem;
    border-radius: 0.375rem;
    line-height: 1.75rem;
  }
  .scene-work.scene-save.tight .ext-done {
    inset: 0 0.75rem 0.75rem;
    font-size: 0.8125rem;
  }

  /* AI. Four windows side by side, as they were: they are four different
     agents, not one app. Tightened only: a label for a bar, a real app's
     type, and narrower, so more of the two at the ends is in view. */
  .scene.tight {
    --win: 21.5rem;
    height: 33rem;
  }
  .scene.tight .scene-track {
    top: 3rem;
  }
  .scene.tight .win {
    height: 30rem;
    font-size: 0.75rem;
    line-height: 1.0625rem;
  }
  .scene.tight .win-bar {
    height: 2rem;
    padding: 0 0.75rem;
  }
  .scene.tight .win-who b {
    font-size: 0.75rem;
  }
  .scene.tight .win-who i,
  .scene.tight .win .contract,
  .scene.tight .win .step,
  .scene.tight .win .group,
  .scene.tight .win .sources li {
    font-size: 0.6875rem;
    line-height: 1rem;
  }
  .scene.tight .win .reply {
    line-height: 1.1875rem;
  }
  .scene.tight .win .contract,
  .scene.tight .win .step,
  .scene.tight .win .writing {
    padding-top: 0.75rem;
  }
  /* The two captions stay under the two middle windows, which are
     narrower now. */
  .scene.tight + .wrap .scene-notes {
    width: min(100%, 43.75rem);
  }

  /* WRITE. The notes are at the LEFT here and the document at the right
     (user, 2026-10-06, on the first tight build, which had them the other
     way round: "Have the notes on the left and the document on the right
     side"). The scene still carries `flip` for its phone layout, so the two
     columns are set back by name.
     AND THE DOCUMENT IS A PAGE, not a column of text (the same message:
     "make it look more like a document that you're personally writing
     yourself"): a sheet standing on a darker desk, as a word processor
     shows one, with page margins, the document's title at its head, its
     words a size up from the notes beside it and a tier brighter, and the
     cursor after its last character. */
  .scene-work.scene-write.tight {
    height: 43.5rem;
    mask-image: linear-gradient(to top, transparent 0, #000 6.5rem);
  }
  .scene-work.scene-write.tight.flip .back {
    right: calc(var(--edge) + 1px);
    left: calc(var(--edge) + var(--side) + 1px);
  }
  .scene-work.scene-write.tight.flip .win.front {
    right: auto;
    left: calc(var(--edge) + 1px);
    border-right: 1px solid var(--border);
    border-left: 0;
  }
  /* THE PAGE IS HELD BY ITS FOOT. The view is scrolled to where the writing
     has got to, as a word processor's is while you type: the cursor stands
     a fixed way above the wash at the scene's foot at every width, and
     where the page's words take more lines (a narrower window) it is the
     page's head that goes up under the label row, not its last line that
     sinks into the wash. The column is a column of boxes packed to its end
     for that, and the label row is laid over its top. */
  .scene-work.scene-write.tight .win.pane {
    display: flex;
    flex-direction: column;
    justify-content: flex-end;
    padding-top: 2rem;
    background: var(--background);
  }
  .scene-work.scene-write.tight .pane .win-bar {
    position: absolute;
    top: 0;
    right: 0;
    left: 0;
    z-index: 2;
    border-bottom: 1px solid var(--border);
    background: var(--panel);
  }
  /* The label says what kind of thing this is; the document's name is on
     its page now. */
  .scene-work.scene-write.tight .pane .win-who b {
    display: none;
  }
  .scene-work.scene-write.tight .pane .win-who i {
    color: var(--t3);
    font-weight: 510;
  }
  /* The page is at least as tall as its column, so where its words are few
     (a wide page) it starts right under the label and no band of desk
     opens over it; where they are many it is taller than the column and is
     held by its foot, as above. */
  .scene-write.tight .draft {
    flex: none;
    width: min(100% - 2.5rem, 40rem);
    max-width: none;
    min-height: calc(100% - 0.75rem);
    margin: 0.75rem auto 0;
    padding: 2rem 2.75rem 8.25rem;
    border: 1px solid var(--border);
    border-bottom: 0;
    border-radius: 0.25rem 0.25rem 0 0;
    background: var(--panel);
    color: var(--t2);
    font-size: 0.8125rem;
    line-height: 1.3125rem;
  }
  .scene-write.tight .draft .doc-title {
    margin: 0 0 1rem;
    color: var(--t1);
    font-size: 1.25rem;
    line-height: 1.625rem;
  }
  .scene-write.tight .draft .h5 {
    margin: 1.125rem 0 0.25rem;
    color: var(--t1);
    font-size: 0.875rem;
    line-height: 1.25rem;
  }
  .scene-write.tight .draft .doc-title + .h5 {
    margin-top: 0;
  }
  @media (max-width: 64rem) {
    .scene-work.scene-write.tight {
      --side: 15rem;
    }
    .scene-write.tight .draft {
      width: calc(100% - 1.5rem);
      padding-inline: 1.75rem;
      font-size: 0.75rem;
      line-height: 1.1875rem;
    }
    .scene-write.tight .draft .doc-title {
      font-size: 1.125rem;
      line-height: 1.5rem;
    }
  }
  .scene-write.tight .win-who i {
    font-size: 0.6875rem;
  }
  /* THE CITATION'S CARD IS THE TOPMOST THING IN THIS WINDOW (user,
     2026-10-06: "This gets cut off by the notes panel at smaller widths.
     Please make sure it's at the very top within this mock, always"). It
     opens to the left of its citation, and on a narrow window that takes
     it past the page's left edge and over the notes. The document's column
     used to cut off whatever left it; now it cuts off only what leaves it
     upward (the page's head, which is meant to slide under the label), and
     the column stands in front of the notes, so the card lies whole over
     both. */
  .scene-work.scene-write.tight.flip .back {
    z-index: 2;
    overflow: visible;
  }
  .scene-work.scene-write.tight .win.pane {
    overflow-x: visible;
    overflow-y: clip;
  }
  .scene-write.tight .pop {
    z-index: 5;
    right: -0.125rem;
    left: auto;
    width: 20rem;
    padding: 0.625rem 0.75rem 0.75rem;
    font-size: 0.75rem;
    line-height: 1.0625rem;
  }
  .scene-write.tight .pop > span {
    font-size: 0.6875rem;
  }
  .scene-write.tight .pop .go {
    height: 1.5rem;
    font-size: 0.6875rem;
  }
}
/* SAVE'S PAGE IS A WEBSITE, NOT A PAPER (user, 2026-10-06: "make this feel
   like it's the NIH website, it sort of just looks like the paper itself.
   There is a download button in the website etc. It needs to feel like a
   webpage"). So it has what that site's article pages have and a paper
   does not: the line across the top saying whose website it is, the site's
   header with its name, a search field and a log-in, and beside the article
   a rail of the site's own actions with the PDF download first. The
   article's three pills (PDF, Cite, Collections) went into that rail,
   where the site has them.
   IT IS THE SITE'S LAYOUT IN THIS PAGE'S COLOURS, with its name in plain
   words: no logo, no seal and none of its blue. Their marks are theirs, and
   every scene on this page wears its section's theme (the user's standing
   call on stills).
   THE PAPER IS THE PROJECT'S OWN: Shiode et al. 2015, the one read in the
   next section, so the page follows it from the web into the reader. It
   was "What is CRISPR/Cas9?" (the paper in the recording this scene was
   drawn from), the one thing on the page outside the cholera project (the
   user: "Is there a better paper that's in theme...? It doesn't have to
   exist in the NIH"). This one does exist there, so everything printed is
   the real page's, read off it on 2026-10-06: its address and PMC number
   (PMC4521606), its PubMed number, the journal line and the PDF's size.
   These parts are set in rem at one size for every layout. The rail is not
   shown on a phone, where there is no room beside the article. */
.scene-save .br-page {
  max-width: none;
  padding: 0;
}
.br-gov {
  margin: 0;
  padding: 0.3125rem 1.25rem;
  background: var(--wash);
  color: var(--t3);
  font-size: 0.6875rem;
  line-height: 1rem;
}
.br-site {
  display: flex;
  align-items: center;
  gap: 1rem;
  padding: 0.5rem 1.25rem;
  border-bottom: 1px solid var(--border);
}
.br-logo {
  display: flex;
  align-items: center;
  gap: 0.5rem;
  min-width: 0;
}
.br-logo b {
  padding: 0.125rem 0.375rem;
  border-radius: 0.1875rem;
  background: var(--t2);
  color: var(--panel);
  font-size: 0.75rem;
  font-weight: 700;
  letter-spacing: 0.02em;
  line-height: 1rem;
}
.br-logo span {
  overflow: hidden;
  color: var(--t2);
  font-size: 0.75rem;
  font-weight: 510;
  line-height: 0.9375rem;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.br-logo i {
  display: block;
  color: var(--t3);
  font-size: 0.625rem;
  font-style: normal;
  font-weight: 400;
}
.br-search {
  display: flex;
  flex: none;
  align-items: center;
  gap: 0.375rem;
  width: 11rem;
  height: 1.5rem;
  margin-left: auto;
  padding-inline: 0.5rem;
  border-radius: 0.25rem;
  box-shadow: inset 0 0 0 1px var(--border);
  color: var(--t4);
  font-size: 0.6875rem;
}
.br-login {
  flex: none;
  color: var(--t3);
  font-size: 0.6875rem;
}
.br-cols {
  display: grid;
  grid-template-columns: minmax(0, 1fr) 11rem;
  gap: 1.5rem;
  padding: 1rem 1.25rem 0;
}
.br-ids {
  margin-top: 0.375rem;
  color: var(--t4);
  font-size: 0.6875rem;
}
.br-rail {
  padding-left: 1rem;
  border-left: 1px solid var(--border);
}
.br-rail-h {
  margin: 0 0 0.5rem;
  color: var(--t4);
  font-size: 0.625rem;
  font-weight: 600;
  letter-spacing: 0.06em;
}
.br-rail-h:not(:first-child) {
  margin-top: 1rem;
}
.br-btn {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 0.375rem;
  height: 1.625rem;
  margin: 0 0 0.375rem;
  border-radius: 0.25rem;
  box-shadow: inset 0 0 0 1px var(--border);
  color: var(--t2);
  font-size: 0.6875rem;
  font-weight: 510;
  white-space: nowrap;
}
.br-btn.on {
  background: var(--t2);
  box-shadow: none;
  color: var(--panel);
}
.br-link {
  margin: 0;
  color: var(--t3);
  font-size: 0.6875rem;
  line-height: 1.375rem;
}
@media (max-width: 48rem) {
  .br-cols {
    grid-template-columns: minmax(0, 1fr);
  }
  .br-rail,
  .br-login {
    display: none;
  }
  .br-search {
    width: 7.5rem;
  }
}
/* The two captions, each under the window it is about. */
.scene-notes {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: 1.5rem 0.75rem;
  width: min(100%, 50.75rem);
  margin: 0.5rem auto 0;
}
.scene-notes p {
  padding-inline: 1.25rem;
}
.scene-notes strong {
  color: var(--foreground-secondary);
  font-weight: 510;
}
/* A RECORDING THAT LOOPS (the browser extension; user, 2026-10-01: "replace
   the selected asset with that video. I want it on a loop"). The user's own
   screen recording, silent, playing over and over with no control: `autoplay`
   and `loop` on the element, no script. It is one file for both modes (a
   recording of Chrome, not of the page's colours), so nothing is hidden and
   nothing downloads twice. With reduced motion on the recording is taken out
   and its first frame stands as the picture. It has a hairline, like the
   other captures. */
.loop {
  position: relative;
  overflow: hidden;
  padding: 0;
  border: 1px solid var(--hairline-strong);
  background: var(--tile, var(--panel)) url("/assets/chrome-extension.webp")
    center / cover no-repeat;
}
.loop video {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
}
@media (prefers-reduced-motion: reduce) {
  .loop video {
    display: none;
  }
}
/* PARALLAX, measured on Dia: over the first 400px of scrolling its window
   gains 140px on the wordmark, sliding up over it, and after that the two
   travel together. Dia does it by moving the window faster than the page.
   Here the words are held back instead, by the same 140px over the same
   400px, so the picture gains on them just as much. Moving the picture would
   leave 140px to give back underneath it, and whatever gave it back showed
   through under the picture's bottom edge while it was still climbing. */
@supports (animation-timeline: scroll()) {
  .hero-text {
    animation: hero-text-lags linear both;
    animation-timeline: scroll(root block);
    animation-range: 0px 25rem;
  }
}
@keyframes hero-text-lags {
  to {
    translate: 0 8.75rem;
  }
}
/* The action under a lockup's paragraph (the extension's "Add to Chrome"). */
.lockup-action {
  margin-top: 3rem;
}
/* A mark before an action's words (Chrome's on "Add to Chrome"; user,
   2026-10-02: "add chrome logo to this button"). One colour, the words' own,
   like the Apple mark on the bar's download button. */
.lockup-action svg {
  flex: none;
  width: 1.125rem;
  height: 1.125rem;
  margin-right: 0.5rem;
  fill: currentColor;
}
.button:disabled {
  color: var(--muted-foreground);
  cursor: default;
  opacity: 0.6;
}
.button.secondary:disabled:hover {
  background: var(--wash);
}
/* Pictures in a row, each with its caption under it. */
.tiles {
  display: grid;
  gap: 1.5rem;
  margin-top: 5rem;
}
/* A picture in the body: a well one step lighter than the page, a 1px ring,
   a 16px corner. (The hero's picture keeps its own shape.) */
.band .shot {
  border: 1px solid var(--hairline);
  border-radius: 1rem;
}
.tiles.duo {
  grid-template-columns: minmax(0, 7fr) minmax(0, 5fr);
}
.tiles.pair {
  grid-template-columns: repeat(2, minmax(0, 1fr));
}
.tiles.trio {
  grid-template-columns: repeat(3, minmax(0, 1fr));
}
.tile {
  margin: 0;
}
.duo .shot {
  height: 100%;
  min-height: clamp(16rem, 34vw, 28rem);
}
.pair .shot {
  aspect-ratio: 4 / 3;
}
.trio .shot {
  aspect-ratio: 1;
}
.tile figcaption {
  max-width: 26rem;
  margin-top: 1.25rem;
  font-size: 1rem;
  letter-spacing: -0.014em;
  line-height: 1.4;
}
.tile figcaption strong {
  color: var(--foreground);
  font-weight: 560;
}

/* WHAT BODHI REPLACES, as Linear sets its statement: ONE large sentence at
   the left, the six tools a researcher juggles in grey and the answer in the
   text colour. No card under it (it stood on a filled tile until the body
   followed Linear). The tools are named by kind, never by brand. */
.replaces {
  color: var(--muted-foreground);
  text-wrap: pretty;
}
.replaces strong {
  color: var(--foreground);
  font-weight: inherit;
}
/* The section OPENS on that sentence (it is the section's heading) and has
   nothing else: a headline and paragraph over it said the same thing twice,
   and a single line under it ("Every claim stays linked to its source.")
   floated between this card and the next block (user, 2026-10-01, both). */
.replaces:first-child {
  margin-top: 0;
}

/* Figures: the value large, what it measures small under it. */
.stats {
  display: flex;
  flex-wrap: wrap;
  gap: 2.5rem 4.5rem;
  margin-top: 4rem;
  padding: 0;
  list-style: none;
}
/* The three reasons under the first headline, each in a card like the plans
   in pricing: a filled shape, no edge, the word large and its line under it,
   set left (user calls, 2026-10-01). */
.stats.row {
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: 1.25rem;
}
.stats.row li {
  align-content: start;
  gap: 0.5rem;
  max-width: none;
  padding: 2rem;
  border-radius: 1.75rem;
  background: var(--tile);
}
.stats.row b {
  font-size: 1.75rem;
}
/* THE EXTENSION'S SCENE, in code like the others (user, 2026-10-02: "now
   work on the chrome extension area's asset"). It replaced the user's own
   screen recording of the extension. What that recording showed is what is
   drawn: a browser on a paper's page, the Bodhi button in its toolbar
   pressed, and the extension's panel open over the page (the title, the
   authors, year and journal, "The PDF comes too.", where to save it, tags, a
   note, Save), then "Saved to your Library." The panel is the extension's
   own, word for word. The page behind it is the page's own dark design: the
   paper's real title, authors and reference, and plain bars where its text
   would be (the text is not reproduced).
   The panel is the thing in focus, at the right, under the toolbar button it
   belongs to; the browser behind it runs off the left edge and dissolves, as
   Read's and Write's views do.
   IT TURNS BETWEEN ITS TWO STATES BY ITSELF, every seven seconds (the
   recording looped; this does the same in CSS): the form, a press on Save,
   then "Saved to your Library". With reduced motion on it rests on the form.
   Under it, the four kinds of page it saves, each with its icon in a square
   tile a little lighter than the page (user ask, 2026-10-01), in one row. */
.scene-save {
  height: 35rem;
}
.scene-save.flip .back {
  right: calc(var(--edge) + 6rem);
}
.scene-save .win.pane {
  height: 33rem;
}
.br-bar {
  display: flex;
  align-items: center;
  gap: 0.75rem;
  height: 3.25rem;
  padding: 0 1rem 0 calc(var(--edge-c) + 1rem);
  border-bottom: 1px solid var(--border);
}
.br-url {
  flex: 1;
  overflow: hidden;
  height: 2rem;
  padding-inline: 1rem;
  border-radius: 9999px;
  background: var(--wash);
  color: var(--muted-foreground);
  font-size: 0.8125rem;
  line-height: 2rem;
  text-overflow: ellipsis;
  white-space: nowrap;
}
/* The toolbar button that was pressed. */
.br-ext {
  display: grid;
  flex: none;
  place-items: center;
  width: 2rem;
  height: 2rem;
  border-radius: 50%;
  background: var(--wash-strong);
  box-shadow: 0 0 0 1px var(--hairline-strong);
}
.br-ext img,
.ext-mark {
  width: 1rem;
  height: 1rem;
  filter: brightness(0) invert(1);
}
@media (prefers-color-scheme: light) {
  .br-ext img,
  .ext-mark {
    filter: brightness(0);
  }
}
.br-page {
  /* The page's words stop short of the panel that lies over its right side. */
  max-width: calc(100% - 17rem);
  padding: 2rem 2rem 0 calc(var(--edge-c) + 1rem);
}
.br-run {
  color: var(--muted-foreground);
  font-size: 0.75rem;
}
.br-page .h5 {
  /* All four sides: an h5's own bottom margin (1.67 of its size, 47px here)
     was left on, and opened a hole under the title. */
  margin: 0.75rem 0 0;
  color: var(--foreground);
  font-size: 1.75rem;
  font-weight: 510;
  letter-spacing: -0.02em;
  line-height: 1.15;
}
.br-by {
  margin-top: 0.75rem;
  color: var(--foreground-secondary);
}
.br-acts {
  display: flex;
  gap: 0.5rem;
  margin-top: 1.25rem;
}
.br-acts i {
  padding: 0.25rem 0.75rem;
  border: 1px solid var(--border);
  border-radius: 9999px;
  color: var(--muted-foreground);
  font-size: 0.75rem;
  font-style: normal;
}
.sk-h {
  margin-top: 2rem;
  color: var(--foreground);
  font-weight: 510;
}
/* Lines where the page's text would be. */
.sk {
  display: grid;
  gap: 0.6875rem;
  margin-top: 0.875rem;
}
.sk i {
  height: 0.4375rem;
  border-radius: 9999px;
  background: var(--wash-strong);
}
.sk i:nth-child(3n + 2) {
  width: 94%;
}
.sk i:nth-child(3n) {
  width: 97%;
}
.sk i:last-child {
  width: 58%;
}
/* The panel's fields, its picker and its buttons are rounded boxes at the
   control radius, not pills (user, 2026-10-02: "use rounded box shape
   instead of pill for the different components that are pill shaped
   here"). */
/* The extension's panel. It hangs just UNDER the browser's toolbar, so the
   button it belongs to is whole above it (user, 2026-10-02: "the plugin box
   needs to move down a bit, I can't see the extension button in the
   browser, half is cut off"). The toolbar ends 6.0625rem down the scene.
   It is on the LIGHTEST surface, like every other floating window in the
   scenes (`.win.front`), so it stands off the browser page behind it. It
   was on the page's own darkest colour, which hid it (user, 2026-10-02:
   "give this chrome plugin a lighter background so it stands out more"). */
.win.ext {
  top: 6.5rem;
  width: 23rem;
  border-radius: 0.875rem;
}
.win.ext .win-who {
  width: 100%;
}
.win.ext .win-who i {
  margin-left: auto;
}
.ext-body {
  position: relative;
  padding: 0 1rem 1rem;
}
.ext-field {
  overflow: hidden;
  height: 2.5rem;
  padding-inline: 0.875rem;
  border: 1px solid var(--border);
  border-radius: var(--radius);
  background: var(--wash);
  color: var(--foreground);
  line-height: 2.5rem;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.ext-field.dim {
  margin-top: 0.75rem;
  color: var(--foreground-dim);
}
.ext-field.area {
  height: 4.75rem;
  border-radius: var(--radius);
}
.ext-meta {
  overflow: hidden;
  margin-top: 0.5rem;
  padding-inline: 0.25rem;
  color: var(--muted-foreground);
  font-size: 0.75rem;
  line-height: 1.125rem;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.ext-meta + .ext-meta {
  margin-top: 0.125rem;
}
.ext-row {
  display: flex;
  align-items: center;
  gap: 0.625rem;
  margin-top: 0.875rem;
  padding-inline: 0.25rem;
  color: var(--muted-foreground);
}
.ext-pick {
  display: inline-flex;
  align-items: center;
  gap: 1.5rem;
  height: 2rem;
  padding-inline: 0.75rem 0.625rem;
  border: 1px solid var(--border);
  border-radius: var(--radius);
  color: var(--foreground);
}
.ext-save {
  height: 2.5rem;
  margin-top: 0.875rem;
  border-radius: var(--radius);
  background: var(--action);
  color: var(--action-foreground);
  font-weight: 510;
  line-height: 2.5rem;
  text-align: center;
}
/* The second state lies over the first, in the same box. */
.ext-done {
  position: absolute;
  inset: 0 1rem 1rem;
  display: grid;
  align-content: center;
  justify-items: center;
  gap: 0.75rem;
  opacity: 0;
  color: var(--foreground);
  font-size: 0.9375rem;
  font-weight: 510;
}
.ext-done .ext-save {
  width: 100%;
  margin-top: 0.5rem;
  font-size: 0.8125rem;
}
.ic.big {
  width: 2.5rem;
  height: 2.5rem;
  padding: 0.6875rem;
  border-radius: 50%;
  background: var(--mark);
  color: var(--foreground);
  stroke-width: 2.5;
}
@media (prefers-reduced-motion: no-preference) {
  .ext-form {
    animation: ext-form 7s ease-in-out infinite;
  }
  .ext-form .ext-save {
    animation: ext-press 7s ease-in-out infinite;
  }
  .ext-done {
    animation: ext-done 7s ease-in-out infinite;
  }
}
@keyframes ext-form {
  0%,
  44% {
    opacity: 1;
  }
  50%,
  94% {
    opacity: 0;
  }
  100% {
    opacity: 1;
  }
}
@keyframes ext-press {
  0%,
  36% {
    filter: none;
    scale: 1;
  }
  40% {
    filter: brightness(0.82);
    scale: 0.985;
  }
  44%,
  100% {
    filter: none;
    scale: 1;
  }
}
@keyframes ext-done {
  0%,
  46% {
    opacity: 0;
    translate: 0 0.375rem;
  }
  52%,
  92% {
    opacity: 1;
    translate: 0 0;
  }
  98%,
  100% {
    opacity: 0;
    translate: 0 0;
  }
}
/* SAVE SAYS ONE THING: one press saves the page you are on (the focus
   pass; DISCOVER above has the method). The white is the paper's title in
   the panel, and "Saved to your Library." when the panel turns to its
   second state; the Save button keeps the brand colour, and the toolbar
   button that opened the panel keeps its ring. Everything else steps down:
   the page behind (its title a step, the rest two, the bars of its text
   fainter), the address, the panel's name and its other fields. */
.scene-save .br-page .h5,
.scene-save .win-who b,
.scene-save .ext-pick {
  color: var(--t2);
}
.scene-save .ext-meta {
  color: var(--t3);
}
.scene-save .br-url,
.scene-save .br-run,
.scene-save .br-by,
.scene-save .br-acts i,
.scene-save .sk-h,
.scene-save .win-who i,
.scene-save .ext-row,
.scene-save .ext-field.dim {
  color: var(--t3);
}
.scene-save .br-acts i,
.scene-save .ext-pick,
.scene-save .ext-field.dim {
  border-color: var(--hairline);
}
.scene-save .sk i {
  background: var(--wash);
}
.scene-save .ext-field:not(.dim) {
  border-color: color-mix(in oklab, var(--foreground) 26%, transparent);
  color: var(--t1);
}
/* What it saves: four kinds, in one row under the scene. */
.kinds {
  display: grid;
  grid-template-columns: repeat(4, minmax(0, 1fr));
  gap: 1.25rem 2rem;
  margin: 2.25rem 0 0;
  padding: 0;
  list-style: none;
}
.kinds li {
  display: flex;
  align-items: center;
  gap: 0.875rem;
}
.kinds b {
  color: var(--foreground);
}
/* Too narrow for the browser and the panel side by side: the browser takes
   the width and the panel sits over its lower part. (These come after the
   scene's own rules on purpose: placed before them, they lose.) */
@media (max-width: 64rem) {
  .scene-save.flip .back {
    right: auto;
    height: 22rem;
  }
  .br-bar,
  .br-page {
    padding-left: 1.25rem;
  }
  /* The browser fits the width here instead of running off it, so its
     toolbar button is on screen, and the panel hangs under that button at
     the right. */
  .scene-save .back,
  .scene-save.flip .back {
    mask-image: linear-gradient(#000 0%, #000 62%, transparent 100%);
  }
  .scene-save .win.pane {
    width: 100%;
  }
  .br-page {
    max-width: none;
  }
  .scene-save .win.ext {
    top: auto;
    width: auto;
    max-width: 23rem;
    margin-top: -17.5rem;
    margin-right: 0;
    margin-left: auto;
  }
  .kinds {
    grid-template-columns: repeat(2, minmax(0, 1fr));
  }
}
.icon-tile {
  display: grid;
  flex: none;
  place-items: center;
  width: 2.75rem;
  height: 2.75rem;
  border-radius: 0.75rem;
  background: var(--wash-strong);
  color: var(--foreground);
}
.icon-tile svg {
  width: 1.375rem;
  height: 1.375rem;
  fill: none;
  stroke: currentColor;
  stroke-linecap: round;
  stroke-linejoin: round;
  stroke-width: 1.75;
}
.stats.row span {
  max-width: 18rem;
  color: var(--muted-foreground);
  font-size: 1rem;
  letter-spacing: -0.014em;
  line-height: 1.4;
}
.stats li {
  display: grid;
  gap: 0.375rem;
  max-width: 16rem;
}
.stats b {
  color: var(--foreground);
  font-size: clamp(1.75rem, 2.8vw, 2.25rem);
  font-weight: 560;
  letter-spacing: -0.035em;
  line-height: 1.05;
}
.stats span {
  color: var(--foreground-secondary);
  text-wrap: balance;
  font-size: 0.875rem;
  letter-spacing: -0.006em;
  line-height: 1.35;
}

/* What stands behind a picture: three names under its caption, most wanted
   first. Plain words in the text colour, a size under the caption, with air
   between them and no rule: Apple lists such things without ornament. They
   live inside the picture's own block so they stay with it when the row
   stacks on a phone. */
.tile ul {
  display: grid;
  gap: 0.625rem;
  margin: 1.75rem 0 0;
  padding: 0 0.25rem;
  list-style: none;
  color: var(--foreground);
  font-size: 0.9375rem;
  font-weight: 510;
  letter-spacing: -0.011em;
  line-height: 1.3;
}

/* A lesser heading: 48, at the left, with one link at its right. */
.band-head {
  display: flex;
  align-items: end;
  justify-content: space-between;
  gap: 1.5rem 3rem;
}
.band-head h2,
.band-head h3 {
  color: var(--foreground);
  text-wrap: balance;
}
/* The same heading part-way down a section, over the pictures it names. */
.band-head.sub {
  margin-top: 6rem;
}
.band-head.sub + .tiles {
  margin-top: 3rem;
}
.band-head h2,
.band-head h3 {
  font-size: clamp(1.875rem, 3.4vw, 2.75rem);
  font-weight: 560;
  letter-spacing: -0.035em;
  line-height: 1.08;
}

/* THE TWO PLANS, as Linear sets its own: open columns with a faint rule
   between them. A label, one large line, a sentence, the action. */
.plans {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: 3rem var(--column-gap);
  margin-top: 6rem;
}
/* A plan is a column, and ITS BUTTON SITS AT ITS FOOT: the two plans are as
   tall as each other (the row stretches them), so their buttons are level
   whatever each says above (user, 2026-10-06, once Pro's list had made its
   card the taller: "line up the two plan buttons"). The space over a button
   is the margin under the thing before it, and anything more is the
   shorter plan's slack. */
.plan {
  position: relative;
  display: flex;
  flex-direction: column;
  align-items: flex-start;
}
.plan > :nth-last-child(2) {
  margin-bottom: 1.75rem;
}
/* The AI choice is the one place with CARDS: two things you pick between
   read better as two objects. A card here is Linear's: the well's fill, a
   1px ring, a 16px corner. */
.plans:has(.choice) {
  gap: 1.5rem;
}
.plan.choice {
  padding: 2rem;
  border: 1px solid var(--hairline);
  border-radius: 1rem;
  background: var(--panel);
}
.plan h3 {
  color: var(--foreground);
  font-size: 1rem;
  font-weight: 560;
  letter-spacing: -0.014em;
  line-height: 1.3;
}
.price {
  margin-top: 0.75rem;
  color: var(--foreground);
  font-size: clamp(1.75rem, 2.8vw, 2.25rem);
  font-weight: 560;
  letter-spacing: -0.03em;
  line-height: 1.05;
}
.price span {
  color: var(--muted-foreground);
  font-size: 1rem;
  font-weight: 400;
  letter-spacing: -0.014em;
}
.plan .price + p {
  max-width: 24rem;
  margin-top: 0.75rem;
  font-size: 1rem;
  letter-spacing: -0.014em;
  line-height: 1.4;
}
.plan .button {
  margin-top: auto;
}
/* WHAT PRO GIVES IS A LIST (2026-10-06, with the section's new copy): one
   short line a thing, a tick before each, so a line can be added when a
   feature goes live without rewriting a paragraph. The lines waiting, each
   for the day its feature is switched on, are in CLAUDE.md: do not add one
   early. Then one grey sentence of reassurance, and the button. */
.plan-gets {
  display: grid;
  gap: 0.375rem;
  margin-top: 0.75rem;
  padding: 0;
  color: var(--foreground-secondary);
  font-size: 0.9375rem;
  letter-spacing: -0.011em;
  line-height: 1.6;
  list-style: none;
}
.plan-gets li {
  display: flex;
  gap: 0.625rem;
  align-items: baseline;
}
.plan-gets .ic {
  position: relative;
  top: 0.125rem;
  color: var(--muted-foreground);
}
.plan-note {
  max-width: 24rem;
  margin-top: 0.75rem;
  font-size: 0.9375rem;
  letter-spacing: -0.011em;
  line-height: 1.6;
}
/* "Founding price": a quiet tag after the price. A subscriber keeps the
   price they bought at (PRICING.md in the main checkout), so it is true. */
.founding {
  margin-left: 0.625rem;
  padding: 0.125rem 0.5rem;
  border-radius: 9999px;
  box-shadow: inset 0 0 0 1px var(--hairline-strong);
  color: var(--muted-foreground);
  font-size: 0.75rem;
  font-style: normal;
  font-weight: 510;
  letter-spacing: 0;
  white-space: nowrap;
  vertical-align: 0.0625rem;
}

/* A CHOICE with two equal sides, in the plans' cards: AI off, which is how
   Bodhi arrives, and AI on. Off is first. The switch at the top of each is a
   drawing of the control in the app's settings, not a control: it cannot be
   pressed, and assistive technology is not told about it. */
.choice h3 {
  margin-top: 1.5rem;
  font-size: 1.75rem;
  letter-spacing: -0.03em;
  line-height: 1.1;
}
.choice p {
  max-width: 26rem;
  margin-top: 0.75rem;
  font-size: 1rem;
  letter-spacing: -0.014em;
  line-height: 1.4;
}
.switch {
  position: relative;
  display: block;
  width: 3rem;
  height: 1.75rem;
  border-radius: 9999px;
  background: var(--hairline-strong);
}
.switch::after {
  position: absolute;
  top: 0.1875rem;
  left: 0.1875rem;
  width: 1.375rem;
  height: 1.375rem;
  border-radius: 50%;
  background: oklch(1 0 0);
  box-shadow: 0 1px 2px oklch(0 0 0 / 0.25);
  content: "";
}
.switch.on {
  background: var(--primary);
}
.switch.on::after {
  left: 1.4375rem;
}
.site-footer a:hover {
  color: var(--foreground);
}

/* THE LIGHT (user, 2026-10-05, after every mockup had been given Linear's
   edges and text: "there are gradients, shadowiness, and things like that
   that make the Linear UI mockup look really nice... there are a lot of very
   small details that need to be thought of to pull this off correctly... I
   don't want excessive, unthoughtful gradient and shadowiness. I want
   everything to be very nice and crisp, just like Linear").
   Every gradient, shadow and mask on linear.app's home page was read off
   that day. They are FEW and they follow one model: ONE LIGHT, FROM A
   CORNER, and everything else is flat. Nothing glows in the middle of
   anything, and no shadow is soft enough to smudge an edge. What each kind
   of thing gets, with Linear's own numbers:
     A WINDOW  a disc of white at 4%, 400 across, centred ON its lit corner
       and clipped by the window, so a quarter of it shows: the corner is a
       touch lighter and it is gone 200px in. With it, the shine already on
       the same corner's edge (1px of white at 8% under a 200px mask).
     A FRAME THAT FLOATS ON THE PAGE (the AI row)  not a stage panel: a
       lighter fill, half a pixel of white at 12%, and the one real shadow
       on the page, 0 2px 32px of black at 25%.
     A CARD IN A PANEL (a canvas card, the opened citation, a step list, a
       message)  the panel's fill under 3% of white, so it is lifted by
       light and not by a new grey; a hairline of white at 8%; a ring of
       black at 20% just outside that, which seats it; and the first 100px
       of its edge from the top left corner at 12%. The dark ring is the
       detail that makes these read as crisp: the light line has something
       to sit against.
     A FIELD  its edge is a gradient across the corner, white at 15% to 5%
       a quarter of the way along, over the same lifted fill and seat.
     A GHOST BUTTON  2% of white inside a ring of white at 5%, drawn inside
       it, with no border of its own.
     A FILLED BUTTON  the short stack of small shadows the page's own
       buttons have (`--shadow-button`).
     A GRID LINE IN A CHART  dashed.
   The light is always the top left, the side the hero window and the tree
   are lit from, except where a window says otherwise (`.lit-right`).
   NOT TAKEN, each for a reason: Linear's GRAIN (asked off this site twice);
   a glow BEHIND a window or on the page (both were here and both were asked
   off: a window's glow is inside it, where its own edge ends it); the
   blurred band of light Linear draws into a floating box's border (the
   shine does that job here).
   On a light page a glow and a bright corner show nothing, so they are
   off there and an edge is a darker hairline (the tokens' light twins). */
.win::before {
  position: absolute;
  top: 0;
  left: 0;
  z-index: 2;
  width: 25rem;
  height: 25rem;
  background: radial-gradient(circle, var(--glow) 0%, transparent 50%);
  content: "";
  pointer-events: none;
  translate: -50% -50%;
}
.win.lit-right::before {
  left: 100%;
}
/* Read's sheet is the card there (the window behind it is not drawn). */
.scene-read .win.pane::before {
  display: none;
}
.scene-read .leaf {
  background-image: radial-gradient(
    circle 12.5rem at 0 0,
    var(--glow),
    transparent
  );
}
/* The AI row: frames that float on the page. */
.scene-track .win {
  border: 0.5px solid var(--hairline-strong);
  background: var(--surface-2);
  box-shadow: 0 2px 2rem oklch(0 0 0 / 0.25);
}
@media (prefers-color-scheme: light) {
  .scene-track .win {
    box-shadow: 0 2px 2rem oklch(0 0 0 / 0.08);
  }
}
/* A card in a panel. */
.card,
.pop,
.win .box,
.win .say {
  border: 0.5px solid var(--edge);
  background: linear-gradient(var(--lift), var(--lift)), var(--panel);
  box-shadow: var(--seat);
}
.scene-track .win .box,
.scene-track .win .say {
  background: linear-gradient(var(--lift), var(--lift)), var(--surface-2);
}
.pop {
  background: linear-gradient(var(--lift-2), var(--lift-2)), var(--panel);
  box-shadow:
    var(--seat),
    0 4px 2rem color-mix(in oklab, var(--background) 60%, transparent);
}
.card::after,
.pop::after {
  position: absolute;
  inset: -0.5px;
  border: 0.5px solid var(--edge-lit);
  border-radius: inherit;
  content: "";
  mask-image: radial-gradient(
    100px 100px at 0 0,
    #000 0%,
    #000 16%,
    transparent 100%
  );
  pointer-events: none;
}
/* A quoted passage keeps the app's own card (a fill and no edge): only the
   seat is added. */
.note blockquote,
.pop .q {
  box-shadow: var(--seat);
}
/* A field. */
.win .search-field,
.ext-field {
  border: 0.5px solid transparent;
  background:
    linear-gradient(var(--lift), var(--lift)) padding-box,
    linear-gradient(var(--panel), var(--panel)) padding-box,
    linear-gradient(135deg, var(--lit-from) 0%, var(--lit-to) 25%) border-box;
  box-shadow: var(--seat);
}
/* A ghost button. */
.win .add,
.pop .go,
.ext-pick {
  border: 0;
  background: color-mix(in oklab, var(--foreground) 2%, transparent);
  box-shadow: inset 0 0 0 1px var(--hairline-faint);
}
/* A filled button. */
.win .add.on,
.ext-save {
  box-shadow: var(--shadow-button);
}
/* A chart's grid. */
.map .g {
  stroke: var(--hairline);
  stroke-dasharray: 3 3;
}
/* THE HERO WINDOW, the same light in its own em. Its four views are Linear's
   hero view: an edge of white at 5% inside a ring of black at 20%. Its
   cards are cards. (On 2026-10-04 the user had each view's own glow and
   graded edge taken out, "crisp hairline borders for the UI itself"; this
   keeps to that: a view has no glow of its own, only the window does.) */
.app-pn {
  border-color: var(--hairline-faint);
  box-shadow: 0 0 0 1px oklch(0 0 0 / 0.2);
}
.ac,
.apop {
  border: 0.5px solid var(--edge);
  background:
    linear-gradient(var(--lift), var(--lift)), var(--fill, var(--panel));
  box-shadow: var(--seat);
}
.apop {
  background: linear-gradient(var(--lift-2), var(--lift-2)), var(--panel);
  box-shadow:
    var(--seat),
    0 0.25em 2em color-mix(in oklab, var(--background) 60%, transparent);
}
.ac::after {
  position: absolute;
  inset: -0.5px;
  border: 0.5px solid var(--edge-lit);
  border-radius: inherit;
  content: "";
  mask-image: radial-gradient(
    6em 6em at 0 0,
    #000 0%,
    #000 16%,
    transparent 100%
  );
  pointer-events: none;
}
.app .leaf {
  border: 0.5px solid var(--edge);
  box-shadow: var(--seat);
}
.apop .go {
  border: 0;
  background: color-mix(in oklab, var(--foreground) 2%, transparent);
  box-shadow: inset 0 0 0 1px var(--hairline-faint);
}
@media (prefers-color-scheme: light) {
  .app-pn {
    box-shadow: 0 0 0 1px oklch(0 0 0 / 0.03);
  }
}
/* THE CLOSING SECTION (user, 2026-10-05: "change the copy to 'Give it a
   try', and right above it, use the folio illustration from Hairline Studio
   and play it in an animated state where the page is flipping. Also, get
   rid of that stack-of-paper sort of effect we have, and remove the
   'Available for macOS, Windows coming soon' text. Just keep the button
   below the description text and make it smaller... add the Apple icon to
   the very left, and get rid of the down arrow... get rid of the text that
   says 'requires a Mac with Apple Silicon' as well").
   A centred head: the folio turning its pages, the line, the description,
   one button. A rule above it and the usual 136 to its first thing, like
   every other section.
   THE STACK OF PAPERS IS GONE (three turned sheets under the line with the
   button on the top one, Dia's download dome reshaped, 2026-10-01; made to
   stop following the window on 10-02), with its yellow highlighter line and
   its fine print. Do not bring any of it back unasked. */
.closing {
  padding-block: 8.5rem 8rem;
  border-top: 1px solid var(--hairline);
}
/* The folio is the one drawing that MOVES BY ITSELF: its stage carries no
   `data-hairline-hold`, so its pages keep turning (the figure's own rule;
   with the flag it would wait for the pointer). Moving over it slows them.
   Centred over the line: its plate ends 40 above it (the drawing's ink 68,
   the plate's own room being 28). */
.lockup.center > .lockup-art {
  margin-bottom: calc(4.25rem - var(--w) * var(--air-bottom));
}
/* Under the description, smaller than a section's button (40 for 44), the
   Apple mark at its left. */
.closing .button {
  gap: 0.5rem;
  height: 2.5rem;
  margin-top: 2rem;
  padding-inline: 1.125rem;
  font-size: 0.9375rem;
}
.closing .button svg {
  width: 1rem;
  height: 1rem;
  margin-top: -0.125rem;
  fill: currentColor;
}
.site-footer {
  padding-block: 3.5rem 2.5rem;
  border-top: 1px solid var(--border);
}
.footer-grid {
  display: grid;
  grid-template-columns: 2fr repeat(3, 1fr);
  gap: 2rem;
}
.footer-grid nav {
  display: grid;
  align-content: start;
  justify-items: start;
}
.footer-grid nav span {
  margin-bottom: 0.75rem;
  color: var(--foreground);
}
.footer-grid nav a {
  display: inline-flex;
  align-items: center;
  min-height: 1.75rem;
  text-decoration: none;
  transition: color 150ms;
}
/* The credit a picture's licence asks for, under the footer's columns, in
   the footer's own grey (the dimmest tier fails contrast for real text). */
/* The line of credits that stood under the footer's columns is gone (user,
   2026-10-02: "do we have to show this? can we shove it somewhere if we
   do? it feels messy"). We do have to: the paper is under CC BY 4.0, which
   asks for its credit and its licence, and allows them to be given by a
   link; the map's tiles ask for theirs. So the paper's credit is on its own
   page, /credits/, linked from the footer's Legal list, and the map's is on
   the map, in its corner, where every map carries it. Do not remove either
   while the paper or the map is shown. */

/* The footer's mark and name are a dictionary HEADWORD (user ask,
   2026-10-01): under them, how the word is said, its part of speech and its
   meaning, in the footer's own face and size (the user tried the hero's
   serif here and chose the sans). The stress mark is a plain apostrophe. */
.entry,
.definition {
  max-width: 17rem;
}
.entry {
  margin-top: 0.75rem;
  color: var(--foreground-secondary);
}
.entry span {
  margin-inline: 0.3em;
  color: var(--foreground-dim);
}
/* Two senses, numbered as a dictionary numbers them: the word, and the tree
   (user, 2026-10-02: "can we add something here about the tree?"). */
.definition {
  margin: 0.125rem 0 0;
  padding-left: 1.25em;
}
.definition li + li {
  margin-top: 0.25rem;
}
.definition li::marker {
  color: var(--foreground-dim);
}

/* The policy pages share this sheet. Their wording is approved and is not
   edited here, only dressed. */
body > header:not(.site-header),
body > footer:not(.site-footer),
.legal {
  width: min(100% - 2 * var(--gutter), 45rem);
  margin-inline: auto;
}
body > header:not(.site-header) {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 1rem;
  min-height: 4.5rem;
  border-bottom: 1px solid var(--hairline);
}
body > header:not(.site-header) nav,
body > footer:not(.site-footer) nav {
  display: flex;
  flex-wrap: wrap;
  gap: 0.25rem 1.5rem;
  font-size: 0.8125rem;
  letter-spacing: 0;
}
body > header:not(.site-header) nav a,
body > footer:not(.site-footer) nav a {
  display: inline-flex;
  align-items: center;
  min-height: 2rem;
  text-decoration: none;
  transition: color 150ms;
}
body > header:not(.site-header) nav a:hover,
body > footer:not(.site-footer) nav a:hover,
body > footer:not(.site-footer) nav a[aria-current] {
  color: var(--foreground);
}
.legal {
  padding-block: 6rem 8rem;
  color: var(--foreground-secondary);
  font-size: 1rem;
  line-height: 1.65;
}
.legal h1 {
  margin-bottom: 1.25rem;
  font-size: clamp(2rem, 1.25rem + 2.4vw, 3rem);
}
.legal h2 {
  margin: 3rem 0 0.75rem;
  font-size: 1.25rem;
  letter-spacing: -0.012em;
  line-height: 1.33;
  text-wrap: pretty;
}
.legal p {
  margin-bottom: 1rem;
}
.legal .eyebrow,
.legal .legal-date {
  color: var(--muted-foreground);
  font-size: 0.8125rem;
  letter-spacing: 0;
}
.legal .eyebrow {
  margin-bottom: 1.5rem;
}
.legal .legal-date {
  margin-bottom: 3rem;
}
.legal a:hover {
  color: var(--foreground);
}
.legal strong,
.policy-summary dt {
  color: var(--foreground);
  font-weight: 510;
}
.policy-list {
  padding-left: 1.25rem;
}
.policy-list li {
  padding-bottom: 0.75rem;
}
.policy-summary {
  margin-block: 1.5rem;
  padding: 0.5rem 1.5rem;
  border: 1px solid var(--hairline-faint);
  border-radius: var(--radius-card);
  background: var(--panel);
}
.policy-summary > div {
  padding-block: 1rem;
}
.policy-summary > div + div {
  border-top: 1px solid var(--hairline);
}
.policy-summary dd {
  color: var(--muted-foreground);
}
body > footer:not(.site-footer) {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: space-between;
  gap: 1rem 2rem;
  padding-block: 1.5rem 2.5rem;
  border-top: 1px solid var(--border);
  font-size: 0.8125rem;
  letter-spacing: 0;
}

@media (max-width: 48rem) {
  :root {
    --gutter: 1.5rem;
    --header: 5rem;
  }
  /* A phone: a shorter bar, and "Features" goes (the page is one scroll). */
  .site-header {
    height: 3.75rem;
  }
  .nav-link[href^="#"] {
    display: none;
  }
  .site-header .brand {
    gap: 0.5rem;
    font-size: 1.1875rem;
  }
  .band,
  .hero + .band {
    padding-block: 5rem;
  }
  .also {
    grid-template-columns: minmax(0, 1fr);
    row-gap: 1rem;
  }
  .tiles.duo,
  .tiles.pair,
  .tiles.trio,
  .plans,
  .lockup {
    grid-template-columns: minmax(0, 1fr);
  }
  /* One column: the drawing first, smaller, then the words, and the rules
     that stood between columns go. */
  .lockup-text,
  .lockup-art {
    grid-column: 1;
    grid-row: auto;
  }
  .lockup-art {
    --w: min(var(--size) * 0.62, 100%);
    order: -1;
    margin-bottom: calc(4.25rem - var(--w) * var(--air-bottom));
  }
  .column + .column::before,
  .plan:not(.choice) + .plan:not(.choice)::before {
    display: none;
  }
  /* A phone has room for one window: the row is moved so the Ask window is
     in the middle, with its neighbours running off at either side. */
  .scene {
    --win: min(21.5rem, 86vw);
    --fade: 7%;
    margin-top: 1rem;
  }
  .scene-track {
    translate: calc(var(--win) * -1.5 - 0.75rem) 0;
  }
  .scene-notes {
    grid-template-columns: minmax(0, 1fr);
    margin-top: 0;
  }
  .scene-notes p {
    padding-inline: 0;
  }
  .hero {
    --hero-top: 7rem;
  }
  /* Too narrow to split the line round the trunk. The line is one centred
     block right above the window, with a little room under it, and it lies
     OVER the tree, which still stands on the window's top edge behind it
     (user, 2026-10-02: "in mobile size please show the text right above the
     screenshot, with a bit of padding at the bottom", then, shown the tree
     moved up to stand on the line instead: "the text should still be on top
     of the tree in mobile"). So the words cross the trunk and the foot of
     the crown, and wear the page-coloured shadow that keeps them readable
     there. Before this the line lay over the top of the crown. */
  .hero-line {
    left: auto;
    width: auto;
    container-type: normal;
    translate: none;
  }
  .hero .hero-line h1 {
    display: block;
    font-size: clamp(1.5rem, 2.7vw, 2.375rem);
    text-shadow:
      0 0 1.5rem var(--background),
      0 0 0.5rem var(--background);
    white-space: normal;
  }
  .hero-tree {
    left: 50%;
  }
  .closing {
    padding-block: 5rem;
  }
  .footer-grid {
    grid-template-columns: repeat(3, minmax(0, 1fr));
  }
  .footer-grid > div {
    grid-column: 1 / -1;
  }
}
/* A finger needs more than a 28px row. */
@media (pointer: coarse) {
  .footer-grid nav a {
    min-height: 2.75rem;
  }
}
@media (prefers-reduced-motion: reduce) {
  html {
    scroll-behavior: auto;
  }
  .button,
  .site-header,
  .nav-link,
  .nav-download,
  .footer-grid nav a {
    transition: none;
  }
  .hero-text {
    animation: none;
  }
  .hero-tree {
    transition: none;
  }
}

/* The narrowest phones: the bar's row only fits without the Apple mark.
   After the 48rem block on purpose. */
@media (max-width: 23rem) {
  .site-header .brand {
    font-size: 1.0625rem;
  }
  .nav-link {
    padding-inline: 0.5rem;
  }
  .nav-download svg {
    display: none;
  }
}

/* A POINTER WORKS SAVE AND READ (user, 2026-10-06: "add the cursor
   interaction for mockups I've selected as well, use the same cursor style
   and make sensible animations and interactions"). scene-hand.js moves it
   and keeps the time, and has the account of both rounds. `by-hand` is on a
   scene only while the pointer has it (tight windows, motion allowed);
   without it each scene is what it was. The pointer is the hero's, in the
   section's own accent. */
.hand {
  z-index: 10;
}
.by-hand .hand {
  opacity: 1;
  transition: opacity 300ms ease;
}
.by-hand .hand.away {
  opacity: 0;
}
/* SAVE. The panel is shut until Bodhi's button in the toolbar is pressed,
   and that button is only ringed while the panel is open. The panel turns
   to "Saved" on the press of Save, not on a timer. */
.scene-save.by-hand :is(.ext-form, .ext-form .ext-save, .ext-done) {
  animation: none;
}
.scene-save.by-hand .br-ext {
  transition:
    background 200ms ease,
    box-shadow 200ms ease;
}
.scene-save.by-hand:not(.is-open) .br-ext {
  background: transparent;
  box-shadow: none;
}
.scene-save.by-hand .win.front.ext {
  transform-origin: 100% 0;
  transition:
    opacity 180ms ease,
    scale 240ms cubic-bezier(0.2, 0.8, 0.2, 1);
}
.scene-save.by-hand:not(.is-open) .win.front.ext {
  opacity: 0;
  scale: 0.96;
}
.scene-save.by-hand .ext-form {
  transition: opacity 250ms ease;
}
.scene-save.by-hand .ext-done {
  translate: 0 0.375rem;
  transition:
    opacity 300ms ease 150ms,
    translate 300ms ease 150ms;
}
.scene-save.by-hand.is-saved .ext-form {
  opacity: 0;
}
.scene-save.by-hand.is-saved .ext-done {
  opacity: 1;
  translate: 0 0;
}
.scene-save.by-hand .ext-form .ext-save {
  transition:
    filter 110ms ease,
    scale 110ms ease;
}
.scene-save.by-hand .ext-form .ext-save.down {
  filter: brightness(0.82);
  scale: 0.985;
}
/* READ. A passage is unmarked until the pointer has run along it. Its wash
   is a picture laid over its lines as one strip (the lines end to end),
   as wide as the pointer has gone (`--sel`); when the pointer lifts, the
   passage is the plain marked passage again (`is-marked`). The note made
   from it is shut until then and opens at the top of the list. At the end
   of a round the window's contents dip out, are put back unseen, and come
   in again (`is-turning`). */
.scene-read.by-hand .leaf mark.land:not(.is-marked) {
  background: linear-gradient(var(--mark-strong), var(--mark-strong)) no-repeat
    0 0 / var(--sel, 0%) 100%;
  box-decoration-break: slice;
  -webkit-box-decoration-break: slice;
  color: inherit;
}
.scene-read.by-hand .leaf mark.land {
  transition: color 300ms ease;
}
.scene-read.by-hand .note[data-with] {
  overflow: hidden;
  max-height: 16rem;
  transition:
    max-height 600ms cubic-bezier(0.2, 0.8, 0.2, 1),
    padding 600ms cubic-bezier(0.2, 0.8, 0.2, 1),
    opacity 400ms ease 150ms;
}
.scene-read.by-hand .note[data-with]:not(.made) {
  max-height: 0;
  padding-block: 0;
  border-top-width: 0;
  opacity: 0;
}
/* With neither note made the first of the others is the top of the list,
   and has no line over it. */
.scene-read.by-hand.is-bare .note[data-with] + .note:not([data-with]) {
  border-top-color: transparent;
}
.scene-read.by-hand :is(.back, .front, .views) {
  transition: opacity 380ms ease;
}
.scene-read.by-hand.is-turning :is(.back, .front, .views) {
  opacity: 0;
}
.scene-read.by-hand.is-turning .note[data-with] {
  transition: none;
}
