/* ============================================================
   The brand, expressed as Bootstrap's own variables.

   This is what makes a Bootstrap-first app possible without a parallel utility
   system: `bg-body`, `text-body-secondary`, `border`, `rounded`, `btn-primary`
   and the rest already read from these, so setting them here re-skins every
   Bootstrap utility and component at once instead of overriding each one.

   Values are taskjane's, per its brand guidelines. Lay Grotesk stays on display
   type; body type is the system stack.
   ============================================================ */
:root {
  /* Accent. #3B82F6 is the tint base the 10-18% alpha fills are struck from and
   * is never a solid fill itself; #2563EB is the accent, #1D4ED8 the pressed
   * and active-text shade. */
  --rb-tint: #3B82F6;
  --rb-accent: #2563EB;
  --rb-accent-strong: #1D4ED8;

  /* What goes on top of a solid accent fill. White for every accent this theme
   * has ever had, and a token rather than a literal because a workspace can
   * choose its own hue now: `BrandAccent` flips this to near-black for a light
   * one, which is the difference between a readable button and an amber one
   * with white text on it. */
  --rb-accent-on: #FFFFFF;

  /* The accent as *text* is a second pair of tokens, identical to the above in
   * this theme. They part company in dark mode: a fill is judged by the white
   * label on it and a label by the page behind it, and no one blue clears both
   * against a near-black canvas. Splitting them here rather than there means
   * the dark block is a table of values and not a second set of rules. */
  --rb-accent-text: var(--rb-accent);
  --rb-accent-text-rgb: 37, 99, 235;
  --rb-accent-text-strong: var(--rb-accent-strong);
  --rb-accent-text-strong-rgb: 29, 78, 216;

  /* The accent below solid strength: a pale fill and its edge, the focus ring,
   * and the tint formula's three steps (the hue at 10-45% alpha). */
  --rb-accent-wash: #EFF6FF;
  --rb-accent-wash-edge: #DBEAFE;
  --rb-accent-ring: rgba(37, 99, 235, 0.2);
  --rb-accent-ring-strong: rgba(37, 99, 235, 0.3);
  --rb-tint-wash: rgba(59, 130, 246, 0.14);
  --rb-tint-wash-soft: rgba(59, 130, 246, 0.1);
  --rb-tint-edge: rgba(59, 130, 246, 0.45);

  /* Ink, through an opacity ladder. One text colour, many volumes — never a
   * second gray. .95/.9 titles, .7/.6 body and metadata, .5/.45 timestamps and
   * eyebrows, .35/.3 chevrons. */
  --rb-ink: #0F172A;
  --rb-ink-rgb: 15, 23, 42;
  --rb-ink-body: rgba(15, 23, 42, 0.75);
  --rb-ink-muted: rgba(15, 23, 42, 0.55);
  --rb-ink-faint: rgba(15, 23, 42, 0.4);

  /* Below the ladder's last step: a list marker and the resting tier dot, which
   * are shapes rather than text and so are allowed under the text minimum. */
  --rb-marker: #CBD5E1;

  /* The faint fill of a row you are pointing at, over whatever surface it is
   * on. Ink at 5%, not a grey: it has to work on the card and on the slab. */
  --rb-wash: rgba(15, 23, 42, 0.05);

  /* The same faint step, but on the ink panel rather than on the canvas: the
   * "Confidential" line under the pre-entry cards. A token rather than a literal
   * at the call site, so the dark-mode pass has one place to answer. */
  --rb-on-ink-faint: rgba(255, 255, 255, 0.4);

  /* Surfaces. The canvas is white: taskjane's chrome reads white because its
   * shell is a translucent sheet over the washed canvas, and a flat slate-50 is
   * a visibly grey page beside it. Slate-50 is the hover step.
   *
   * The `-rgb` triplets are not redundant: several Bootstrap utilities compose
   * their colour as `rgba(var(--bs-*-rgb), var(--bs-bg-opacity))`, so `bg-body`
   * reads the triplet and never the colour. Setting one without the other is
   * how `bg-body` stays white on a dark page. */
  --rb-canvas: #FFFFFF;
  --rb-canvas-rgb: 255, 255, 255;
  --rb-subtle: #F8FAFC;
  --rb-subtle-rgb: 248, 250, 252;
  --rb-hairline: #E9EDF3;
  --rb-hairline-soft: #F1F5F9;
  --rb-hairline-soft-rgb: 241, 245, 249;

  /* A border with something to say: a field's edge, an outline button's. One
   * step stronger than a hairline, which only separates. */
  --rb-border-strong: #CBD5E1;

  /* The navy slab: the investor-facing hero, the pre-auth field, the preview
   * banner. A surface that is dark in *both* themes, which is why it cannot be
   * `--rb-ink` — that is a text colour, and in dark mode it is nearly white. */
  --rb-slab: #0F172A;

  /* A chip reversed out of the page: the current dashboard tab, the selected
   * segment, the rail's hover tip. Flips with the theme, unlike the slab. */
  --rb-invert-bg: #0F172A;
  --rb-invert-fg: #FFFFFF;

  /* A translucent sheet over the canvas: the filter bar's chips and its search
   * field, which are meant to read as glass rather than as boxes. */
  --rb-sheet: rgba(255, 255, 255, 0.7);
  --rb-sheet-hover: rgba(255, 255, 255, 0.95);
  --rb-sheet-edge: rgba(15, 23, 42, 0.08);
  --rb-sheet-edge-strong: rgba(15, 23, 42, 0.12);
  --rb-sheet-rule: rgba(15, 23, 42, 0.1);

  /* The phone header, which blurs the page under itself. */
  --rb-header-sheet: rgba(255, 255, 255, 0.9);

  /* A code block is its own small dark surface in the light theme and has to
   * stay one: it is read as code, not as prose. */
  --rb-code-bg: #0F172A;
  --rb-code-fg: #F1F5F9;

  /* A column that stands for no data and a column that stands for example data.
   * A hairline's grey in this theme, which is what they have always been; the
   * dark block lifts it, because there a hairline is not a visible bar. */
  --rb-bar-quiet: var(--rb-hairline);

  /* Destructive and provisional. Never a fill unless something has gone wrong. */
  --rb-danger: #B91C1C;
  --rb-danger-edge: #FCA5A5;
  --rb-danger-wash: #FEF2F2;

  /* The red on a PDF's tile. Its own three tokens rather than `--rb-danger`,
   * which is the same colour family and the wrong idea: danger is the delete
   * button and the refusal, and a room is mostly PDFs. Sharing the token would
   * mean the day someone retunes the destructive red, every file in every room
   * changes with it. */
  /* What a file is, as one colour each, worn by the glyph and by nothing else.
   *
   * The tile keeps the hairline and the canvas every other tile has: a coloured
   * border and a coloured fill made every row in a room a small block of colour
   * and turned a list into bunting. The mark inside is the only thing that
   * changes, which is enough, because it is the only thing being asked about.
   *
   * Borrowed from what each format is already coloured elsewhere in the world,
   * because recognising one is the whole job here and this is not a place to be
   * original. Their own tokens rather than the palette's: `--rb-accent` is a
   * link, `--rb-danger` is a refusal, and a Word file is neither.
   *
   * Slides sits at orange rather than at the red its own suite uses, so that a
   * deck and a PDF are not two reds four pixels apart. */
  --rb-type-pdf: #DC2626;
  --rb-type-doc: #1E40AF;
  --rb-type-slides: #EA580C;
  --rb-type-sheet: #047857;
  --rb-type-image: #7C3AED;
  --rb-type-video: #7C3AED;

  /* The series of a chart on an authored page, in this order and never cycled.
   * The dataviz reference palette's first six slots, run through its validator
   * against this canvas: every adjacent pair stays apart under each kind of
   * colour blindness (worst 9.1) and to full-colour eyes (worst 19.6). Three sit
   * under 3:1 on white, which is allowed only because every chart carries its
   * numbers as a table. The order is the safety, so a new series goes on the
   * end, and a seventh is refused by `DocumentChart` rather than invented.
   *
   * `DocumentChart::PALETTE` holds the same eight as presentation attributes,
   * for the exported file that has no stylesheet; change both or neither.
   *
   * Seven and eight are only ever reached by name, since a chart holds six
   * series: they are the violet and red a founder can pick for a series, and
   * the eight still validate as an order (worst adjacent CVD 9.1, normal 19.6). */
  --rb-chart-1: #2A78D6;
  --rb-chart-2: #EB6834;
  --rb-chart-3: #1BAF7A;
  --rb-chart-4: #EDA100;
  --rb-chart-5: #E87BA4;
  --rb-chart-6: #008300;
  --rb-chart-7: #4A3AA7;
  --rb-chart-8: #E34948;

  /* The count inside a filter chip: metadata about a filter, not an alert. */
  --rb-count-bg: rgba(100, 116, 139, 0.14);
  --rb-count-fg: rgba(71, 85, 105, 0.9);

  /* The fade at the edge of an authored table, which says there is more of it
   * that way. A darkening, so it has to deepen rather than invert. */
  --rb-table-fade: rgba(5, 9, 17, 0.1);

  /* A single drop under something that is genuinely floating over the page: the
   * rail's hover tip. The card ladder is `--shadow-card*`; this is tighter. */
  --rb-shadow-drop: rgba(15, 23, 42, 0.22);
  --rb-shadow-soft: rgba(15, 23, 42, 0.1);

  /* The radius ladder. Don't interpolate new stops. */
  --rb-radius-row: 14px;
  --rb-radius-card: 16px;
  --rb-radius-pill: 999px;

  /* ---- Bootstrap's variables, pointed at the above ---- */
  --bs-body-bg: var(--rb-canvas);
  --bs-body-bg-rgb: var(--rb-canvas-rgb);
  --bs-body-color: var(--rb-ink-body);
  --bs-body-color-rgb: var(--rb-ink-rgb);
  --bs-emphasis-color: var(--rb-ink);
  --bs-emphasis-color-rgb: var(--rb-ink-rgb);
  --bs-secondary-color: var(--rb-ink-muted);
  --bs-secondary-bg: var(--rb-hairline-soft);
  --bs-secondary-bg-rgb: var(--rb-hairline-soft-rgb);
  --bs-tertiary-color: var(--rb-ink-faint);
  --bs-tertiary-bg: var(--rb-subtle);
  --bs-tertiary-bg-rgb: var(--rb-subtle-rgb);
  --bs-border-color: var(--rb-hairline);
  --bs-border-radius: 0.5rem;
  --bs-border-radius-lg: var(--rb-radius-card);
  --bs-border-radius-xl: var(--rb-radius-card);

  --bs-primary: var(--rb-accent);
  --bs-primary-rgb: 37, 99, 235;
  --bs-link-color: var(--rb-accent-text);
  --bs-link-color-rgb: var(--rb-accent-text-rgb);
  --bs-link-hover-color: var(--rb-accent-text-strong);
  --bs-link-hover-color-rgb: var(--rb-accent-text-strong-rgb);

  --bs-body-font-family: system-ui, -apple-system, "Segoe UI", Roboto, "Helvetica Neue", "Noto Sans", sans-serif;
  --bs-body-font-size: 0.95rem;
  --bs-body-line-height: 1.55;

  --rb-display-font: "Lay Grotesk", "Helvetica Neue", Arial, sans-serif;

  /* ---- The ramp, under the names the authored CSS already uses ----
   *
   * These were Tailwind's `@theme` colours, which is how `components.css` and
   * `document-body` still address the palette. Kept rather than renamed to
   * `--bs-*`: renaming them would be a large diff through eight hundred lines
   * of authored CSS that changes nothing on screen, and `--color-navy` says
   * what it is at the point of use.
   *
   * Most of them now point at an `--rb-*` token instead of carrying a literal,
   * which is what lets the dark block below restate a dozen values rather than
   * all twenty-nine: a name whose job is already said elsewhere should not have
   * to be said twice. The grays that are left as literals are left on purpose —
   * they are a contrast ramp with reasoning attached (see the caption rule in
   * components.css, where gray-500 fails AA and gray-600 clears it), and
   * folding them into the four-step ink ladder would lose that. */
  --font-display: var(--rb-display-font);
  --color-white: var(--rb-canvas);

  --color-cobalt: var(--rb-accent-text);
  --color-cobalt-hover: var(--rb-accent-text-strong);
  --color-tint: var(--rb-tint);

  --color-navy: var(--rb-ink);

  --color-beige-50: var(--rb-canvas);
  --color-beige-100: var(--rb-subtle);
  --color-beige-200: var(--rb-hairline);
  --color-beige-300: var(--rb-marker);

  --color-gray-50: #FCFDFE;
  --color-gray-100: var(--rb-hairline-soft);
  --color-gray-200: var(--rb-hairline);
  --color-gray-300: var(--rb-border-strong);
  --color-gray-400: #94A3B8;
  --color-gray-500: #64748B;
  --color-gray-600: #475569;
  --color-gray-700: #334155;
  --color-gray-800: #1E293B;
  --color-gray-900: var(--rb-ink);
  --color-gray-950: #020617;

  --color-blue-50: var(--rb-accent-wash);
  --color-blue-100: var(--rb-accent-wash-edge);
  --color-blue-200: #BFDBFE;
  --color-blue-500: var(--rb-tint);
  --color-blue-600: var(--rb-accent);
  --color-blue-700: var(--rb-accent-strong);
  --color-blue-800: #1E40AF;

  /* Elevation, from the guidelines' ladder. Resting is a hairline and nothing
   * else; only cards lift. */
  --shadow-card: 0 1px 2px rgba(2, 6, 23, 0.04), 0 10px 30px -18px rgba(2, 6, 23, 0.16);
  --shadow-card-lg: 0 2px 4px rgba(2, 6, 23, 0.05), 0 18px 44px -20px rgba(2, 6, 23, 0.22);

  --radius-row: 16px;
  --radius-card: 18px;
}

/* ============================================================
   The same brand, after dark.

   One attribute on <html>, Bootstrap's own `data-bs-theme`, and no parallel
   system: `theme.js` sets it and an inline script in each layout sets it again
   before first paint. Everything below is a table of values, not a second set
   of rules — because every rule in this file and in components.css reads the
   palette through the tokens above, restating the tokens re-skins the app.
   Where that was not true it has been made true rather than patched here.

   Three things about this palette are decisions and not conversions:

   1. The ink ladder is not the light one with its alphas inverted. Light-on-dark
      gains contrast faster than dark-on-light loses it, and the low steps are
      where a theme becomes unreadable, so each step was worked out against the
      canvas: .78 lands at about 11.6:1, .62 at 7.7:1, .45 at 4.2:1 on #0B1220.
      Every step is at or above its light counterpart (11.6 vs 8.6, 7.7 vs 5.4,
      4.2 vs 3.4) because small light text on a dark ground reads thinner.

   2. The slab does not flip. `--rb-bg-ink` is the investor's hero, the pre-auth
      field and the preview banner: a confidential surface that is dark in both
      themes and carries a white-struck ladder of its own. It goes *below* the
      canvas here rather than above it, so it stays the deeper surface it is in
      the light theme, and so the pre-auth card still lifts off it.

   3. The accent splits. #2563EB stays the fill, because a white label on it
      clears AA either way and the button should still be the brand's blue; the
      accent as *text* lifts to #9CC0FF, because #2563EB on #0B1220 is 3.6:1 and
      every link in an authored document is set in it.
   ============================================================ */
[data-bs-theme="dark"] {
  --rb-tint: #60A5FA;
  --rb-accent: #2563EB;
  --rb-accent-strong: #1D4ED8;

  --rb-accent-text: #9CC0FF;
  --rb-accent-text-rgb: 156, 192, 255;
  --rb-accent-text-strong: #CFE0FF;
  --rb-accent-text-strong-rgb: 207, 224, 255;

  /* The pale fill has nothing pale to be, so the wash is the tint at low alpha
   * over whatever it sits on rather than a lighter blue. */
  --rb-accent-wash: rgba(96, 165, 250, 0.16);
  --rb-accent-wash-edge: rgba(96, 165, 250, 0.34);
  --rb-accent-ring: rgba(96, 165, 250, 0.35);
  --rb-accent-ring-strong: rgba(96, 165, 250, 0.5);
  --rb-tint-wash: rgba(96, 165, 250, 0.18);
  --rb-tint-wash-soft: rgba(96, 165, 250, 0.13);
  --rb-tint-edge: rgba(96, 165, 250, 0.45);

  --rb-ink: #F1F5F9;
  --rb-ink-rgb: 241, 245, 249;
  --rb-ink-body: rgba(241, 245, 249, 0.78);
  --rb-ink-muted: rgba(241, 245, 249, 0.62);
  --rb-ink-faint: rgba(241, 245, 249, 0.45);
  --rb-marker: rgba(241, 245, 249, 0.35);
  --rb-wash: rgba(241, 245, 249, 0.07);

  /* Not black. A pure-black page under a saturated blue accent buzzes, and the
   * brand's ink is a navy, so the canvas is that navy taken down rather than
   * desaturated. The card is the canvas, exactly as it is in the light theme:
   * resting elevation here is a hairline and nothing else. */
  --rb-canvas: #0B1220;
  --rb-canvas-rgb: 11, 18, 32;
  --rb-subtle: #151D2E;
  --rb-subtle-rgb: 21, 29, 46;
  --rb-hairline: #232D42;
  --rb-hairline-soft: #1A2334;
  --rb-hairline-soft-rgb: 26, 35, 52;
  --rb-border-strong: #33405A;

  --rb-slab: #050B14;

  --rb-invert-bg: #E2E8F0;
  --rb-invert-fg: #0B1220;

  --rb-sheet: rgba(241, 245, 249, 0.06);
  --rb-sheet-hover: rgba(241, 245, 249, 0.12);
  --rb-sheet-edge: rgba(241, 245, 249, 0.12);
  --rb-sheet-edge-strong: rgba(241, 245, 249, 0.18);
  --rb-sheet-rule: rgba(241, 245, 249, 0.16);
  --rb-header-sheet: rgba(11, 18, 32, 0.9);

  --rb-code-bg: #04080F;
  --rb-code-fg: #E2E8F0;

  /* The slab deepened, so white at the light theme's 0.4 would land on the same
   * 3.8:1 it had before and be the one step on the panel that did not gain what
   * the canvas ladder gained. 0.45 puts it at about 4.5:1 on #050B14, which is
   * where `--rb-ink-faint` sits on the canvas after dark. */
  --rb-on-ink-faint: rgba(255, 255, 255, 0.45);

  /* A hairline is enough to draw a border with and not enough to draw a bar
   * with. In the light theme the two are the same grey; here #232D42 on #0B1220
   * is a column you have to hunt for, and the example chart's whole job is to
   * be seen. See `--rb-bar-quiet` in the light block for what it says there. */
  --rb-bar-quiet: #3A4661;

  /* Both literals, and not one written as `var()` of the other. Custom
   * properties resolve against the cascade, not against the line above them, so
   * `--rb-danger: var(--rb-danger-edge)` in this block took the #7F1D1D below it
   * and set the *text* colour to a red darker than the page it is read on. The
   * two swap jobs after dark rather than swapping values: the pale red becomes
   * the label, and the label's red becomes a border with something to say. */
  --rb-danger: #FCA5A5;
  --rb-danger-edge: rgba(252, 165, 165, 0.4);
  --rb-danger-wash: rgba(185, 28, 28, 0.18);

  /* The same swap the pair above make after dark: the glyph lightens to be
   * read against a near-black tile, and the fill it sits on is the light
   * theme's ink at low alpha rather than a pale orange, which on this canvas
   * would glow. */
  /* Each one lifted until it clears a near-black card. These are read as a
   * 16px glyph on the canvas rather than as a fill, so they answer the text
   * minimum rather than the fill's: the light theme's #1E40AF on #0B1220 is a
   * shape you have to hunt for. */
  --rb-type-pdf: #F87171;
  --rb-type-doc: #93C5FD;
  --rb-type-slides: #FDBA74;
  --rb-type-sheet: #6EE7B7;
  --rb-type-image: #C4B5FD;

  /* Selected for the dark canvas rather than flipped: the same eight hues
   * stepped into the dark band, validated as a set against #0B1220 (adjacent
   * CVD 8.4, normal vision 19.3, every one over 3:1). */
  --rb-chart-1: #3987E5;
  --rb-chart-2: #D95926;
  --rb-chart-3: #199E70;
  --rb-chart-4: #C98500;
  --rb-chart-5: #D55181;
  --rb-chart-6: #008300;
  --rb-chart-7: #9085E9;
  --rb-chart-8: #E66767;

  --rb-count-bg: rgba(148, 163, 184, 0.22);
  --rb-count-fg: rgba(226, 232, 240, 0.85);

  --rb-table-fade: rgba(0, 0, 0, 0.55);
  --rb-shadow-drop: rgba(0, 0, 0, 0.65);
  --rb-shadow-soft: rgba(0, 0, 0, 0.35);

  /* The grays that stayed literal above, inverted step for step. */
  --color-gray-50: #0E1626;
  --color-gray-400: #7C8798;
  --color-gray-500: #94A3B8;
  --color-gray-600: #B6C0CE;
  --color-gray-700: #D3DAE4;
  --color-gray-800: #E4E9F0;
  --color-gray-950: #F8FAFC;

  --color-blue-200: #1E3A8A;
  --color-blue-800: #BFDBFE;

  /* A drop shadow over near-black is invisible at the light theme's alphas, so
   * the ladder is the same two steps struck from black at full strength. */
  --shadow-card: 0 1px 2px rgba(0, 0, 0, 0.5), 0 10px 30px -18px rgba(0, 0, 0, 0.9);
  --shadow-card-lg: 0 2px 4px rgba(0, 0, 0, 0.55), 0 18px 44px -20px rgba(0, 0, 0, 0.95);
}

/* ============================================================
   The colours that do not have a dark value, because they are already on a
   dark surface.

   Declared under both selectors rather than under `:root` alone, so "the same
   in both themes" is something the CSS says and not something a comment
   promises. `test/views/theme_tokens_test.rb` requires every literal colour
   with no dark restatement to be here: a token declared with a hex under
   `:root` and forgotten is the one mistake this file can make silently, and the
   difference between forgetting and deciding is a block, not a memory.

   Two surfaces qualify. The slab (`.rb-bg-ink` — the investor's hero, the
   pre-entry panels, the preview banner) is dark in the light theme and darker in
   the dark one, so anything struck against it was never light-theme-only. And
   the preview banner's amber is the one element in the app whose whole job is to
   not look like the room around it, which it cannot do by following the room.
   ============================================================ */
:root,
[data-bs-theme="dark"] {
  /* The slab's own blues: an eyebrow and a dot on the hero, and the dot field
   * behind it. These were `--rb-on-ink-blue` and `--rb-on-ink-dots`, Tailwind
   * names that said which blue and not which ground, so the question "does this
   * need a dark value" had to be answered by opening five templates. */
  --rb-on-ink-blue: #93C5FD;
  --rb-on-ink-dots: #60A5FA;

  /* The same blue at speaking volume: the ⟨their first name⟩ placeholder in the
   * overview editor, which stands in for text the founder has not written. */
  --rb-on-ink-hint: rgba(147, 197, 253, 0.6);

  /* Amber, alone in the app. The fill of the preview tag and the rule along the
   * top of the banner it sits in. */
  --rb-warn: #FBBF24;
  --rb-warn-edge: rgba(251, 191, 36, 0.6);

  /* The backing of a rendered PDF page. A page is paper in both themes: the
   * bytes being drawn onto it are black on white, and answering the palette
   * here would put dark ink on a dark page. */
  --rb-paper: #FFFFFF;
  /* The ink on that paper, which is not the palette's. `--rb-ink-rgb` is near
   * white in the dark theme, and the skeleton lines and shimmer that stand in
   * for a page have to be legible against the page's own white either way. */
  --rb-paper-ink-rgb: 15, 23, 42;

  /* A workbook's cells are the same paper. A fill in a spreadsheet was chosen
   * against a white sheet and travels with the file, so after dark the theme
   * cannot have the cells: a white fill under near-white ink is what the
   * financial model looked like the first time somebody opened it in the dark.
   * The ink and the rule between cells are the page's, not the palette's. The
   * coordinate headers around the sheet are the viewer's and stay themed. */
  --rb-paper-ink: #0F172A;
  --rb-paper-hairline: rgba(15, 23, 42, 0.14);
}

@font-face {
  font-family: "Lay Grotesk";
  src: url("/fonts/LayGrotesk-Legalio-Medium.otf") format("opentype"),
       url("/fonts/LayGrotesk-Legalio-Medium.ttf") format("truetype");
  font-weight: 500;
  font-style: normal;
  font-display: swap;
}

/* ============================================================
   The supplement: what Bootstrap has no answer for.

   Deliberately short. Anything Bootstrap covers — display, flex, spacing,
   borders, radius, text alignment, weights — is used from Bootstrap and does
   not appear here.
   ============================================================ */

/* Display type is always Medium with tight tracking. That pairing is the brand,
 * so it lives in one class rather than being repeated per view. */
.rb-display {
  font-family: var(--rb-display-font);
  font-weight: 500;
  letter-spacing: -0.02em;
  line-height: 1.05;
  color: var(--rb-ink);
}

/* taskjane's most distinctive typographic move: a tiny uppercase label with
 * wide tracking, naming a group without adding visual weight. */
.rb-eyebrow {
  font-size: 0.72rem;
  font-weight: 700;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--rb-ink-muted);
}

/* The ink ladder, as the few steps templates actually reach for. */
.rb-ink { color: var(--rb-ink); }
.rb-body { color: var(--rb-ink-body); }
.rb-muted { color: var(--rb-ink-muted); }
.rb-faint { color: var(--rb-ink-faint); }
.rb-accent { color: var(--rb-accent-text); }

a.rb-ink:hover { color: var(--rb-accent-text); }
a.rb-muted:hover, a.rb-body:hover { color: var(--rb-ink); }

/* A link already in the accent darkens rather than changing hue: it is the one
 * step on the ladder with nowhere brighter to go. */
a.rb-accent:hover { color: var(--rb-accent-text-strong); }
a.rb-faint:hover { color: var(--rb-ink); }

/* On the dark hero and the preview banner the ladder runs the other way: text
 * is white at some opacity and approaches full white rather than ink. */
a.rb-on-dark { color: rgba(255, 255, 255, 0.7); }
a.rb-on-dark:hover { color: #fff; }

/* A control on a dark surface lights by fill, not by colour: there is no
 * lighter ink to move to. */
.rb-wash-dark { transition: background-color 0.15s ease; }
.rb-wash-dark:hover { background-color: rgba(255, 255, 255, 0.1); }

/* The wordmark is black on transparent, so a dark background inverts it rather
 * than carrying a second file. */
.rb-wordmark { height: 18px; }
.rb-wordmark-sm { height: 16px; }
.rb-invert { filter: invert(1); }

/* Which is why the dark theme has to invert it on the *canvas* too, and only
 * where the caller has not already asked for it: `rb-invert` is passed when the
 * wordmark sits on the slab, and the slab is dark in both themes, so inverting
 * a second time would put a black logo back on a black band. This is the whole
 * of the logo-swapping mechanism here — the wordmark is one file and the theme
 * is a filter over it, rather than a `data-theme-logo` pair to keep in step. */
[data-bs-theme="dark"] .rb-wordmark:not(.rb-invert),
[data-bs-theme="dark"] .rb-wordmark-sm:not(.rb-invert) { filter: invert(1); }
.rb-tracking-wide { letter-spacing: 0.1em; }

/* A quiet link in running text: the back links and the "forgot password" line.
 * Larger than body because the pre-auth column runs a size up.
 *
 * inline-block because every auth screen's version of this link is the last
 * thing in the card, under a full-width button, and passes `mt-3` or `mt-4` to
 * stand off it. An `a` is inline, and margin-top does nothing to an inline box,
 * so all five of them sat flush against the button they were meant to clear —
 * a spacing utility that silently does nothing looks exactly like one that was
 * never written. */
.rb-link {
  display: inline-block;
  font-size: 15px;
  font-weight: 500;
  color: var(--rb-ink-body);
  text-decoration: none;
}

.rb-link:hover { color: var(--rb-ink); }

/* Bootstrap's `small` is 0.875em of its parent, which compounds when nested.
 * These are absolute, which is what a table cell and a row subtitle need. */
.rb-sm { font-size: 0.82rem; }
.rb-xs { font-size: 0.75rem; }
.rb-md { font-size: 15px; }

/* The two fills that are not a Bootstrap theme colour: the slab (the
 * investor-facing hero) and the accent at wash strength (a current tab, a
 * selected row). */
.rb-bg-ink { background-color: var(--rb-slab); }
.rb-bg-accent-soft { background-color: var(--rb-accent-wash); }

/* The copy buttons. They were `rb-bg-ink text-white`, which is the slab — and
 * the slab has to stay dark in the dark theme, because the investor's hero is
 * drawn from it, so those three buttons came out near-black on a near-black
 * page. A button is reversed out of whatever the page is; a slab is a surface
 * that happens to be dark. One class was doing both jobs. */
.rb-btn-ink,
.rb-btn-ink:hover {
  background-color: var(--rb-invert-bg);
  color: var(--rb-invert-fg);
}

/* And the other way round: a chip *on* the slab, which is light in both themes
 * for the same reason. The ink ladder cannot say this — at `rb-ink` the label
 * turned near-white in the dark theme and vanished into its own chip. */
.rb-chip-on-slab {
  background-color: #fff;
  color: #0F172A;
}

/* The preview banner's amber tag, which is amber in both themes because its
 * whole job is to not look like the room around it. So its label cannot be on
 * the ink ladder: `rb-ink` turned near-white after dark and put white on amber
 * at about 1.7:1. A fixed fill takes a fixed label. */
.rb-chip-warn {
  background-color: var(--rb-warn);
  color: #0F172A;
}

/* Flex children that must be allowed to shrink below their content — the fix
 * for every truncating cell. Bootstrap has no equivalent. */
.rb-min0 { min-width: 0; }

/* Bootstrap's spacers stop at 1rem then jump to 1.5rem. The row rhythm sits at
 * 0.75rem, which is the step it does not have. */
.rb-py-row { padding-top: 0.75rem; padding-bottom: 0.75rem; }
.rb-gap-row { gap: 0.75rem; }

/* ============================================================
   The components the helpers name.

   Everything below replaces a class string that used to be assembled in
   `ApplicationHelper` or `DashboardHelper`. They are components, not a utility
   system: each is one thing the app has (a button, a field, a row, a pill),
   named once, so the arbitrary values that used to be spelled per template
   — `text-[15px]`, `tracking-[-0.02em]`, `ring-cobalt/20` — live in one place
   and can be reasoned about.

   Bootstrap's own components are themed through its variables rather than
   overridden property by property, which is what `--bs-btn-*` is for.
   ============================================================ */

/* ============================================================
   Where Bootstrap's reboot and this app disagree.

   Tailwind's preflight had stripped the browser defaults these restore, and
   every template was written on top of that. Bootstrap's reboot is the right
   baseline for a Bootstrap app, but three of its defaults are wrong here, and
   each was found on screen rather than reasoned about.
   ============================================================ */

/* Lists indent by 2rem and keep their markers. Almost every list in this app is
 * rows or navigation, and the rail's items were sitting 32px to the right of
 * where they belong because of it. The lists that really are prose live inside
 * `.document-body` and `.docs-body`, which set their own padding and markers
 * and are specific enough to win this back. */
ul,
ol {
  padding-left: 0;
  margin-bottom: 0;
  list-style: none;
}

/* The first row in a card needs no rule above it: the card's own edge is
 * already that line, and drawing both put a hairline a pixel under the border
 * on every list in the app. This was `first:border-t-0` per template, which the
 * conversion could not carry over — `:first-child` is not something a utility
 * class or an inline style can say. `!important` because Bootstrap's own
 * `.border-top` carries one. */
.rb-card ul > li:first-child { border-top-width: 0 !important; }

/* And the last row needs no rule under it, for the same reason at the other
 * end. Lists in this app separate with a border on one side of each row, so
 * whichever side it is lands on the card's own edge once. */
.rb-card ul > li:last-child { border-bottom-width: 0 !important; }

/* Headings and paragraphs carry bottom margins. Every template in this app was
 * written on top of Tailwind's preflight, which had none, and spaces things
 * with flex gaps and stacks instead. The margin is not merely extra air: an h1
 * with 8px under it inside a centred flex row sits four pixels high, which is
 * what pulled every page title off the sidebar toggle's line. Prose sets its
 * own spacing in `.document-body` and `.docs-body`, and wins on specificity. */
h1, h2, h3, h4, h5, h6, p, figure, dl, dd {
  margin-top: 0;
  margin-bottom: 0;
}

/* Bootstrap underlines every link in its reboot. This app's links are nav, row
 * names and actions, none of which are running text: an underline on all of
 * them turns a table of documents into a page of hyperlinks. Prose keeps its
 * own, in `.document-body a`, where the underline is doing real work. */
a {
  text-decoration: none;
}

/* ---- Type ---- */

/* The display face, exactly as the Tailwind utility drew it. Kept as a bare
 * class name because templates say `display` in ninety places. */
.display {
  font-family: var(--rb-display-font);
  font-weight: 500;
  letter-spacing: -0.04em;
  line-height: 1;
}

.eyebrow {
  font-size: 0.72rem;
  font-weight: 700;
  letter-spacing: 0.08em;
  text-transform: uppercase;
}

/* The signature halftone, as a dot field rather than an image, so it tints with
 * `color` and costs nothing. */
.halftone {
  background-image: radial-gradient(currentColor 1.7px, transparent 1.8px);
  background-size: 16px 16px;
  pointer-events: none;
}

/* The screen title, and the ancestors on its line. Built from the same pieces
 * so the row is one size throughout: a trail at 0.875rem beside a 1.125rem
 * title read as a heading that changes size halfway along. Only the colour
 * separates them, which is the whole hierarchy the row needs. */
.rb-title {
  font-family: var(--rb-display-font);
  font-weight: 500;
  font-size: 1.125rem;
  letter-spacing: -0.02em;
  line-height: 1;
  color: var(--rb-ink);
}

.rb-crumb {
  font-family: var(--rb-display-font);
  font-weight: 500;
  font-size: 1.125rem;
  letter-spacing: -0.02em;
  line-height: 1;
  color: var(--rb-ink-muted);
  text-decoration: none;
  transition: color 0.15s ease;
}

.rb-crumb:hover { color: var(--rb-ink); }

/* ---- The page ---- */

/* Full width, deliberately. Each page used to pick its own cap, which meant a
 * six-column table scrolled sideways inside 1152px while a 27" display spent
 * the other half of itself on margin. The tables are the product. */
.rb-frame { padding: 1.5rem 1rem 2rem; }

/* The desktop rail already gives the app a strong top edge. Pull the first
 * working line up one step there, while leaving the roomier phone spacing in
 * place. The workspace switcher moves with it in `layouts/_nav_rail`. */
@media (min-width: 992px) {
  .rb-frame { padding-top: 1rem; }
}

/* Fields do not want the screen. A page is full width; the inputs on it keep a
 * readable measure, because a text box two thousand pixels wide is harder to
 * use rather than easier: the eye loses the line it is on. */
.rb-measure { max-width: 42rem; margin-inline: auto; width: 100%; }
.rb-measure-wide { max-width: 48rem; margin-inline: auto; width: 100%; }
.rb-prose { max-width: 48rem; }

/* Lex keeps Jane's useful prompt-chip pattern without borrowing the actions
 * this product does not have. The questions wrap as a quiet cluster rather
 * than becoming a second primary-action bar. */
.rb-lex-welcome { padding-block: clamp(1rem, 8vh, 4rem); }

.rb-lex-prompts {
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  gap: 0.5rem;
}

.rb-lex-prompt {
  appearance: none;
  padding: 0.4rem 0.75rem;
  border: 1px solid var(--rb-tint-edge);
  border-radius: 999px;
  background: var(--rb-tint-wash);
  color: var(--rb-accent-text);
  font: inherit;
  font-size: 0.8125rem;
  font-weight: 600;
  line-height: 1.25;
}

.rb-lex-prompt:hover { background: var(--rb-tint); }
.rb-lex-prompt:focus-visible { outline: 2px solid var(--rb-accent); outline-offset: 2px; }

.rb-lex-turn + .rb-lex-turn {
  padding-top: 1rem;
  border-top: 1px solid var(--rb-hairline);
}

.rb-lex-question {
  padding: 0.625rem 0.75rem;
  border-radius: 0.75rem;
  background: var(--rb-subtle);
  color: var(--rb-ink);
  font-size: 0.875rem;
  font-weight: 600;
  line-height: 1.4;
}

.rb-lex-answer {
  min-width: 0;
  line-height: 1.55;
}

.rb-lex-answer > :last-child { margin-bottom: 0; }
.rb-lex-answer p { margin: 0 0 0.75rem; }
.rb-lex-answer :is(h1, h2, h3, h4, h5, h6) {
  margin: 1rem 0 0.375rem;
  color: var(--rb-ink);
  font-family: inherit;
  font-size: 0.8125rem;
  font-weight: 700;
  line-height: 1.35;
}
.rb-lex-answer > :is(h1, h2, h3, h4, h5, h6):first-child { margin-top: 0; }
.rb-lex-answer :is(ul, ol) { margin: 0.375rem 0 0.875rem; padding-left: 1.25rem; }
.rb-lex-answer ul { list-style: disc outside; }
.rb-lex-answer ol { list-style: decimal outside; }
.rb-lex-answer ul ul { list-style: circle outside; }
.rb-lex-answer ol ol { list-style: lower-alpha outside; }
.rb-lex-answer li { padding-left: 0.125rem; }
.rb-lex-answer li + li { margin-top: 0.5rem; }
.rb-lex-answer li::marker { color: var(--rb-ink-muted); }
.rb-lex-answer blockquote {
  margin: 0 0 0.75rem;
  padding-left: 0.75rem;
  border-left: 2px solid var(--rb-hairline);
  color: var(--rb-ink-muted);
}
.rb-lex-answer code { font-size: 0.875em; }
.rb-lex-answer pre {
  overflow-x: auto;
  padding: 0.75rem;
  border-radius: 0.5rem;
  background: var(--rb-subtle);
}

/* A numbered source marker, drawn the way Claude and ChatGPT draw one: a small
   raised pill carrying the number, sitting tight against the word it follows.
   The label the reader hovers is the document and page; the number alone is
   what is on the page, because a title inline would break the sentence.

   `vertical-align: super` with a smaller size rather than a `<sup>`, so a run of
   them — [1][2] — keeps the line height of the paragraph it is in. */
.rb-lex-ref {
  display: inline-block;
  min-width: 1.05rem;
  margin-left: 0.125rem;
  padding: 0 0.25rem;
  border: 1px solid var(--rb-hairline);
  border-radius: 0.375rem;
  background: var(--rb-subtle);
  color: var(--rb-ink-muted);
  font-size: 0.625rem;
  font-weight: 600;
  line-height: 1.35;
  text-align: center;
  text-decoration: none;
  vertical-align: super;
}

.rb-lex-ref + .rb-lex-ref { margin-left: 0.125rem; }

.rb-lex-ref:hover,
.rb-lex-ref:focus-visible {
  border-color: var(--rb-accent);
  background: var(--rb-accent-wash);
  color: var(--rb-accent);
  text-decoration: none;
}

.rb-lex-table-wrap {
  max-width: 100%;
  margin: 0.5rem 0 0.875rem;
  overflow-x: auto;
  border: 1px solid var(--rb-hairline);
  border-radius: 0.625rem;
}

.rb-lex-answer table {
  width: max-content;
  min-width: 100%;
  border-collapse: collapse;
  font-size: 0.75rem;
}

.rb-lex-answer :is(th, td) {
  padding: 0.5rem 0.625rem;
  border-bottom: 1px solid var(--rb-hairline);
  text-align: left;
  vertical-align: top;
}

.rb-lex-answer th { color: var(--rb-ink); font-weight: 700; }
.rb-lex-answer tr:last-child td { border-bottom: 0; }
.rb-lex-citation {
  text-decoration-thickness: 1px;
  text-underline-offset: 0.14em;
}

.rb-lex-answer.is-streaming::after {
  display: inline-block;
  width: 0.42rem;
  height: 1em;
  margin-left: 0.2rem;
  border-radius: 2px;
  background: var(--rb-accent);
  content: "";
  vertical-align: -0.12em;
  animation: rb-lex-cursor 0.9s steps(1) infinite;
}

@keyframes rb-lex-cursor {
  50% { opacity: 0.2; }
}

@media (prefers-reduced-motion: reduce) {
  .rb-lex-answer.is-streaming::after { animation: none; }
}

/* A writing surface with a quiet settings rail, rather than a narrow stack of
 * every field a document owns. */
.rb-document-workspace { max-width: 106rem; margin-inline: auto; width: 100%; }

.rb-document-editor-grid {
  display: grid;
  grid-template-columns: minmax(0, 1fr);
  gap: 1rem;
  align-items: start;
}

.rb-document-savebar {
  position: sticky;
  top: 0.75rem;
  z-index: 20;
  border: 1px solid var(--rb-hairline);
  border-radius: 1rem;
  background: rgba(var(--bs-body-bg-rgb), 0.94);
  box-shadow: 0 0.5rem 1.5rem rgba(15, 23, 42, 0.06);
  padding: 0.75rem 0.875rem;
  backdrop-filter: blur(10px);
  -webkit-backdrop-filter: blur(10px);
}

.rb-document-paper,
.rb-document-sidecard {
  border: 1px solid var(--rb-hairline);
  border-radius: 1rem;
  background: var(--rb-canvas);
}

.rb-document-paper { padding: clamp(1.25rem, 3vw, 2.5rem); }
.rb-document-sidecard { padding: 1rem; }

.rb-document-title-input {
  display: block;
  width: 100%;
  border: 0;
  border-bottom: 1px solid var(--rb-hairline);
  border-radius: 0;
  background: transparent;
  padding: 0 0 0.75rem;
  font-family: var(--rb-display-font);
  font-size: clamp(1.75rem, 4vw, 2.35rem);
  font-weight: 500;
  letter-spacing: -0.035em;
  color: var(--rb-ink);
}

.rb-document-title-input:focus { outline: 0; border-color: var(--rb-accent); }
.rb-document-title-input::placeholder { color: var(--rb-ink-faint); }
.rb-document-sidecard-title { margin: 0 0 0.5rem; font-size: 0.78rem; font-weight: 700; letter-spacing: 0.08em; text-transform: uppercase; color: var(--rb-ink-faint); }

.rb-setting-row {
  display: flex;
  align-items: flex-start;
  gap: 0.75rem;
  padding-block: 0.65rem;
  cursor: pointer;
}

.rb-setting-row + .rb-setting-row { border-top: 1px solid var(--rb-hairline); }

.rb-document-summary {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 1rem;
  cursor: pointer;
}

.rb-document-sidecard[open] > .rb-document-summary .rb-caret { transform: rotate(90deg); }

/* Preview chrome stays unmistakable without taking a second mobile header's
 * worth of space away from the room underneath it. */
/* The room is exactly a window tall and `main` is what scrolls inside it, so the
 * preview bar has to come out of that height rather than be padded around: it is
 * fixed over the bottom of the window, and 7rem of a 100vh frame is the foot of
 * the contents rail. What the frame gives up, `.rb-preview-spacer` below it
 * fills, so the two still add to one window and nothing scrolls behind the bar.
 * The numbers are that spacer's, at both of its heights. */
.rb-viewer-shell { height: 100vh; }
.rb-viewer-shell.is-previewing { height: calc(100vh - 7rem); }

@media (max-width: 575.98px) {
  .rb-viewer-shell.is-previewing { height: calc(100vh - 4.75rem); }
}

.rb-preview-spacer { height: 7rem; }
.rb-preview-banner { z-index: 50; border-top: 1px solid var(--rb-warn-edge); }
.rb-preview-banner-inner { display: flex; align-items: center; justify-content: space-between; gap: 1.5rem; padding: 1rem 1.5rem; }
.rb-preview-summary, .rb-preview-controls { display: flex; min-width: 0; align-items: center; gap: 0.75rem; }
.rb-preview-description { min-width: 0; font-size: 1rem; font-weight: 500; }
.rb-preview-exit { height: 2.25rem; }
/* The bar's one settings control, on the bar's ink rather than the page's
 * canvas: the same wash the audience pills sat in, at the exit's height, so the
 * two read as a pair at the bar's far end. Here with the rest of the bar rather
 * than in components.css, because every colour on it is the bar's own. */
.rb-preview-settings { height: 2.25rem; background-color: rgba(255, 255, 255, 0.1); color: #fff; white-space: nowrap; }
.rb-preview-settings:hover,
.rb-preview-settings:focus-visible { background-color: rgba(255, 255, 255, 0.18); }

/* A clipped edge is the quiet affordance that these rows continue. It replaces
 * permanent helper copy and disappears with the breakpoint that needs it. */
.rb-table-scroll, .rb-scroll-strip { scrollbar-width: none; }
.rb-table-scroll::-webkit-scrollbar, .rb-scroll-strip::-webkit-scrollbar { display: none; }

@media (min-width: 992px) {
  .rb-document-editor-grid { grid-template-columns: minmax(0, 1fr) 21rem; gap: 1.5rem; }
  .rb-document-editor-grid > aside { position: sticky; top: 6.25rem; }
}

@media (max-width: 575.98px) {
  .rb-preview-spacer { height: 4.75rem; }
  .rb-preview-banner-inner { min-height: 4.75rem; gap: 0.5rem; padding: 0.65rem 1rem; }
  .rb-preview-summary { gap: 0.5rem; }
  .rb-preview-description { max-width: 9rem; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; font-size: 0.8rem; }
  .rb-preview-controls { margin-left: auto; gap: 0.4rem; }
  .rb-preview-exit { padding-inline: 0.8rem !important; }

  .rb-document-savebar { top: 0.5rem; flex-wrap: nowrap !important; gap: 0.5rem !important; padding: 0.55rem 0.65rem; }
  .rb-document-save-context .rb-sm { display: none; }
  .rb-document-save-context { flex: 0 1 auto; }
  .rb-document-save-actions { flex-shrink: 0; }
  .rb-document-paper { padding: 1rem; }
  .rb-document-title-input { font-size: 1.75rem; }
  .rb-document-row-title.text-truncate { display: -webkit-box !important; overflow: hidden; -webkit-box-orient: vertical; -webkit-line-clamp: 2; white-space: normal; }

  /* Not `.msg-chip-row`: it wraps at this width rather than scrolling, so a
   * faded right edge would be promising a continuation that is not there. */
  .rb-table-scroll, .rb-scroll-strip {
    -webkit-mask-image: linear-gradient(to right, #000 0, #000 calc(100% - 1.25rem), transparent 100%);
    mask-image: linear-gradient(to right, #000 0, #000 calc(100% - 1.25rem), transparent 100%);
  }
}

/* The reader's page. Wider than any of the above, because the room is a header,
 * a contents column and a reading column side by side, and narrower than the
 * founder's full-width tables, because it is read rather than scanned.
 *
 * Named here because it was `max-width: 80rem` written inline in four places
 * alongside `w-100 mx-auto px-6` — and `px-6` is a Tailwind step Bootstrap does
 * not have, so it matched nothing and the header, the contents and the footer
 * all ran flush to the shell's edge while the hero kept its own `px-4` and
 * started 24px further in. The wordmark was meant to sit above the contents;
 * measured, it was a rail's width off. The founder's overview editor previews
 * this page, so it takes the same cap and the preview cannot end up wider than
 * any reader's screen. */
.rb-viewer-measure { max-width: 80rem; margin-inline: auto; width: 100%; }

/* The reader's main column, centred in the space the contents rail leaves. The
 * room's hero and its document list are not prose and want more than a 48rem
 * measure, but the full width of a wide screen makes the header and the list
 * hard to read across, so they take a middle measure and centre in it. A deck
 * or a file viewer will grow past this once there is a control to expand it. */
.rb-reader { max-width: 64rem; margin-inline: auto; width: 100%; }

/* The reading column's first line, boxed to the height a page title's row has.
 *
 * It sat on the contents rail's first row while a full-width bar ran over both
 * columns. The rail goes to the top of the window now and opens with the
 * wordmark, which lines up with the reading column's own bar instead; this
 * keeps the 2.25rem so every room page still starts on one height.
 *
 * It was a 2rem row pulled up half a step, which was tuned against a rail whose
 * rows were `px-2 py-1` and floated in the reading column. Both rails run on one
 * chassis now, so this is the founder's own arrangement instead: `2.25rem`
 * always centred is exactly what `shared/_page_header` gives a page title, and
 * the rail's first row is a `sidebar-row` under the same `pt-4` on both sides.
 * The pair is what `click_through_test` measures on the founder's side, and
 * copying it rather than re-deriving it is the point.
 *
 * The overview wears this on the block it opens with, and every page inside the
 * room on the row holding its way back (`viewer/shared/_page_top`), so the first
 * line of the reading column is the rail's first line wherever the reader is. */
.rb-viewer-top { min-height: 2.25rem; }

/* ---- Reading mode ---- */

/* The reader's two controls, on the document's own line rather than floating
 * over it: a deck is a rectangle and a control sitting on top of one covers a
 * slide. The line is shared with the way back out (`viewer/shared/_page_top`),
 * and these are the far end of it.
 *
 * `margin-left: auto` rather than the row's own `justify-content`, because in
 * reading mode the way back is hidden and these are the only child left: a row
 * with one item has nothing to space apart, and the controls slid to the left
 * edge of the page, which is not where a reader who put them there is looking. */
.rb-reading-controls {
  display: flex;
  align-items: center;
  gap: 0;
  margin-left: auto;
}

.rb-reading-out {
  display: none;
}

/* The page's own way back stays in reading mode, which it did not used to.
 * It was hidden and a cross took its place in the corner, on the reasoning that
 * two ways out side by side are clutter. That was right about the clutter and
 * wrong about which one to keep: the cross said "leave" without saying where
 * to, so a reader in reading mode could see no part of the room, including
 * where in it they were standing. The trail is one control that answers both,
 * so the cross is gone and this rule with it. */

.rb-reading .rb-reading-in {
  display: none;
}

.rb-reading .rb-reading-out {
  display: block;
}

/* What reading mode puts away: the header naming the workspace, the contents
 * rail, and the footer naming the reader. All three belong to the room, and the
 * point of the mode is the document. */
.rb-reading .rb-viewer-chrome {
  display: none;
}

/* `!important` because the thing being hidden already carries one. The
 * contents rail is shown by a Bootstrap display utility, `d-lg-flex` on the
 * column, and those are declared `!important`, so an ordinary rule here loses
 * and the room's contents stay on screen beside a document that asked to be
 * alone. The phone's drawer needs nothing: it is a closed `dialog` until the
 * button in the header opens it, and the header is chrome this mode hides. */
.rb-reading .rb-viewer-toc {
  display: none !important;
}

/* `gap` came off with the flex row: the frame used to be the row holding the
 * rail beside the reading column, and it is now the padded block inside `main`
 * alone. */
.rb-reading .rb-viewer-frame {
  padding: 0.75rem;
}

/* The measures come off with the room. `rb-reader` is a width chosen so a deck
 * does not run past the contents rail, and the rail is what has just been put
 * away; keeping it would give a reader who asked for the whole screen the same
 * 64rem they already had, in the middle of an empty one.
 *
 * `rb-prose` stays. A page of text read at full window width is a worse read,
 * not a better one, and reading mode is supposed to be the better one. */
.rb-reading .rb-reader {
  max-width: none;
}

/* An embedded deck, at the size of the screen it was opened on.
 *
 * The ratio is the deck's, and the cap is the window's: without the cap a wide
 * monitor gives a 16:9 box taller than the viewport, so the reader arrives at
 * the top third of a slide and has to scroll to see one. With it, a wider
 * screen means a bigger deck rather than a longer page. The provider letterboxes
 * inside the frame on the rare screen wide enough to bind on height, which is
 * what every video player on the web does with the same problem.
 *
 * `dvh` rather than `vh`: on a phone `vh` is the viewport with the browser
 * chrome hidden, which is not the viewport anyone is looking at. */
.rb-embed {
  aspect-ratio: 16 / 9;
  width: 100%;
  max-height: calc(100dvh - 14rem);
}

/* ---- Cards and rows ---- */

/* Resting elevation is a hairline and nothing else, per the guidelines' ladder:
 * only things that genuinely float get a shadow. A white card with a drop
 * shadow on a white page is a grey halo, and it reads as a fill behind the
 * header row rather than as lift. */
.rb-card {
  background-color: var(--rb-canvas);
  border: 1px solid var(--rb-hairline);
  border-radius: var(--rb-radius-card);
}

/* ---- Buttons ---- */

/* Sized to the toolbar it sits above: 2.25rem, 0.875rem, 600. These ran at
 * 2.75rem and 17px in the display face, which put a marketing-sized button
 * beside a 0.82rem filter row and made the button the loudest thing on a page
 * whose job is the table under it. */
.btn {
  --bs-btn-padding-y: 0.40625rem;
  --bs-btn-padding-x: 1rem;
  --bs-btn-font-size: 0.875rem;
  --bs-btn-font-weight: 600;
  --bs-btn-border-radius: 50rem;
  --bs-btn-line-height: 1.5;
  transition: color 0.15s ease, background-color 0.15s ease, border-color 0.15s ease, transform 0.1s ease;
}

.btn:active { transform: scale(0.98); }

/* Bootstrap compiles its variants with literal hexes, so the brand has to be
 * restated into their variables. This is the one place it is said. */
.btn-primary {
  --bs-btn-bg: var(--rb-accent);
  --bs-btn-border-color: var(--rb-accent);
  --bs-btn-hover-bg: var(--rb-accent-strong);
  --bs-btn-hover-border-color: var(--rb-accent-strong);
  --bs-btn-active-bg: var(--rb-accent-strong);
  --bs-btn-active-border-color: var(--rb-accent-strong);
  --bs-btn-disabled-bg: var(--rb-accent);
  --bs-btn-disabled-border-color: var(--rb-accent);
  --bs-btn-color: var(--rb-accent-on);
  --bs-btn-hover-color: var(--rb-accent-on);
  --bs-btn-active-color: var(--rb-accent-on);
  --bs-btn-disabled-color: var(--rb-accent-on);
}

/* The secondary: outlined, white, navy label. Not `btn-outline-secondary`,
 * which fills with grey on hover and turns a quiet action into a loud one. */
.btn-outline-rb {
  --bs-btn-color: var(--rb-ink);
  --bs-btn-bg: var(--rb-canvas);
  --bs-btn-border-color: var(--rb-border-strong);
  --bs-btn-hover-color: var(--rb-ink);
  --bs-btn-hover-bg: var(--rb-subtle);
  --bs-btn-hover-border-color: var(--rb-border-strong);
  --bs-btn-active-color: var(--rb-ink);
  --bs-btn-active-bg: var(--rb-subtle);
  --bs-btn-active-border-color: var(--rb-border-strong);
}

/* Deleting a section is a thing you can do, not the thing the page is for, so
 * a destructive action is outlined and only turns red on approach. */
.btn-danger-rb {
  --bs-btn-color: var(--rb-ink-body);
  --bs-btn-bg: var(--rb-canvas);
  --bs-btn-border-color: var(--rb-border-strong);
  --bs-btn-hover-color: var(--rb-danger);
  --bs-btn-hover-bg: var(--rb-canvas);
  --bs-btn-hover-border-color: var(--rb-danger-edge);
  --bs-btn-active-color: var(--rb-danger);
  --bs-btn-active-bg: var(--rb-danger-wash);
  --bs-btn-active-border-color: var(--rb-danger-edge);
}

/* Inside a confirmation it is the other way round: deleting is the whole reason
 * the dialog is on the screen, so it wears the danger wash from the moment it
 * appears rather than waiting to be approached. Outlined, it was the same pill
 * as the Cancel sitting a few pixels to its left, and those two must not look
 * alike when one of them cannot be undone. */
.rb-modal .btn-danger-rb {
  --bs-btn-color: var(--rb-danger);
  --bs-btn-bg: var(--rb-danger-wash);
  --bs-btn-border-color: var(--rb-danger-edge);
}

/* ---- Fields ---- */

/* Bootstrap's focus ring is its own blue and is compiled, not variable-driven,
 * so it is restated here. Without this every field in the app focuses to
 * #0d6efd, which is not a colour this app uses anywhere. */
.form-control,
.form-select {
  --bs-border-radius: 0.75rem;
  border-width: 1.5px;
  border-color: var(--rb-border-strong);
  padding: 0.625rem 0.875rem;
  font-size: 1rem;
  letter-spacing: -0.02em;
  color: var(--rb-ink);
}

.form-control:focus,
.form-select:focus {
  border-color: var(--rb-accent);
  box-shadow: 0 0 0 3px var(--rb-accent-ring);
}

.form-control::placeholder { color: var(--rb-ink-faint); }

/* A checkbox is a field, so it takes a field's edge and not a hairline.
 * Bootstrap draws it from `--bs-border-color`, which is the rule that separates
 * two rows — at 1.15:1 on the white card and 1.4:1 on the dark one, an unticked
 * box was a box you had to know was there. `.form-control` has always used the
 * stronger step; this is the same control wearing the same border. */
.form-check-input {
  border-color: var(--rb-border-strong);
}

.form-check-input:checked {
  background-color: var(--rb-accent);
  border-color: var(--rb-accent);
}

.form-check-input:focus {
  border-color: var(--rb-accent);
  box-shadow: 0 0 0 3px var(--rb-accent-ring);
}

.form-label {
  font-size: 0.72rem;
  font-weight: 700;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--rb-ink-muted);
  margin-bottom: 0.5rem;
}

.form-text {
  margin-top: 0.5rem;
  font-size: 0.875rem;
  line-height: 1.6;
  color: var(--rb-ink-muted);
}

/* `min-inline-size: min-content` is the one thing a fieldset does that no other
 * element does: it refuses to be narrower than its widest child, so on a phone
 * a single wide row inside it pushes the fieldset past the form instead of
 * scrolling within it. */
.rb-fieldset {
  min-width: 0;
  border: 1px solid var(--rb-hairline);
  border-radius: var(--rb-radius-card);
  padding: 1.25rem;
}

.rb-legend {
  font-size: 0.72rem;
  font-weight: 700;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--rb-ink-muted);
  padding-inline: 0.25rem;
}

/* A fieldset you have to open: the far half of a choice, where the near half is
 * what nearly everybody wants. The move screen is the case it was written for —
 * the sections of this room are the list, and every other room's sections are
 * behind one line, because a flat list of both made the ten-times-an-afternoon
 * move carry the weight of the once-a-quarter one.
 *
 * Bordered like `.rb-fieldset` on purpose: closed, it should read as the next
 * block of the form rather than as a link under it. The marker goes because the
 * summary carries its own chevron, which is where every other disclosure in the
 * app puts it. */
.rb-disclose {
  min-width: 0;
  border: 1px solid var(--rb-hairline);
  border-radius: var(--rb-radius-card);
  padding: 1.25rem;
}

.rb-disclose > summary { list-style: none; }
.rb-disclose > summary::-webkit-details-marker { display: none; }
.rb-disclose[open] > summary { color: var(--rb-ink); }

/* A checkbox with a sentence after it is a decision rather than a field, so the
 * sentence goes inside the thing you click. */
.rb-choice {
  display: flex;
  align-items: flex-start;
  gap: 0.75rem;
  border: 1px solid var(--rb-hairline);
  border-radius: 0.75rem;
  padding: 1rem;
  transition: background-color 0.15s ease;
}

.rb-choice:hover { background-color: var(--rb-subtle); }

/* ---- The person picker ---- */

/* One control that opens a searchable list of people, with "Someone new" as its
 * last row. As a flat stack of radio cards the same list was the page, and as a
 * `select` it could neither be typed at nor reach anybody who was not in it.
 *
 * A native `details`, like every other menu here: `user_menu_controller.js`
 * closes it on Escape and on a click past it, and `person_picker_controller.js`
 * names the chosen row on the summary and shuts the panel behind the choice. */
.rb-picker-group { position: relative; }

/* A summary that reads as a field, because that is what it is standing in for:
 * `.form-control`'s border, radius and padding, minus the disclosure triangle
 * no other control on this page has. */
.rb-field-summary {
  border: 1.5px solid var(--rb-border-strong);
  border-radius: 0.75rem;
  padding: 0.625rem 0.875rem;
  cursor: pointer;
  list-style: none;
}

/* `list-style` does not reach Safari's marker, which is its own pseudo. */
.rb-field-summary::-webkit-details-marker { display: none; }

.rb-picker[open] > .rb-field-summary,
.rb-field-summary:focus-visible {
  border-color: var(--rb-accent);
  outline: none;
}

.rb-field-summary:focus-visible { box-shadow: 0 0 0 3px var(--rb-accent-ring); }

/* Over the fields below it rather than pushing them down the page, which is
 * what every other menu here does and what a picker is expected to do. */
.rb-picker-panel { z-index: 40; }

/* The list scrolls inside the panel; the search above it and the "Someone new"
 * row below it do not, so both stay reachable however far down you are. */
.rb-picker-list {
  max-height: 15rem;
  overflow-y: auto;
}

.rb-row {
  padding: 0.5rem 0.75rem;
  cursor: pointer;
  transition: background-color 0.15s ease;
}

.rb-row:hover { background-color: var(--rb-subtle); }
.rb-row:has(input:checked) { background-color: var(--rb-accent-wash); }

/* A choice whose consequences are fields rather than a sentence: folded away
 * until it is the answer. Three empty text fields under the picker made a form
 * that asks for one person read as a data-entry screen. CSS rather than script,
 * so the form is the same thing with JS off.
 *
 * Not `rb-reveal`, which is taken further down by the hover-reveal on a row's
 * actions: that one is `opacity: 0` under a media query, so the fields laid
 * out, took their space and painted nothing at all. */
.rb-unfold { display: none; }
.rb-picker-group:has(.rb-new-person:checked) > .rb-unfold { display: block; }

/* ---- Menus ---- */

/* Bootstrap's dropdown, pointed at the brand the same way everything else here
 * is. The menus are native `details` elements, so `.dropdown-menu.show` is
 * static markup and Bootstrap's JS never runs — only the painting is Bootstrap's.
 *
 * The active row is why this block exists: Bootstrap hard-codes #0D6EFD for it,
 * which is a different blue from ours and was the one wrong colour on the page. */
.dropdown-menu {
  --bs-dropdown-border-color: var(--rb-hairline);
  --bs-dropdown-border-radius: var(--rb-radius-row);
  --bs-dropdown-link-color: var(--rb-ink-body);
  --bs-dropdown-link-hover-color: var(--rb-ink);
  --bs-dropdown-link-hover-bg: var(--rb-subtle);
  --bs-dropdown-link-active-color: var(--rb-accent-text);
  --bs-dropdown-link-active-bg: var(--rb-hairline-soft);
  --bs-dropdown-item-padding-y: 0.375rem;
  --bs-dropdown-item-padding-x: 0.875rem;
  --bs-dropdown-min-width: 9rem;
}

/* The selected row is the accent on a wash, not white on a solid fill: the same
 * rule the sidebar's active row follows, for the same reason. A near-black or
 * saturated fill inside a small menu is louder than the choice deserves. */
.dropdown-item.active,
.dropdown-item:active {
  font-weight: 600;
}

/* ---- Pills ---- */

/* A badge is for a state that wants acting on, never for a classification.
 * Classifications are a dot and a word, inline in the row: the tier is a
 * property of the thing, not an alarm about it. */
.rb-pill {
  display: inline-flex;
  align-items: center;
  border-radius: 50rem;
  padding: 0.125rem 0.625rem;
  font-size: 0.75rem;
  font-weight: 600;
  letter-spacing: 0.025em;
}

.rb-pill-strong { background-color: var(--rb-invert-bg); color: var(--rb-invert-fg); }
.rb-pill-accent { background-color: var(--rb-accent-wash); color: var(--rb-accent-text); }
.rb-pill-quiet { background-color: var(--rb-hairline-soft); color: var(--rb-ink-body); }
.rb-pill-outline { border: 1px solid var(--rb-border-strong); color: var(--rb-ink-body); }

/* Tier is a rank, not a category, so it reads as one accent getting stronger.
 * The eight-colour badge palette it replaces made every row look urgent. */
.rb-tier-dot {
  display: inline-block;
  flex: 0 0 auto;
  width: 0.5rem;
  height: 0.5rem;
  border-radius: 50%;
  background-color: var(--rb-marker);
}

/* Five, cycled by position rather than mapped by meaning: a room may have one
 * audience or six, so the dot distinguishes rather than ranks. */
.rb-tier-dot-1 { background-color: var(--rb-accent); }
.rb-tier-dot-2 { background-color: var(--rb-ink); }
.rb-tier-dot-3 { background-color: var(--rb-warn); }
.rb-tier-dot-4 { background-color: var(--rb-on-ink-blue); }

/* ---- Tables ---- */

/* The dashboard tabs are the one place in the app that renders a real table.
 * Six columns of numbers about the same population, so the widths should come
 * from the content and one header should describe the whole column. Below `lg`
 * it scrolls sideways rather than folding columns away, so no number silently
 * disappears on a phone. */
.rb-table {
  width: 100%;
  min-width: 46rem;
  border-collapse: collapse;
  text-align: left;
}

/* No fill. A filled header band is a heavy horizontal bar across the top of
 * every table; a list header here is muted uppercase text over a hairline and
 * nothing else. The rule does the separating; the shade was doing it a second
 * time, louder. */
.rb-th {
  font-size: 0.72rem;
  font-weight: 700;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  white-space: nowrap;
  border-bottom: 1px solid var(--rb-hairline);
  padding: 0.5rem 0.75rem;
  color: var(--rb-ink-muted);
}

.rb-td {
  border-top: 1px solid var(--rb-hairline-soft);
  padding: 0.625rem 0.75rem;
  vertical-align: middle;
  font-size: 0.875rem;
  color: var(--rb-ink-muted);
}

/* A table stacked under a list of documents keeps that list's gutter and its
 * line height.
 *
 * `rb-td` is 0.625rem/0.75rem, which is right for a table that is the whole of
 * its page. Under a section's documents it is not: the document rows are px-4
 * and py-2, so the two lists in the same card stack started 12px apart at the
 * left and ran at different row heights — 62px against 57px — which reads as
 * one of them being slightly wrong without saying which. The gutter is set on
 * the edge cells only, so the middle columns keep the table's own spacing. */
.rb-flush-table > thead > tr > :first-child,
.rb-flush-table > tbody > tr > :first-child { padding-left: 1.5rem; }
.rb-flush-table > thead > tr > :last-child,
.rb-flush-table > tbody > tr > :last-child { padding-right: 1.5rem; }
.rb-flush-table > tbody > tr > td { padding-top: 0.5rem; padding-bottom: 0.5rem; }

/* Counts and durations line up under each other or they are not worth putting
 * in a column. */
.rb-num { white-space: nowrap; text-align: right; font-variant-numeric: tabular-nums; }

/* The column a row is named by: ink, and never truncated away to nothing. */
.rb-row-name {
  font-size: 1rem;
  font-weight: 600;
  letter-spacing: -0.015em;
  color: var(--rb-ink);
  text-decoration: none;
}

.rb-row-name:hover { color: var(--rb-accent-text); }

/* The same row name at the size the document list uses.
 *
 * `rb-row-name` is 1rem/600, which is right for a table that is the whole of
 * its page — Contacts, Links, the dashboard. It is wrong for the one table
 * that sits directly under a list of documents: a section page showed
 * "Josh Diamond" at 16px under "Org and hiring" at 13px, so the people
 * outweighed the material they can open. Same colour, same hover, the list's
 * own size and weight. */
.rb-row-name-sm {
  font-size: 0.82rem;
  font-weight: 500;
  letter-spacing: -0.01em;
  color: var(--rb-ink);
  text-decoration: none;
}

.rb-row-name-sm:hover { color: var(--rb-accent-text); }

/* Closed, an internal note is a chip on the row's line. Opened, it takes the
 * row's full width, rather than a second line that every document with a note
 * would pay for whether or not anyone is reading it. This was three `open:`
 * variants, which is a state no utility framework but Tailwind expresses. */
.rb-note-toggle[open] {
  display: block;
  width: 100%;
  margin-top: 0.5rem;
}

/* The overview editor reveals a row's controls on approach. It was Tailwind's
 * `group-hover`, which has no Bootstrap equivalent and cannot be an inline
 * style, so the rule is written here against the row that owns it. Pointer
 * devices only: on a touch screen the hover never happens, and the controls
 * would never appear at all.
 *
 * The rooms list used this too, and no longer does: a room's actions are the
 * only way to reach half its pages from that list, and on a phone they were
 * never drawn. Only rows whose controls repeat something reachable elsewhere
 * belong behind a hover. */
@media (hover: hover) {
  .rb-reveal { opacity: 0; transition: opacity 0.15s ease; }

  .rb-reveal-row:hover .rb-reveal,
  .rb-reveal-row:focus-within .rb-reveal { opacity: 1; }
}

/* ---- The opens chart ---- */

/* An SVG with no height rule does not collapse, it takes its viewBox's aspect
 * ratio, so losing the height utility turned a 96px strip into a quarter of the
 * page. And an SVG rect with no fill rule is black: the empty baseline came out
 * as a row of heavy dashes across the card. Both are stated here rather than
 * left to a utility, because both fail loudly and neither fails at build time. */
.rb-chart { height: 6rem; overflow: visible; }
.rb-bar { fill: var(--rb-accent); }
.rb-bar-empty { fill: var(--rb-bar-quiet); }

/* The example chart drawn where there is no data yet. Grey rather than accent,
 * so it cannot be mistaken for a reading of anything, and taller than the empty
 * tick so the space reads as "your numbers go here" instead of as a hairline.
 *
 * `--rb-bar-quiet` and not `--rb-hairline`: these are bars and not borders, and
 * after dark the hairline they used to share is a shade you cannot see against
 * the canvas. A column chart nobody can make out is indistinguishable from the
 * card failing to draw one, which is the thing the example exists to prevent. */
.rb-bar-example { fill: var(--rb-bar-quiet); }

/* Two states set from Stimulus rather than from markup. They were Tailwind
 * utilities toggled by class name from JavaScript — `opacity-50` on a row being
 * dragged, `ring-2 ring-cobalt` on a drop target — which is the one kind of
 * Tailwind usage a template sweep cannot see, because the class name lives in a
 * .js file. Named here so the controller and the stylesheet agree. */
.rb-dragging { opacity: 0.5; }
.rb-grab { cursor: grab; }
.rb-grab:active { cursor: grabbing; }
.rb-dropping { box-shadow: 0 0 0 2px var(--rb-accent-text); }

/* Dragging a file is a page-level act, so its signal fills the viewport rather
 * than tracing the large and mostly invisible edge of the page frame. The
 * overlay does not catch the pointer: the folder card underneath still picks
 * the upload destination. */
.rb-file-drop-overlay {
  position: fixed;
  inset: 0;
  z-index: 1080;
  display: grid;
  place-items: center;
  pointer-events: none;
  padding: 2rem;
  background: color-mix(in srgb, var(--rb-canvas) 86%, var(--rb-accent-text));
  backdrop-filter: blur(3px);
}
.rb-file-drop-overlay[hidden] { display: none; }
.rb-file-drop-overlay-card {
  width: min(34rem, 100%);
  padding: 3rem 2rem;
  border: 2px dashed var(--rb-accent-text);
  border-radius: var(--radius-card);
  color: var(--rb-accent-text);
  background: var(--rb-surface);
  box-shadow: 0 18px 48px var(--rb-shadow-drop);
}

/* The drop target inside the Add modal: a dashed box with room to breathe,
 * drawn as a place to drop things. The dialog around it carries the dropzone
 * controller and takes the `.rb-dropping` outline while a file is over it, so
 * this box is the affordance and the keyboard path within it. */
.rb-dropzone {
  padding: 3rem 1.5rem;
  border: 1px dashed var(--rb-border-strong);
  border-radius: var(--radius-card);
  background-color: var(--rb-subtle);
}

/* The same box once files are queued. The dialog is now the report of what is
 * happening to them, so the box folds to one line: its invitation goes, its
 * pickers stay, because the file you forgot is added mid-run rather than by
 * closing the dialog and starting again. `rb-modal-uploading` is set by
 * `dropzone#busy` for as long as the queue has rows in it. */
.rb-dropzone-busy { display: none; }
.rb-modal-uploading .rb-dropzone {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: center;
  gap: 0.5rem 0.75rem;
  padding: 0.75rem 1rem;
}
.rb-modal-uploading .rb-dropzone-idle { display: none; }
.rb-modal-uploading .rb-dropzone-busy { display: block; margin: 0; }

/* The tile that leads a row is also its drag handle. At rest the row shows the
 * folder or kind icon and nothing else; on hover the icon fades and a grip
 * takes its square, so reordering lives where the eye already is rather than in
 * a permanent column of handles running down the left of the list. The handle
 * fills the same box the tile does, so nothing shifts as they trade places. */
.rb-lead-icon,
.rb-lead-handle { transition: opacity 0.12s ease; }
.rb-lead-handle {
  position: absolute;
  inset: 0;
  display: flex;
  align-items: center;
  justify-content: center;
  opacity: 0;
  color: var(--rb-ink-faint);
}
.rb-lead-row:hover .rb-lead-icon { opacity: 0; }
.rb-lead-row:hover .rb-lead-handle { opacity: 1; }
/* A row mid-drag keeps its grip lit even as the pointer leaves it. */
.rb-dragging .rb-lead-icon { opacity: 0; }
.rb-dragging .rb-lead-handle { opacity: 1; }

/* The library's selection box rides in the same slot: the kind tile shows at
 * rest, and the checkbox takes its square on row hover, on focus, or once it is
 * ticked, so an unselected list reads as icons rather than a column of empty
 * boxes. The checkbox is centered on the tile and the mt-1 the shared class
 * carries is dropped, since it is no longer sitting in a row of its own. */
.rb-lead-check {
  position: absolute;
  top: 50%;
  left: 50%;
  margin: 0;
  transform: translate(-50%, -50%);
  opacity: 0;
  cursor: pointer;
  transition: opacity 0.12s ease;
}
tr:hover .rb-lead-check,
.rb-lead:focus-within .rb-lead-check,
.rb-lead:has(.rb-lead-check:checked) .rb-lead-check { opacity: 1; }
tr:hover .rb-lead-icon,
.rb-lead:focus-within .rb-lead-icon,
.rb-lead:has(.rb-lead-check:checked) .rb-lead-icon { opacity: 0; }

/* The header's select-all keeps that square at the head of the column, always
 * shown: it is the one standing sign that the rows can be selected at all. */
.rb-lead-head {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 2rem;
  height: 2rem;
}
.rb-lead-head .form-check-input { margin: 0; }

/* An empty state's box is dashed, which is the one border style Bootstrap has
 * no utility for: nothing is here yet, rather than here is a thing. */
.rb-dashed { border-style: dashed; }

/* A column that should take no more width than its own contents: the checkbox
 * and the row-actions columns, which must not steal width from the name. */
.rb-w0 { width: 0; }

/* The one surface in the app that genuinely floats: the investor-facing hero,
 * which sits above the page rather than on it. Everything else rests on a
 * hairline. */
.rb-lift { box-shadow: var(--shadow-card); }

/* ---- Tab bars ---- */

/* Underlined rather than pilled, so it reads as one table with four ways in
 * rather than four buttons.
 *
 * The rule is written out because Bootstrap cannot say it: `border-2` sets a
 * 2px width on all four sides, so pairing it with `border-bottom` and a colour
 * drew a box around the current tab instead of a line under it. The negative
 * margin lifts the tab's 2px rule over the nav's 1px one so they occupy the
 * same line rather than stacking. */
.rb-tab {
  margin-bottom: -1px;
  border-bottom: 2px solid transparent;
  text-decoration: none;
  transition: color 0.15s ease, border-color 0.15s ease;
}

.rb-tab:hover { color: var(--rb-ink); }
.rb-tab[aria-current] { border-bottom-color: var(--rb-ink); }

/* ---- Filter pills ---- */

/* A row of small pills, one of them current. Deliberately not the underlined
 * tab bar: these choose how the page is read, and the tabs choose what it is
 * reading, two different questions that should not look like the same one. */
.rb-segment {
  display: inline-flex;
  align-items: center;
  gap: 0.25rem;
  border: 1px solid var(--rb-hairline);
  border-radius: 50rem;
  background-color: var(--rb-canvas);
  padding: 0.25rem;
}

.rb-segment-option {
  border-radius: 50rem;
  padding: 0.25rem 0.75rem;
  font-size: 0.875rem;
  color: var(--rb-ink-body);
  text-decoration: none;
  transition: color 0.15s ease, background-color 0.15s ease;
}

.rb-segment-option:hover { color: var(--rb-ink); }

.rb-segment-option.is-current {
  background-color: var(--rb-invert-bg);
  color: var(--rb-invert-fg);
  font-weight: 500;
}

/* ---- The pre-auth column ---- */

/* One size larger than the app: these screens hold a single field and a single
 * action, and at app size they read as a fragment of a page rather than the
 * whole of one. */
.rb-auth-field {
  display: block;
  width: 100%;
  height: 3.25rem;
  border: 1.5px solid var(--rb-border-strong);
  border-radius: 50rem;
  background-color: var(--rb-canvas);
  padding-inline: 1.25rem;
  font-size: 17px;
  letter-spacing: -0.02em;
  color: var(--rb-ink);
}

.rb-auth-field::placeholder { color: var(--rb-ink-faint); }

.rb-auth-field:focus {
  border-color: var(--rb-accent);
  outline: none;
  box-shadow: 0 0 0 3px var(--rb-accent-ring);
}

/* "Who's reading" on a company link: the part before the @ typed, the firm's
 * domain fixed beside it. One field to the eye, so the fixed half reads as
 * part of the address rather than as a second question. */
.rb-auth-split {
  display: flex;
  align-items: stretch;
  padding: 0;
  overflow: hidden;
}

.rb-auth-split:focus-within {
  border-color: var(--rb-accent);
  box-shadow: 0 0 0 3px var(--rb-accent-ring);
}

/* The typed half takes whatever the domain leaves, and nothing more. A basis
 * of zero is the point: at `auto` an input brings its own intrinsic width of
 * about twenty characters, both halves shrank to fit, and the domain was the
 * one clipped ("@long-ridge.con"). */
.rb-auth-split input {
  flex: 1 1 0;
  width: 0;
  min-width: 0;
  border: 0;
  background: transparent;
  padding-inline: 1.25rem 0.5rem;
  color: inherit;
  font: inherit;
  letter-spacing: inherit;
  outline: none;
}

/* Sized to its own text and never shrunk below it, up to a ceiling that keeps
 * room to type; only a domain longer than that is cut, with an ellipsis, and
 * the whole of it is in the title. Block rather than flex, because
 * `text-overflow` does nothing to a flex container's text. */
.rb-auth-split .rb-auth-domain {
  display: block;
  flex: 0 0 auto;
  min-width: 0;
  max-width: 65%;
  border: 0;
  border-left: 1px solid var(--rb-border-strong);
  background-color: var(--rb-subtle);
  padding-inline: 0.875rem 1.25rem;
  color: var(--rb-ink-muted);
  font: inherit;
  /* After `font`, which is a shorthand that resets it. The field's height less
   * its two borders, so one line of text sits in the middle as a block. */
  line-height: calc(3.25rem - 3px);
  letter-spacing: inherit;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

/* `submit_tag` renders an input, which centres its own label; an anchor has to
 * be told to. Same button, two tags. */
.rb-auth-btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  height: 3.25rem;
  width: 100%;
  border-radius: 50rem;
  font-family: var(--rb-display-font);
  font-size: 1.125rem;
  font-weight: 500;
  letter-spacing: -0.03em;
  border: 1.5px solid transparent;
  transition: background-color 0.15s ease, color 0.15s ease, transform 0.1s ease;
}

.rb-auth-btn:active { transform: scale(0.98); }

.rb-auth-btn-primary { background-color: var(--rb-accent); color: #fff; }
.rb-auth-btn-primary:hover { background-color: var(--rb-accent-strong); color: #fff; }

.rb-auth-btn-secondary {
  background-color: var(--rb-canvas);
  border-color: var(--rb-border-strong);
  color: var(--rb-ink);
}

.rb-auth-btn-secondary:hover { background-color: var(--rb-subtle); }

/* What the page has to say but nobody came to read: why the mail may not
 * arrive, what a forwarded link does. Quiet, and never a warning colour. */
.rb-note {
  border: 1px solid var(--rb-hairline);
  border-radius: var(--rb-radius-card);
  background-color: var(--rb-subtle);
  padding: 1rem 1.25rem;
  font-size: 15px;
  line-height: 1.6;
  color: var(--rb-ink-body);
}

/* ---- Fields that are the thing they edit ---- */

/* The overview page is the investor's page with its words made editable in
 * place, so these carry no border and no background of their own: they take the
 * typography of what they will render as. The affordance is a faint tint on
 * hover and a real ring on focus, because an input you cannot see is an input
 * nobody knows is there.
 *
 * `field-sizing: content` grows a textarea to its text with no JavaScript at
 * all. Where it is unsupported the `rows` fallback still shows several lines. */
.rb-inplace {
  width: 100%;
  border: 0;
  border-radius: 0.5rem;
  background-color: transparent;
  transition: background-color 0.15s ease;
  field-sizing: content;
  resize: none;
}

.rb-inplace:focus { outline: none; }

.rb-inplace-dark:hover { background-color: rgba(255, 255, 255, 0.05); }
.rb-inplace-dark:focus {
  background-color: rgba(255, 255, 255, 0.1);
  box-shadow: 0 0 0 2px rgba(147, 197, 253, 0.4);
}

.rb-inplace-light:hover,
.rb-inplace-light:focus { background-color: var(--rb-subtle); }
.rb-inplace-light:focus { box-shadow: 0 0 0 2px var(--rb-accent-ring-strong); }

.rb-inplace-headline {
  margin-inline: -0.5rem;
  padding-inline: 0.5rem;
  font-family: var(--rb-display-font);
  font-weight: 500;
  font-size: 40px;
  line-height: 0.98;
  letter-spacing: -0.04em;
  color: #fff;
}

.rb-inplace-headline::placeholder { color: rgba(255, 255, 255, 0.3); }

.rb-inplace-summary {
  margin-inline: -0.5rem;
  padding-inline: 0.5rem;
  font-size: 1.25rem;
  line-height: 1.6;
  letter-spacing: -0.015em;
  color: rgba(255, 255, 255, 0.7);
}

.rb-inplace-summary::placeholder { color: rgba(255, 255, 255, 0.25); }

.rb-inplace-label {
  font-size: 0.72rem;
  font-weight: 700;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--rb-ink-muted);
}

.rb-inplace-value {
  font-family: var(--rb-display-font);
  font-weight: 500;
  font-size: 1.875rem;
  letter-spacing: -0.04em;
  line-height: 1;
  color: var(--rb-ink);
}

.rb-inplace-mono {
  font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
  font-size: 0.875rem;
  color: var(--rb-ink-body);
}

.rb-inplace-text {
  margin-inline: -0.5rem;
  padding-inline: 0.5rem;
  font-size: 1.125rem;
  line-height: 1.6;
  letter-spacing: -0.015em;
  color: var(--rb-ink-body);
}

.rb-inplace-label::placeholder,
.rb-inplace-value::placeholder,
.rb-inplace-mono::placeholder,
.rb-inplace-text::placeholder { color: var(--rb-ink-faint); }

/* ---- Things you point at ---- */

/* Tailwind's `hover:` variants had nowhere to go: an inline style cannot carry
 * a pseudo-class, so the conversion would have silently dropped every hover in
 * the chrome. They are collected here instead, on the components that own them.
 *
 * `rb-hover` is the shared transition, so the same three-property declaration
 * is not repeated inline on every row and link in the app. */
.rb-hover,
.sidebar-row,
.account-menu > summary,
.account-popover a,
.account-popover button {
  transition: color 0.15s ease, background-color 0.15s ease, border-color 0.15s ease;
}

/* 14px, which is the rail's own step: between Bootstrap's 12 and 16, and the
 * radius the rows were drawn at. */
.sidebar-row { border-radius: 14px; }

/* The active row is the accent on a blue wash, not white on ink. A near-black
 * fill is the loudest thing on a page whose job is the table beside it, and no
 * light fill carries the state on its own, so the signal is the label and icon
 * turning accent and the wash only supports it. The tint formula is from the
 * brand guidelines: the hue at 14% alpha behind its own 600 shade. */
/* `.is-current` alongside the attribute, for the rows that cannot carry one. A
 * section in the room's contents rail is a <summary>: the disclosure control is
 * the row and the link is the folder's name inside it, so `aria-current` belongs
 * on the name and the state has nowhere to sit but a class. `sidebar_row_class`
 * writes both. */
.sidebar-row[aria-current],
.sidebar-row.is-current { background-color: var(--rb-tint-wash); }

/* The one fill in the chrome that is not carrying a state. */
.sidebar-row:hover,
.account-menu > summary:hover,
.account-popover a:hover,
.account-popover button:hover {
  background-color: var(--rb-subtle);
  color: var(--rb-ink);
}

/* An active row keeps its accent when pointed at: the wash below it is the
 * state, and replacing it with the hover fill reads as the row switching off. */
.sidebar-row[aria-current]:hover,
.sidebar-row.is-current:hover { color: var(--rb-accent-text); }

/* A control that is only an icon: the rail's collapse toggle. Quiet until
 * pointed at, because it sits on the title's line and must not compete with it. */
.rb-icon-btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 2rem;
  height: 2rem;
  border: 0;
  border-radius: 0.5rem;
  background-color: transparent;
  color: var(--rb-ink-muted);
  transition: color 0.15s ease, background-color 0.15s ease;
}

.rb-icon-btn:hover {
  background-color: var(--rb-wash);
  color: var(--rb-ink);
}

/* The viewer's labelled header actions: Download, Share room and Ask. Each
 * keeps the icon button's quiet treatment and lets its word make the action
 * clear without a tooltip. Download and Share used to sit behind an ellipsis,
 * which told a reader nothing about what was in it. `.rb-viewer-ask` stays as
 * a name for the Ask row, which the tests and the pane opener reach for. */
.rb-viewer-action,
.rb-viewer-ask {
  width: auto;
  gap: 0.375rem;
  padding-inline: 0.625rem;
  text-decoration: none;
  white-space: nowrap;
}

/* The room's search, drawn as the field it opens. A magnifier alone in the
 * corner read as decoration; a pill with a placeholder in it reads as the
 * thing a reader looking for a word would reach for. It is a link, so it
 * takes the field's clothes rather than being one: border, subtle fill,
 * muted placeholder, and the key hint at the far end the way the dialog's own
 * field carries Esc. Below `md` the words go and the icon stays. */
.rb-viewer-search {
  display: inline-flex;
  align-items: center;
  gap: 0.5rem;
  width: 15rem;
  height: 2rem;
  padding: 0 0.75rem;
  border: 1px solid var(--rb-hairline);
  border-radius: 999px;
  background-color: var(--rb-subtle);
  color: var(--rb-ink-muted);
  font-size: 0.8125rem;
  text-decoration: none;
  white-space: nowrap;
  transition: color 0.15s ease, border-color 0.15s ease, background-color 0.15s ease;
}
.rb-viewer-search:hover,
.rb-viewer-search:focus-visible {
  border-color: var(--rb-border-strong);
  background-color: var(--rb-canvas);
  color: var(--rb-ink);
}
.rb-viewer-search-label { flex: 1 1 auto; min-width: 0; overflow: hidden; text-overflow: ellipsis; }
.rb-viewer-search-kbd { flex-shrink: 0; }
@media (max-width: 767.98px) {
  .rb-viewer-search { width: 2rem; padding: 0; justify-content: center; }
  .rb-viewer-search-label,
  .rb-viewer-search-kbd { display: none; }

  /* And so do the three beside it, for the same reason and one the search did
   * not have: they were `flex-shrink-0` around words that `nowrap` refused to
   * break, so on a phone the cluster was 356px wide in a 375px window and drew
   * straight over the wordmark. Every room opened on a phone had a header with
   * "Download" written through the brand. Icon-only here rather than folded
   * into the contents drawer, because Ask and Download are the two things a
   * reader does from any page, and the drawer is a list of documents.
   *
   * The word is hidden rather than removed: `display: none` would take the
   * accessible name with it, and these buttons have no `aria-label` of their
   * own. 2.25rem rather than the search's 2rem because these are pressed with
   * a thumb rather than reached for with a pointer. */
  .rb-viewer-action,
  .rb-viewer-ask {
    width: 2.25rem;
    padding-inline: 0;
    justify-content: center;
  }
  .rb-viewer-action > span,
  .rb-viewer-ask > span {
    position: absolute;
    width: 1px;
    height: 1px;
    padding: 0;
    margin: -1px;
    overflow: hidden;
    clip-path: inset(50%);
    white-space: nowrap;
    border: 0;
  }
}

/* Insurance behind the rule above, and the thing whose absence turned a tight
 * header into a broken one: the wordmark is an image with no width of its own,
 * so it painted over whatever was beside it once the bar ran out of room. It
 * gives way now instead. */
.rb-viewer-chrome .rb-wordmark-sm,
.rb-viewer-brand .rb-wordmark-sm {
  /* `min-width: 0` and not only `max-width`: a flex item's `min-width: auto`
   * resolves to its intrinsic width, and an image's intrinsic width is the
   * whole logo, so it refused to shrink and went on painting past its box. */
  min-width: 0;
  max-width: 100%;
  object-fit: contain;
  object-position: left center;
}

/* The same control on a phone's header, where 2rem is smaller than the thumb
 * pressing it. 2.75rem is Apple's 44pt, and the glyph inside goes up with it:
 * three 1.6px lines across 20px sat next to a 32px avatar and a wordmark and
 * read as the lightest thing on the bar, which is not what the one control that
 * opens the navigation should be. The negative inline margin is optical, not
 * spacing: it keeps the glyph over the 16px the header's own padding puts
 * everything else on rather than pushing it 6px in with the bigger box. */
.rb-icon-btn-lg {
  width: 2.75rem;
  height: 2.75rem;
  margin-inline-start: -0.375rem;
}

/* The same control on a row of the folder tree, where 2rem is taller than the
 * row it would sit on. */
.rb-icon-btn-sm {
  width: 1.5rem;
  height: 1.5rem;
  border-radius: 0.375rem;
}

/* ---- What drops out of a kebab ---- */

/* The panel a row's menu opens. It was eight utility classes and an inline
 * z-index, written out again at each of the four places that draw one, which is
 * how they drifted to three different widths and two different radii.
 *
 * The shadow is a token rather than Bootstrap's `.shadow-lg`, whose rgba is
 * fixed: a shadow tuned for a white page is invisible on a near-black one. */
.rb-menu-panel {
  z-index: 40;
  overflow: hidden;
  border: 1px solid var(--rb-hairline);
  border-radius: 0.75rem;
  background-color: var(--rb-canvas);
  box-shadow: 0 12px 32px var(--rb-shadow-drop);
}

/* One entry, whether it is a link, a plain button, or the submit of a
 * one-button `button_to` form.
 *
 * The reset is the whole reason this class exists. Three of these menus put
 * entries in a `button_to`, and a `<button>` that is given no appearance of its
 * own is painted by the operating system: a grey ButtonFace and a 2px outset
 * black border. On a white page that is a 1998 dialog button sitting in a
 * modern menu; on a dark one it is a white slab with the label unreadable on
 * it. The `<a>` entries beside them inherited the app's own colours and looked
 * right, which is why this read as one dropdown being broken rather than as
 * every menu button in the app never having been styled at all. */
.rb-menu-item {
  display: block;
  width: 100%;
  appearance: none;
  margin: 0;
  border: 0;
  border-radius: 0;
  background-color: transparent;
  padding: 0.5rem 0.75rem;
  text-align: left;
  font-size: 0.82rem;
  line-height: 1.45;
  color: var(--rb-ink-body);
  cursor: pointer;
  transition: color 0.15s ease, background-color 0.15s ease;
}

.rb-menu-item:hover,
.rb-menu-item:focus-visible {
  background-color: var(--rb-subtle);
  color: var(--rb-ink);
}

/* The rule that separates a group of entries from the one below it. On the
 * entry rather than between them, because the entry above is often the one that
 * is conditionally absent. */
.rb-menu-item.rb-menu-cut { border-top: 1px solid var(--rb-hairline); }

/* Deleting is the last entry in every menu that has one, and it is the only one
 * that reads as a warning before it is pressed. */
.rb-menu-item-danger { color: var(--rb-danger); }

.rb-menu-item-danger:hover,
.rb-menu-item-danger:focus-visible {
  background-color: var(--rb-danger-wash);
  color: var(--rb-danger);
}

/* ---- Sections, as disclosures ---- */

/* Every `<summary>` in this app carries its own glyph, so the browser's marker
 * would be a second arrow beside the first. This was
 * `[&::-webkit-details-marker]:hidden` per element, which is a pseudo-element
 * and therefore something no utility class or inline style can reach. */
summary { list-style: none; }
summary::-webkit-details-marker { display: none; }

/* The section's own arrow turns to point down when it opens. Tailwind said this
 * with a named group variant (`group-open/section:rotate-90`), which has no
 * equivalent anywhere else — but `details[open]` is a plain CSS selector and
 * always was. */
.rb-caret {
  display: inline-block;
  transition: transform 0.15s ease;
}

.rb-section[open] > summary .rb-caret { transform: rotate(90deg); }

/* A section row takes the same faint wash as every other row you can point at.
 * `:hover` on the summary rather than the details, so an open section does not
 * stay lit while the pointer is down among its documents. */
.rb-section > summary:hover { background-color: var(--rb-subtle); }

/* The first section needs no rule above it: the column header already drew one.
 * The `<ul><li>` version of this is above; a `<details>` is not an `<li>`. */
.rb-card .rb-section:first-of-type { border-top-width: 0 !important; }

/* ---- The room's rows ---- */

/* The tree and the list beside it, and the query container for both. What
 * decides whether the two fit side by side is the width of this row, which the
 * detail pane moves as it is dragged — not the viewport, which does not change
 * at all while somebody drags the pane. The list column is a second container
 * inside it, for the rows.
 *
 * A viewport media query got this wrong twice. The tree is `d-lg-block` at a
 * fixed 14rem and does not shrink, so at a 992px viewport with the pane at its
 * default the content row is 248px, the tree takes all of it, and the list
 * column beside it is *zero pixels wide* — every card a sliver behind the tree.
 * The viewport gate stays, because it is answering a different question (is
 * this a screen with a second column at all); this one answers whether there
 * is room for it right now. */
.rb-room-split { container-type: inline-size; }
.rb-room-list { container-type: inline-size; }

/* 14rem of tree plus the 1.5rem gap is 248px, and the list needs a readable
 * measure left over. Below that the tree folds away and the list has the row
 * to itself: the breadcrumbs still say where you are standing. 680px is where
 * the tree first appears today with no pane open at `lg`, so nothing that
 * currently shows a tree loses one. */
@container (max-width: 679.98px) {
  .rb-room-tree { display: none !important; }
}

/* One grid for every row in the room, so the controls line up the whole way
 * down it. Bootstrap's twelve columns cannot say this, which is why it is
 * written out.
 *
 * Two fixed columns of 64px and 84px used to sit between the name and the
 * controls, for Views and Updated. Those counts moved into the quiet line under
 * each name and the spans were left behind holding nothing — in all three
 * templates, with no column header above them. Empty or not, a grid column
 * takes its width: 64 + 84 and two 1.5rem gaps is 196px of a row spent on
 * nothing, and the `minmax(0, 1fr)` name is what paid for it. In a 600px list
 * column the name had 130px and every title on the page read "Financial mo...".
 *
 * With them gone the last column can be `auto` again. It was fixed at 200px
 * because a section row's four controls were wider than a document row's three,
 * which put those metadata columns in a different place on every line; every
 * row carries exactly three controls now, so `auto` is the same width on all of
 * them and there is nothing left to hold in place. */
.rb-room-row {
  display: grid;
  grid-template-columns: minmax(0, 1fr) auto;
  align-items: center;
  column-gap: 1.5rem;
}

/* Controls sit out of the way until the row is pointed at or tabbed into, and
 * only where there is a pointer to do the pointing: on a touch screen the hover
 * never happens, so there they stay put. Opacity rather than display, so the
 * column keeps its width and no row reflows under the pointer. */
.rb-room-actions {
  display: flex;
  flex-shrink: 0;
  align-items: center;
  justify-content: flex-end;
  gap: 0.5rem;
}

/* The one thing left that the list column's width decides. The row's columns
 * are the same at every width now. */
@container (min-width: 600px) {
  .rb-room-actions { opacity: 0; transition: opacity 0.15s ease; }

  .rb-room-row:hover .rb-room-actions,
  .rb-room-row:focus-within .rb-room-actions { opacity: 1; }
}

/* The access grid: the room down the side, one column per audience. The column
 * count is only known at render time, so the template comes in as an inline
 * style from `access_grid_columns_style` and this carries the rest.
 *
 * No responsive fold, unlike `.rb-room-row`. The whole point of the page is
 * reading a row across every audience at once, and a narrowed version showing
 * two of four columns would answer the question wrongly rather than partially;
 * the card scrolls sideways instead. */
.rb-access-row {
  display: grid;
  align-items: center;
  column-gap: 1rem;
}

/* Below this, the row stops being a row.
 *
 * The note above refuses a responsive fold and is right about the reason: two
 * of four audiences answers the question wrongly rather than partially. What it
 * did instead was scroll sideways, and on a phone that took the answer with it
 * — four audiences is 640px of grid in a 341px card, so reaching the third
 * column put the document's own name 266px off the left edge. What was left was
 * a column of View and Download chips belonging to a row nobody could name, on
 * the one page in the app where *which* row you are setting is the whole
 * question.
 *
 * A frozen name column is the usual answer and CSS grid cannot give one: a grid
 * item's containing block is its own grid area, so `position: sticky` on the
 * first cell holds only until that cell's own column has scrolled past, which
 * is no help at all. A real `table` can do it, and turning this page into one
 * is a larger change than the fault deserves.
 *
 * So the row stacks. Every audience is still shown, which is what the note was
 * protecting; each cell names the audience it is for, so the label is beside
 * the control rather than three columns away; and nothing scrolls sideways at
 * all. The inline `grid-template-columns` goes inert under `display: block`,
 * so the helper that writes it does not need to know this exists. */
.rb-access-cell-label { display: none; }

@media (max-width: 48rem) {
  .rb-access-row {
    display: block;
    padding-top: 0.75rem;
    padding-bottom: 0.75rem;
  }

  /* The name leads its own block rather than sharing a line with a control. */
  .rb-access-row > :first-child {
    margin-bottom: 0.5rem;
    font-weight: 600;
  }

  /* One audience per line: what it is called, and what it may do. */
  .rb-access-row > :not(:first-child) {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 0.75rem;
    padding: 0.25rem 0;
  }

  .rb-access-cell-label { display: block; }

  .rb-access-row > :not(:first-child) select { flex: 0 0 9rem; }

  /* The header row named the columns, and there are no columns now. Every cell
   * carries its own audience name instead. */
  .rb-access-row:not([data-access-grid-row]) { display: none; }
}

/* The square that leads a document's row. A list of filenames is a wall of
 * text; a list that starts with what each thing IS can be scanned. */
.rb-tile {
  display: inline-flex;
  flex-shrink: 0;
  align-items: center;
  justify-content: center;
  width: 2rem;
  height: 2rem;
  border: 1px solid;
  border-radius: 0.5rem;
}

.rb-tile-icon { width: 1rem; height: 1rem; }

/* A founder's emoji in the glyph's place (`DataRoomHelper#item_glyph`). It
 * wears the glyph's own class, so the box is the glyph's box and nothing beside
 * it moves; this only centres the character in it and sizes it to read as the
 * glyph did. An emoji is a little wider than its font size, so it is set a
 * touch under the box and allowed to overflow it rather than be clipped.
 *
 * The family is named because a heading font with no colour glyphs would
 * otherwise draw some of these as monochrome outlines. Colours are the emoji's
 * own in both themes; only the tile around it follows the tokens. */
.rb-emoji,
.rb-title-emoji {
  font-family: "Apple Color Emoji", "Segoe UI Emoji", "Noto Color Emoji", sans-serif;
  font-style: normal;
  font-weight: 400;
  letter-spacing: 0;
  text-transform: none;
}
.rb-emoji {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  overflow: visible;
  font-size: 0.875rem;
  line-height: 1;
}
.rb-tile .rb-emoji { font-size: 1.0625rem; }
.rb-tile-lg .rb-emoji { font-size: 1.3125rem; }
.rb-search-icon .rb-emoji { font-size: 1.0625rem; }
/* Before a heading: a little under the letters' size, so it reads as part of
 * the name rather than as a second, louder heading beside it. */
.rb-title-emoji { margin-right: 0.3em; font-size: 0.85em; }
/* An eyebrow is small capitals at 0.72rem, and an emoji scaled under that is
 * a speck; beside capitals it wants to be the height of a capital or more. */
.eyebrow .rb-title-emoji { margin-right: 0.45em; font-size: 1.3em; vertical-align: -0.1em; }

/* The picker that sets it. The button is the tile the emoji will sit in, at
 * the size of a form field's height, so what is chosen is previewed in the
 * shape it will have; the empty face is faint because it is a prompt, not a
 * value. */
.rb-icon-picker { flex-shrink: 0; }
.rb-icon-picker > summary.rb-icon-picker-button {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 2.5rem;
  height: 2.5rem;
  border: 1px solid var(--rb-hairline);
  border-radius: 0.625rem;
  background-color: var(--rb-canvas);
  color: var(--rb-ink-faint);
  cursor: pointer;
}
.rb-icon-picker > summary.rb-icon-picker-button:hover { background-color: var(--rb-subtle); color: var(--rb-ink-muted); }
.rb-icon-picker > summary.rb-icon-picker-button:focus-visible { outline: 2px solid var(--rb-accent); outline-offset: 2px; }
.rb-icon-picker-button .rb-emoji { font-size: 1.375rem; }
.rb-icon-picker-empty { display: inline-flex; }
.rb-icon-picker-panel { width: 17rem; padding-top: 0.5rem; }
.rb-icon-grid {
  display: grid;
  grid-template-columns: repeat(6, 1fr);
  gap: 0.125rem;
  padding: 0 0.5rem 0.5rem;
}
.rb-icon-choice {
  display: flex;
  align-items: center;
  justify-content: center;
  aspect-ratio: 1;
  appearance: none;
  border: 1px solid transparent;
  border-radius: 0.5rem;
  background: transparent;
  font-family: "Apple Color Emoji", "Segoe UI Emoji", "Noto Color Emoji", sans-serif;
  font-size: 1.25rem;
  line-height: 1;
  cursor: pointer;
}
.rb-icon-choice:hover,
.rb-icon-choice:focus-visible { background-color: var(--rb-subtle); outline: none; border-color: var(--rb-hairline); }
.rb-icon-choice[aria-pressed="true"] { background-color: var(--rb-accent-wash); border-color: var(--rb-accent-wash-edge); }
.rb-icon-picker-paste { padding: 0.5rem 0.75rem 0.625rem; border-top: 1px solid var(--rb-hairline); }

/* The workspace's initials at the top of the rail, and the whole of what is
 * left of it once the rail collapses.
 *
 * 1.25rem so it occupies exactly the box a nav icon does. It is deliberately
 * not an `.rb-tile`: those are 2rem and would push this row's centre off the
 * page title's, which is a four-pixel shift across every page in the app and
 * the thing `click_through_test`'s title-alignment test exists to catch.
 *
 * Accent rather than grey. It is the one piece of chrome that says which
 * tenant's material is on screen, and in a workspace switcher that is worth
 * being the loudest quiet thing in the column. */
.rb-workspace-mark {
  width: 1.25rem;
  height: 1.25rem;
  border-radius: 0.3125rem;
  background-color: var(--rb-accent-wash);
  color: var(--rb-accent-text);
  font-size: 0.5625rem;
  font-weight: 700;
  letter-spacing: 0.02em;
  line-height: 1;
}

/* The switcher's own row. `.sidebar-row` gives it the nav's hover and its
 * collapsed-rail behaviour for free; this is only what differs, which is that a
 * `summary` carries a default marker and a list-item display in Safari. */
.workspace-switcher > summary { list-style: none; }
.workspace-switcher > summary::-webkit-details-marker { display: none; }
.workspace-switcher > summary:hover { background-color: var(--rb-wash); }

/* Top-level folders take a slightly larger square than the documents inside
 * them: the size step is what says "container" at a glance. */
.rb-tile-lg { width: 2.5rem; height: 2.5rem; }

/* The document list partial draws a rule above each row so rows separate from
 * whatever sat above them. At the top of the folder page's card there is
 * nothing above the first row but the card's own edge, and two lines there
 * read as a mistake. */
.rb-folder-contents > :first-child,
.rb-folder-contents > ul:first-child > li:first-child { border-top-width: 0 !important; }

/* The icon ladder. `nav_icon` takes a class and not a style, so an icon's size
 * cannot be a one-off inline rule the way a box's can. Four steps, and every
 * one of them shrink-proof: an icon that has been squeezed by the text beside
 * it is worse than one that overflows. */
.rb-icon-sm { width: 0.875rem; height: 0.875rem; flex-shrink: 0; }
/* A legend is a label above a group, and a label does not float.
 *
 * Bootstrap's reboot floats every `legend` and puts the layout back with
 * `legend + * { clear: left }`, which works only while the very next element is
 * a visible block. The folder form's fieldset opens with the hidden input that
 * makes "no audience" a real answer, and `clear` on a `display: none` element
 * clears nothing — so the audience checkboxes laid themselves out in the strip
 * beside the floated legend and ran off the side of the card. The form was
 * unusable and the markup was correct, which is the worst pair to debug. */
fieldset > legend { float: none; width: auto; }

/* `hidden` beats a display utility, which it did not.
 *
 * Bootstrap ships `[hidden] { display: none !important }` in its reboot and
 * `.d-flex { display: flex !important }` a few thousand lines later. Same
 * specificity, both important, so the later one wins and an element carrying
 * both is visible. The access grid's save bar is exactly that — `hidden`
 * plus `d-flex` — so it sat over every access page announcing "0 changes
 * waiting to be saved" from the moment the page loaded, while the controller
 * that sets `hidden` worked perfectly. Restated here, after both. */
[hidden] { display: none !important; }

.rb-icon { width: 1.25rem; height: 1.25rem; flex-shrink: 0; }
.rb-icon-lg { width: 1.5rem; height: 1.5rem; flex-shrink: 0; }
/* Inside a button, beside its label. `rb-icon` is drawn for a control that is
 * only an icon, and at 20px against a 14px label it is the loud half of a
 * button whose label is the half you read. */
.rb-icon-sm { width: 1rem; height: 1rem; flex-shrink: 0; }

/* The new-link preview: one row per section, showing what the chosen audience
 * reaches. A row an audience cannot reach is dimmed rather than dropped, so the
 * panel says both what is open and what is held back for this audience. */
.rb-preview-row { padding: 0.25rem 0; }
.rb-preview-off { opacity: 0.45; }

/* The pre-auth column is a fixed measure beside the dark panel rather than a
 * fraction of it: at 40% of a wide screen the single field in it stretched to
 * something no one would type into. A media query, so it cannot be inline. */
/* The card the pre-auth screens are: a single readable measure, centred, with
 * the brand behind it. Narrower than the app's form measure because there is
 * only ever one field and one button on it. */
/* 30rem, from 26: at 26 the email field on "Who's reading" left the typed half
 * of an address about a third of the card once the fixed domain took its share,
 * and the founder asked for a little more room on every one of these screens
 * rather than a second line. */
.rb-auth-card { max-width: 30rem; }
.rb-auth-card-wide { max-width: 40rem; }

/* The development sign-in shortcut, pinned out of the way.
 *
 * It used to sit inside the card, between the Log in button and the investor
 * link, which meant the screen we look at every day was not the screen anybody
 * else gets: the card's spacing was being judged around a dashed box that does
 * not exist in production. In the corner it is still one click, and the page
 * behind it is the real one. Never rendered outside development. */
.rb-dev-shortcut {
  position: fixed;
  right: 1rem;
  bottom: 1rem;
  z-index: 50;
  max-width: 18rem;
  border: 1px dashed rgba(255, 255, 255, 0.25);
  border-radius: 0.75rem;
  background-color: rgba(255, 255, 255, 0.06);
  padding: 0.625rem 0.75rem;
  backdrop-filter: blur(6px);
}

/* On the dark panel the ink ladder has nothing to say, so the label and the
 * links are struck from white instead. */
.rb-dev-shortcut .rb-muted { color: rgba(255, 255, 255, 0.5); }

/* `button_to` posts, so each shortcut is a real button and arrives wearing the
 * user agent's grey chrome — there is no `.btn` on it and nothing else strips
 * that. It should read as the link it behaves like. */
.rb-dev-shortcut button {
  border: 0;
  background: none;
  padding: 0;
  text-align: left;
  color: rgba(255, 255, 255, 0.85);
  text-decoration: underline;
  text-underline-offset: 2px;
}

.rb-dev-shortcut button:hover { color: #fff; }

.rb-tile-planned { border-color: var(--rb-border-strong); background-color: var(--rb-subtle); color: var(--rb-ink-faint); }
.rb-tile-folder { border-color: var(--rb-hairline); background-color: var(--rb-subtle); color: var(--rb-ink-muted); }
.rb-tile-link { border-color: var(--rb-accent-wash-edge); background-color: var(--rb-accent-wash); color: var(--rb-accent-text); }
.rb-tile-file { border-color: var(--rb-hairline); background-color: var(--rb-canvas); color: var(--rb-ink-muted); }
/* Worn alongside `.rb-tile-file`, never instead of it: the tile is the same
 * hairline square it is for every other row and only the mark inside it takes
 * the colour. */
.rb-type-pdf { color: var(--rb-type-pdf); }
.rb-type-doc { color: var(--rb-type-doc); }
.rb-type-slides { color: var(--rb-type-slides); }
.rb-type-sheet { color: var(--rb-type-sheet); }
.rb-type-image { color: var(--rb-type-image); }
.rb-type-video { color: var(--rb-type-video); }


/* The title row is the only thing in the frame that is not flush with the
 * sidebar toggle: the toggle sits on this line, at the gutter the cards below
 * start from, so the title steps in far enough to clear it. */
@media (min-width: 992px) {
  .rb-title-indent { padding-left: 2rem; }
}

/* ---- The markdown toolbar ---- */

/* Square-ish, quiet until hovered. 2rem so a row of them is shorter than the
 * fields below it: the toolbar is furniture, not the first thing on the form. */
.rb-tool {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  height: 2rem;
  min-width: 2rem;
  border-radius: 0.5rem;
  border: 0;
  background-color: transparent;
  padding-inline: 0.5rem;
  font-size: 0.875rem;
  color: var(--rb-ink-body);
  transition: background-color 0.15s ease, color 0.15s ease;
}

.rb-tool:hover { background-color: var(--rb-subtle); color: var(--rb-ink); }
.rb-tool:focus { outline: none; }
.rb-tool:focus-visible { box-shadow: 0 0 0 2px var(--rb-accent-ring-strong); }

/* ---- Tabs ---- */

/* One row of choices above a panel, used by the new-room form for template /
 * scratch / generate. Ships hidden and is revealed by
 * `room_start_controller.js`: without JS the panels render stacked and the form
 * still works, so a tab row on screen would be a control that does nothing.
 *
 * A segmented row on a wash rather than an underline, because it sits inside a
 * fieldset that already has a hairline border and a second horizontal rule an
 * inch below the first reads as a mistake. */
.rb-tabs {
  display: inline-flex;
  gap: 0.125rem;
  padding: 0.1875rem;
  border-radius: 50rem;
  background-color: var(--rb-subtle);
  border: 1px solid var(--rb-hairline);
}

.rb-tab {
  border: 0;
  border-radius: 50rem;
  background-color: transparent;
  padding: 0.3125rem 0.875rem;
  font-size: 0.82rem;
  font-weight: 600;
  color: var(--rb-ink-muted);
  transition: background-color 0.15s ease, color 0.15s ease;
}

.rb-tab:hover { color: var(--rb-ink); }
.rb-tab:focus { outline: none; }
.rb-tab:focus-visible { box-shadow: 0 0 0 2px var(--rb-accent-ring-strong); }

/* The selected tab is the canvas lifted off the wash, not a filled accent. The
 * loudest thing in this fieldset should stay the template you are choosing. */
.rb-tab-active {
  background-color: var(--rb-canvas);
  color: var(--rb-ink);
  box-shadow: 0 1px 2px var(--rb-wash);
}

/* ---- Modal ---- */

/* A native `dialog`, so Escape, the backdrop, the focus trap and the top layer
 * are the browser's job rather than ours. Only the painting is here.
 *
 * `max-height` and the scrolling body are the whole reason this block is not
 * two lines: the new-room form holds every template and every room you could
 * duplicate, and a dialog that grows past the viewport puts its own submit
 * button somewhere you cannot reach. */
.rb-modal {
  width: min(40rem, calc(100vw - 2rem));
  /* As tall as it needs up to the viewport, rather than a fixed cap. 46rem was
   * an invented number and it made a nine-tile grid scroll on a screen with
   * room to spare. `dvh` because on a phone `vh` is the address bar's idea of
   * the viewport rather than the one you can see.
   *
   * There was a second rule for this element in application.css — margin,
   * max-height and `overflow: auto` on `dialog[data-modal-target]`, written for
   * the modal controller back when nothing used it. Element-plus-attribute
   * outranks a class, so it silently won both the height and the scrolling, and
   * the dialog and its body were two scroll containers arguing. It is gone; this
   * block is the only thing that styles a modal. */
  margin: auto;
  max-height: calc(100dvh - 3rem);
  padding: 0;
  border: 1px solid var(--rb-hairline);
  border-radius: var(--rb-radius-card);
  background-color: var(--rb-canvas);
  color: var(--rb-ink-body);
  overflow: hidden;
}

.rb-modal::backdrop { background-color: var(--rb-shadow-drop); }

/* ---- The phone's navigation drawer ---- */

/* The same native `dialog` and the same controller as a modal, and none of the
 * modal's geometry: a card centred in the viewport is the wrong shape for a
 * list of places, and it would leave the page visible around all four edges of
 * its own navigation. So this is a panel the height of the screen against the
 * left edge, which is the side the button that opens it is on.
 *
 * `margin: 0` and `margin-right: auto` rather than `left: 0`: a dialog in the
 * top layer is positioned by its margins, and `auto` on one side only is what
 * pins it to the other. `100dvh` for the reason `.rb-modal` uses `dvh` too, and
 * it matters more here: an address bar's worth of a full-height drawer is the
 * last row of the list. */
.rb-nav-drawer {
  width: min(19rem, 84vw);
  max-width: none;
  height: 100dvh;
  max-height: none;
  margin: 0;
  margin-right: auto;
  padding: 0;
  border: 0;
  border-right: 1px solid var(--rb-hairline);
  background-color: var(--rb-canvas);
  color: var(--rb-ink-body);
  overflow: hidden;
}

.rb-nav-drawer::backdrop { background-color: var(--rb-shadow-drop); }

/* Guarded by `[open]` for the reason `.rb-modal[open]` is: a bare
 * `display: flex` on a dialog beats the `display: none` that keeps a closed one
 * closed, and this one would sit over every page at every width. */
.rb-nav-drawer[open] {
  display: flex;
  flex-direction: column;
}

/* A keyframe rather than a transition with `@starting-style`: the drawer comes
 * from off the left edge, and this way it does so in every browser that ships
 * `dialog` rather than only the ones that ship discrete transitions. */
@media (prefers-reduced-motion: no-preference) {
  .rb-nav-drawer[open] { animation: rb-drawer-in 0.18s ease-out; }
}

@keyframes rb-drawer-in {
  from { transform: translateX(-100%); }
}

/* Guarded by `[open]`. A bare `display: flex` on a `dialog` overrides the
 * `display: none` that keeps a closed one closed, so the form would sit on the
 * page under the list, outside the top layer, always. */
.rb-modal[open] {
  display: flex;
  flex-direction: column;
}

/* A row can remain hovered or focused when its upload dialog opens. Its
 * controls and high-z-index tooltips are useful above the row but have no
 * business being painted through a modal. Hide the entire action cluster while
 * any native dialog is open; it returns unchanged when the dialog closes. */
body:has(.rb-modal[open]) .rb-room-actions {
  visibility: hidden;
}

/* `min-height: 0` because a flex child refuses to shrink below its content by
 * default, which is the shape of this bug: the body would grow to its natural
 * height, the dialog would clip it, and the scrollbar that should be here would
 * be on nothing. */
.rb-modal-body {
  min-height: 0;
  overflow-y: auto;
  padding: 1.25rem;
}

/* ---- A rendered PDF page ---- */

/* One page of a deck, as `pdf_viewer_controller.js` draws it.
 *
 * These were Tailwind classes set from the controller — `relative mx-auto
 * w-full overflow-hidden rounded-xl bg-white shadow-card` — and they outlived
 * Tailwind by being written in JavaScript, where nothing was looking for them.
 * The half that broke is worse than the half that vanished: `w-full` stopped
 * existing, but `mx-auto` is Bootstrap's as well, and an auto side margin on a
 * flex item takes the free space rather than stretching into it. So the frame
 * sized itself to its content, its content was a canvas the controller had just
 * scaled to the frame's width, and the width was zero.
 *
 * Every page of every PDF rendered 0 x 0. The deck fetched, parsed and drew
 * without an error worth logging, the page counter said "Page 1 of 3", and
 * there was nothing on the page — in the investor's viewer, which is the whole
 * product, for as long as Tailwind has been gone.
 */
.rb-pdf-page {
  position: relative;
  width: 100%;
  overflow: hidden;
  border-radius: 12px;
  background-color: var(--rb-paper);
}

.rb-pdf-page canvas {
  display: block;
  width: 100%;
  height: auto;
}

/* A frame the observer has not reached yet. `:empty` is exactly the state: a
 * frame holds nothing until `draw` puts a canvas in it. Without this, scrolling
 * ahead of the renderer is a column of blank rectangles that look like pages
 * with nothing on them rather than pages on their way. */
.rb-pdf-page:empty {
  background-image: linear-gradient(
    100deg,
    transparent 20%,
    rgba(var(--rb-paper-ink-rgb), 0.06) 40%,
    transparent 60%
  );
  background-repeat: no-repeat;
  background-size: 220% 100%;
  animation: rb-pdf-sweep 1.6s ease-in-out infinite;
}

/* Drawn pages arrive rather than appear. A canvas swapped in at full strength
 * is a flicker on every page of a sixty-slide deck; a third of a second of fade
 * turns the same thing into the page settling onto the paper it is already on. */
.rb-pdf-page canvas {
  animation: rb-pdf-page-in 0.3s ease-out;
}

/* ---- The pane's preview, borrowed by a form ---- */

/* The edit form shows the file it is about to replace, and it shows it with
 * `shared/_document_preview` rather than a second renderer. That partial is
 * written for the pane, so it hands back `.rb-pane-scroll` children that expect
 * a flex column with a height to fill, and the container supplies the column.
 * It used to supply the children's own rules as well, copied, because they
 * lived inside the `min-width: 992px` block; they no longer do, because a pane
 * open when the window narrows fills the screen rather than closing.
 *
 * `max-height` rather than `height`: a photograph and a one-page memo should
 * take the room they need, and only a deck should have to scroll. */
.rb-inline-preview {
  display: flex;
  flex-direction: column;
  max-height: 34rem;
  overflow: hidden;
  border: 1px solid var(--rb-hairline);
  border-radius: var(--rb-radius-card);
  background-color: var(--rb-canvas);
}

/* ---- The wait in front of a PDF ---- */

/* Shaped like the document rather than described in a sentence. See
 * `shared/_pdf_loading` for why this is worth any markup at all: a watermarked
 * deck on a slow line is a twenty-second wait, and the reader's question
 * throughout is whether anything is still happening.
 *
 * No padding and no width of its own. It stands where the pages will stand, so
 * it is inset by whatever insets them, and a sheet narrower than the page it
 * becomes is a step sideways at the moment of the swap. That was the first
 * version of this and it was wrong in the pane, where the pages fill the width
 * and the sheet was capped at 30rem. */
.rb-pdf-loading {
  display: flex;
  flex-direction: column;
  gap: 0.5rem;
}

.rb-pdf-loading[hidden] {
  display: none;
}

.rb-pdf-loading-sheet {
  display: flex;
  flex-direction: column;
  gap: 0.75rem;
  width: 100%;
  padding: 4% 8% 7%;
  border-radius: 12px;
  background-color: var(--rb-paper);
  /* The page's own proportion, which is also what the controller gives an
   * undrawn frame. The sheet and the page it becomes are the same rectangle. */
  aspect-ratio: 1 / 1.414;
  overflow: hidden;
}

/* Where a chart or a photograph would be. Takes the space the lines do not. */
.rb-pdf-loading-figure {
  flex: 1;
  display: grid;
  place-items: center;
  min-height: 4rem;
  margin: 0.5rem 0;
  border-radius: 8px;
  background-color: rgba(var(--rb-paper-ink-rgb), 0.05);
  background-image: linear-gradient(
    100deg,
    transparent 20%,
    rgba(var(--rb-paper-ink-rgb), 0.05) 40%,
    transparent 60%
  );
  background-repeat: no-repeat;
  background-size: 220% 100%;
  animation: rb-pdf-sweep 1.6s ease-in-out infinite;
  animation-delay: 0.3s;
}

/* The rhythm of a page, not its content: a heading and a few lines, faint
 * enough that nobody reads them as text that failed to load. */
.rb-pdf-loading-line {
  height: 0.6rem;
  border-radius: 999px;
  background-color: rgba(var(--rb-paper-ink-rgb), 0.07);
  background-image: linear-gradient(
    100deg,
    transparent 20%,
    rgba(var(--rb-paper-ink-rgb), 0.055) 40%,
    transparent 60%
  );
  background-repeat: no-repeat;
  background-size: 220% 100%;
  animation: rb-pdf-sweep 1.6s ease-in-out infinite;
}

.rb-pdf-loading-line.is-title {
  width: 55%;
  height: 1.1rem;
  margin-bottom: 0.75rem;
  background-color: rgba(var(--rb-paper-ink-rgb), 0.12);
}

.rb-pdf-loading-line.is-short {
  width: 72%;
}

/* Each line starts its sweep a beat after the one above, so the light travels
 * down the page instead of the whole sheet pulsing at once. */
.rb-pdf-loading-sheet > *:nth-child(2) { animation-delay: 0.1s; }
.rb-pdf-loading-sheet > *:nth-child(3) { animation-delay: 0.2s; }
.rb-pdf-loading-sheet > *:nth-child(5) { animation-delay: 0.4s; }
.rb-pdf-loading-sheet > *:nth-child(6) { animation-delay: 0.5s; }

.rb-pdf-loading-readout {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 0.35rem;
  color: rgba(var(--rb-paper-ink-rgb), 0.44);
  font-size: 0.8125rem;
  text-align: center;
}

/* Tabular figures: a percentage counting up must not shift the label beside it
 * every time it passes a digit of a different width. */
.rb-pdf-loading-value {
  font-family: var(--rb-display-font);
  font-variant-numeric: tabular-nums;
  font-size: clamp(1.75rem, 5vw, 3rem);
  font-weight: 500;
  letter-spacing: -0.03em;
  line-height: 1;
}

@keyframes rb-pdf-sweep {
  from { background-position: 180% 0; }
  to { background-position: -80% 0; }
}

@keyframes rb-pdf-page-in {
  from { opacity: 0; }
  to { opacity: 1; }
}

/* Nothing here carries meaning that the movement is the only copy of: the
 * percentage is written out, and the sheet is still a sheet standing still. */
@media (prefers-reduced-motion: reduce) {
  .rb-pdf-page:empty,
  .rb-pdf-page canvas,
  .rb-pdf-loading-line,
  .rb-pdf-loading-figure {
    animation: none;
  }
}

/* A deck can be read as one long document or presented one slide at a time.
 * The switch stays deliberately quieter than the document itself; the arrows
 * only enter the page when slide mode asks for them. */
.rb-pdf-view-toggle {
  display: inline-flex;
  align-items: center;
  gap: 0.125rem;
  padding: 0.125rem;
  border: 1px solid var(--rb-hairline);
  border-radius: 999px;
  background-color: rgba(var(--rb-canvas-rgb), 0.6);
  letter-spacing: normal;
  text-transform: none;
}

/* The same guard `.rb-pdf-arrow[hidden]` carries below, and for the same
 * reason: `display` here beats the `hidden` attribute's `display: none`, so a
 * toggle the controller hides on a one-page file would stay on the bar. */
.rb-pdf-view-toggle[hidden] {
  display: none;
}

.rb-pdf-view-option {
  min-height: 1.75rem;
  padding: 0.2rem 0.625rem;
  border: 0;
  border-radius: 999px;
  background: transparent;
  color: var(--rb-ink-muted);
  font: inherit;
  font-weight: 600;
  cursor: pointer;
}

.rb-pdf-view-option[aria-pressed="true"] {
  background-color: var(--rb-canvas);
  color: var(--rb-ink);
  box-shadow: 0 1px 3px rgba(var(--rb-ink-rgb), 0.12);
}

.rb-pdf-stage {
  position: relative;
}

.is-slides .rb-pdf-pages {
  display: block;
}

.is-slides .rb-pdf-page {
  display: none;
}

.is-slides .rb-pdf-page.is-current {
  display: block;
}

.rb-pdf-arrow {
  position: absolute;
  top: 50%;
  z-index: 2;
  display: grid;
  width: 2.5rem;
  height: 2.5rem;
  padding: 0;
  border: 1px solid var(--rb-hairline);
  border-radius: 999px;
  background-color: rgba(var(--rb-canvas-rgb), 0.94);
  box-shadow: 0 6px 20px rgba(var(--rb-ink-rgb), 0.16);
  color: var(--rb-ink);
  font-size: 1.75rem;
  line-height: 1;
  place-items: center;
  transform: translateY(-50%);
  cursor: pointer;
}

.rb-pdf-arrow-prev {
  left: 1.5rem;
}

.rb-pdf-arrow-next {
  right: 1.5rem;
}

.rb-pdf-arrow:disabled {
  opacity: 0.35;
  cursor: default;
}

.rb-pdf-arrow[hidden] {
  display: none;
}

/* An image and a text file read in the room. The same paper the PDF's pages
 * sit on, so a photograph, a README and a deck are one object in a reader's
 * hands rather than three. No colour of their own: both read the palette the
 * pages above them already read. */
/* `max-width` rather than `width`: a logo or a small chart blown up to the
 * column's width is a blurred rectangle, and an image is the one thing here
 * that has a right size of its own. Centred, so a narrow one does not sit off
 * to one edge of a wide page. */
.rb-viewer-image {
  display: block;
  max-width: 100%;
  height: auto;
  margin-inline: auto;
  border-radius: 12px;
  background-color: var(--rb-paper);
}

/* `--rb-subtle` and not `--rb-paper`, which is the one difference between this
 * and the image above it. Paper is white in both themes because a rendered page
 * carries its own black-on-white bytes; this text is drawn by us in the theme's
 * own ink, so on paper it came out white on white after dark and could not be
 * read at all. A surface that answers the palette is what pairs with ink that
 * answers the palette. */
.rb-viewer-text {
  margin: 0;
  padding: 1.5rem;
  border-radius: 12px;
  background-color: var(--rb-subtle);
  color: var(--rb-ink);
  font-size: 0.875rem;
  line-height: 1.7;
  /* Wrapped rather than scrolled sideways: a line of prose in a `.txt` runs to
   * whatever width it was saved at, and a reader should not have to drag a
   * horizontal scrollbar to finish a sentence. `anywhere` is for the other
   * case, a minified line with nowhere to break. */
  white-space: pre-wrap;
  overflow-wrap: anywhere;
}

/* ---- The detail pane ---- */

/* A third column, to the right of `main`: a gutter with a card floating in it.
 * A row opens here instead of navigating, so a founder reading down a section
 * keeps their place in it.
 *
 * The card is told apart from the page by the gutter behind it and a hairline,
 * and by nothing else. The app this pattern comes from floats the same card on
 * a drop shadow; that would be the only resting elevation in this theme, and
 * the dark block a thousand lines up says out loud that a card here is the
 * canvas and a hairline. A shadow would have been the one place that stopped
 * being true.
 *
 * The width is a custom property on `:root` rather than an inline style on the
 * aside, so the layout's pre-paint script can restore a remembered width before
 * anything is drawn, the same way the sidebar's rail is restored. Setting it on
 * the element instead would mean the pane renders at its default and snaps.
 *
 * Below the breakpoint there is no pane to open: `side_pane.js` stops pointing
 * the row links at the frame, so a click is an ordinary navigation to the
 * document's own page. That page already exists and is better than a phone
 * could show in a drawer, which is why there is no drawer here. A pane that
 * was already open when the window narrowed is the exception, and it fills the
 * screen rather than closing.
 */
.rb-pane {
  --rb-pane-pad: 0.6rem;
  --rb-pane-gutter: 1rem;
  display: none;
  flex-direction: column;
  min-width: 0;
  padding: var(--rb-pane-pad) var(--rb-pane-pad) var(--rb-pane-pad) var(--rb-pane-gutter);
  background-color: var(--rb-subtle);
}

/* The inside of the card is not behind the breakpoint, though it used to be.
 * Only the pane's place beside the list is. A pane that is already open when
 * the window narrows stays open and fills the screen (see `side_pane.js`), and
 * with these rules inside the block it would fill it as a card with no column
 * to lay itself out in: no scroller, so a deck stops at its first page, and
 * the controls out of their corner and into the flow. The pane is
 * `display: none` below the breakpoint unless it is filling the screen, so
 * none of this reaches a page with no pane on it. */

/* The frame itself is the card. `turbo-frame` is an inline element until told
 * otherwise, so without the display it would not lay its own children out. */
.rb-pane-card {
  display: flex;
  flex-direction: column;
  flex: 1 1 auto;
  min-height: 0;
  background-color: var(--rb-canvas);
  border: 1px solid var(--rb-hairline);
  border-radius: var(--rb-radius-card);
  overflow: hidden;
}

/* The card that stands in for the frame between the click and the answer.
 *
 * A sibling of the frame rather than markup inside it: Turbo replaces the
 * frame's children on render and would take the placeholder with them at the
 * moment it is still doing its job. So the frame is hidden while this shows,
 * and this is removed when the frame has something. */
.rb-pane.is-loading .rb-pane-card {
  display: none;
}

.rb-pane-loading {
  flex: 1 1 auto;
  min-height: 0;
  padding: 1rem 1.25rem;
  background-color: var(--rb-canvas);
  border: 1px solid var(--rb-hairline);
  border-radius: var(--rb-radius-card);
}

/* Head and foot hold; only the middle scrolls. The foot is empty for a
 * document and is where an assistant's composer goes, which is the whole
 * reason the card is three parts rather than one scrolling box.
 *
 * Scrolling belongs to `.rb-pane-scroll` and clipping to the card, rather
 * than both to the card: a single `overflow: hidden` there to clip the
 * rounded corners takes the vertical scroll with it, and the pane cannot be
 * read past its first screen. */
.rb-pane-head {
  flex: 0 0 auto;
  padding: 1rem 1.25rem;
  border-bottom: 1px solid var(--rb-hairline);
}

.rb-pane-scroll {
  flex: 1 1 auto;
  min-height: 0;
  overflow-x: hidden;
  overflow-y: auto;
  padding: 1.25rem;
}

/* A preview draws to the card's edges: the padding that suits a paragraph is
 * a margin around a page, and this column has none to spare. Its own children
 * carry what padding they need instead. */
.rb-pane-flush {
  padding: 0;
}

/* The page counter, sticky against the body rather than the window, because
 * the body is what scrolls here. Translucent so a page passing behind it is
 * still visibly a page. */
.rb-pane-pager {
  position: sticky;
  top: 0;
  z-index: 1;
  padding: 0.5rem 0.875rem;
  border-bottom: 1px solid var(--rb-hairline);
  background-color: rgba(var(--rb-canvas-rgb), 0.9);
  backdrop-filter: blur(8px);
  -webkit-backdrop-filter: blur(8px);
  font-size: 0.75rem;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--rb-ink-muted);
}

.rb-pane-foot {
  flex: 0 0 auto;
  padding: 0.75rem 1rem;
  border-top: 1px solid var(--rb-hairline);
}

.rb-pane-footer-actions {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: flex-end;
  gap: 0.5rem;
}

.rb-pane-facts {
  display: grid;
  gap: 0.35rem;
}

.rb-pane-fact {
  display: grid;
  grid-template-columns: 5.25rem minmax(0, 1fr);
  align-items: baseline;
  gap: 0.5rem;
}

/* The label half of a footer fact. Fixed width so the two lines read as a
 * small table rather than as two sentences that happen to be stacked. */
.rb-pane-label {
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--rb-ink-faint);
  white-space: nowrap;
}

/* Over the card's top-right corner rather than inside the head, so the head
 * can be laid out without leaving a hole for it. */
/* No gap. Each control is a 2rem box around a 1.25rem glyph, so the padding
 * inside the boxes is already the space between the marks; adding a gap on
 * top of it spaced them like three separate buttons rather than one cluster.
 *
 * The glyphs run at the nav's size rather than the small one. At 0.875rem in
 * a 2rem box a close cross reads as a stray mark somebody left in the corner,
 * and the app's own scale for an icon that means something is 1.25rem. */
.rb-pane-actions {
  position: absolute;
  top: calc(var(--rb-pane-pad) + 0.375rem);
  right: calc(var(--rb-pane-pad) + 0.375rem);
  z-index: 3;
  display: flex;
  align-items: center;
  gap: 0;
}

@media (min-width: 992px) {
  .rb-pane {
    position: relative;
    display: flex;
    flex: 0 0 clamp(20rem, var(--rb-pane-width, min(26rem, 34vw)), 60vw);
  }

  /* Closed is the default and the state the server renders. The pane opens when
   * the frame inside it says it has something, not when a row is clicked, so a
   * request that fails leaves no empty card behind. */
  .rb-pane.is-closed {
    display: none;
  }

  /* The whole left gutter is the drag target, not the 4px bar: the bar is the
   * affordance and a 4px hit area is a fight. `side_pane.js` puts this element
   * in, because a handle the server renders is a handle in the markup of every
   * page that never resizes anything. */
  .rb-pane-grip {
    position: absolute;
    top: var(--rb-pane-pad);
    bottom: var(--rb-pane-pad);
    left: 0;
    width: var(--rb-pane-gutter);
    display: flex;
    align-items: center;
    justify-content: center;
    cursor: col-resize;
    z-index: 2;
  }

  .rb-pane-grip-bar {
    width: 4px;
    height: 2.125rem;
    border-radius: 999px;
    background-color: var(--rb-marker);
    opacity: 0;
    transition: opacity 0.12s ease, background-color 0.12s ease, height 0.12s ease;
  }

  .rb-pane-grip:hover .rb-pane-grip-bar,
  .rb-resizing .rb-pane-grip-bar {
    opacity: 1;
  }

  .rb-pane-grip:hover .rb-pane-grip-bar {
    background-color: var(--rb-accent);
    height: 2.875rem;
  }

  /* In the contents tree the whole link is the row. Keep its text neutral;
   * selection is carried by the wash, not by turning a document title blue. */
  .rb-viewer-toc .rb-pane-current {
    background-color: var(--rb-tint-wash);
    color: var(--rb-ink-body) !important;
    font-weight: 600;
  }

  /* In document lists the title and action are two links to the same pane, so
   * both receive the marker. Paint their shared row once and leave both pieces
   * of text in their ordinary colours. */
  .rb-row-link:has(.rb-pane-current) {
    border-radius: 0.75rem;
    background-color: var(--rb-tint-wash);
  }

  .rb-row-link .rb-pane-current {
    background-color: transparent;
    color: inherit !important;
    font-weight: inherit;
  }

  /* The URL still belongs to the page under the pane, but while a row is open
   * only the row being read should look selected. The semantic aria-current
   * remains on the underlying route; this is deliberately only visual. */
  .rb-pane-open .rb-viewer-toc .sidebar-row.is-current:not(.rb-pane-current) {
    background-color: transparent;
    color: var(--rb-ink-body) !important;
    font-weight: 400 !important;
  }
}

/* Filling the screen.
 *
 * Outside the breakpoint block on purpose: this is the one part of the pane
 * that is worth having on a narrow screen, where a deck read in a column the
 * width of a phone is the thing it is meant to rescue.
 *
 * Fixed to the viewport rather than the Fullscreen API. The API would hand the
 * browser its own chrome and take the page's with it, and what is wanted here
 * is the app's own screen given over to one document, not a kiosk: the reader
 * keeps the controls, and Escape means the same thing it means everywhere else
 * in the app rather than being intercepted by the browser.
 */
.rb-pane.is-full {
  display: flex;
  position: fixed;
  inset: 0;
  z-index: 60;
  max-width: none;
  padding: var(--rb-pane-pad, 0.6rem);
  background-color: var(--rb-subtle);
}

/* A closed pane is closed, whatever else is on it.
 *
 * `is-full` is declared out here rather than inside the breakpoint block, and
 * later in the file, so at equal specificity it beat `.rb-pane.is-closed`
 * above: a pane holding both classes was a fixed, empty, full-screen white
 * sheet over the whole app. That is what "the close button gives you a blank
 * page" was, and it took a reader who had put one document full screen and a
 * page that came back out of Turbo's cache still wearing the class.
 *
 * `side_pane.js` takes `is-full` off on the way out for its own reasons, and
 * the pane must not depend on it having remembered to: the failure is the
 * whole window, and nothing on the screen says what happened. */
.rb-pane.is-closed {
  display: none;
}

/* The grip is a left edge that no longer exists once the pane is the window. */
.rb-pane.is-full .rb-pane-grip {
  display: none;
}

/* A full-screen pane has room for the two footer facts to sit side by side.
 * Keeping the narrow-column stack there stranded both lines in the far-left
 * corner and made the first label wrap into two words on two rows. */
.rb-pane.is-full .rb-pane-facts {
  grid-template-columns: repeat(2, minmax(0, 1fr));
  column-gap: 3rem;
}

@media (max-width: 767px) {
  .rb-pane.is-full .rb-pane-facts {
    grid-template-columns: 1fr;
  }
}

/* One button, two glyphs, whichever the state is not. Swapped in CSS rather
 * than by rewriting the button, so the control cannot get out of step with the
 * class that is actually on the pane. */
.rb-pane-full-out,
.rb-pane.is-full .rb-pane-full-in {
  display: none;
}

.rb-pane.is-full .rb-pane-full-out {
  display: block;
}

/* Below the breakpoint an open pane can only fill the screen, so the control
 * that offers the other shape would offer nothing: pressed, it changed a
 * preference and left the screen exactly as it was. The choice it holds is
 * kept, and the button comes back with the room to act on it. Written as the
 * negation of `side_pane.js`'s query rather than as a max-width, so the two
 * cannot both be false on a window that is a fraction of a pixel wide. */
@media not all and (min-width: 992px) {
  .rb-pane .rb-pane-full {
    display: none;
  }
}

/* While dragging, every element answers with the resize cursor. Without it the
 * cursor flickers back to a text caret the moment the pointer crosses a label,
 * and a drag that passes over text selects it. */
.rb-resizing,
.rb-resizing * {
  cursor: col-resize !important;
  user-select: none !important;
}

/* ---- Source tiles ---- */

/* Named `rb-source-*` after the `source` radio group they drive, and not
 * `rb-tile-*`, which was already a 2rem icon chip forty lines up. The collision
 * cost an afternoon's worth of confusion in one render: the old rule's
 * `width: 2rem` won, every tile collapsed to a 32px strip, and nothing about
 * the new CSS looked wrong.
 */

/* Existing rooms are choices, not slots in a fixed matrix. Auto-fit keeps a
 * short list compact instead of painting empty cells to complete a row. */
.rb-source-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(13rem, 1fr));
  gap: 0.625rem;
}

.rb-source-tile {
  display: flex;
  flex-direction: row;
  align-items: center;
  gap: 0.75rem;
  min-height: 4.5rem;
  padding: 0.875rem 1rem;
  border: 1px solid var(--rb-hairline);
  border-radius: var(--rb-radius-row);
  background-color: var(--rb-canvas);
  color: var(--rb-ink-body);
  text-align: left;
  cursor: pointer;
  transition: background-color 0.15s ease, color 0.15s ease;
}

.rb-source-tile:hover { background-color: var(--rb-subtle); color: var(--rb-ink); }

/* Selected is a wash and an inset ring, not a filled accent: this is a choice
 * among nine peers, and a solid block of brand would be the loudest thing in a
 * dialog whose job is the name field above it. `:has` rather than a class the
 * controller toggles, so the state is right with no JS at all. */
.rb-source-tile:has(input:checked) {
  background-color: var(--rb-accent-wash);
  color: var(--rb-accent-text);
  box-shadow: inset 0 0 0 1px var(--rb-accent);
}

.rb-source-tile:has(input:focus-visible) { box-shadow: inset 0 0 0 2px var(--rb-accent); }

.rb-source-glyph { width: 22px; height: 22px; flex-shrink: 0; }

.rb-source-name {
  display: block;
  font-size: 0.8125rem;
  font-weight: 600;
  line-height: 1.25;
  text-wrap: balance;
}

.rb-source-copy { display: flex; min-width: 0; flex-direction: column; gap: 0.2rem; }
.rb-source-meta { color: var(--rb-ink-muted); font-size: 0.75rem; }

/* What the selected tile means, and the only place the audience summary is
 * said. Quiet on a wash so it reads as a consequence of the choice above it
 * rather than as another thing to choose. */
.rb-source-detail {
  border-radius: var(--rb-radius-row);
  background-color: var(--rb-subtle);
  padding: 0.875rem 1rem;
}

/* ---- Room creation studio ---- */

.rb-create-room-shell {
  --rb-template-preview-width: clamp(21rem, 29vw, 26rem);
  width: min(100%, 80rem);
  margin-inline: auto;
}

.rb-create-room-header {
  display: block;
}

.rb-create-room-intro {
  text-align: left;
}

.rb-create-private-note {
  display: inline-flex;
  align-items: center;
  gap: 0.625rem;
  max-width: 25rem;
  margin-top: 0.625rem;
  color: var(--rb-ink-body);
  font-size: 0.875rem;
}

.rb-room-create-form {
  display: grid;
  gap: 1rem;
}

.rb-create-stage {
  min-width: 0;
}

.rb-create-tabs-wrap {
  margin-bottom: 1rem;
}

.rb-create-tabs {
  display: inline-flex;
  max-width: 100%;
  gap: 0.25rem;
  overflow-x: auto;
  padding: 0.25rem;
  border-radius: var(--rb-radius-pill);
  background: var(--rb-subtle);
  scrollbar-width: none;
}

.rb-create-tabs::-webkit-scrollbar { display: none; }

.rb-create-tab {
  display: inline-flex;
  align-items: center;
  flex: 0 0 auto;
  gap: 0.5rem;
  border: 0;
  border-radius: var(--rb-radius-pill);
  padding: 0.55rem 0.8rem;
  background: transparent;
  color: var(--rb-ink-body);
  text-align: left;
  transition: background-color 0.15s ease, box-shadow 0.15s ease;
}

.rb-create-tab:hover { background: var(--rb-subtle); color: var(--rb-ink); }

.rb-create-tab.rb-tab-active {
  background: var(--rb-canvas);
  color: var(--rb-ink);
  box-shadow: 0 1px 3px var(--rb-shadow-soft);
}

.rb-create-tab-icon {
  display: inline-flex;
  color: var(--rb-accent-text);
}

.rb-create-tab-icon .rb-icon { width: 1rem; height: 1rem; }
.rb-create-tab-label { color: var(--rb-ink); font-size: 0.8125rem; font-weight: 650; white-space: nowrap; }

.rb-create-panel {
  grid-column: 1 / -1;
  min-width: 0;
}

.rb-template-workspace {
  display: grid;
  grid-template-columns: minmax(0, 1fr) var(--rb-template-preview-width);
  gap: 1rem;
  align-items: start;
}

.rb-template-catalog,
.rb-template-preview,
.rb-create-mode-card,
.rb-create-finalize {
  border: 1px solid var(--rb-hairline);
  border-radius: var(--rb-radius-card);
  background: var(--rb-canvas);
}

.rb-template-catalog {
  height: clamp(30rem, calc(100vh - 20rem), 46rem);
  padding: 1.25rem;
  overflow-y: auto;
  overscroll-behavior: contain;
}
.rb-template-catalog-heading { margin-bottom: 1.25rem; }
.rb-template-category + .rb-template-category { margin-top: 1.25rem; }

.rb-template-category-title {
  margin: 0 0 0.625rem;
  color: var(--rb-ink-muted);
  font-size: 0.75rem;
  font-weight: 700;
  letter-spacing: 0.09em;
  text-transform: uppercase;
}

.rb-template-grid {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: 0.625rem;
}

.rb-template-card {
  display: grid;
  grid-template-columns: auto minmax(0, 1fr);
  gap: 0.875rem;
  min-height: 7.75rem;
  padding: 0.875rem;
  border: 1px solid var(--rb-hairline);
  border-radius: var(--rb-radius-row);
  background: var(--rb-canvas);
  cursor: pointer;
  transition: border-color 0.15s ease, background-color 0.15s ease, box-shadow 0.15s ease;
}

.rb-template-card:hover {
  border-color: var(--rb-ink-faint);
  background: var(--rb-subtle);
}

.rb-template-card:has(input:checked) {
  border-color: var(--rb-accent);
  background: var(--rb-accent-wash);
  box-shadow: inset 0 0 0 1px var(--rb-accent);
}

.rb-template-card:has(input:focus-visible) { box-shadow: 0 0 0 2px var(--rb-accent-ring-strong); }

.rb-template-card-mark,
.rb-template-preview-mark,
.rb-create-mode-mark {
  display: grid;
  place-items: center;
  width: 2.5rem;
  height: 2.5rem;
  border-radius: 0.75rem;
  background: var(--rb-subtle);
  color: var(--rb-accent-text);
}

.rb-template-card-copy { display: flex; min-width: 0; flex-direction: column; gap: 0.4rem; }
.rb-template-card-name { color: var(--rb-ink); font-weight: 700; line-height: 1.25; }
.rb-template-card-summary { color: var(--rb-ink-body); font-size: 0.8125rem; line-height: 1.4; }
.rb-template-card-meta { margin-top: auto; color: var(--rb-ink-muted); font-size: 0.75rem; }

.rb-template-recommended,
.rb-template-cadence {
  display: inline-flex;
  align-items: center;
  width: fit-content;
  border-radius: var(--rb-radius-pill);
  background: var(--rb-accent-wash);
  color: var(--rb-accent-text);
  padding: 0.2rem 0.5rem;
  font-size: 0.6875rem;
  font-weight: 700;
}

.rb-template-preview-wrap {
  position: sticky;
  top: 1rem;
  width: auto;
  height: clamp(30rem, calc(100vh - 20rem), 46rem);
  min-height: 0;
}
.rb-template-preview {
  height: 100%;
  padding: 1.25rem;
  overflow-y: auto;
  overscroll-behavior: contain;
}
.rb-template-preview-heading { display: grid; grid-template-columns: auto minmax(0, 1fr); gap: 0.875rem; }

.rb-template-facts {
  display: flex;
  flex-wrap: wrap;
  gap: 0.5rem 1rem;
  margin-top: 1.25rem;
  padding-block: 0.875rem;
  border-block: 1px solid var(--rb-hairline);
  color: var(--rb-ink-muted);
  font-size: 0.8125rem;
}

.rb-template-audiences { padding-block: 1rem; }
.rb-template-tree { border-top: 1px solid var(--rb-hairline); }
.rb-template-section { border-bottom: 1px solid var(--rb-hairline); }

.rb-template-section > summary {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 1rem;
  padding-block: 0.8rem;
  cursor: pointer;
  list-style: none;
}

.rb-template-section > summary::-webkit-details-marker { display: none; }
.rb-template-section-name { color: var(--rb-ink); font-size: 0.875rem; font-weight: 650; }
.rb-template-section-count { color: var(--rb-ink-muted); font-size: 0.75rem; white-space: nowrap; }

.rb-template-document-list {
  display: grid;
  gap: 0.35rem;
  margin: 0 0 0.875rem;
  padding-left: 1.15rem;
  color: var(--rb-ink-body);
  font-size: 0.8125rem;
}

.rb-template-subsection { margin-top: 0.25rem; }
.rb-template-subsection ul { display: grid; gap: 0.3rem; margin-top: 0.35rem; padding-left: 1rem; }

.rb-template-private {
  margin-top: 1rem !important;
  padding: 0.75rem;
  border-radius: var(--rb-radius-row);
  background: var(--rb-subtle);
  color: var(--rb-ink-muted);
  font-size: 0.8125rem;
}

.rb-create-mode-card {
  width: min(100%, 54rem);
  margin-inline: auto;
  padding: 1.5rem;
}

.rb-create-mode-heading { display: flex; align-items: center; gap: 1rem; }
.rb-create-mode-mark { flex: 0 0 auto; }

.rb-create-finalize {
  display: grid;
  grid-template-columns: minmax(0, 1fr) auto;
  gap: 1.5rem;
  align-items: center;
  width: min(100%, 54rem);
  margin-inline: auto;
  padding: 1rem;
}

.rb-create-finalize-actions {
  display: flex;
  align-items: center;
  gap: 0.5rem;
}

@media (max-width: 1199.98px) {
  .rb-template-workspace { grid-template-columns: 1fr; }
  .rb-template-catalog { height: clamp(28rem, calc(100vh - 14rem), 36rem); }
  .rb-template-preview-wrap { position: static; width: auto; height: clamp(26rem, calc(100vh - 14rem), 32rem); }
}

@media (max-width: 639.98px) {
  .rb-create-tabs { display: flex; width: 100%; border-radius: var(--rb-radius-card); }
  .rb-create-tab { flex: 1 1 0; justify-content: center; padding-inline: 0.6rem; }
  .rb-create-tab-icon { display: none; }
  .rb-template-grid { grid-template-columns: 1fr; }
  .rb-template-card { min-height: auto; }
  .rb-template-card-summary { display: none; }
  .rb-template-card-mark { width: 2.25rem; height: 2.25rem; }
  .rb-template-catalog { height: auto; overflow-y: visible; }
  .rb-template-preview-wrap { height: auto; }
  .rb-template-preview { height: auto; padding: 1rem; overflow: visible; }
  .rb-template-tree, .rb-template-private { display: none; }
  .rb-create-mode-card { padding: 1.25rem; }
  .rb-create-finalize { grid-template-columns: 1fr; }
  .rb-create-finalize-actions > * { flex: 1; text-align: center; }
}

/* Links inside a PDF, positioned over the canvas.
 *
 * The canvas has already drawn the blue text the author styled, so these add no
 * colour of their own: they are hit areas. The faint wash on hover is the only
 * thing that says a rectangle is there at all, which is what stops a reader
 * hunting for the one part of a slide that is clickable. */
.rb-pdf-links {
  position: absolute;
  inset: 0;
  pointer-events: none;
}

.rb-pdf-link {
  position: absolute;
  pointer-events: auto;
  border-radius: 0.125rem;
  background: transparent;
  transition: background-color 120ms ease;
}

.rb-pdf-link:hover,
.rb-pdf-link:focus-visible {
  background: var(--rb-accent-ring);
  outline: 2px solid var(--rb-accent);
  outline-offset: 1px;
}

/* The frame is the positioning context for the layer above. It is a plain block
 * today, so without this the anchors would place themselves against the page. */
.rb-pdf-page { position: relative; }

/* A video in the room. Bounded by the reading column rather than the window: a
   founder intro blown up to 2000px is a face nobody wants that close. */
.rb-viewer-video {
  inline-size: 100%;
  max-block-size: 70vh;
  border-radius: 0.5rem;
  background: #000;
  display: block;
}

/* The same black behind a video embedded in a page, so one narrower than the
   column letterboxes rather than floating on the page's tint. */
.document-media-player {
  display: block;
  width: 100%;
  max-height: min(68vh, 52rem);
  background: #000;
}
