/*
 * MakeHybrid design system.
 *
 * A superset of the original styles.css class vocabulary: every selector that
 * file defined is defined here too, with the same meaning, so a page can move
 * between the two stylesheets by changing one <link> and nothing else.
 * docs.html deliberately still points at styles.css -- the Unlimited API
 * reference belongs to the other track and is not changed from here.
 *
 * Layers, in order: tokens -> base -> layout -> components -> docs -> utility.
 */

/* ---------------------------------------------------------------- tokens -- */

:root {
  color-scheme: light;

  /* Brand. --blue is kept as the historical name of the accent. */
  --blue: #2563eb;
  /* The foreground for anything sitting ON --blue. A hard-coded #fff was
     fine in light mode and 2.43:1 in dark, where --blue is a pale tint. */
  --on-accent: #fff;
  --blue-dark: #1d4ed8;
  --blue-soft: #eff4ff;
  --blue-line: #c7d9fb;

  --bg: #f7f8fa;
  --bg-sunken: #eef1f5;
  --card: #ffffff;
  --border: #e4e7ec;
  --border-strong: #d2d7df;

  --text: #121417;
  --text-soft: #40464f;
  --muted: #666d78;
  --faint: #6b7280;

  --danger: #d6342c;
  --danger-soft: #fdeceb;
  --ok: #17824a;
  --ok-soft: #e7f5ec;
  --warn: #9a6400;
  --warn-soft: #fdf3e0;
  --violet: #6d3fd4;
  --violet-soft: #f1ecfd;

  /*
   * The brand mark, [@], as two mask shapes: the brackets and the @. Not colours: each is painted by
   * .brand-mark with a colour that is decided there, which is the whole
   * reason the mark is two layers instead of one picture.
   *
   * Defined once, in :root, so the dark block below inherits them -- the
   * shape does not change between themes, only what paints it.
   */
  --brand-mark-ink: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 32 32'%3E%3Cpath d='M6.6 3.2H2.2v25.6h4.4M25.4 3.2h4.4v25.6h-4.4' fill='none' stroke='%23000' stroke-width='4' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E");
  --brand-mark-accent: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 32 32'%3E%3Cpath d='M18.88 13.12v3.74a1.92 1.92 0 0 0 3.84 0v-.86a6.72 6.72 0 1 0-2.74 5.4M18.88 16a2.88 2.88 0 1 1-5.76 0a2.88 2.88 0 1 1 5.76 0' fill='none' stroke='%23000' stroke-width='2.6' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E");

  /* Code surfaces stay dark in both themes -- a terminal reads as a terminal. */
  --code-bg: #10141b;
  --code-fg: #d6dde8;
  --code-dim: #7d8899;

  --radius-sm: 6px;
  --radius: 10px;
  --radius-lg: 14px;

  --shadow-sm: 0 1px 2px rgba(16, 20, 27, .05);
  --shadow: 0 1px 3px rgba(16, 20, 27, .07), 0 8px 24px -12px rgba(16, 20, 27, .14);
  --shadow-lg: 0 2px 6px rgba(16, 20, 27, .07), 0 24px 56px -24px rgba(16, 20, 27, .26);

  --font: ui-sans-serif, system-ui, -apple-system, "Segoe UI", Roboto,
          "Helvetica Neue", Arial, sans-serif;
  /*
   * Headings read this instead, so a white label can give them a face of
   * their own without touching body copy. Ours are the same font: the
   * token exists for the brands, and costs nothing when nobody sets it.
   */
  --font-heading: var(--font);
  --mono: ui-monospace, SFMono-Regular, "SF Mono", Menlo, Consolas,
          "Liberation Mono", monospace;

  --header-h: 58px;
  --page-max: 1120px;
  --sidebar-w: 268px;
}

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

    --blue: #7aa5ff;
    --on-accent: #0b1220;
    --blue-dark: #9bbcff;
    --blue-soft: #16203a;
    --blue-line: #2b3c63;

    --bg: #0d1016;
    --bg-sunken: #0a0d12;
    --card: #141922;
    --border: #242c38;
    --border-strong: #333d4d;

    --text: #eef1f6;
    --text-soft: #c2c9d4;
    --muted: #949cab;
    --faint: #99a1b0;

    --danger: #ff8b84;
    --danger-soft: #2d1817;
    --ok: #68d79a;
    --ok-soft: #10251a;
    --warn: #e9b65c;
    --warn-soft: #2a2113;
    --violet: #b89bf5;
    --violet-soft: #1e1733;

    --code-bg: #090c11;
    --code-fg: #d6dde8;

    --shadow-sm: 0 1px 2px rgba(0, 0, 0, .4);
    --shadow: 0 1px 3px rgba(0, 0, 0, .5), 0 8px 24px -12px rgba(0, 0, 0, .6);
    --shadow-lg: 0 2px 6px rgba(0, 0, 0, .5), 0 24px 56px -24px rgba(0, 0, 0, .8);
  }
}

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

  --blue: #7aa5ff;
  --on-accent: #0b1220;
  --blue-dark: #9bbcff;
  --blue-soft: #16203a;
  --blue-line: #2b3c63;

  --bg: #0d1016;
  --bg-sunken: #0a0d12;
  --card: #141922;
  --border: #242c38;
  --border-strong: #333d4d;

  --text: #eef1f6;
  --text-soft: #c2c9d4;
  --muted: #949cab;
  --faint: #99a1b0;

  --danger: #ff8b84;
  --danger-soft: #2d1817;
  --ok: #68d79a;
  --ok-soft: #10251a;
  --warn: #e9b65c;
  --warn-soft: #2a2113;
  --violet: #b89bf5;
  --violet-soft: #1e1733;

  --code-bg: #090c11;
  --code-fg: #d6dde8;

  --shadow-sm: 0 1px 2px rgba(0, 0, 0, .4);
  --shadow: 0 1px 3px rgba(0, 0, 0, .5), 0 8px 24px -12px rgba(0, 0, 0, .6);
  --shadow-lg: 0 2px 6px rgba(0, 0, 0, .5), 0 24px 56px -24px rgba(0, 0, 0, .8);
}

/* ------------------------------------------------------------------ base -- */

* { box-sizing: border-box; }

html {
  -webkit-text-size-adjust: 100%;
  /*
   * A link to a heading on the page it is already on should travel, not
   * teleport. A jump leaves the reader to work out whether the page
   * changed or merely moved; a scroll shows them the distance and they
   * arrive already knowing where they are.
   *
   * Anchors on this site are mostly in a sticky-header layout, so the
   * scroll-margin-top rules further down are the other half of this --
   * without them smooth scrolling just glides the target neatly under the
   * header. Both halves, or neither.
   *
   * Overridden by the prefers-reduced-motion block at the end of this
   * file: its universal selector covers <html> and carries !important, so
   * a reader who has asked their system for less movement still gets the
   * instant jump. Motion is the default here, not the requirement.
   */
  scroll-behavior: smooth;
}

body {
  margin: 0;
  font: 400 15px/1.6 var(--font);
  color: var(--text);
  background: var(--bg);
  -webkit-font-smoothing: antialiased;
  text-rendering: optimizeLegibility;
}

a { color: var(--blue); text-decoration: none; }
a:hover { text-decoration: underline; }

h1, h2, h3, h4 {
  font-family: var(--font-heading);
  color: var(--text);
  letter-spacing: -.015em;
  line-height: 1.25;
  font-weight: 650;
}

h1 { font-size: 30px; margin: 0 0 10px; }
h2 { font-size: 20px; margin: 36px 0 12px; }
h3 { font-size: 15px; margin: 0 0 12px; }

p { margin: 0 0 12px; }
p.lead { color: var(--muted); font-size: 16px; margin-bottom: 26px; }

hr { border: 0; border-top: 1px solid var(--border); margin: 28px 0; }

/*
 * One rule for every image on every page.
 *
 * There was none, which is fine while a stylesheet has no images in it and
 * stops being fine the moment one does: an <img> renders at its intrinsic
 * width, so a 320px portrait inside a card at a 320px viewport overflows
 * its own column and gives the whole page horizontal scroll.
 */
img { max-width: 100%; height: auto; }

::selection { background: var(--blue-soft); color: var(--text); }

:where(a, button, input, select, textarea, summary, [tabindex]):focus-visible {
  outline: 2px solid var(--blue);
  outline-offset: 2px;
  border-radius: var(--radius-sm);
}

.skip-link {
  position: absolute;
  left: -9999px;
  top: 8px;
  z-index: 100;
  background: var(--card);
  color: var(--text);
  border: 1px solid var(--border-strong);
  border-radius: var(--radius-sm);
  padding: 8px 14px;
  font-weight: 600;
}
.skip-link:focus { left: 8px; }

/* ---------------------------------------------------------------- layout -- */

header {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 14px;
  height: var(--header-h);
  padding: 0 22px;
  background: color-mix(in srgb, var(--card) 88%, transparent);
  backdrop-filter: saturate(180%) blur(12px);
  border-bottom: 1px solid var(--border);
  position: sticky;
  top: 0;
  z-index: 40;
}

header .brand {
  display: flex;
  align-items: center;
  /* Tight: the mark's brackets reach the edges of its box, so the box's
     own edge is the mark's edge and this is the whole distance to the name. */
  gap: 7px;
  font-weight: 700;
  font-size: 15.5px;
  letter-spacing: -.02em;
  color: var(--text);
}
header .brand a { color: inherit; text-decoration: none; }
header .brand:hover { text-decoration: none; }

/*
 * On the home page the name goes nowhere -- it already points here -- so it
 * is the way back to the OTHER version of the page (home.js). Drawn as a
 * control rather than as a link that does nothing: the name dotted-underlined,
 * and a swap mark after it.
 *
 * Both appear only once `data-home` names a version. While the first-run
 * chooser is still up the name must look like an ordinary name, because
 * pressing it then would answer a question the reader has not been asked.
 */
.brand-home { display: inline-flex; align-items: center; gap: 7px; }
.brand-swap { display: none; }
html[data-home="simple"] .brand-swap,
html[data-home="detailed"] .brand-swap {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 22px;
  height: 22px;
  flex: none;
  border-radius: 999px;
  background: var(--blue-soft);
  color: var(--blue);
}
.brand-swap svg { width: 13px; height: 13px; }
html[data-home="simple"] .brand-home .brand-name,
html[data-home="detailed"] .brand-home .brand-name {
  text-decoration: underline;
  text-decoration-style: dotted;
  text-decoration-thickness: 2px;
  text-decoration-color: var(--blue-line);
  text-underline-offset: 4px;
}
html[data-home="simple"] .brand-home:hover .brand-name,
html[data-home="detailed"] .brand-home:hover .brand-name,
html[data-home="simple"] .brand-home:focus-visible .brand-name,
html[data-home="detailed"] .brand-home:focus-visible .brand-name { text-decoration-color: var(--blue); }
/* The name is the one thing in the header that may give way; the mark is not. */
.brand-home .brand-name { min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }

/*
 * The brand mark: two brackets holding one opening between them.
 *
 * It was a lone capital M, white, on a violet gradient. Make.com is an
 * automation and integration platform with a strongly violet identity
 * selling into developer and automation tooling, which overlaps this; a
 * violet rounded square with an M in it sat about as close to their mark
 * as a mark can without meaning to. A logo cannot cure a name conflict --
 * that is not what this is for -- but it can stop the identity adding to
 * one. Six directions were drawn side by side and this is the one that was
 * picked; the page they were drawn on is gone, and is in git if it is ever
 * wanted back.
 *
 * Drawn here rather than in markup so the mark lives in exactly one file:
 * changing it is this rule, not fourteen pages.
 *
 * Two mask layers rather than one picture, and that is the load-bearing
 * part. The left bracket is painted in `currentColor`, so it follows the
 * text beside it and needs no dark-mode variant at all; the right is
 * painted in --blue, so a white-labelled brand's own accent reaches the
 * mark exactly the way it already reaches every button on the page. A
 * baked-in two-colour image would lose both.
 *
 * The element itself paints nothing; see the @supports block below for
 * why that matters.
 */
.brand-mark {
  width: 22px;
  height: 22px;
  flex: none;
  position: relative;
}

/*
 * BOTH brackets are pseudo-elements, and that is not a style choice.
 *
 * The first version masked the element itself for one bracket and used
 * ::after for the other. A mask applies to an element's whole subtree,
 * pseudo-elements included -- so the second bracket was being clipped by
 * the first bracket's mask, and since the two shapes do not overlap, it
 * was clipped to nothing. The header rendered a lone `[`, which is a
 * perfectly valid stylesheet and the wrong mark. Nothing but looking at it
 * would have caught that.
 *
 * Behind @supports because the fallback for a mask nobody honours is two
 * solid 22px blocks -- a black square in the header, which is worse than
 * no mark at all. Without mask support there is simply no mark.
 */
@supports ((-webkit-mask-image: linear-gradient(#000, #000)) or (mask-image: linear-gradient(#000, #000))) {
  .brand-mark::before,
  .brand-mark::after {
    content: "";
    position: absolute;
    inset: 0;
    -webkit-mask-size: contain;
            mask-size: contain;
    -webkit-mask-position: center;
            mask-position: center;
    -webkit-mask-repeat: no-repeat;
            mask-repeat: no-repeat;
  }
  .brand-mark::before {
    background-color: currentColor;
    -webkit-mask-image: var(--brand-mark-ink);
            mask-image: var(--brand-mark-ink);
  }
  .brand-mark::after {
    background-color: var(--blue);
    -webkit-mask-image: var(--brand-mark-accent);
            mask-image: var(--brand-mark-accent);
  }
}

/*
 * A white-labelled brand with a name but no logo of its own falls back to
 * its own initial, which brand.js writes in. The mask has to come off for
 * that: the brackets are pseudo-elements, and a letter sitting under two
 * opaque layers is a letter nobody sees.
 */
.brand-mark.is-initial {
  background: var(--blue);
  border-radius: 6px;
  display: grid;
  place-items: center;
  color: var(--on-accent);
  font-size: 11px;
  font-weight: 800;
  letter-spacing: -.04em;
}
.brand-mark.is-initial::before,
.brand-mark.is-initial::after { content: none; }

header nav {
  display: flex;
  align-items: center;
  gap: 4px;
  flex-wrap: wrap;
}
header nav a {
  margin-left: 0;
  font-size: 13.5px;
  color: var(--text-soft);
  padding: 6px 10px;
  border-radius: var(--radius-sm);
  white-space: nowrap;
}
header nav a:hover { background: var(--bg-sunken); color: var(--text); text-decoration: none; }
header nav a[aria-current="page"] { color: var(--blue); background: var(--blue-soft); }
header nav .muted { padding: 0 6px; }
/*
 * The public site's menu: the products, the guides and the price beside
 * the name, then the sign-in at the right edge, ahead of the language and
 * theme controls. The nav takes the row's spare width and `.nav-end` --
 * the sign-in, or the first of the member's own links that nav-session.js
 * puts in its place -- spends it.
 */
header nav.site-nav { flex: 1 1 auto; }
header nav.site-nav .nav-end { margin-inline-start: auto; }

/*
 * The header is sticky, so anything an in-page link targets has to reserve
 * its height -- otherwise "Plans" scrolls the top of the first card
 * underneath it. The docs page sets this per-element; this covers the
 * marketing pages.
 */
:where(main, .home-stack) > section[id],
:where(main, .home-stack) > [id]:not(:first-child) {
  scroll-margin-top: calc(var(--header-h) + 16px);
}

main {
  max-width: 760px;
  margin: 0 auto;
  padding: 44px 20px 64px;
}
main.wide { max-width: var(--page-max); }
main.narrow { max-width: 440px; }

footer {
  text-align: center;
  color: var(--muted);
  font-size: 12.5px;
  padding: 28px 20px 40px;
  border-top: 1px solid var(--border);
}
footer a { color: var(--muted); }
footer a:hover { color: var(--text); }

/* ------------------------------------------------------------ components -- */

.card {
  background: var(--card);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  padding: 20px;
  margin-bottom: 18px;
  box-shadow: var(--shadow-sm);
}
.card h3 { margin: 0 0 12px; font-size: 15px; }
/*
 * Trim the trailing gap under a card's last block -- but a <dialog> is not
 * one of its blocks.
 *
 * This rule is more specific than `.modal`, so on a card whose last child
 * is the verification dialog it was setting `margin-bottom: 0` on a box
 * that centres itself with `margin: auto`. Auto margins split the free
 * space between them; with the bottom one pinned to zero, all of it went to
 * the top and the dialog sat flush against the bottom of the screen. It
 * looked like a positioning bug in the modal and was a text rule reaching
 * into the top layer.
 */
.card > :last-child:not(dialog) { margin-bottom: 0; }

.card-tight { padding: 14px 16px; }

form { display: flex; flex-direction: column; gap: 12px; max-width: 380px; }
form.wide-form { max-width: none; }

label {
  font-size: 12.5px;
  color: var(--text-soft);
  font-weight: 600;
  display: block;
  margin-bottom: -4px;
}

input, select, textarea {
  padding: 10px 12px;
  border: 1px solid var(--border-strong);
  border-radius: var(--radius-sm);
  font: inherit;
  font-size: 14.5px;
  width: 100%;
  background: var(--card);
  color: var(--text);
  transition: border-color .12s, box-shadow .12s;
}
input::placeholder, textarea::placeholder { color: var(--faint); }
input:focus, select:focus, textarea:focus {
  outline: none;
  border-color: var(--blue);
  box-shadow: 0 0 0 3px color-mix(in srgb, var(--blue) 18%, transparent);
}
input:disabled, select:disabled, textarea:disabled { opacity: .6; cursor: not-allowed; }
textarea { resize: vertical; min-height: 84px; font-family: var(--mono); font-size: 13px; }

button {
  padding: 10px 16px;
  border: 1px solid transparent;
  border-radius: var(--radius-sm);
  background: var(--blue);
  color: var(--on-accent);
  font: inherit;
  font-weight: 600;
  font-size: 14px;
  cursor: pointer;
  transition: background .12s, border-color .12s, opacity .12s;
  line-height: 1.3;
}
button:hover { background: var(--blue-dark); }
button:disabled { opacity: .5; cursor: default; }
button.secondary {
  background: var(--card);
  color: var(--text);
  border-color: var(--border-strong);
}
button.secondary:hover { background: var(--bg-sunken); }
button.ghost { background: transparent; color: var(--blue); border-color: transparent; }
button.ghost:hover { background: var(--blue-soft); }
button.danger { background: var(--danger-soft); color: var(--danger); border-color: transparent; }
button.danger:hover { background: var(--danger-soft); filter: brightness(.96); }
button.small { padding: 6px 11px; font-size: 12.5px; width: auto; }
button.block { width: 100%; }

:root[data-theme="dark"] button.secondary,
:root[data-theme="dark"] button.danger { color: var(--text); }

/*
 * `.btn` exists so a navigation control can be an <a> and still look like a
 * button. Wrapping a <button> in an <a> is invalid HTML -- interactive
 * content inside a link -- and gives screen readers two nested controls and
 * the keyboard two tab stops for one action.
 */
a.btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  padding: 10px 16px;
  border: 1px solid transparent;
  border-radius: var(--radius-sm);
  background: var(--blue);
  color: var(--on-accent);
  font: inherit;
  font-weight: 600;
  font-size: 14px;
  line-height: 1.3;
  cursor: pointer;
  text-decoration: none;
  transition: background .12s, border-color .12s;
}
a.btn:hover { background: var(--blue-dark); text-decoration: none; }
a.btn.secondary {
  background: var(--card);
  color: var(--text);
  border-color: var(--border-strong);
}
a.btn.secondary:hover { background: var(--bg-sunken); }
a.btn.ghost { background: transparent; color: var(--blue); border-color: transparent; }
a.btn.ghost:hover { background: var(--blue-soft); }
a.btn.small { padding: 6px 11px; font-size: 12.5px; }
a.btn.block { width: 100%; }
.pricing a.btn { width: 100%; }

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

.error { color: var(--danger); font-size: 13px; min-height: 18px; }
/* Out of the flow entirely, not merely zero-height: an empty slot was still
   taking the form's 12px gap and a paragraph margin above every submit
   button. role="alert" still announces, because writing text re-enters it. */
.error:empty { display: none; }
.ok-text { color: var(--ok); font-size: 13px; }
.muted { color: var(--muted); font-size: 13.5px; }
.small-text { font-size: 12.5px; }

.row {
  /* A long account name used to push Remove past the right edge with nothing
     able to wrap. */
  flex-wrap: wrap;
  display: flex;
  justify-content: space-between;
  align-items: center;
  padding: 11px 0;
  border-bottom: 1px solid var(--border);
  gap: 12px;
}
.row:last-child { border-bottom: none; }
.row:first-child { padding-top: 0; }
/* The id a member passes as account_id, under each connected account. */
.account-id { display: flex; flex-wrap: wrap; align-items: center; gap: 6px; margin-top: 4px; }

.dot {
  width: 8px; height: 8px; border-radius: 50%;
  display: inline-block; margin-right: 7px; flex-shrink: 0;
  background: var(--faint);
}
.dot.healthy, .dot.ok { background: var(--ok); }
.dot.needs_reconnect, .dot.checkpoint, .dot.credentials { background: var(--warn); }
.dot.halted, .dot.banned, .dot.error { background: var(--danger); }
.dot.paused, .dot.stopped { background: var(--faint); }

code, pre, .mono { font-family: var(--mono); font-size: 13px; }
/*
 * A paragraph of them is usually one long address, or several. Break it
 * anywhere rather than let it push the page sideways on a phone --
 * `anywhere` and not `break-word` because it is the paragraph's
 * min-content width that a grid column is sized from. `pre` keeps its
 * own horizontal scroll; code is read line by line.
 */
p.mono { overflow-wrap: anywhere; }

pre {
  background: var(--code-bg);
  color: var(--code-fg);
  padding: 15px 16px;
  border-radius: var(--radius-sm);
  overflow-x: auto;
  line-height: 1.6;
  margin: 0 0 14px;
  border: 1px solid rgba(255, 255, 255, .06);
  tab-size: 2;
}
pre:last-child { margin-bottom: 0; }

code.inline {
  background: var(--bg-sunken);
  border: 1px solid var(--border);
  padding: 1px 5px;
  border-radius: 4px;
  font-size: 12.5px;
  /*
   * `nowrap` was right for what this class was made for -- `/v1`,
   * `X-API-KEY`, a path -- and wrong for the one place it carries a whole
   * JSON error body, which at 320px hung 67px past the edge of the screen
   * and gave the landing page horizontal scroll.
   *
   * `pre-wrap` keeps the spaces inside a chip and `anywhere` breaks a token
   * only when it would otherwise overflow, so every short chip still sits
   * on one line exactly as before.
   */
  white-space: pre-wrap;
  overflow-wrap: anywhere;
  color: var(--text-soft);
}

/* Code block with a copy affordance. */
.code-wrap { position: relative; margin: 0 0 14px; }
.code-wrap pre { margin: 0; }
.copy-btn {
  position: absolute;
  top: 8px;
  right: 8px;
  padding: 4px 9px;
  font-size: 11.5px;
  font-weight: 600;
  border-radius: 5px;
  background: rgba(255, 255, 255, .09);
  color: var(--code-fg);
  border: 1px solid rgba(255, 255, 255, .14);
  opacity: 0;
  transition: opacity .12s, background .12s;
  cursor: pointer;
}
.code-wrap:hover .copy-btn,
.copy-btn:focus-visible { opacity: 1; }
.copy-btn:hover { background: rgba(255, 255, 255, .17); }
.copy-btn.copied { color: #8fe0ae; }
@media (hover: none) { .copy-btn { opacity: 1; } }

.keyval {
  display: flex;
  align-items: center;
  gap: 8px;
  background: var(--blue-soft);
  border: 1px solid var(--blue-line);
  border-radius: var(--radius-sm);
  padding: 9px 11px;
  word-break: break-all;
}

.badge {
  display: inline-block;
  /*
   * .option-card and .plan-card are flex columns, which BLOCKIFIES an
   * inline-block child and stretches it across the cross axis. The pill
   * became a full-width bar with its label stranded at the left -- the
   * loudest thing wrong with the connect and pricing cards. align-self is
   * inert outside a flex or grid parent, so every inline use is untouched.
   */
  align-self: start;
  font-size: 11px;
  font-weight: 600;
  padding: 3px 8px;
  border-radius: 999px;
  background: var(--blue-soft);
  color: var(--blue);
  border: 1px solid var(--blue-line);
  letter-spacing: .01em;
  vertical-align: middle;
}
.badge.neutral { background: var(--bg-sunken); color: var(--muted); border-color: var(--border); }
.badge.ok { background: var(--ok-soft); color: var(--ok); border-color: transparent; }
.badge.warn { background: var(--warn-soft); color: var(--warn); border-color: transparent; }
.badge.danger { background: var(--danger-soft); color: var(--danger); border-color: transparent; }
.badge.violet { background: var(--violet-soft); color: var(--violet); border-color: transparent; }

.kbd {
  font-family: var(--mono);
  font-size: 11px;
  padding: 2px 6px;
  border-radius: 5px;
  border: 1px solid var(--border-strong);
  border-bottom-width: 2px;
  background: var(--card);
  color: var(--muted);
}

.hidden { display: none !important; }
.visually-hidden {
  position: absolute; width: 1px; height: 1px;
  padding: 0; margin: -1px; overflow: hidden;
  clip: rect(0 0 0 0); white-space: nowrap; border: 0;
}

table { width: 100%; border-collapse: collapse; font-size: 13.5px; }
table th, table td {
  text-align: left;
  padding: 9px 10px;
  border-bottom: 1px solid var(--border);
  vertical-align: top;
}
table th {
  color: var(--muted);
  font-weight: 600;
  font-size: 11px;
  text-transform: uppercase;
  letter-spacing: .05em;
}
table tr:last-child td { border-bottom: none; }
.table-scroll { overflow-x: auto; }

.method {
  font-weight: 700;
  font-size: 10.5px;
  padding: 3px 7px;
  border-radius: 5px;
  margin-right: 8px;
  letter-spacing: .04em;
  font-family: var(--mono);
  display: inline-block;
  vertical-align: middle;
  text-transform: uppercase;
}
.method.get { background: var(--ok-soft); color: var(--ok); }
.method.post { background: var(--blue-soft); color: var(--blue); }
.method.patch, .method.put { background: var(--warn-soft); color: var(--warn); }
.method.delete { background: var(--danger-soft); color: var(--danger); }

.endpoint {
  margin-bottom: 24px;
  scroll-margin-top: calc(var(--header-h) + 16px);
}
.endpoint .path {
  font-size: 14px;
  font-weight: 600;
  font-family: var(--mono);
  display: flex;
  align-items: center;
  gap: 2px;
  flex-wrap: wrap;
  word-break: break-all;
}

/* Callouts. */
.callout {
  border: 1px solid var(--border);
  border-left: 3px solid var(--muted);
  background: var(--bg-sunken);
  border-radius: var(--radius-sm);
  padding: 12px 14px;
  margin: 0 0 14px;
  font-size: 13.5px;
  color: var(--text-soft);
}
.callout > :last-child { margin-bottom: 0; }
.callout b, .callout strong { color: var(--text); }
.callout.info { border-left-color: var(--blue); background: var(--blue-soft); }
.callout.warn { border-left-color: var(--warn); background: var(--warn-soft); }
.callout.danger { border-left-color: var(--danger); background: var(--danger-soft); }
.callout.ok { border-left-color: var(--ok); background: var(--ok-soft); }

/* Numbered steps. */
.steps { list-style: none; counter-reset: step; margin: 0; padding: 0; }
.steps > li {
  counter-increment: step;
  position: relative;
  padding: 0 0 20px 38px;
  border-left: 1px solid var(--border);
  margin-left: 13px;
}
.steps > li:last-child { border-left-color: transparent; padding-bottom: 0; }
.steps > li::before {
  content: counter(step);
  position: absolute;
  left: -13px;
  top: -2px;
  width: 26px;
  height: 26px;
  border-radius: 50%;
  background: var(--card);
  border: 1px solid var(--border-strong);
  color: var(--text-soft);
  font-size: 12px;
  font-weight: 700;
  display: grid;
  place-items: center;
}
.steps > li > b:first-child,
.steps > li > strong:first-child { display: block; margin-bottom: 3px; }

/*
 * A short numbered how-to inside a card.
 *
 * Deliberately not .steps, which draws a numbered disc and a rule down the
 * side: right for the three stages of getting an account connected, far too
 * loud for "click this, then that". This is a plain ordered list with its
 * indent pulled in to the card's own rhythm.
 */
.howto {
  margin: 0 0 12px;
  padding-left: 20px;
}
.howto > li { margin-bottom: 5px; }
.howto > li:last-child { margin-bottom: 0; }

/* Segmented control / tabs. */
.seg {
  display: inline-flex;
  padding: 3px;
  gap: 3px;
  background: var(--bg-sunken);
  border: 1px solid var(--border);
  border-radius: 8px;
}
.seg button, .seg .tab {
  background: transparent;
  color: var(--muted);
  border: none;
  padding: 6px 13px;
  font-size: 13px;
  font-weight: 600;
  border-radius: 6px;
  cursor: pointer;
}
.seg button:hover, .seg .tab:hover { color: var(--text); background: transparent; }
.seg button[aria-selected="true"], .seg .tab[aria-selected="true"], .seg button[aria-pressed="true"] {
  background: var(--card);
  color: var(--text);
  box-shadow: var(--shadow-sm);
}

/* Choice cards -- two ways to do the same thing. */
.option-grid {
  display: grid;
  /* min() lets the single-column case collapse to the container instead of
     overflowing it: at 360px the card's content box is ~234px, narrower than
     a bare 260px floor, and the cards broke out through its rounded edge. */
  grid-template-columns: repeat(auto-fit, minmax(min(260px, 100%), 1fr));
  gap: 16px;
}
/*
 * A grid's gap sits between its own children and leaves nothing after
 * itself, and `.card` carries only a bottom margin -- so a card following
 * a grid sat flush against it, which is what the account page looked like.
 * Today only that page has the adjacency; this is written for any element
 * so the next one to appear does not reintroduce it.
 */
.option-grid + * { margin-top: 18px; }
/*
 * Three cards whose third sums up the two before it: one column on a
 * phone, three on a wide screen, and in between two -- with the third
 * as wide as both above it rather than alone in a half.
 */
.option-grid.trio { grid-template-columns: minmax(0, 1fr); }
@media (min-width: 560px) {
  .option-grid.trio { grid-template-columns: repeat(2, minmax(0, 1fr)); }
  .option-grid.trio > :nth-child(3) { grid-column: 1 / -1; }
}
@media (min-width: 900px) {
  .option-grid.trio { grid-template-columns: repeat(3, minmax(0, 1fr)); }
  .option-grid.trio > :nth-child(3) { grid-column: auto; }
}
/*
 * Two across, never three.
 *
 * `auto-fit` is right for cards that hold a sentence each and wrong for
 * cards that hold a list of ten article titles: at this container's width
 * it fits three and leaves the fourth alone on a row of its own, with
 * every title in all four wrapping twice to make it fit. The general index
 * is the one grid here whose cards have real content in them.
 */
@media (min-width: 760px) {
  .option-grid.pairs { grid-template-columns: 1fr 1fr; }
}

.option-card {
  background: var(--card);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  padding: 18px;
  display: flex;
  flex-direction: column;
  box-shadow: var(--shadow-sm);
}
.option-card.recommended { border-color: var(--blue); box-shadow: 0 0 0 1px var(--blue), var(--shadow-sm); }
.option-card :is(h3, h4) { margin: 8px 0 6px; font-size: 15.5px; }
/*
 * The body no longer grows. It used to carry `flex: 1`, with the form a
 * sibling AFTER it, so on the shorter of the two cards the form was shoved
 * to the bottom and the middle of the card was a chasm. Content now runs
 * top-down and the leftover height falls at the BOTTOM of the card, where it
 * reads as padding rather than as something missing.
 */
.option-card .option-body { flex: 0 0 auto; }
/* `form` caps at 380px for the 408px auth card. Inside a wider card that
   left every input, the textarea and the full-width submit ending 67px short
   of the copy above them. */
.option-card form,
.modal form:not(.modal-dismiss) { max-width: none; }
.option-card form { margin-top: 14px; }
.option-card .option-cta { margin-top: 14px; }
/* The two routes are a comparison, so their headings and first lines must sit
   on the same baseline regardless of how much copy each one carries. */
.option-grid { align-items: start; }

/* ----------------------------------------------------------------- modal -- */

/*
 * A challenge is a hard stop: nothing else on the connect card can be done
 * until it is answered. It gets a modal rather than a notice further down a
 * long page, which is read as commentary on a page that still looks usable.
 */
.modal {
  /*
   * A modal dialog is centred by the user agent through `margin: auto` on an
   * `inset: 0` box -- but only while nothing else claims those properties.
   * Declared here so the dialog's position does not depend on which other
   * rules happen to match it, which is how it ended up sitting low and left.
   */
  position: fixed;
  inset: 0;
  margin: auto;
  /* Taller than the viewport is a dead end otherwise: no scroll, no buttons. */
  max-height: calc(100dvh - 32px);
  overflow-y: auto;
  width: min(460px, calc(100vw - 32px));
  height: fit-content;
  padding: 22px;
  border: 1px solid var(--border);
  border-radius: var(--radius);
  background: var(--card);
  color: var(--text);
  box-shadow: 0 18px 50px rgba(0, 0, 0, .45);
}
.modal::backdrop { background: rgba(8, 11, 18, .66); }
.modal h3 { font-size: 17px; letter-spacing: -.01em; }
.modal > p.muted { margin-bottom: 16px; }
.modal form:not(.modal-dismiss) { margin-top: 4px; }
.modal-dismiss { position: absolute; top: 10px; right: 10px; margin: 0; }
.modal-dismiss button {
  width: 30px;
  height: 30px;
  padding: 0;
  font-size: 19px;
  line-height: 1;
  background: transparent;
  color: var(--muted);
  border: 1px solid transparent;
  border-radius: 8px;
}
.modal-dismiss button:hover { background: var(--bg-sunken); color: var(--text); border-color: var(--border); }

.waiting { display: flex; align-items: center; gap: 10px; color: var(--muted); margin: 2px 0 0; }
.spinner {
  flex: 0 0 auto;
  width: 15px;
  height: 15px;
  border: 2px solid var(--border);
  border-top-color: var(--blue);
  border-radius: 50%;
  animation: mh-spin .8s linear infinite;
}
@keyframes mh-spin { to { transform: rotate(360deg); } }
/* A spinner that never stops is a worse signal than none for anyone who finds
   motion distracting; the text alone still says what is happening. */
@media (prefers-reduced-motion: reduce) {
  .spinner { animation: none; border-top-color: var(--border); }
}

/* ------------------------------------------------------------------ home -- */

.hero { margin-bottom: 30px; }
.hero h1 {
  font-size: clamp(30px, 5vw, 44px);
  line-height: 1.12;
  max-width: 17em;
  letter-spacing: -.03em;
  margin-bottom: 14px;
}
.hero .lead { max-width: 56ch; font-size: 17px; }
/* The words on the left, and on a wide screen the one exchange that sums it up on the right. */
.hero-grid { display: grid; grid-template-columns: minmax(0, 1fr); gap: 26px; align-items: center; }
@media (min-width: 1000px) {
  .hero-grid { grid-template-columns: minmax(0, 1.2fr) minmax(0, .8fr); gap: 40px; }
  .hero-grid .hero h1, .hero-grid h1 { max-width: none; }
}
.hero-cta { display: flex; gap: 10px; flex-wrap: wrap; align-items: center; margin-top: 22px; }
.hero-cta button { width: auto; padding: 11px 20px; font-size: 14.5px; }
/*
 * On a phone the picture belongs under the title, not under the whole
 * column: the title says what this is and the exchange shows it, and a
 * reader who has to scroll past a lead, a price and three buttons to reach
 * the picture never reaches it. `display: contents` dissolves .hero-copy so
 * its children and the illustration are items of one column, which `order`
 * can then interleave. Only the painting order moves; the document order,
 * which is what a screen reader follows, is left alone.
 */
@media (max-width: 999px) {
  .hero-grid { display: flex; flex-direction: column; gap: 18px; }
  .hero-grid > .hero-copy { display: contents; }
  .hero-grid > .hero-copy > .badge { order: 1; margin-bottom: 0; }
  .hero-grid > .hero-copy > h1 { order: 2; margin-bottom: 0; }
  .hero-grid > .illus { order: 3; }
  .hero-grid > .hero-copy > .lead { order: 4; margin: 0; }
  .hero-grid > .hero-copy > .hero-price { order: 5; margin: 0; }
  .hero-grid > .hero-copy > .hero-cta { order: 6; margin-top: 0; }
}
/*
 * And tighter on a phone, because it is now between the title and
 * everything that follows it: the exchange reserves its finished height so
 * nothing jumps as it plays, and at the desktop's size that reserved block
 * pushed the price and the buttons a screen and a half down.
 */
@media (max-width: 639px) {
  .hero-grid > .illus-ask { gap: 6px; padding: 12px; }
  .hero-grid > .illus-ask .ill-msg { font-size: 13px; }
  .hero-grid > .illus-ask .ill-done { padding: 7px 10px; font-size: 12.5px; }
}

.home-stack { display: flex; flex-direction: column; gap: 18px; }
.home-stack > .card { margin-bottom: 0; }

.pricing {
  display: flex;
  flex-direction: row;
  flex-wrap: nowrap;
  gap: 18px;
  margin: 0;
  align-items: stretch;
}
.pricing .card,
.plan-card {
  margin-bottom: 0;
  display: flex;
  flex-direction: column;
  flex: 1 1 0;
  width: 50%;
  min-width: 0;
  position: relative;
}
.pricing .price,
.price {
  font-size: 34px;
  font-weight: 700;
  margin: 6px 0 8px;
  letter-spacing: -.03em;
}
.price span.muted { font-size: 14px; font-weight: 400; letter-spacing: 0; }
.pricing button { width: 100%; }
.plan-card.featured { border-color: var(--blue); box-shadow: 0 0 0 1px var(--blue), var(--shadow); }
/* Beside the plan card, what the plan buys. Same column rules as the card. */
.plan-includes .plan-list li b { color: var(--text); }
.plan-list { margin: 0 0 18px; padding-left: 0; list-style: none; flex: 1; }
.plan-list li {
  margin: 9px 0;
  padding-left: 24px;
  position: relative;
  color: var(--text-soft);
  font-size: 13.5px;
}
.plan-list li::before {
  content: "";
  position: absolute;
  left: 2px;
  top: 7px;
  width: 11px;
  height: 6px;
  border-left: 2px solid var(--ok);
  border-bottom: 2px solid var(--ok);
  transform: rotate(-45deg);
}
.plan-cta { margin-top: auto; }
.plan-cta p { margin: 10px 0 0; }
/* A link line brand.js emptied still takes a line, so the cards' buttons stay level. */
.plan-cta p:empty::before { content: "\00a0"; }

/* The guides index: one subject gathered from every section, above them. */
.topic { margin-bottom: 18px; }
.topic-list { columns: 2; column-gap: 32px; }
.topic-list li { break-inside: avoid; }

.feature-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(210px, 1fr));
  gap: 16px;
}
.feature h3, .feature h4 { margin: 0 0 5px; font-size: 14.5px; }
.feature p { margin: 0; color: var(--muted); font-size: 13.5px; }
.feature p + p { margin-top: 8px; }

/* ------------------------------------------------------------------ auth -- */

.auth-wrap {
  min-height: calc(100vh - var(--header-h));
  display: grid;
  grid-template-columns: 1fr;
  place-items: start center;
  padding: 48px 20px 64px;
}
.auth-card {
  width: 100%;
  max-width: 408px;
  background: var(--card);
  border: 1px solid var(--border);
  border-radius: var(--radius-lg);
  padding: 30px 28px;
  box-shadow: var(--shadow-lg);
}
.auth-card h1 { font-size: 23px; margin-bottom: 6px; }
.auth-card form { max-width: none; gap: 14px; }
.auth-card .lead { font-size: 14px; margin-bottom: 22px; }
.auth-foot {
  margin: 20px 0 0;
  padding-top: 18px;
  border-top: 1px solid var(--border);
  font-size: 13px;
  color: var(--muted);
  text-align: center;
}
.auth-aside {
  width: 100%;
  max-width: 408px;
  margin: 16px auto 0;
  font-size: 12.5px;
  color: var(--faint);
  text-align: center;
}
.label-row {
  display: flex;
  justify-content: space-between;
  align-items: baseline;
  gap: 10px;
  /* The bare `label` rule tightens the gap to its input with a negative
     margin. Inside a flex container that margin applies to the label box,
     not the row, so the row has to carry it or the field below sits 4px
     lower than its neighbours. */
  margin-bottom: -4px;
}
.label-row label { margin-bottom: 0; }
.label-row a { font-size: 12.5px; font-weight: 500; }

/* ------------------------------------------------------------------ docs -- */

.docs-shell {
  display: grid;
  grid-template-columns: var(--sidebar-w) minmax(0, 1fr);
  gap: 36px;
  max-width: var(--page-max);
  margin: 0 auto;
  padding: 0 20px;
  align-items: start;
}
.docs-shell.with-toc {
  grid-template-columns: var(--sidebar-w) minmax(0, 1fr) 190px;
}

.docs-sidebar {
  position: sticky;
  top: var(--header-h);
  max-height: calc(100vh - var(--header-h));
  overflow-y: auto;
  padding: 24px 6px 48px 0;
  font-size: 13.5px;
  scrollbar-width: thin;
}

.doc-search { position: relative; margin-bottom: 18px; }
.doc-search input { padding: 8px 11px 8px 30px; font-size: 13.5px; }
.doc-search::before {
  content: "";
  position: absolute;
  left: 11px;
  top: 50%;
  width: 11px;
  height: 11px;
  margin-top: -7px;
  border: 1.6px solid var(--faint);
  border-radius: 50%;
  pointer-events: none;
}
.doc-search::after {
  content: "";
  position: absolute;
  left: 20px;
  top: 50%;
  width: 6px;
  height: 1.6px;
  margin-top: 3px;
  background: var(--faint);
  transform: rotate(45deg);
  pointer-events: none;
}

.side-group { margin-bottom: 20px; }
.side-group > h4 {
  font-size: 10.5px;
  text-transform: uppercase;
  letter-spacing: .07em;
  color: var(--faint);
  margin: 0 0 7px 10px;
  font-weight: 700;
}
.side-group ul { list-style: none; margin: 0; padding: 0; }

.side-link {
  display: flex;
  align-items: center;
  gap: 7px;
  padding: 5px 10px;
  border-radius: var(--radius-sm);
  color: var(--text-soft);
  line-height: 1.4;
}
.side-link:hover { background: var(--bg-sunken); color: var(--text); text-decoration: none; }
.side-link[aria-current="true"],
.side-link.active {
  background: var(--blue-soft);
  color: var(--blue);
  font-weight: 600;
}
.side-link .method { margin-right: 0; font-size: 9px; padding: 2px 5px; flex: none; }
.side-link .side-text { min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }

/*
 * margin: 0 overrides `main { margin: 0 auto }`. An auto margin makes a grid
 * item size to its content rather than its column, and the widest code line
 * in the catalog then pushed the column out under "On this page".
 */
.docs-main { padding: 24px 0 72px; min-width: 0; margin: 0; }
.docs-main h2 {
  scroll-margin-top: calc(var(--header-h) + 16px);
  padding-top: 8px;
  margin-top: 44px;
}
.docs-main h2:first-of-type { margin-top: 8px; }
.docs-main h3 { font-size: 16px; margin: 26px 0 10px; scroll-margin-top: calc(var(--header-h) + 16px); }

.onthispage {
  position: sticky;
  top: var(--header-h);
  padding: 28px 0 40px;
  font-size: 12.5px;
  max-height: calc(100vh - var(--header-h));
  overflow-y: auto;
}
.onthispage h4 {
  font-size: 10.5px;
  text-transform: uppercase;
  letter-spacing: .07em;
  color: var(--faint);
  margin: 0 0 8px;
  font-weight: 700;
}
.onthispage ul { list-style: none; margin: 0; padding: 0; }
.onthispage a {
  display: block;
  padding: 4px 0 4px 11px;
  border-left: 2px solid var(--border);
  color: var(--muted);
  line-height: 1.45;
}
.onthispage a:hover { color: var(--text); text-decoration: none; }
.onthispage a.active { border-left-color: var(--blue); color: var(--blue); font-weight: 600; }

/* Anchor affordance on headings. */
.anchor {
  color: var(--faint);
  margin-left: 8px;
  font-weight: 400;
  opacity: 0;
  text-decoration: none;
  font-size: .8em;
}
h2:hover .anchor,
h3:hover .anchor,
.op > summary:hover .anchor,
.anchor:focus-visible { opacity: 1; }

/* Endpoint card used by the generated reference. */
.op {
  border: 1px solid var(--border);
  border-radius: var(--radius);
  background: var(--card);
  padding: 16px 18px;
  margin: 0 0 14px;
  scroll-margin-top: calc(var(--header-h) + 16px);
  box-shadow: var(--shadow-sm);
}
.op > summary {
  list-style: none;
  cursor: pointer;
  display: flex;
  align-items: center;
  gap: 10px;
  flex-wrap: wrap;
  margin: -16px -18px;
  padding: 16px 18px;
  border-radius: var(--radius);
}
.op > summary::-webkit-details-marker { display: none; }
.op > summary:hover { background: var(--bg-sunken); }
.op[open] > summary { margin-bottom: 4px; border-bottom-left-radius: 0; border-bottom-right-radius: 0; }
.op .op-path {
  font-family: var(--mono);
  font-size: 13px;
  font-weight: 600;
  word-break: break-all;
  min-width: 0;
}
.op .op-summary { color: var(--muted); font-size: 13px; width: 100%; margin: 0; }
.op .op-body { padding-top: 14px; border-top: 1px solid var(--border); margin-top: 12px; }

.filter-bar {
  display: flex;
  gap: 10px;
  flex-wrap: wrap;
  align-items: center;
  margin-bottom: 16px;
}
.filter-bar input { max-width: 300px; }
.result-count { color: var(--muted); font-size: 13px; }

.breadcrumb { font-size: 12.5px; color: var(--muted); margin-bottom: 10px; }
.breadcrumb a { color: var(--muted); }

.pager {
  display: flex;
  justify-content: space-between;
  gap: 14px;
  margin-top: 44px;
  padding-top: 20px;
  border-top: 1px solid var(--border);
}
.pager a {
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
  padding: 11px 14px;
  flex: 1 1 0;
  background: var(--card);
}
.pager a:hover { border-color: var(--blue); text-decoration: none; }
.pager .pager-dir { display: block; font-size: 11px; color: var(--faint); text-transform: uppercase; letter-spacing: .06em; }
.pager .pager-next { text-align: right; }

/* The sidebar toggle only exists on narrow screens. */
.sidebar-toggle { display: none; }

.theme-toggle {
  background: transparent;
  border: 1px solid var(--border);
  color: var(--text-soft);
  padding: 5px 9px;
  border-radius: var(--radius-sm);
  font-size: 12.5px;
  line-height: 1;
  cursor: pointer;
}
.theme-toggle:hover { background: var(--bg-sunken); }

/* --------------------------------------------------------------- utility -- */

.stack-sm > * + * { margin-top: 8px; }
.stack > * + * { margin-top: 16px; }
.mt-0 { margin-top: 0 !important; }
.mb-0 { margin-bottom: 0 !important; }

/*
 * The quiet last line of a card, under whatever it was asking you to do.
 *
 * `p` is `margin: 0 0 12px`, so a paragraph after a button row had no top
 * margin at all and the only gap was its line-height -- the reference
 * link sat against the bottom of "Add a card" as though it were part of
 * the control. Named rather than an `.mt-3` utility, because the existing
 * utilities here are only ever zero and one member of a numeric scale is
 * not a scale.
 */
.card-foot { margin: 18px 0 0; }
.nowrap { white-space: nowrap; }
.flex-between { display: flex; justify-content: space-between; align-items: center; gap: 12px; flex-wrap: wrap; }

/* The TOC card from the original stylesheet. */
.toc p { margin: 0; }
.toc a { white-space: nowrap; }

/* ----------------------------------------------------------- responsive -- */

@media (max-width: 1080px) {
  .docs-shell.with-toc { grid-template-columns: var(--sidebar-w) minmax(0, 1fr); }
  .onthispage { display: none; }
}

@media (max-width: 860px) {
  .docs-shell, .docs-shell.with-toc { grid-template-columns: minmax(0, 1fr); gap: 0; }
  .docs-sidebar {
    position: static;
    max-height: none;
    overflow: visible;
    padding: 16px 0 0;
    border-bottom: 1px solid var(--border);
    margin-bottom: 20px;
  }
  .docs-sidebar[hidden] { display: none; }
  .sidebar-toggle {
    display: inline-flex;
    align-items: center;
    gap: 7px;
    margin: 18px 0 0;
    width: auto;
  }
  .docs-main { padding-top: 16px; }
}

/*
 * The menu button, and the sheet it opens, exist only on a phone (see the
 * script after THEME_SCRIPT in seo/render.ts).
 */
/*
 * `a.nav-book`, not `.nav-book`: the call is an <a class="btn">, and
 * `a.btn { display: inline-flex }` outranks a lone class whatever the
 * order -- which is how this button came to be in the header on a desktop
 * too, where the trial button already is.
 */
.nav-burger, .nav-sheet, a.nav-book { display: none; }
@media (max-width: 639px) {
  /*
   * One sticky row, and only three things in it: the name, the call, and
   * the menu button that holds everything else.
   *
   * The links used to scroll sideways here, which meant the fourth one and
   * everything after it -- including, on a signed-in screen, Inbox and the
   * way to the dashboard -- lived past the right edge of a 375px screen
   * with nothing but a fading mask to say so. They are all in the sheet
   * now. The <nav> stays in the page, hidden: it is still where
   * nav-session.js and member-nav.js put a member's own links, and the
   * sheet is built by copying its children each time it opens.
   */
  :root { --header-h: 54px; }
  header {
    flex-wrap: nowrap;
    gap: 6px;
    padding: 0 8px 0 14px;
  }
  /* The name takes the row's spare width, so whatever follows it is against
     the right edge whether or not the call button is there to be shown. */
  header .brand { flex: none; margin-inline-end: auto; }
  header nav { display: none; }
  header > .header-tools { display: none; }
  /*
   * The call: at the right of the row, against the menu. It is ours and it
   * is for a stranger, so a white-label host drops it (data-brand-hide) and
   * it leaves the row the moment a session is found.
   *
   * It is the one thing in the row that may give way. The name is the name
   * and the menu button is a 38px target, so when a language writes "Book a
   * call" long enough that the three do not fit -- Portuguese, Spanish and
   * Italian, at 320px -- it is this label that is cut, rather than the menu
   * button that falls off the right edge.
   */
  a.nav-book {
    display: inline-flex;
    flex: 0 1 auto;
    min-width: 0;
    padding: 7px 12px;
    font-size: 13px;
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
  }
  .nav-burger {
    display: inline-flex;
    flex: none;
    align-items: center;
    justify-content: center;
    width: 38px;
    height: 38px;
    padding: 0;
    border: 1px solid var(--border);
    border-radius: 9px;
    background: var(--card);
    color: var(--text);
    cursor: pointer;
  }
  .nav-burger[aria-expanded="true"] { background: var(--bg-sunken); }
  .nav-sheet:not([hidden]) {
    display: flex;
    flex-direction: column;
    gap: 10px;
    position: absolute;
    top: calc(100% + 6px);
    inset-inline-end: 8px;
    min-width: 220px;
    max-width: calc(100vw - 16px);
    padding: 10px;
    border: 1px solid var(--border);
    border-radius: 12px;
    background: var(--card);
    box-shadow: 0 12px 32px rgba(15, 23, 42, .18);
  }
  .nav-sheet-links { display: flex; flex-direction: column; gap: 2px; }
  .nav-sheet-links a {
    padding: 9px 10px;
    border-radius: 8px;
    color: var(--text);
    font-size: 14.5px;
    text-decoration: none;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
  }
  .nav-sheet-links a:hover { background: var(--bg-sunken); }
  /* The page the reader is on, marked in the sheet as it is in the row on a
     desktop: the copy keeps the attribute, but not the `header nav` selector. */
  .nav-sheet-links a[aria-current="page"] { color: var(--blue); background: var(--blue-soft); }
  .nav-sheet-links a.btn { color: var(--on-accent); justify-content: center; }
  .nav-sheet > .header-tools { margin: 0; padding-top: 10px; border-top: 1px solid var(--border); justify-content: space-between; }
  .nav-sheet .lang-menu-list { position: static; margin-top: 6px; box-shadow: none; }
  main { padding: 28px 16px 48px; }
  .pricing { flex-direction: column; flex-wrap: wrap; }
  .pricing .card, .plan-card { width: 100%; max-width: none; }
  .topic-list { columns: 1; }
  .hero h1 { font-size: 28px; }
  .auth-wrap { padding: 24px 16px 48px; }
  .auth-card { padding: 24px 20px; border-radius: var(--radius); }
  .docs-shell { padding: 0 16px; }
  .pager { flex-direction: column; }
  .pager .pager-next { text-align: left; }
}

@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after {
    animation-duration: .001ms !important;
    transition-duration: .001ms !important;
    scroll-behavior: auto !important;
  }
}

@media print {
  header, footer, .docs-sidebar, .onthispage, .copy-btn, .pager { display: none !important; }
  .docs-shell { grid-template-columns: 1fr; }
  body { background: #fff; }
}

/* ------------------------------------------------- small screens, touch -- */

/*
 * Anything under 16px makes iOS zoom the page on focus, and the verification
 * modal autofocuses its code box -- so the one moment the member most needs
 * to see the whole dialog is the moment the page jumps. Only below the
 * pointer-fine widths, so the denser desktop form keeps its sizing.
 */
@media (max-width: 860px) {
  input,
  select,
  textarea { font-size: 16px; }
  textarea { font-size: 15px; }
}

/*
 * A coarse pointer needs a target it can actually hit. The visual size of
 * these controls is deliberate, so the padding grows rather than the type.
 */
@media (pointer: coarse) {
  button,
  a.btn {
    min-height: 44px;
  }
  button.small,
  a.btn.small {
    min-height: 44px;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    padding-block: 0;
    padding-inline: 14px;
  }
  .modal-dismiss button { width: 44px; height: 44px; }
  header nav a {
    min-height: 44px;
    display: inline-flex;
    align-items: center;
  }
  .option-card summary { padding-block: 10px; }
}

/*
 * The connect card is reached by #connect from the landing page and from the
 * dashboard's own links. The existing scroll-margin rule only covers direct
 * children of main, and this one is nested inside the step rail, so the
 * sticky header sat on top of the heading it had just jumped to.
 */
.steps > li[id],
main[id] { scroll-margin-top: calc(var(--header-h) + 16px); }

/* The hero's actions are <a class="btn">, which the button-only rule above
   never matched, so the primary CTA rendered at ordinary body-button size. */
.hero-cta a.btn { width: auto; padding: 13px 22px; font-size: 15.5px; }
/*
 * And on anything wider the same, for the hero alone: the row is as wide
 * as the words above it and the buttons share it evenly, so the price and
 * the ask under it read as one block rather than as a sentence with two
 * small links after it. Not `.hero-cta` everywhere -- the short home
 * page's closing pair is centred under centred text, and stretching it
 * would make two buttons the width of the page.
 */
/*
 * On a desktop the hero's buttons are as wide as the words above them: the
 * row fills the copy column and the three share it evenly, so the price and
 * the ask under it read as one block rather than as a sentence with three
 * small links after it.
 *
 * From 1000px up, because that is where the hero becomes two columns and
 * the copy column is a block with a width of its own. Below it the hero is
 * one centred stack -- the title, the picture, the lead and the price each
 * centred on their own width -- and a row stretched to the full page there
 * would be the one thing wider than everything around it.
 *
 * Not `.hero-cta` everywhere either: the short home page's closing pair is
 * centred under centred text, and stretching it would make two buttons the
 * width of the page.
 */
@media (min-width: 1000px) {
  .hero .hero-cta { flex-wrap: nowrap; align-items: stretch; }
  .hero .hero-cta a.btn { flex: 1 1 0; min-width: 0; }
}
/*
 * On a phone the hero's buttons are one above the other, each the width of
 * the screen.
 *
 * They used to share a row, and at 375px that gave each of them half of it
 * -- which "Start free trial" does not fit on, so the primary call to
 * action was a two-line button next to another two-line button. A full-width
 * button is the easiest thing on the page to hit and its label fits on one
 * line in every language, which is the whole argument.
 */
@media (max-width: 639px) {
  /* The row itself is the screen: under a centred column it would otherwise
     be as wide as its widest label, and a "full-width" button inside it
     would be that width too. */
  .hero-cta { flex-wrap: wrap; gap: 8px; align-items: stretch; width: 100%; }
  .hero-cta a.btn { flex: 1 1 100%; min-width: 0; padding: 12px 10px; font-size: 15px; line-height: 1.25; text-align: center; white-space: nowrap; }
  /*
   * Two buttons, not three. "Book a call" is in the header on a phone,
   * beside the name, so the row here is the pair that opens an account --
   * three buttons on a 375px screen gave each of them two lines.
   */
  .hero-cta .hero-book { display: none; }
}

/* ----------------------------------------------------------- marketplace -- */

/*
 * A logo belonging to somebody else, on a plate.
 *
 * The white is deliberately a literal rather than a token: it is not a
 * surface colour that should follow the theme, it is a sheet of paper to
 * print a logo on. Most logos are dark ink on transparency, and on the dark
 * theme's #141922 card they would simply disappear.
 */
.logo-mark {
  width: 40px;
  height: 40px;
  flex: none;
  border-radius: 8px;
  object-fit: contain;
  background: #fff;
  border: 1px solid var(--border);
  padding: 4px;
}

/* A face, cropped square and round, however it was shot. */
.avatar { width: 72px; height: 72px; flex: none; border-radius: 50%; object-fit: cover; }

/*
 * A figure beside a column of text, which stacks on a phone by itself.
 *
 * None of the three flex rows already here would do: `.row` is
 * space-between with a rule under it, `.flex-between` spreads, and `.check`
 * is a form control. The 220px basis on the text column is what makes it
 * wrap rather than squeeze.
 */
.media { display: flex; gap: 14px; align-items: flex-start; flex-wrap: wrap; }
.media > :last-child { flex: 1 1 220px; min-width: 0; }

/*
 * Cards in a row of equals, rather than a row of comparisons.
 *
 * `.option-grid` sets `align-items: start` so two cards being compared
 * share a baseline. In a row of three products that leaves each card as
 * tall as its own text and the buttons at three different heights.
 */
.option-grid.even { align-items: stretch; }
.option-grid.even > .option-card { height: 100%; }

/* A list that is a list, not a list of things you get. `.plan-list` ticks
   every item, which reads as "included" wherever it is used. */
.plain-list { margin: 0 0 12px; padding-left: 18px; color: var(--text-soft); }
.plain-list li { margin: 6px 0; }

/* A price inside a product card. `.price` is 34px, which is right for the
   two plan cards and shouts over the heading of anything smaller. */
.meta-price { font-size: 15px; font-weight: 650; color: var(--text); letter-spacing: -.01em; margin: 0 0 8px; }

/* The action at the foot of a card, pushed down so a row of cards lines up. */
.card-cta { margin-top: auto; padding-top: 14px; }

/*
 * A textarea for prose rather than for code.
 *
 * The base `textarea` is monospace at 13px, which is right for a request
 * body and wrong for "what are you working on" -- it makes an ordinary
 * sentence look like something that has to be typed exactly.
 */
textarea.prose {
  font-family: inherit;
  font-size: 13.5px;
  min-height: 72px;
  line-height: 1.5;
}

/* The price is the number a visitor came for, so it sits on its own line
   under the headline rather than as a clause inside the lead paragraph.

   One line, at every width and in every language. A price that wraps reads
   as two facts -- a price, and then some other thing -- rather than as the
   one sentence it is, so the line never wraps and the type shrinks instead.
   The three sizes are one size: the number and the tail are `em` of the
   whole, so they shrink together and keep their proportions, and the whole
   follows the viewport until it reaches the size a desktop wants. The
   coefficient is what the longest of the seventeen languages needs at
   320px; see the measurement in the hero tests. */
.hero-price {
  margin: 0 0 10px;
  white-space: nowrap;
  font-size: clamp(10px, 3.1vw, 17px);
  color: var(--text);
}
.hero-price strong { font-size: 1.41em; font-weight: 700; letter-spacing: -.02em; }
.hero-price .muted { font-size: .85em; }

/* ------------------------------------------------------------- webhooks -- */

.events { border: 1px solid var(--border); border-radius: 10px; padding: 12px 14px; margin: 0; }
.events legend { font-size: 12.5px; font-weight: 600; color: var(--text-soft); padding: 0 6px; }
.check {
  display: flex;
  align-items: center;
  gap: 9px;
  font-size: 13.5px;
  font-weight: 400;
  color: var(--text);
  margin: 0;
  padding: 5px 0;
}
.check input { width: auto; margin: 0; }
/* A secret shown once has to survive being copied out of a narrow column. */
.break-all { overflow-wrap: anywhere; word-break: break-all; }

/* The dialog's own actions, so dismissing it is not a 30px glyph hunt. */
.modal-actions {
  display: flex;
  flex-wrap: wrap;
  gap: 8px;
  margin-top: 16px;
}
.modal-actions button { width: auto; flex: 1 1 auto; }

/*
 * A card's blurb may be mostly one unbreakable token.
 *
 * The blurb of a generated page starts with the route, and a route like
 * /linkedin/v2/{account_id}/posts/{post_id}/comments/{comment_id}/reactions
 * has no space in it to wrap at -- so it ran straight out of the card and
 * was clipped by the grid. It shows on the English index too; it is only
 * obvious in a language whose own words are short enough that the path is
 * the widest thing on the page.
 */
.option-card .option-body p { overflow-wrap: anywhere; }

/* ------------------------------------------------------------ languages -- */

/*
 * The language picker in the footer of a written page.
 *
 * Plain links, wrapping, because there are seventeen of them and they are
 * the one piece of chrome that must work with no JavaScript: a picker a
 * crawler cannot follow is a picker that does half its job. The separator
 * is a border rather than a character so it never ends up read aloud.
 */
/*
 * A row of links under the footer's main line: the language picker on a
 * written page, and the links to the written sections on every other page.
 *
 * Those links are the reason this exists twice over. The glossary,
 * templates, notes and examples are thousands of pages and NOTHING on the
 * site linked to them -- they were reachable only from sitemap.xml, which
 * is the weakest form of discovery there is and leaves them orphaned for
 * ranking purposes. It is also how the language picker became invisible:
 * it lives on those pages, and there was no way to reach one.
 */
.lang-row,
.foot-nav {
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  align-items: baseline;
  gap: 4px 10px;
  margin-top: 14px;
  line-height: 1.7;
}
/* The fallback sits above the legal line, where the columns go. */
.foot-nav { margin-bottom: 14px; }
.lang-row a,
.foot-nav a { white-space: nowrap; }
.lang-row a:hover,
.foot-nav a:hover { color: var(--text); }

/*
 * The footer's columns: one per written section, its recent pages in it.
 *
 * The row above was the first attempt and it was not enough. Four words on
 * one line, in the footer's 12.5px muted grey, under a sentence about
 * LinkedIn affiliation -- the links were there and nobody saw them, which
 * the brief put plainly: "hardly even noticable".
 *
 * A column each, with real titles under it, is what a footer is for. It
 * also does the job the one-line row could not: a crawler arriving on any
 * page now has a dozen article URLs in front of it instead of four index
 * pages it has to decide to open.
 *
 * `text-align: start` rather than `left` -- `footer` centres everything,
 * and these have to un-centre in a way that still flips for Arabic.
 */
.foot-guides { margin-bottom: 26px; padding-bottom: 26px; border-bottom: 1px solid var(--border); }
.foot-cols {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(min(190px, 100%), 1fr));
  gap: 22px 28px;
  max-width: 1040px;
  margin: 0 auto;
  padding-bottom: 22px;
  text-align: start;
}
.foot-col h2 {
  margin: 0 0 10px;
  font-size: 11.5px;
  font-weight: 600;
  letter-spacing: 0.05em;
  text-transform: uppercase;
  color: var(--text);
}
.foot-col h2 a { color: inherit; text-decoration: none; }
.foot-col h2 a:hover { text-decoration: underline; }
.foot-col ul { list-style: none; margin: 0; padding: 0; }
.foot-col li { margin: 0 0 6px; line-height: 1.45; }
/*
 * Titles wrap rather than being cut off. A truncated one is a link whose
 * destination the reader cannot tell apart from its neighbour, and these
 * are generated titles that frequently share a first three words.
 */
.foot-col a { text-decoration: none; }
.foot-col a:hover { text-decoration: underline; color: var(--text); }
.foot-all { display: inline-block; margin-top: 8px; font-weight: 500; }

/*
 * Right-to-left, for the languages that are written that way.
 *
 * Only the rules the translated pages actually use -- the written pages,
 * the funnel, and the account and dashboard screens. A blanket flip of the
 * whole stylesheet would reach the reference pages, which are not
 * translated and would be flipped for nothing -- and the rules below are
 * the ones whose left/right is decoration rather than meaning. Code is
 * NOT one of them: a curl command reads left-to-right in every language,
 * so `pre` and inline code are pinned back.
 *
 * A field that holds an identifier -- an email, a URL, a key, a host --
 * carries `dir="auto"` in the markup instead of a rule here: empty or Latin,
 * it lays out left-to-right, so `price_…` does not render as `…price_`.
 *
 * Inline code needs `unicode-bidi: isolate` as well. On an inline element
 * `direction` does nothing by itself, so `direction: ltr` alone left a
 * header like `t=<seconds>,v1=<hex>` to the surrounding Arabic, which
 * reorders its punctuation.
 */
[dir="rtl"] .callout {
  border-left: 1px solid var(--border);
  border-right: 3px solid var(--muted);
}
[dir="rtl"] .callout.info { border-right-color: var(--blue); border-left-color: var(--border); }
[dir="rtl"] .callout.warn { border-right-color: var(--warn); border-left-color: var(--border); }
[dir="rtl"] .callout.danger { border-right-color: var(--danger); border-left-color: var(--border); }
[dir="rtl"] .callout.ok { border-right-color: var(--ok); border-left-color: var(--border); }
[dir="rtl"] .plain-list { padding-left: 0; padding-right: 18px; }
[dir="rtl"] .skip-link { left: auto; right: -9999px; }
[dir="rtl"] .skip-link:focus { left: auto; right: 8px; }
/* The numbered steps -- the dashboard's four, the landing page's "how it
 * starts" -- draw their disc and rule on the side a reader starts from. */
[dir="rtl"] .steps > li {
  padding: 0 38px 20px 0;
  border-left: 0;
  border-right: 1px solid var(--border);
  margin-left: 0;
  margin-right: 13px;
}
[dir="rtl"] .steps > li:last-child { border-right-color: transparent; }
[dir="rtl"] .steps > li::before { left: auto; right: -13px; }
[dir="rtl"] .howto { padding-left: 0; padding-right: 20px; }
/* The dialog's × sits in the corner the title does not start from. */
[dir="rtl"] .modal-dismiss { right: auto; left: 10px; }
[dir="rtl"] pre { direction: ltr; text-align: left; }
[dir="rtl"] code.inline { direction: ltr; unicode-bidi: isolate; }

/* ------------------------------------------------- header tools (right) -- */

/*
 * Language and theme, in the corner of the header.
 *
 * The footer keeps its language row. These are not two copies of one
 * control: this one is for the reader who has just arrived and is reading
 * the wrong language, and that one is for the reader who got to the bottom
 * -- and on a written page that does not exist in every language, the
 * footer row is the only one of the two that can say so.
 *
 * They carry no `data-brand-hide`: brand.js strips MakeHybrid's own
 * promotional furniture from a white-labelled page, and these are not
 * that. A customer's own customers still need to read in their language.
 */
.header-tools {
  display: flex;
  align-items: center;
  gap: 8px;
  margin-inline-start: auto;
}

/*
 * A <details> rather than a <select>: the list is real links, so a crawler
 * follows them and a reader can open one in a new tab. The cost is having
 * to position the panel, which is all the rest of this block is.
 */
.lang-menu { position: relative; }

.lang-menu > summary {
  list-style: none;
  cursor: pointer;
  user-select: none;
  display: inline-flex;
  align-items: center;
  gap: 5px;
  padding: 5px 9px;
  border: 1px solid var(--border);
  border-radius: 7px;
  font-size: 12.5px;
  color: var(--text-soft);
  background: var(--card);
  white-space: nowrap;
}
/* Safari still paints its own triangle without this. */
.lang-menu > summary::-webkit-details-marker { display: none; }

.lang-menu > summary::after {
  content: "";
  width: 0;
  height: 0;
  border-inline: 3.5px solid transparent;
  border-top: 4px solid currentColor;
  opacity: .65;
}
.lang-menu > summary:hover { color: var(--text); border-color: var(--border-strong); }
.lang-menu[open] > summary::after { transform: rotate(180deg); }

.lang-menu-list {
  position: absolute;
  /* `inset-inline-end` rather than `right` so it flips for Arabic. */
  inset-inline-end: 0;
  top: calc(100% + 6px);
  z-index: 50;
  display: grid;
  /*
   * Seventeen languages is too many for one column on a laptop and far
   * too many for a phone, so it wraps into however many fit and scrolls
   * only if it still cannot.
   */
  grid-template-columns: repeat(2, minmax(104px, 1fr));
  gap: 1px 10px;
  max-height: min(64vh, 420px);
  overflow-y: auto;
  padding: 8px;
  background: var(--card);
  border: 1px solid var(--border);
  border-radius: 9px;
  box-shadow: var(--shadow-lg);
}

.lang-menu-list a {
  padding: 5px 7px;
  border-radius: 5px;
  font-size: 12.5px;
  color: var(--text-soft);
  white-space: nowrap;
}
.lang-menu-list a:hover { background: var(--bg-sunken); color: var(--text); }

/*
 * One icon button, at the right end of the header. It shows the mode in
 * force and a press moves to the next one; its label says the mode in
 * words. The picture is chosen here, from `data-theme` on <html> --
 * which THEME_BOOT sets before the first paint -- rather than by a
 * script, so the icon is never the wrong one for a moment.
 *
 * Quiet, not accented. --blue belongs to the one thing in the header a
 * reader is being asked to press; a theme control wearing it competes
 * with the trial button beside it, and the loudest thing on the page
 * ends up being the one that changes the least.
 */
.theme-toggle {
  appearance: none;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 32px;
  height: 32px;
  padding: 0;
  margin: 0;
  border: 1px solid var(--border);
  border-radius: 7px;
  background: var(--card);
  color: var(--text-soft);
  cursor: pointer;
  flex: none;
}
.theme-toggle:hover { color: var(--text); border-color: var(--border-strong); background: var(--bg-sunken); }
.theme-toggle .theme-icon { display: none; }
:root:not([data-theme="light"]):not([data-theme="dark"]) .theme-toggle .theme-icon[data-mode="system"],
:root[data-theme="light"] .theme-toggle .theme-icon[data-mode="light"],
:root[data-theme="dark"] .theme-toggle .theme-icon[data-mode="dark"] { display: block; }
@media (pointer: coarse) {
  .theme-toggle { width: 44px; height: 44px; }
}

/*
 * The header is the one row on the page with no slack in it. On a phone
 * the nav already wraps; the language menu tightens, and the theme
 * button, one icon wide, stays.
 */
@media (max-width: 720px) {
  .lang-menu > summary { padding: 4px 7px; font-size: 12px; }
  .lang-menu-list { grid-template-columns: repeat(2, minmax(92px, 1fr)); }
}

/*
 * Project rooms: a chat app.
 *
 * Two columns under the header: the organization's rooms, the open room's
 * menu and what every room shares on the left, the conversation on the
 * right. The conversation scrolls inside its column and the composer stays
 * put under it, the way every chat app a member already knows behaves. The
 * left column, hidden, gives its width back to the conversation.
 *
 * Under 960px the conversation has the screen, and the left column is a
 * drawer that opens over it from a button in the room's header.
 */
main.rooms-app {
  max-width: none;
  padding: 0;
  display: grid;
  grid-template-columns: 280px minmax(0, 1fr);
  /* --rooms-top is the header's real height, measured by rooms.js: on a
     phone the header wraps onto two lines and --header-h is `auto`. */
  height: calc(100dvh - var(--rooms-top, 58px));
  min-height: 420px;
}
main.rooms-app:has(> .rooms-left.hidden) { grid-template-columns: minmax(0, 1fr); }

.rooms-side {
  min-height: 0;
  overflow-y: auto;
  padding: 12px 14px 16px;
  background: var(--card);
  border-right: 1px solid var(--border);
}
.rooms-side-head { display: flex; align-items: center; justify-content: space-between; gap: 8px; margin-bottom: 10px; }
.rooms-title { font-size: 18px; margin: 0; }
.rooms-side-head h2 { font-size: 15px; margin: 0; }
.rooms-list { list-style: none; margin: 0 0 10px; padding: 0; }
.rooms-list a {
  display: block;
  padding: 7px 10px;
  border-radius: 8px;
  color: var(--text);
  text-decoration: none;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.rooms-list a:hover { background: var(--bg-sunken); }
.rooms-list a[aria-current] { background: var(--blue-soft); color: var(--blue); font-weight: 600; }

/*
 * The list as a tree: rooms, and under the open one its chats. A row is a
 * name and its ⋮; a chat's settings drop down under its name, indented.
 */
.rooms-tree .tree-row { position: relative; display: flex; align-items: center; gap: 2px; border-radius: 8px; }
.rooms-tree .tree-row:hover, .rooms-tree .tree-row:focus-within { background: var(--bg-sunken); }
.rooms-side .rooms-tree .tree-name {
  flex: 1;
  min-width: 0;
  display: block;
  padding: 6px 10px;
  border: 0;
  border-radius: 8px;
  background: none;
  color: var(--text);
  font: inherit;
  font-size: 13.5px;
  text-align: start;
  text-decoration: none;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  cursor: pointer;
}
.rooms-tree .tree-room.is-open > .tree-row .tree-name { font-weight: 600; }
.rooms-tree .tree-name[aria-current] { background: var(--blue-soft); color: var(--blue); font-weight: 600; }
.rooms-side .rooms-tree .tree-more {
  flex: none;
  width: 26px;
  height: 26px;
  padding: 0;
  border: 0;
  border-radius: 6px;
  background: none;
  color: var(--muted);
  font-size: 16px;
  line-height: 1;
  opacity: 0;
  cursor: pointer;
}
.rooms-tree .tree-row:hover .tree-more, .rooms-tree .tree-more:focus-visible, .rooms-tree .tree-more[aria-expanded="true"],
.rooms-tree .tree-name[aria-current] + .tree-more { opacity: 1; }
.rooms-tree .tree-more:hover { background: var(--border); color: var(--text); }
@media (hover: none) { .rooms-tree .tree-more { opacity: 1; } }
.rooms-tree .tree-chats { list-style: none; margin: 2px 0 6px; padding: 0 0 0 12px; border-inline-start: 1px solid var(--border); margin-inline-start: 12px; }
.rooms-side .rooms-tree .tree-chats .tree-name { padding: 5px 10px; font-size: 13px; }
.rooms-side .rooms-tree .tree-add { border: 0; background: none; padding: 5px 10px; color: var(--blue); font-size: 12.5px; font-weight: 600; cursor: pointer; }
.rooms-tree .tree-add:hover { text-decoration: underline; }
.rooms-tree .tree-rename { flex: 1; min-width: 0; margin: 2px 0; }
.rooms-tree .tree-menu {
  position: absolute;
  z-index: 20;
  top: calc(100% + 2px);
  inset-inline-end: 0;
  display: flex;
  flex-direction: column;
  min-width: 150px;
  padding: 4px;
  border: 1px solid var(--border);
  border-radius: 10px;
  background: var(--card);
  box-shadow: 0 8px 24px rgba(15, 23, 42, .14);
}
.rooms-side .rooms-tree .tree-menu button { padding: 6px 10px; border: 0; border-radius: 6px; background: none; color: var(--text); font-size: 13px; text-align: start; cursor: pointer; }
.rooms-tree .tree-menu button:hover, .rooms-tree .tree-menu button:focus-visible { background: var(--bg-sunken); }
.rooms-tree .tree-menu button.is-danger { color: var(--danger); }
.rooms-tree .tree-settings { display: flex; flex-direction: column; gap: 6px; margin: 4px 0 8px; padding: 8px 10px; border-radius: 8px; background: var(--bg-sunken); }
.rooms-tree .tree-set-head { margin: 4px 0 0; font-size: 11px; font-weight: 600; letter-spacing: .04em; text-transform: uppercase; color: var(--muted); }
.rooms-tree .tree-set-head:first-child { margin-top: 0; }
.rooms-side .rooms-tree label.tree-set { display: grid; grid-template-columns: minmax(0, 1fr) auto; align-items: center; gap: 6px; font-size: 12px; }
.rooms-tree .tree-set span { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.rooms-side .rooms-tree .tree-set select { width: auto; max-width: 140px; padding: 2px 4px; font-size: 12px; }
/* A setting whose choices are long sentences gets the row to itself. */
.rooms-side .rooms-tree label.tree-set.is-wide { grid-template-columns: minmax(0, 1fr); gap: 2px; }
.rooms-side .rooms-tree .tree-set.is-wide select { max-width: none; width: 100%; }
.rooms-tree .tree-set-note { margin: 0; font-size: 11.5px; color: var(--muted); }
.rooms-chat-of:not(:empty)::before { content: " / "; color: var(--muted); font-weight: 400; }
.rooms-chat-of { color: var(--text-soft); font-weight: 500; }
.rooms-inline { display: flex; gap: 8px; max-width: none; margin: 0 0 14px; }
.rooms-inline input { flex: 1; min-width: 0; }
.rooms-panel { border-top: 1px solid var(--border); padding: 6px 0 5px; }
.rooms-panel.rooms-first { border-top: 0; padding-top: 0; }
.rooms-panel > summary, .rooms-panel-title { cursor: pointer; font-weight: 600; font-size: 13.5px; color: var(--text); margin: 0; }
.rooms-panel-title { cursor: default; }
.rooms-panel[open] > summary { margin-bottom: 4px; }
.rooms-panel form { max-width: none; }

/*
 * The side columns are dense on purpose: a member should see every panel
 * of a room, open, without scrolling a 900-pixel window. So the controls
 * here are a size smaller than a form page's, related fields share a row,
 * and each panel says what it is for in one short line, not a paragraph.
 */
.rooms-side form, .rooms-form { display: flex; flex-direction: column; gap: 5px; margin: 0; }
.rooms-side form.rooms-inline { flex-direction: row; }
.rooms-side label:not(.check), .rooms-side legend { font-size: 11.5px; margin: 0; padding: 0; }
.rooms-side input, .rooms-side select, .rooms-side textarea { padding: 4px 7px; font-size: 12.5px; line-height: 1.35; }
.rooms-side textarea.prose { min-height: 0; font-size: 12.5px; line-height: 1.35; }
.rooms-side button { padding: 4px 10px; font-size: 12.5px; line-height: 1.35; }
.rooms-side .rooms-list a { padding: 5px 10px; }
.rooms-side .btn-row { gap: 6px; }
.rooms-side .check { padding: 0; gap: 7px; font-size: 12.5px; line-height: 1.35; align-items: flex-start; }
.rooms-side .check input { margin-top: 2px; }
.rooms-side fieldset { border: 0; margin: 0; padding: 0; min-width: 0; }
.rooms-field { display: flex; flex-direction: column; gap: 2px; min-width: 0; }
.rooms-pair { display: grid; grid-template-columns: minmax(0, 1fr) minmax(0, 1fr); gap: 6px; }
/* A select whose choices are words, not values, keeps its own width rather than cutting them off. */
.rooms-pair.select-first { grid-template-columns: minmax(0, max-content) minmax(6em, 1fr); }
.rooms-pair.select-last { grid-template-columns: minmax(7em, 1fr) minmax(0, max-content); }
.rooms-checks { display: flex; flex-wrap: wrap; gap: 0 12px; }
.rooms-note { margin: 0 0 4px; font-size: 12px; line-height: 1.35; color: var(--muted); }
.rooms-rows { list-style: none; margin: 0 0 6px; padding: 0; font-size: 12.5px; color: var(--text-soft); }
.rooms-rows li { display: flex; align-items: center; justify-content: space-between; gap: 6px; padding: 2px 0; min-width: 0; }
.rooms-rows li > span { min-width: 0; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.rooms-rows button { padding: 1px 6px; font-size: 12px; }
/* A row's button keeps its width: squeezed, a CJK label wraps one character per line. */
.rooms-rows button, .rooms-connect button { flex: none; white-space: nowrap; }
.rooms-connect { border: 1px solid var(--border); border-radius: 8px; padding: 6px 8px; margin: 0 0 8px; font-size: 12.5px; }
.rooms-connect-head { display: flex; align-items: center; justify-content: space-between; gap: 8px; }
.rooms-connect .rooms-note { margin: 3px 0 0; }

/*
 * Project rooms have two columns: the rooms, with the open room's menu under
 * them, and the conversation. What used to be a third column -- the room's
 * agents, channels, task tools and settings -- opens as a dialog from that
 * menu, so the conversation keeps its width and each part gets room to
 * breathe.
 */
.rooms-nav { display: flex; flex-direction: column; gap: 2px; margin: 0 0 12px; padding-top: 12px; border-top: 1px solid var(--border); }
/* The new-room form above it has no margin of its own in the column: the space is the menu's. */
.rooms-side form#newRoom { margin-bottom: 14px; }
.rooms-nav-title { margin: 0 0 4px; padding: 0 10px; font-size: 11px; font-weight: 600; letter-spacing: .04em; text-transform: uppercase; color: var(--muted); }
.rooms-side button.rooms-nav-item,
.rooms-nav-item {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 8px;
  width: 100%;
  padding: 7px 10px;
  border: 0;
  border-radius: 8px;
  background: none;
  color: var(--text);
  font-size: 13.5px;
  font-weight: 500;
  text-align: start;
  cursor: pointer;
}
.rooms-nav-item:hover, .rooms-nav-item:focus-visible { background: var(--bg-sunken); }
.rooms-nav-item b:empty { display: none; }
.rooms-nav-item b { min-width: 20px; padding: 0 6px; border-radius: 999px; background: var(--bg-sunken); color: var(--text-soft); font-size: 11.5px; font-weight: 600; line-height: 20px; text-align: center; }
.rooms-nav-item.is-wanted b { background: var(--warn-soft); color: var(--warn); }

/* A room's part, in a dialog: a title with Close beside it, and the room's usual spacing, a size up. */
dialog.rooms-panel-dialog { width: min(560px, calc(100vw - 24px)); padding: 18px 20px 20px; }
.rooms-dialog-head { display: flex; align-items: center; justify-content: space-between; gap: 12px; margin: 0 0 10px; }
.rooms-dialog-head h2 { margin: 0; font-size: 18px; }
.rooms-dialog-head button { flex: none; }
.rooms-panel-dialog .rooms-form, .rooms-panel-dialog form { display: flex; flex-direction: column; gap: 10px; max-width: none; margin: 0; }
.rooms-panel-dialog form.rooms-inline { flex-direction: row; }
.rooms-panel-dialog .rooms-note { margin: 0 0 10px; font-size: 13px; line-height: 1.45; }
.rooms-panel-dialog .rooms-rows { margin: 0 0 14px; font-size: 13.5px; }
.rooms-panel-dialog .rooms-rows li { padding: 6px 0; border-bottom: 1px solid var(--border); }
.rooms-panel-dialog .rooms-rows li:last-child { border-bottom: 0; }
/* A dialog has the width to say a row whole: it wraps rather than trailing off. */
.rooms-panel-dialog .rooms-rows li > span { white-space: normal; overflow: visible; }
@media (max-width: 480px) {
  .rooms-panel-dialog .rooms-pair, .rooms-panel-dialog .rooms-pair.select-first, .rooms-panel-dialog .rooms-pair.select-last { grid-template-columns: minmax(0, 1fr); }
}
.rooms-panel-dialog .rooms-rows button { padding: 3px 9px; font-size: 12.5px; }
.rooms-panel-dialog .rooms-panel { padding: 12px 0; }
.rooms-panel-dialog .rooms-panel.rooms-first { padding-top: 0; }
.rooms-panel-dialog .rooms-panel[open] > summary { margin-bottom: 10px; }
.rooms-panel-dialog .rooms-panel > summary { font-size: 14px; }
.rooms-panel-dialog .btn-row { margin-top: 2px; }
.rooms-panel-dialog fieldset { border: 0; margin: 0; padding: 0; min-width: 0; }
.rooms-panel-dialog label:not(.check), .rooms-panel-dialog legend { font-size: 12.5px; margin: 0 0 2px; padding: 0; }
.rooms-panel-dialog .rooms-connect { padding: 10px 12px; margin: 0 0 12px; }
.rooms-panel-dialog .room-chip { font-size: 12px; padding: 2px 8px; }
.rooms-panel-dialog .rooms-chips { gap: 5px; }
.rooms-danger { margin-top: 8px; padding-top: 14px; border-top: 1px solid var(--border); }

/*
 * A room's tasks: a bar (view, sort, show), a line to add one, then a list
 * or a board of three columns. A task is a row or a card: its title opens
 * it, and its chips say how urgent, for whom, by when, how many links and
 * where it came from.
 */
.tasks-bar { display: flex; flex-wrap: wrap; align-items: center; gap: 10px 14px; margin: 0 0 12px; }
.tasks-pick { display: inline-flex; align-items: center; gap: 6px; margin: 0; font-size: 13px; color: var(--muted); font-weight: 500; }
.tasks-pick select { width: auto; padding: 4px 8px; font-size: 13px; }
form.tasks-quick { display: flex; flex-direction: row; gap: 8px; margin: 0 0 12px; max-width: none; }
form.tasks-quick input { flex: 1; min-width: 0; }
form.tasks-quick button { flex: none; width: auto; }
.tasks-list, .tasks-board ul { list-style: none; margin: 0; padding: 0; }
.tasks-list { display: flex; flex-direction: column; gap: 6px; margin-bottom: 14px; }
.tasks-item {
  display: flex;
  align-items: flex-start;
  gap: 10px;
  padding: 10px 12px;
  border: 1px solid var(--border);
  border-left: 3px solid var(--border);
  border-radius: 10px;
  background: var(--card);
}
.tasks-item.is-high { border-left-color: var(--warn); }
.tasks-item.is-urgent { border-left-color: var(--danger); }
.tasks-item > input[type="checkbox"] { width: auto; margin: 3px 0 0; flex: none; }
.tasks-body { min-width: 0; flex: 1; display: flex; flex-direction: column; gap: 5px; }
button.tasks-title {
  padding: 0;
  border: 0;
  background: none;
  color: var(--text);
  font: inherit;
  font-size: 14.5px;
  font-weight: 600;
  line-height: 1.35;
  text-align: start;
  cursor: pointer;
  overflow-wrap: anywhere;
}
button.tasks-title:hover { color: var(--blue); }
.tasks-item.is-done button.tasks-title { color: var(--muted); text-decoration: line-through; font-weight: 500; }
.tasks-meta { display: flex; flex-wrap: wrap; gap: 4px 6px; }
.tasks-chip { padding: 1px 7px; border-radius: 999px; background: var(--bg-sunken); color: var(--text-soft); font-size: 11.5px; line-height: 1.6; }
.tasks-chip.is-high { background: var(--warn-soft); color: var(--warn); font-weight: 600; }
.tasks-chip.is-urgent { background: var(--danger-soft); color: var(--danger); font-weight: 600; }
.tasks-chip.is-low { color: var(--muted); }
.tasks-chip.is-doing { background: var(--blue-soft); color: var(--blue); }
.tasks-chip.is-late { background: var(--danger-soft); color: var(--danger); }
.tasks-chip.is-source { background: none; border: 1px dashed var(--border-strong); }
.tasks-board { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: 12px; align-items: start; margin-bottom: 14px; }
.tasks-col { min-width: 0; padding: 10px; border-radius: 12px; background: var(--bg-sunken); }
.tasks-col h3 { margin: 0 0 8px; font-size: 12.5px; font-weight: 600; letter-spacing: .03em; text-transform: uppercase; color: var(--muted); }
.tasks-col ul { display: flex; flex-direction: column; gap: 8px; min-height: 40px; }
.tasks-col.is-over { outline: 2px dashed var(--blue); outline-offset: -2px; }
.tasks-col .tasks-item { cursor: grab; }
.tasks-item.is-dragged { opacity: .5; }
.tasks-none { color: var(--faint); font-size: 13px; text-align: center; padding: 6px 0; }
.tasks-auto { display: flex; flex-wrap: wrap; align-items: center; gap: 6px 10px; padding-top: 12px; border-top: 1px solid var(--border); font-size: 13px; }
.tasks-auto label { margin: 0; font-weight: 600; font-size: 13px; }
.tasks-auto select { width: auto; max-width: 100%; padding: 4px 8px; font-size: 13px; }
@media (max-width: 720px) {
  .tasks-board { grid-template-columns: minmax(0, 1fr); }
}
#taskLinks a { min-width: 0; overflow-wrap: anywhere; }

/*
 * Files and docs: a room's documents by link, each with what it is (a Google
 * Doc, a sheet...), who added it, and Preview / PDF / Remove; then files,
 * then notes. A preview is a large dialog with Google's own page in it.
 */
form.docs-add { display: flex; flex-direction: row; flex-wrap: wrap; gap: 8px; margin: 0 0 8px; max-width: none; }
form.docs-add input[type="url"] { flex: 2 1 260px; min-width: 0; }
form.docs-add input[maxlength] { flex: 1 1 160px; min-width: 0; }
form.docs-add input[type="file"] { flex: 1 1 220px; min-width: 0; }
form.docs-add button { flex: none; width: auto; }
.docs-heading { margin: 18px 0 8px; font-size: 12.5px; font-weight: 600; letter-spacing: .03em; text-transform: uppercase; color: var(--muted); }
.docs-list { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: 6px; }
.docs-item { display: flex; align-items: center; gap: 12px; padding: 10px 12px; border: 1px solid var(--border); border-radius: 10px; background: var(--card); }
.docs-kind { flex: none; min-width: 92px; padding: 2px 8px; border-radius: 6px; background: var(--bg-sunken); color: var(--text-soft); font-size: 11.5px; font-weight: 600; text-align: center; }
.docs-item.is-doc .docs-kind { background: var(--blue-soft); color: var(--blue); }
.docs-item.is-sheet .docs-kind { background: var(--ok-soft); color: var(--ok); }
.docs-item.is-slides .docs-kind { background: var(--warn-soft); color: var(--warn); }
.docs-body { flex: 1; min-width: 0; display: flex; flex-direction: column; gap: 2px; }
.docs-body a { font-weight: 600; overflow-wrap: anywhere; }
.docs-actions { flex: none; display: flex; gap: 6px; align-items: center; }
.docs-actions button, .docs-actions .btn { padding: 4px 10px; font-size: 12.5px; }
.docs-none { color: var(--muted); font-size: 13.5px; padding: 6px 0; }
dialog.rooms-dialog.docs-preview-dialog { width: min(1100px, calc(100vw - 24px)); height: min(88dvh, 900px); padding: 14px 16px; display: none; flex-direction: column; gap: 8px; overflow: hidden; }
dialog.rooms-dialog.docs-preview-dialog[open] { display: flex; }
dialog.docs-preview-dialog .rooms-dialog-head { margin: 0; }
dialog.docs-preview-dialog .rooms-dialog-head h2 { min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; font-size: 16px; }
dialog.docs-preview-dialog .rooms-dialog-head .btn-row { flex: none; gap: 6px; }
#previewFrame { flex: 1; width: 100%; min-height: 0; border: 1px solid var(--border); border-radius: 8px; background: #fff; }
@media (max-width: 640px) {
  .docs-item { flex-wrap: wrap; }
  .docs-kind { min-width: 0; }
  .docs-actions { width: 100%; justify-content: flex-end; }
}

/* The built-in agents: one chip per @handle, so fourteen fit in five short rows. */
.rooms-chips { display: flex; flex-wrap: wrap; gap: 3px; margin: 0 0 5px; }
.room-chip {
  display: inline-flex;
  align-items: center;
  padding: 1px 6px;
  border: 1px solid var(--border-strong);
  border-radius: 999px;
  background: var(--card);
  color: var(--text);
  font-size: 11px;
  font-weight: 500;
  line-height: 1.45;
}
.rooms-side button.room-chip { padding: 1px 6px; font-size: 11px; font-weight: 500; line-height: 1.45; }
button.room-chip:hover { background: var(--bg-sunken); }
.room-chip[aria-pressed="true"] { border-color: color-mix(in srgb, var(--blue) 45%, var(--border-strong)); background: var(--blue-soft); color: var(--blue); }
.room-chip[aria-pressed="false"] { color: var(--faint); text-decoration: line-through; }
.room-chip.needs-connect { border-style: dashed; }
.room-chip:disabled, .room-chip[aria-disabled="true"] { opacity: .55; text-decoration: line-through; cursor: default; }

.rooms-center { display: flex; flex-direction: column; min-width: 0; min-height: 0; }
.rooms-center > .callout { margin: 12px 18px 0; }
.rooms-empty { margin: auto; max-width: 560px; padding: 40px 24px; text-align: center; }
/* Taller than a phone's screen, it scrolls inside the column rather than
   spilling over the footer under the fixed-height app. */
.rooms-center:has(> .rooms-empty:not(.hidden)) { overflow-y: auto; }
/* Its buttons sit under the centred words, not at the left edge of the column. */
.rooms-empty .btn-row { justify-content: center; }
.rooms-join { margin: 40px auto; width: calc(100% - 32px); max-width: 420px; }
.rooms-chat { display: flex; flex-direction: column; flex: 1; min-height: 0; }
.rooms-chat-head { display: flex; align-items: center; gap: 10px; padding: 12px 18px; border-bottom: 1px solid var(--border); }
.rooms-chat-title { flex: 1; min-width: 0; }
.rooms-chat-title h2 { font-size: 17px; margin: 0; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
/* The installed app's two buttons: beside the title on a wide screen, on a line of their own on a phone. */
.rooms-chat-head #installApp, .rooms-chat-head #pushToggle { width: auto; flex: none; }
@media (max-width: 600px) {
  .rooms-chat-head { flex-wrap: wrap; row-gap: 8px; }
  .rooms-chat-head .rooms-chat-title { min-width: 45%; }
  .rooms-chat-head #installApp, .rooms-chat-head #pushToggle { order: 10; flex: 1 1 40%; padding: 6px 11px; font-size: 13px; }
}

/* A conversation, so it reads like one: each message a block with who said
   it above what they said, the room's own notices quieter than people.
   `pre-wrap` because a message's line breaks are the writer's, and an
   agent's numbered list is lines. */
.room-messages { flex: 1; min-height: 0; overflow-y: auto; padding: 14px 18px; }
.room-msg { max-width: 820px; margin: 0 auto; padding: 9px 0; border-bottom: 1px solid var(--border); }
.room-msg:last-child { border-bottom: 0; }
.room-msg-who { font-size: 12.5px; color: var(--text-soft); margin-bottom: 2px; }
.room-msg-who b { color: var(--text); }
.room-msg-text { white-space: pre-wrap; overflow-wrap: anywhere; line-height: 1.55; }
/* A room or chat shared to the web: the conversation as a page, its chats one after another. */
.shared-room { max-width: 860px; padding-block: 28px 40px; }
.shared-room h1 { font-size: 26px; margin-bottom: 18px; }
.shared-chat + .shared-chat { margin-top: 28px; }
.shared-chat h2 { font-size: 18px; margin: 0 0 6px; padding-bottom: 6px; border-bottom: 1px solid var(--border); }
.shared-cta { margin-top: 32px; }
.room-msg.is-system .room-msg-text { color: var(--text-soft); font-size: 13px; font-style: italic; }
.room-msg.is-ai .room-msg-who b { color: var(--blue); }
.rooms-typing { margin: 0; padding: 0 18px; min-height: 18px; }
form.room-composer {
  display: flex;
  flex-direction: row;
  gap: 10px;
  align-items: flex-end;
  max-width: none;
  margin: 0;
  padding: 10px 18px 4px;
  border-top: 1px solid var(--border);
}
.room-composer textarea { flex: 1; min-height: 46px; max-height: 220px; margin: 0; }

/* @ in the composer: the room's agents in a list just above it. */
form.room-composer { position: relative; }
.room-mentions {
  position: absolute;
  inset-inline-start: 18px;
  bottom: calc(100% + 4px);
  width: min(380px, calc(100% - 36px));
  max-height: 268px;
  overflow-y: auto;
  margin: 0;
  padding: 4px;
  list-style: none;
  background: var(--card);
  border: 1px solid var(--border-strong);
  border-radius: 10px;
  box-shadow: var(--shadow-lg);
  z-index: 20;
}
.room-mentions li { padding: 5px 8px; border-radius: 6px; cursor: pointer; min-width: 0; }
.room-mentions li[aria-selected="true"] { background: var(--blue-soft); }
.room-mention-who { font-size: 13px; color: var(--text); }
.room-mention-who b { color: var(--blue); font-weight: 600; }
.room-mention-about { font-size: 11.5px; color: var(--muted); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }

/* An @agent's card: what it does, whether it answers here, and where to read more. */
.room-card {
  position: fixed;
  z-index: 70;
  width: 280px;
  max-width: calc(100vw - 16px);
  padding: 10px 12px;
  background: var(--card);
  border: 1px solid var(--border-strong);
  border-radius: 10px;
  box-shadow: var(--shadow-lg);
  font-size: 12.5px;
  line-height: 1.45;
}
.room-card-head { display: flex; align-items: baseline; gap: 6px; flex-wrap: wrap; margin-bottom: 2px; }
.room-card-head b { font-size: 13.5px; color: var(--text); }
.room-card-head span { color: var(--blue); }
.room-card-meta { color: var(--muted); font-size: 11.5px; margin: 0 0 6px; }
.room-card p { margin: 0 0 6px; color: var(--text-soft); }
.room-card .room-card-quote { white-space: pre-wrap; font-style: italic; }
.room-card .room-card-warn { color: var(--warn, var(--text-soft)); }
.room-card a { font-size: 12px; overflow-wrap: anywhere; }
.room-card > :last-child { margin-bottom: 0; }
.rooms-hint { margin: 0; padding: 0 18px 12px; }

/* The buttons that open and close a drawer exist only where there are drawers. */
.rooms-close, .rooms-open-left, .rooms-chat-head [data-open="right"] { display: none; }
.rooms-drawer-head { display: none; }

@media (max-width: 960px) {
  main.rooms-app,
  main.rooms-app:has(> .rooms-left.hidden) { grid-template-columns: minmax(0, 1fr); }
  /* Over the header too, so the drawer's own Close is always on screen. */
  .rooms-side {
    position: fixed;
    top: 0;
    bottom: 0;
    width: min(340px, 88vw);
    z-index: 60;
    box-shadow: var(--shadow-lg);
    transition: transform .18s ease;
  }
  .rooms-left { left: 0; transform: translateX(-105%); }
  main.show-left .rooms-left { transform: none; }
  .rooms-close, .rooms-open-left { display: inline-flex; }
  .rooms-drawer-head { display: flex; justify-content: flex-end; }
  /* With the side columns in drawers, nothing open grows the page like any
     other, instead of scrolling inside a box the height of the screen. */
  main.rooms-app:has(> .rooms-center > .rooms-empty:not(.hidden)) { height: auto; min-height: calc(100dvh - var(--rooms-top, 58px)); }
}

/* A token or an invite link is one long word: wrap it rather than cut it off. */
.room-token { white-space: pre-wrap; overflow-wrap: anywhere; word-break: break-all; }
/*
 * A callout straight after a list of steps. The last step has no bottom
 * padding of its own, so without this the callout sat against the step's
 * last line and read as part of it.
 */
.steps + .callout { margin-top: 28px; }

/* White-label settings: the done-for-you prices, a step apart from the choice above them. */
#readyPrices { margin-top: 12px; }

/* Project room settings: a dialog over the rooms, presets first, then every number they set. */
.rooms-side-head .rooms-settings-open { padding: 3px 9px; font-size: 12px; margin-inline-start: auto; }
dialog.rooms-dialog {
  width: min(640px, calc(100vw - 24px));
  max-height: calc(100dvh - 32px);
  padding: 20px 22px;
  border: 1px solid var(--border-strong);
  border-radius: 14px;
  background: var(--card);
  color: var(--text);
  box-shadow: var(--shadow-lg);
  /* Long settings scroll down, never sideways. */
  overflow-x: hidden;
}
dialog.rooms-dialog::backdrop { background: rgba(16, 20, 27, .45); }
form.rooms-settings { max-width: none; gap: 12px; }
.rooms-settings h2 { font-size: 18px; margin: 0; }
.rooms-presets { display: grid; grid-template-columns: repeat(auto-fit, minmax(132px, 1fr)); gap: 8px; }
.rooms-preset {
  display: flex;
  flex-direction: column;
  gap: 3px;
  margin: 0;
  padding: 9px 10px;
  border: 1px solid var(--border-strong);
  border-radius: 10px;
  font-weight: 400;
  font-size: 12px;
  color: var(--text-soft);
  cursor: pointer;
  /* The radio inside is out of sight and out of the layout: placed against its own card, one pixel wide. */
  position: relative;
}
.rooms-preset b { font-size: 13.5px; color: var(--text); }
.rooms-preset input { position: absolute; top: 0; left: 0; width: 1px; height: 1px; margin: 0; opacity: 0; pointer-events: none; }
.rooms-preset:has(input:checked) { border-color: var(--blue); background: var(--blue-soft); }
.rooms-preset:has(input:focus-visible) { outline: 2px solid var(--blue); outline-offset: 2px; }
fieldset.rooms-set { border: 1px solid var(--border); border-radius: 10px; margin: 0; padding: 10px 12px 12px; display: flex; flex-direction: column; gap: 8px; }
fieldset.rooms-set legend { font-weight: 600; font-size: 13.5px; color: var(--text); padding: 0 4px; }
/* The white-label page's ready-made products: each one's mark beside its name. */
fieldset.rooms-set legend .muted { font-weight: 400; }
.ready-logo { width: 20px; height: 20px; border-radius: 5px; vertical-align: -5px; margin-right: 4px; }

/* The white-label page's assistant: a short conversation above the full form. */
.wl-assistant { margin-bottom: 18px; }
.wl-chat { list-style: none; margin: 0 0 10px; padding: 0; display: flex; flex-direction: column; gap: 6px; max-height: 360px; overflow-y: auto; }
.wl-msg { max-width: 88%; padding: 8px 12px; border-radius: 12px; line-height: 1.45; overflow-wrap: anywhere; }
.wl-bot { align-self: flex-start; background: var(--bg-sunken); color: var(--text); border-bottom-left-radius: 4px; }
.wl-me { align-self: flex-end; background: var(--blue-soft); color: var(--text); border: 1px solid var(--blue-line); border-bottom-right-radius: 4px; }
.wl-swatch { display: inline-block; width: 14px; height: 14px; border-radius: 4px; border: 1px solid var(--border-strong); vertical-align: -2px; margin-right: 6px; }
.wl-logo-preview { width: 32px; height: 32px; border-radius: 6px; object-fit: contain; vertical-align: middle; margin-right: 8px; background: var(--card); border: 1px solid var(--border); }
.wl-chips { display: flex; flex-wrap: wrap; gap: 6px; margin: 0 0 10px; }
.wl-chips:empty { display: none; }
/* The white-label page's share card editor: the card at half size, and its upload buttons. */
.wl-card-preview { display: block; width: 100%; max-width: 600px; height: auto; aspect-ratio: 1200 / 630; margin: 10px 0 6px; border-radius: 10px; border: 1px solid var(--border); background: var(--bg-sunken); }
.wl-upload { margin-top: 8px; }
.wl-card-bg { display: flex; align-items: center; gap: 14px; flex-wrap: wrap; }
.wl-card-bg input[type="color"] { width: 56px; height: 36px; padding: 2px; flex: none; }
.wl-card-logo { border: 0; padding: 0; margin: 14px 0 6px; display: flex; flex-wrap: wrap; gap: 6px 16px; }
.wl-card-logo legend { padding: 0; margin-bottom: 6px; font-weight: 600; }
.wl-assistant form.wl-ask { display: flex; flex-direction: row; align-items: stretch; gap: 8px; max-width: none; margin: 0; }
.wl-ask input { flex: 1; min-width: 0; margin: 0; }
/* The white-label page's own sending address: the part before the @ and the domain, on one line. */
.wl-address { display: flex; align-items: center; gap: 6px; }
.wl-address input { min-width: 0; }
.wl-address input:first-child { flex: 1 1 0; }
.wl-address input:last-child { flex: 2 1 0; }
.wl-records td { overflow-wrap: anywhere; word-break: break-all; }
.wl-assistant .wl-ask button { width: auto; flex: none; }
.wl-all > summary { cursor: pointer; font-weight: 600; margin: 0 0 12px; }
.rooms-set-row { display: grid; grid-template-columns: minmax(0, 1fr) 220px; gap: 10px; align-items: center; }
.rooms-set-row label { margin: 0; font-size: 13px; font-weight: 500; color: var(--text); }
.rooms-set-row input, .rooms-set-row select { padding: 5px 8px; font-size: 13px; }
.rooms-unit { display: block; font-size: 11.5px; font-weight: 400; color: var(--muted); }
.rooms-usage { list-style: none; margin: 0; padding: 0; font-size: 12px; color: var(--muted); }
/* Save and Cancel stay in view at the foot of the dialog while the settings above them scroll. */
.rooms-set-actions {
  justify-content: flex-end;
  position: sticky;
  bottom: -20px;
  margin: 0 -22px -20px;
  padding: 12px 22px 16px;
  background: var(--card);
  border-top: 1px solid var(--border);
}
@media (max-width: 560px) {
  .rooms-set-row { grid-template-columns: minmax(0, 1fr); gap: 3px; }
}

/* A mention held for a member, above the composer. */
.room-handoffs { display: flex; flex-direction: column; gap: 6px; padding: 8px 18px 0; }
.room-handoff {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: 8px;
  padding: 7px 10px;
  border: 1px solid color-mix(in srgb, var(--blue) 35%, var(--border));
  border-radius: 10px;
  background: var(--blue-soft);
  font-size: 13px;
}
.room-handoff span { flex: 1; min-width: 12em; }
.room-handoff button { padding: 4px 10px; font-size: 12.5px; }

/* White-label settings: a Google Ads ID beside its button, a customer picker beside the ID. */
.btn-row .gads-cid { width: 15ch; flex: none; font-family: var(--mono); }
.btn-row .gads-org { width: auto; flex: 1 1 14em; min-width: 0; }
#gadsStep .row .badge { margin-inline-start: 6px; }
#gadsStep h3 { margin-top: 20px; }
/* Admin: a partner's Google Ads link request, with the note staff leave for it. */
[data-gads] > div:first-child { flex: 1 1 18em; min-width: 0; }
[data-gads] input { margin-top: 6px; padding: 6px 9px; }
/* A Google Ads change an agent proposed: amber, because applying it changes a live account. */
.room-change { border-color: color-mix(in srgb, var(--warn) 40%, var(--border)); background: var(--warn-soft); }
.room-change details { flex-basis: 100%; font-size: 12.5px; }
.room-change summary { cursor: pointer; color: var(--muted); }
.room-change pre { max-height: 180px; overflow: auto; margin: 6px 0 0; padding: 8px; background: var(--card); border-radius: 8px; font-size: 12px; white-space: pre-wrap; word-break: break-word; }
/* From the account to the customer, in the direction the line reads. */
.gads-arrow { display: inline-block; }
[dir="rtl"] .gads-arrow { transform: scaleX(-1); }

/* Guides: a page's questions, each a heading a reader can scan for, its answer under it. */
.faq h3 { margin: 18px 0 4px; font-size: 16px; }
.faq h3:first-child { margin-top: 6px; }
.faq p { margin-top: 0; }

/*
 * The project rooms landing page's example room: the room's own classes
 * (.rooms-chat-head, .room-msg, .room-handoff), in a frame of its own,
 * at a reading width rather than the app's full-height columns. Nothing
 * in it can be pressed, so its "buttons" are spans dressed as buttons.
 */
.rooms-preview { margin: 0; }
.rooms-preview-frame {
  border: 1px solid var(--border);
  border-radius: var(--radius);
  background: var(--card);
  box-shadow: var(--shadow-lg);
  overflow: hidden;
}
.rooms-preview .rooms-chat-title h3 { font-size: 16px; margin: 0; }
.rooms-preview .room-messages { overflow: visible; }
.rooms-preview .room-handoff { max-width: 820px; margin: 10px auto 0; }
.rooms-preview .room-handoff .rooms-preview-btn { flex: none; min-width: 0; }
.rooms-preview-btn {
  display: inline-flex;
  align-items: center;
  padding: 4px 10px;
  border: 1px solid var(--blue);
  border-radius: var(--radius-sm);
  background: var(--blue);
  color: #fff;
  font-size: 12.5px;
  font-weight: 600;
}
.rooms-preview-btn.secondary { background: var(--card); color: var(--text); border-color: var(--border-strong); }
.rooms-preview-composer {
  margin: 0 18px 14px;
  padding: 12px 14px;
  border: 1px solid var(--border-strong);
  border-radius: var(--radius-sm);
  color: var(--muted);
  font-size: 13.5px;
}
.rooms-preview figcaption { margin-top: 8px; }

/* A room's tabs: its conversation, and the work beside it. */
.rooms-tabs { padding: 8px 18px 0; }
.rooms-tab { display: flex; flex-direction: column; flex: 1; min-height: 0; }
.rooms-work { overflow-y: auto; padding: 12px 18px; gap: 10px; }
.rooms-work .rooms-rows { font-size: 13.5px; }
.rooms-rows li:has(.rooms-error) { flex-wrap: wrap; }
.rooms-error { flex-basis: 100%; color: var(--danger); }
.rooms-qr { display: block; width: 220px; height: 220px; margin: 8px auto 0; background: #fff; padding: 6px; border-radius: 8px; }

/* ------------------------------------------------------ builders' group -- */

/*
 * The builders' group on the landing page: what it is on the left, the
 * join form on the right.
 *
 * `form` caps at 380px for the auth card, so in this card's right column
 * every field stopped ~130px short of the card's edge while the copy on
 * the left ran the full width of its own. And `.option-grid` starts both
 * columns at the top and leaves each as tall as its content, so the copy
 * ended half way down a card the form filled. The columns now stretch,
 * the form fills its column, and the founder's line is pushed to the foot
 * of the copy, level with the submit button beside it. Where the copy is
 * the taller column, the textarea takes up the difference, so the two
 * buttons still end on one line.
 *
 * Two columns from a fixed width rather than `auto-fit`, so the stacked
 * layout -- where the founder's line needs a rule under it as well, or the
 * form's first label reads as its caption -- has a breakpoint to hang on.
 */
.community-grid { grid-template-columns: 1fr; align-items: stretch; }
@media (min-width: 721px) {
  .community-grid { grid-template-columns: 1fr 1fr; }
}
.community-grid form { max-width: none; }
.community-grid textarea { flex: 1 0 auto; }
.community-copy { display: flex; flex-direction: column; }
/* Beside a taller form, the words sit in the middle of the room above the founder's row. */
.community-copy > :first-child { margin-block: auto; }
/* An empty status line kept the form's 12px gap and its own 12px margin,
   so "Ask to join" sat 36px under the textarea where every other gap is
   12. Out of the flow rather than `display: none` like `.error:empty`: a
   polite live region that is not in the tree when its text arrives is not
   reliably announced, where role="alert" is. */
.community-grid .ok-text:empty { position: absolute; margin: 0; }

.founder {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 10px 12px;
  padding-top: 16px;
  border-top: 1px solid var(--border);
}
.founder-photo { width: 52px; height: 52px; border-radius: 12px; flex: none; object-fit: cover; }
.founder-who { flex: 1 1 96px; min-width: 0; margin: 0; font-size: 13.5px; line-height: 1.35; }
.founder-who b { display: block; color: var(--text); }
.founder-who span { color: var(--muted); }
.founder-actions { display: flex; flex-wrap: wrap; gap: 8px; flex: none; }
.founder-cta, .founder-book { flex: none; gap: 8px; }
.founder-cta svg { flex: none; }
@media (max-width: 720px) {
  .founder { padding-bottom: 16px; border-bottom: 1px solid var(--border); }
  /* Under the name, the two buttons side by side; a long label wraps inside its button. */
  .founder-actions { flex: 1 1 100%; flex-wrap: nowrap; }
  .founder-actions .founder-book { flex: none; }
  .founder-actions .founder-cta { flex: 1 1 auto; min-width: 0; padding-inline: 12px; text-align: center; }
}

/*
 * The installed app's Inbox (inbox.html): the rooms' layout with two
 * columns, the conversations and the open one. Under 960px the list is a
 * drawer, as the rooms' are.
 */
main.rooms-app.inbox-app { grid-template-columns: 300px minmax(0, 1fr); }
main.rooms-app.inbox-app:has(> .rooms-left.hidden) { grid-template-columns: minmax(0, 1fr); }
.inbox-status { margin-bottom: 8px; width: 100%; }
.inbox-status button { flex: 1; }
.inbox-filters { margin-bottom: 8px; }
.inbox-list a.inbox-row {
  display: grid;
  grid-template-columns: minmax(0, 1fr) auto;
  gap: 1px 8px;
  align-items: baseline;
  position: relative;
}
.inbox-list .inbox-who { min-width: 0; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.inbox-list .inbox-when { font-size: 11px; color: var(--muted); }
.inbox-list .inbox-last, .inbox-list .inbox-chan {
  grid-column: 1 / -1;
  min-width: 0;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  font-weight: 400;
}
.inbox-list .inbox-last { font-size: 12px; color: var(--text-soft); }
.inbox-list .inbox-chan { font-size: 11px; color: var(--muted); }
.inbox-list a.is-unread .inbox-who, .inbox-list a.is-unread .inbox-last { font-weight: 600; color: var(--text); }
.inbox-list .inbox-unread { position: absolute; right: 8px; bottom: 6px; font-size: 10.5px; padding: 0 6px; }
.inbox-tools {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 6px 8px;
  padding: 8px 18px;
  border-bottom: 1px solid var(--border);
  font-size: 12.5px;
  color: var(--text-soft);
}
.inbox-tools label { margin: 0; font-size: 12.5px; }
.inbox-tools select, .inbox-tools input { width: auto; padding: 3px 7px; font-size: 12.5px; }
.inbox-tools-gap { margin-inline-start: 8px; }
/* The label tools sit in the bar as if unwrapped (.hidden takes them out for a website chat's conversation). */
.inbox-label-tools { display: contents; }
/* A colour, in a form: the swatch at a field's height, not a hairline. */
.rooms-form input[type="color"] { height: 38px; padding: 3px; cursor: pointer; }
.room-msg .inbox-ai { margin-inline-start: 4px; font-size: 11px; font-weight: 600; color: var(--text-soft); }
.inbox-chips { display: inline-flex; flex-wrap: wrap; gap: 4px; }
.inbox-label button { padding: 0 0 0 4px; font-size: 12px; line-height: 1; background: none; border: 0; color: inherit; }
.inbox-label-form { display: inline-flex; flex-direction: row; align-items: center; gap: 4px; margin: 0; max-width: none; }
.inbox-label-form input { width: 150px; }
.inbox-label-form button { width: auto; padding: 3px 9px; font-size: 12px; }
.room-msg.is-outgoing .room-msg-who b { color: var(--blue); }
.room-msg.is-note { background: var(--warn-soft); border-radius: 8px; padding: 8px 10px; border-bottom: 0; margin-bottom: 4px; }
.room-msg.is-activity .room-msg-text { color: var(--text-soft); font-size: 13px; font-style: italic; }
.inbox-file { display: inline-block; margin-top: 4px; font-size: 13px; }
.inbox-file img { display: block; max-width: min(320px, 100%); max-height: 260px; border-radius: 8px; border: 1px solid var(--border); }
.inbox-send { display: flex; flex-direction: column; gap: 6px; align-items: stretch; }
.inbox-send .check { font-size: 12px; white-space: nowrap; margin: 0; }
.room-composer.is-note textarea { background: var(--warn-soft); }
@media (max-width: 960px) {
  main.rooms-app.inbox-app, main.rooms-app.inbox-app:has(> .rooms-left.hidden) { grid-template-columns: minmax(0, 1fr); }
}

/*
 * Illustrations: what the product does, shown moving rather than described.
 *
 * Plain elements and CSS, no pictures: the words in them are the page's own
 * (translated like every other word, renamed by brand.js on a brand's host),
 * and a reader who asks for less motion gets every frame at once, still.
 * illus.js plays the sequences ([data-illus-seq]: one child at a time, an AI
 * agent's after a moment of typing) and the cycles ([data-illus-cycle]),
 * only while they are on screen.
 */
.illus {
  position: relative;
  border: 1px solid var(--border);
  border-radius: var(--radius-lg);
  background:
    radial-gradient(120% 90% at 0% 0%, var(--blue-soft), transparent 60%),
    var(--card);
  padding: 16px;
  overflow: hidden;
}

/* The hero's exchange: one ask, one answer, then what was done, one line each. */
.illus-ask { gap: 9px; padding: 18px; }
.illus-ask .ill-msg { font-size: 14px; }
.ill-done {
  display: flex;
  align-items: center;
  gap: 9px;
  margin: 0;
  padding: 8px 11px;
  border-radius: 10px;
  background: var(--card);
  border: 1px solid var(--border);
  font-size: 13.5px;
  color: var(--text);
}
.ill-done::after { content: "✓"; margin-inline-start: auto; color: var(--ok); font-weight: 700; }
.ill-agent { font-style: normal; font-size: 13px; line-height: 1; }

/*
 * The hub: who asks and what is the member's own on the left, their
 * Service and Product hybrid in the middle, everything it reaches on the
 * right.
 *
 * One rhythm runs the whole picture. Every row on both sides is
 * `--hub-pitch` tall, whatever its chip holds, so a side's rows sit at
 * known heights rather than at whatever its text happened to measure; and
 * both sides are centred on the processor. That is what lets the three
 * cords a side plug in at the same three heights, mirrored -- centre, and
 * one pitch either way -- and lets the processor's side pins be drawn
 * exactly there instead of as a repeating band a cord lands on by luck.
 */
.illus-hub {
  --hub-pitch: 44px;
  /*
   * The gap between a side and the processor: the stub from a chip to the
   * collector, then the cord from the collector to a pin. Both are as
   * short as they can be and still read as cables, because at the page's
   * full width the two columns in and the four out leave the picture
   * almost nothing to spare -- and a language whose words are longer than
   * English's has even less. Turkish is the longest of the seventeen and
   * is what these numbers were measured against.
   */
  --hub-stub: 10px;
  --hub-cord: 24px;
  /* Between two chips in the same row. Not a cable, so not a cable's length. */
  --hub-gap: 8px;
  display: grid;
  grid-template-columns: max-content max-content max-content max-content max-content;
  justify-content: center;
  align-items: center;
  gap: 0;
  margin-top: 14px;
  /* Narrow sides: at the page's full width the two columns in and the four out leave little to spare. */
  padding: 26px 8px;
}
.hub-side { position: relative; display: grid; grid-auto-rows: var(--hub-pitch); align-items: center; column-gap: var(--hub-gap); }
/* In: three rows of two -- the assistant, then the thing of the member's own it reaches. */
.hub-in { grid-template-columns: max-content max-content; justify-items: end; }
/*
 * The inner chip fills its column, the outer one does not.
 *
 * Both columns are `max-content`, so a chip narrower than the widest in
 * its column has room to spare -- and with the side right-aligned that
 * room falls on its LEFT. On the outer column that is the ragged edge the
 * picture wants; on the inner one it was a gap between the cord and the
 * chip it plugs into, as wide as the difference between "Your members"
 * and "Your app/API". Filling the track closes it, and keeps every cord
 * to the processor the same length.
 */
.hub-in .hub-chip:nth-child(even) { justify-self: stretch; justify-content: center; }
/* Out, four across: the channels, the task tools, other agents, any API. */
.hub-out { grid-template-columns: repeat(4, max-content); }
.hub-chip {
  position: relative;
  display: inline-flex;
  align-items: center;
  gap: 7px;
  padding: 5px 10px;
  border: 1px solid var(--border-strong);
  border-radius: 999px;
  background: var(--card);
  font-size: 12.5px;
  font-weight: 500;
  color: var(--text);
  white-space: nowrap;
}
.hub-chip.is-lead { border-color: var(--blue); box-shadow: 0 0 0 3px var(--blue-soft); font-weight: 600; }

/*
 * "Your Brand" as a processor: a die inside a package, pins on every side
 * (the ::before rows top and bottom, the ::after columns left and right),
 * and a slow pulse.
 */
.hub-core {
  position: relative;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 4px;
  /*
   * Narrow enough that the four columns out and the two in still fit beside
   * it at the page's full width, in every language. The name wraps onto two
   * even lines rather than widening the die, which is the one thing here
   * that nothing else can give room back for.
   */
  width: max-content;
  min-width: 194px;
  max-width: 214px;
  min-height: 150px;
  padding: 18px 22px;
  border-radius: 14px;
  background:
    linear-gradient(var(--card), var(--card)) padding-box,
    linear-gradient(135deg, var(--blue), var(--blue-line) 60%, var(--blue)) border-box;
  border: 1.5px solid transparent;
  box-shadow: inset 0 0 0 7px var(--card), inset 0 0 0 8px var(--blue-line), var(--shadow);
  text-align: center;
  animation: hub-breathe 3.2s ease-in-out infinite;
}
/* Each paints only its outer bands, so it can sit above the pulse without covering the die. */
.hub-core::before,
.hub-core::after {
  content: "";
  position: absolute;
  pointer-events: none;
}
.hub-core::before {
  inset: -9px 20px;
  background:
    repeating-linear-gradient(90deg, var(--blue-line) 0 4px, transparent 4px 13px) top / 100% 9px no-repeat,
    repeating-linear-gradient(90deg, var(--blue-line) 0 4px, transparent 4px 13px) bottom / 100% 9px no-repeat;
}
/*
 * The side pins: three a side, where the three cords actually land --
 * centre, and one pitch either way. They used to be a repeating band, so
 * whether a cord met a pin or the gap between two was decided by how tall
 * the chips above it happened to be.
 */
.hub-core::after {
  /*
   * The accent, where the pins top and bottom are the line colour: these
   * are the six the cords actually land on, and the processor's own pulse
   * washes a band of --blue-soft over exactly this strip, which a pin in
   * the line colour disappears into. They reach a little further out than
   * that band, so the cord is seen to end ON something.
   */
  --pin: linear-gradient(var(--blue), var(--blue));
  inset: 0 -12px;
  /*
   * 0 and 100% rather than `left` and `right`: a keyword followed by a
   * length is the three-value position syntax, and the whole declaration
   * is dropped when a `/` size follows it -- which is how these pins came
   * to be absent rather than merely misplaced.
   */
  background:
    var(--pin) 0 calc(50% - var(--hub-pitch)) / 12px 5px no-repeat,
    var(--pin) 0 50% / 12px 5px no-repeat,
    var(--pin) 0 calc(50% + var(--hub-pitch)) / 12px 5px no-repeat,
    var(--pin) 100% calc(50% - var(--hub-pitch)) / 12px 5px no-repeat,
    var(--pin) 100% 50% / 12px 5px no-repeat,
    var(--pin) 100% calc(50% + var(--hub-pitch)) / 12px 5px no-repeat;
}
.hub-core .brand-mark { width: 30px; height: 30px; }
/* "Your Brand"'s logo: a place for one, until a brand's host puts its own there (brand.js). */
.hub-core .brand-mark.is-placeholder {
  border: 2px dashed var(--blue-line);
  border-radius: 9px;
  background: var(--blue-soft);
}
.hub-core .brand-mark.is-placeholder::before,
.hub-core .brand-mark.is-placeholder::after { content: none; }
/* Balanced, or a name this long leaves one word alone on its last line. */
.hub-core b { font-size: 16px; text-wrap: balance; }
.hub-core small { font-size: 11.5px; color: var(--muted); line-height: 1.35; text-wrap: balance; }

/*
 * The cables. Traffic runs both ways: a blue pulse going out, a green one
 * coming back.
 *
 * Within a row, each chip links to the one before it; the first chip of a
 * row links to its side's collector. The collector is what the three cords
 * leave from, which is how four rows out and three rows in can still plug
 * into the processor at the same three heights.
 */
.hub-in .hub-chip::after,
.hub-out .hub-chip::before,
.hub-bus > span {
  content: "";
  position: absolute;
  height: 3px;
  margin-top: -1.5px;
  border-radius: 2px;
  background:
    radial-gradient(circle, var(--blue) 0 2.5px, transparent 3px) 0 50% / 9px 9px no-repeat,
    radial-gradient(circle, var(--ok) 0 2.5px, transparent 3px) 100% 50% / 9px 9px no-repeat,
    linear-gradient(var(--blue-line), var(--blue-line));
  animation: hub-cable 2.4s linear infinite;
  animation-delay: calc(var(--n, 0) * -.37s);
}
/* A link within a row is short, so its pulse runs quicker over it. */
.hub-in .hub-chip::after,
.hub-out .hub-chip::before { top: 50%; width: var(--hub-stub); animation-duration: 1.2s; }
/* In, the row runs right: every chip links rightwards, the inner one to the collector. */
.hub-in .hub-chip::after { left: 100%; }
/* Out, the row runs left, back towards the processor. */
.hub-out .hub-chip::before { right: 100%; }

/*
 * A side's collector, and the three cords from it into the processor.
 *
 * The cords are children rather than one element's background layers
 * because each carries its own pulse at its own offset; the collector is
 * the bar they all leave from, spanning that side's rows.
 */
.hub-bus {
  position: relative;
  align-self: center;
  width: calc(var(--hub-cord) + var(--hub-stub));
  /* As tall as the three cords reach, which is one pitch either side of centre. */
  height: calc(var(--hub-pitch) * 2);
}
.hub-bus::before {
  content: "";
  position: absolute;
  top: 0;
  bottom: 0;
  width: 3px;
  border-radius: 2px;
  background: var(--blue-line);
}
/*
 * The cords are always the part nearest the processor and the stubs the
 * part nearest the chips, so on the way in they are the far side of the
 * bus and on the way out the near one. The collector stands between them.
 */
/*
 * Only OUT has a collector. In has three rows and the processor three pins
 * a side, so each row runs straight into its own pin and a bar across them
 * would be a junction with nothing to junction.
 */
.hub-bus-in::before { content: none; }
.hub-bus-out::before { inset-inline-start: var(--hub-cord); }
/*
 * Out has four rows, so its collector reaches past the cords to meet the
 * first and last of them. In has three, which the cords already span.
 */
.hub-bus-out::before { top: calc(var(--hub-pitch) * -.5); bottom: calc(var(--hub-pitch) * -.5); }
.hub-bus > span { width: var(--hub-cord); }
/* With no collector in the way, a cord in is the whole gap: the chip's stub joins it at the far end. */
.hub-bus-in > span { width: 100%; inset-inline-end: 0; }
.hub-bus-out > span { inset-inline-start: 0; }
.hub-bus > span:nth-child(1) { top: 0; --n: 0; }
.hub-bus > span:nth-child(2) { top: 50%; --n: 2; }
.hub-bus > span:nth-child(3) { top: 100%; --n: 4; }
.hub-chip:nth-child(1) { --n: 0; } .hub-chip:nth-child(2) { --n: 1; } .hub-chip:nth-child(3) { --n: 2; } .hub-chip:nth-child(4) { --n: 3; }
.hub-chip:nth-child(5) { --n: 4; } .hub-chip:nth-child(6) { --n: 5; } .hub-chip:nth-child(7) { --n: 6; } .hub-chip:nth-child(8) { --n: 7; }
.hub-chip:nth-child(9) { --n: 8; } .hub-chip:nth-child(10) { --n: 9; } .hub-chip:nth-child(11) { --n: 10; } .hub-chip:nth-child(12) { --n: 11; }
.hub-chip:nth-child(13) { --n: 12; } .hub-chip:nth-child(14) { --n: 13; } .hub-chip:nth-child(15) { --n: 14; } .hub-chip:nth-child(16) { --n: 15; }
@keyframes hub-cable {
  from { background-position: -9px 50%, calc(100% + 9px) 50%, 0 0; }
  to { background-position: calc(100% + 9px) 50%, -9px 50%, 0 0; }
}
@keyframes hub-cable-v {
  from { background-position: 50% -9px, 50% calc(100% + 9px), 0 0; }
  to { background-position: 50% calc(100% + 9px), 50% -9px, 0 0; }
}
@keyframes hub-breathe {
  0%, 100% { box-shadow: inset 0 0 0 7px var(--card), inset 0 0 0 8px var(--blue-line), var(--shadow), 0 0 0 0 var(--blue-soft); }
  50% { box-shadow: inset 0 0 0 7px var(--card), inset 0 0 0 8px var(--blue-line), var(--shadow), 0 0 0 10px var(--blue-soft); }
}
@media (prefers-reduced-motion: reduce) {
  .hub-in .hub-chip::after, .hub-out .hub-chip::before, .hub-side::before, .hub-side::after, .hub-core { animation: none; }
}

/*
 * Narrower than the three columns need: one under the other, in, the
 * processor, out, each a grid of equal columns. Every chip of the row
 * nearest the processor drops a cable to a bus, the bus one to the
 * processor; a chip further away links to the one beside it, as on a desktop.
 */
@media (max-width: 1100px) {
  .illus-hub {
    grid-template-columns: minmax(0, 1fr);
    gap: 22px;
    justify-items: center;
    /*
     * The processor is given an exact width here, rather than the wide
     * layout's max-content, because the cords below depend on knowing
     * where its pins are -- and a pin band that is a few pixels out is a
     * row of cords that each miss by the same few pixels.
     */
    --hub-core-w: 214px;
    /* Its pin band: the package, less the 20px its pins stop short of each end. */
    --hub-pins: calc(var(--hub-core-w) - 40px);
  }
  .hub-core { width: var(--hub-core-w); max-width: var(--hub-core-w); }
  .hub-side { position: relative; display: grid; grid-template-columns: repeat(4, 1fr); grid-auto-rows: auto; gap: 8px; width: 100%; max-width: 560px; }
  /*
   * In keeps its pairs: three across and filled DOWN each column, so a
   * column is still the assistant over the thing of the member's own it
   * reaches, as it is side by side on a wide screen.
   */
  /* And it loses the wide layout's right-alignment, or its cords miss the bus. */
  .hub-in { grid-template-columns: repeat(3, 1fr); grid-template-rows: repeat(2, auto); grid-auto-flow: column; justify-items: stretch; }
  /* Nothing plugs into the processor's sides here, so its side pins go quiet. */
  .hub-core::after { --pin: linear-gradient(var(--blue-line), var(--blue-line)); }
  .hub-chip { justify-content: center; }
  /* The cords and their collectors are the wide layout's; here a side has its own bus. */
  .hub-bus { display: none; }
  .hub-in .hub-chip::after,
  .hub-out .hub-chip::before,
  .hub-side::before,
  .hub-side::after {
    content: "";
    position: absolute;
    left: 50%;
    width: 3px;
    height: 11px;
    margin: 0 0 0 -1.5px;
    border-radius: 2px;
    background:
      radial-gradient(circle, var(--blue) 0 2.5px, transparent 3px) 50% 0 / 9px 9px no-repeat,
      radial-gradient(circle, var(--ok) 0 2.5px, transparent 3px) 50% 100% / 9px 9px no-repeat,
      linear-gradient(var(--blue-line), var(--blue-line));
    animation: hub-cable-v 1.6s linear infinite;
    animation-delay: calc(var(--n, 0) * -.37s);
  }
  .hub-in .hub-chip::after { top: 100%; left: 50%; }
  .hub-out .hub-chip::before { top: auto; right: auto; bottom: 100%; }
  /* The top of each pair reaches the bus through the one under it. */
  .hub-in .hub-chip:nth-child(odd)::after { height: 8px; }
  /*
   * From the bus to the processor, above it and below it -- one cord per
   * pin rather than one into the middle.
   *
   * It is drawn as the processor's own pin pattern (4px every 13px, from
   * 20px in) over the width of that band, so each cord lands ON a pin
   * instead of on the package between two. The pulse stays on the bus
   * above it and on the stubs from the chips; these are the last few
   * pixels, where a travelling dot would read as noise rather than as
   * traffic.
   */
  .hub-side::before {
    left: 50%;
    width: var(--hub-pins);
    margin: 0;
    transform: translateX(-50%);
    background: repeating-linear-gradient(90deg, var(--blue-line) 0 4px, transparent 4px 13px);
    animation: none;
  }
  .hub-in::before { top: calc(100% + 11px); }
  .hub-out::before { top: -22px; }
  /* The buses, from the first column's centre to the last's. */
  .hub-in::after,
  .hub-out::after {
    left: calc(12.5% - 3px);
    right: calc(12.5% - 3px);
    width: auto;
    height: 3px;
    margin: 0;
    background:
      radial-gradient(circle, var(--blue) 0 2.5px, transparent 3px) 0 50% / 9px 9px no-repeat,
      radial-gradient(circle, var(--ok) 0 2.5px, transparent 3px) 100% 50% / 9px 9px no-repeat,
      linear-gradient(var(--blue-line), var(--blue-line));
    animation-name: hub-cable;
    animation-duration: 2.4s;
  }
  .hub-in::after { top: calc(100% + 9.5px); }
  /* In is three columns wide, not four, so its bus runs between other centres. */
  .hub-in::after { left: calc(16.666% - 3px); right: calc(16.666% - 3px); }
  .hub-out::after { top: -12.5px; }
  /* A row further out reaches the processor through the chip in front of it. */
  .hub-out .hub-chip:nth-child(n + 5)::before { height: 8px; }
}
@media (max-width: 560px) {
  .hub-side { grid-template-columns: repeat(2, 1fr); }
  /*
   * Two across, and the pairs land as rows on their own: the source order
   * is assistant then what it reaches, so a row IS a pair again and the
   * chain runs down the columns, as it does on the other side.
   */
  .hub-in { grid-template-rows: auto; grid-auto-flow: row; }
  .hub-in::after, .hub-out::after { left: calc(25% - 2px); right: calc(25% - 2px); }
  .hub-in .hub-chip:nth-child(-n + 4)::after { height: 8px; }
  .hub-in .hub-chip:nth-child(n + 5)::after { height: 11px; }
  .hub-out .hub-chip:nth-child(n + 3)::before { height: 8px; }
}

/* The hub and what it does, one card with a line between them. */
.hub-card { padding: 0; overflow: hidden; margin-top: 14px; }
.hub-card > .illus-hub { margin-top: 0; border: 0; border-bottom: 1px solid var(--border); border-radius: 0; }
.hub-card > .feature-grid { padding: 20px; }

/* A channel's colour, as a dot beside its name. */
.ch { display: inline-block; width: 9px; height: 9px; border-radius: 50%; background: var(--muted); flex: none; }
.ch-linkedin { background: #0a66c2; }
.ch-whatsapp { background: #25d366; }
.ch-instagram { background: linear-gradient(135deg, #f58529, #dd2a7b 55%, #8134af); }
.ch-messenger { background: #0084ff; }
.ch-telegram { background: #229ed9; }
.ch-x { background: var(--text); }
.ch-discord { background: #5865f2; }
.ch-slack { background: conic-gradient(#e01e5a 0 25%, #36c5f0 0 50%, #2eb67d 0 75%, #ecb22e 0); }
.ch-email { background: #8a94a6; }
.ch-web { background: var(--blue); }
.ch-tiktok { background: linear-gradient(135deg, #25f4ee, #fe2c55); }
.ch-chatwoot { background: #1f93ff; }
.ch-hubspot { background: #ff7a59; }
.ch-asana { background: #f06a6a; }
.ch-clickup { background: linear-gradient(135deg, #7b68ee, #fd71af); }
.ch-agents { background: conic-gradient(var(--blue), #8b5cf6, #22c55e, var(--blue)); }
.ch-api { background: var(--text-soft); border-radius: 2px; }

/* "See it work": four short scenes, the LinkedIn API's first and widest. */
.illus-grid { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 16px; }
.illus-card { margin: 0; display: flex; flex-direction: column; gap: 10px; }
.illus-card figcaption { display: flex; flex-direction: column; gap: 2px; }
.illus-card figcaption b { font-size: 16px; color: var(--text); }
.illus-card figcaption span { font-size: 13.5px; color: var(--text-soft); }
.illus-card .illus { flex: 1; min-height: 250px; }
@media (max-width: 760px) { .illus-grid { grid-template-columns: minmax(0, 1fr); } }

/* Chat scenes: bubbles that arrive one after another. */
.illus-chat { display: flex; flex-direction: column; gap: 8px; }
/* The room scene on the Rooms page is drawn with the room's own classes; its typing dots sit in each message. */
.rooms-preview .room-msg { position: relative; }
.rooms-preview .room-msg.is-typing::after { left: 0; }
.ill-msg {
  position: relative;
  max-width: 86%;
  margin: 0;
  padding: 7px 11px;
  border-radius: 14px;
  font-size: 13px;
  line-height: 1.45;
  background: var(--bg-sunken);
  color: var(--text);
}
.ill-msg.is-me { align-self: flex-end; background: var(--blue); color: var(--on-accent); border-bottom-right-radius: 4px; }
.ill-msg.is-ai { align-self: flex-start; background: var(--card); border: 1px solid var(--border-strong); border-bottom-left-radius: 4px; }
.ill-msg.is-person { align-self: flex-start; border-bottom-left-radius: 4px; }
.ill-who { display: block; font-size: 11px; font-weight: 600; color: var(--muted); margin-bottom: 1px; }
.ill-msg.is-ai .ill-who { color: var(--violet); }
.ill-card {
  align-self: stretch;
  margin: 0;
  padding: 8px 11px;
  border-radius: 10px;
  font-size: 12.5px;
  font-weight: 600;
  border: 1px solid transparent;
}
.ill-ok { background: var(--ok-soft); color: var(--ok); }
.ill-note { background: var(--blue-soft); color: var(--blue-dark); border-color: var(--blue-line); }

/* The inbox scene: every channel's message in one list. */
.illus-inbox { display: flex; flex-direction: column; gap: 6px; }
.ill-row {
  display: grid;
  grid-template-columns: auto auto minmax(0, 1fr);
  align-items: center;
  gap: 8px;
  padding: 7px 10px;
  border-radius: 10px;
  background: var(--card);
  border: 1px solid var(--border);
  font-size: 13px;
}
.ill-row b { font-weight: 600; }
.ill-row span { color: var(--text-soft); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }

/* Played by illus.js: what has not arrived yet holds its place, unseen, so nothing jumps. */
[data-illus-seq].is-playing > * { transition: opacity .35s ease, transform .35s ease; }
[data-illus-seq].is-playing > :not(.is-on):not(.is-typing) { opacity: 0; transform: translateY(6px); }
[data-illus-seq] > .is-typing > :not(.ill-who):not(.room-msg-who) { visibility: hidden; }
[data-illus-seq] > .is-typing::after {
  content: "";
  position: absolute;
  left: 12px;
  bottom: 10px;
  width: 30px;
  height: 8px;
  background: radial-gradient(circle, var(--muted) 0 3px, transparent 3.5px) 0 50% / 10px 8px repeat-x;
  animation: ill-typing 1s steps(4) infinite;
  clip-path: inset(0 100% 0 0);
}
@keyframes ill-typing { to { clip-path: inset(0 -10% 0 0); } }

/* The white-label scene: one window, three brands, the same product. */
.illus-brand { display: grid; place-items: center; }
.ill-window {
  position: relative;
  width: min(100%, 360px);
  height: 196px;
  border-radius: 12px;
  border: 1px solid var(--border-strong);
  background: var(--card);
  box-shadow: var(--shadow);
  overflow: hidden;
}
.ill-skin { position: absolute; inset: 0; display: flex; flex-direction: column; opacity: 0; transition: opacity .6s ease; }
.ill-skin.is-on { opacity: 1; }
.ill-addr { margin: 8px 10px; padding: 4px 10px; border-radius: 999px; background: var(--bg-sunken); font: 11.5px var(--mono); color: var(--text-soft); }
.ill-head { display: flex; align-items: center; gap: 8px; padding: 6px 14px 10px; border-bottom: 1px solid var(--border); font-weight: 700; font-size: 14px; }
.ill-logo { width: 20px; height: 20px; border-radius: 6px; background: var(--skin); }
.ill-body { display: flex; flex-direction: column; gap: 7px; padding: 12px 14px; }
.ill-bar { height: 8px; border-radius: 4px; background: var(--bg-sunken); }
.ill-bar.w60 { width: 60%; } .ill-bar.w80 { width: 80%; } .ill-bar.w40 { width: 40%; }
.ill-btn { margin-top: 4px; width: 92px; height: 22px; border-radius: 6px; background: var(--skin); }
.ill-skin.s1 { --skin: var(--blue); }
.ill-skin.s2 { --skin: #e8590c; }
.ill-skin.s3 { --skin: #0f9d77; }

/* The dashboard's three steps to connect an AI, the current one lit. */
.illus-steps { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: 10px; padding: 12px; margin: 12px 0; }
.ill-step {
  display: flex;
  flex-direction: column;
  gap: 6px;
  padding: 10px 12px;
  border-radius: 10px;
  border: 1px solid var(--border);
  background: var(--card);
  font-size: 13px;
  color: var(--text-soft);
  transition: border-color .3s ease, box-shadow .3s ease;
}
.ill-step b { color: var(--text); font-size: 13.5px; }
.ill-step .ill-num {
  display: inline-grid;
  place-items: center;
  width: 22px;
  height: 22px;
  border-radius: 50%;
  background: var(--bg-sunken);
  font-size: 12px;
  font-weight: 700;
  color: var(--text-soft);
}
.ill-step.is-on { border-color: var(--blue); box-shadow: 0 0 0 3px var(--blue-soft); }
.ill-step.is-on .ill-num { background: var(--blue); color: var(--on-accent); }
.ill-chip { align-self: flex-start; padding: 3px 9px; border-radius: 999px; background: var(--bg-sunken); font: 11.5px var(--mono); color: var(--text-soft); }
/* A picture of the button, not the button: pale, so nobody tries to press it. */
.ill-pill { align-self: flex-start; padding: 3px 9px; border-radius: 7px; border: 1px solid var(--blue); background: var(--blue-soft); color: var(--blue); font-size: 12px; font-weight: 600; }
@media (max-width: 640px) { .illus-steps { grid-template-columns: minmax(0, 1fr); } }
.ill-step .ill-msg { align-self: flex-end; font-size: 12px; padding: 5px 9px; }
/* The Inbox screen before there is a conversation: the scene, then the three steps. */
.inbox-empty-title { margin: 0 0 8px; font-size: 24px; line-height: 1.25; }
.inbox-illus { text-align: left; margin: 14px auto 6px; max-width: 420px; }
.inbox-how { text-align: left; margin: 12px auto; max-width: 440px; padding-left: 20px; font-size: 14px; color: var(--text-soft); display: flex; flex-direction: column; gap: 4px; }
.inbox-how b { color: var(--text); }
/* What a new channel needs pasted elsewhere: a snippet, an address. Selectable, and never wider than the dialog. */
.inbox-add-code { white-space: pre-wrap; overflow-wrap: anywhere; max-height: 180px; overflow-y: auto; font-size: 12px; user-select: all; }
/* The dashboard's longer explanation, folded under the steps. */
.dash-ai-more { margin: 4px 0 12px; }
.dash-ai-more > summary { cursor: pointer; font-weight: 600; font-size: 14px; color: var(--blue); margin-bottom: 8px; }

/* Still, for whoever asked for less motion: every frame shown, nothing running. */
@media (prefers-reduced-motion: reduce) {
  .hub-core, .hub-in .hub-chip::after, .hub-out .hub-chip::before, .hub-side::before, .hub-side::after, [data-illus-seq] > .is-typing::after { animation: none; }
  [data-illus-seq].is-playing > * { transition: none; }
  .ill-skin { transition: none; }
}

/* ----------------------------------------------------- book a call -- */

/*
 * The popup book.js opens: who the call is with, then the days as a row of
 * chips (scrolling sideways on a phone), the day's times as a grid, and the
 * email form. One column, never wider than a phone needs.
 */
.book-dialog {
  width: min(520px, calc(100vw - 24px));
  max-height: calc(100dvh - 32px);
  padding: 0;
  border: 1px solid var(--border);
  border-radius: 16px;
  background: var(--card);
  color: var(--text);
  box-shadow: var(--shadow-lg);
}
.book-dialog::backdrop { background: rgba(15, 23, 42, .45); }
.book-head { display: flex; align-items: flex-start; gap: 12px; padding: 18px 18px 12px; border-bottom: 1px solid var(--border); }
.book-photo { width: 44px; height: 44px; flex: none; border-radius: 10px; }
.book-head h2 { margin: 0 0 2px; font-size: 18px; }
.book-head p { margin: 0; font-size: 13.5px; }
.book-head > div { flex: 1; min-width: 0; }
.book-close { flex: none; width: 34px; height: 34px; padding: 0; font-size: 22px; line-height: 1; }
.book-body { padding: 16px 18px 20px; overflow-y: auto; }
.book-body > p { margin-top: 0; }
.book-days { display: flex; gap: 8px; overflow-x: auto; padding-bottom: 6px; margin-bottom: 12px; scrollbar-width: thin; }
.book-day {
  flex: none;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 1px;
  min-width: 64px;
  padding: 8px 10px;
  border: 1px solid var(--border);
  border-radius: 10px;
  background: var(--card);
  color: var(--text);
  font-size: 12.5px;
  cursor: pointer;
}
.book-day b { font-size: 14px; }
.book-day[aria-pressed="true"] { border-color: var(--blue); background: var(--blue-soft); color: var(--blue); }
.book-times { display: grid; grid-template-columns: repeat(auto-fill, minmax(84px, 1fr)); gap: 8px; margin-bottom: 12px; }
.book-time {
  padding: 9px 6px;
  border: 1px solid var(--border);
  border-radius: 9px;
  background: var(--card);
  color: var(--text);
  font-size: 14px;
  font-variant-numeric: tabular-nums;
  cursor: pointer;
}
.book-time:hover, .book-time:focus-visible, .book-day:hover { border-color: var(--blue); }
.book-picked { display: flex; flex-wrap: wrap; align-items: baseline; gap: 6px 10px; }
.link-button { padding: 0; border: 0; background: none; color: var(--blue); font: inherit; font-size: 13.5px; cursor: pointer; text-decoration: underline; }
.book-note { padding: 8px 10px; border-radius: 8px; background: var(--warn-soft); color: var(--warn); font-size: 13.5px; }
.book-done { font-size: 16px; font-weight: 600; }

/* ---------------------------------------------------- booking pages -- */
/*
 * A member's booking page (book-page.html at /book/<slug>): the same steps
 * as the popup, inline in one card. And its settings (booking.html): the
 * fields in a grid that folds to one column on a phone.
 */
.book-page { max-width: 640px; }
.book-card h1 { margin: 2px 0 4px; }
.book-card .book-body { padding: 14px 0 0; }
.booking-settings form { max-width: none; }
.booking-settings .option-card form { gap: 8px; }
.booking-settings .option-card form p { margin: 0; }
.booking-fields { display: grid; grid-template-columns: repeat(auto-fit, minmax(240px, 1fr)); gap: 14px 18px; }
.booking-fields label { margin-bottom: 6px; }
.booking-slug { display: flex; align-items: center; gap: 4px; }
.booking-slug input { flex: 1; min-width: 0; }
.booking-days { border: 0; padding: 0; margin: 0; min-width: 0; }
.booking-days legend { font-size: 12.5px; color: var(--text-soft); font-weight: 600; padding: 0; margin-bottom: 6px; }
.booking-days > div { display: flex; flex-wrap: wrap; gap: 2px 14px; }
.booking-hours { display: grid; grid-template-columns: 1fr 1fr; gap: 12px; }
.booking-call { display: flex; flex-wrap: wrap; align-items: center; justify-content: space-between; gap: 6px 12px; padding: 10px 0; border-bottom: 1px solid var(--border); }
.booking-call:last-child { border-bottom: none; }
.booking-call b { font-weight: 600; }

/* ------------------------------------------------- the two home pages -- */

/*
 * The landing page is two pages and a door between them.
 *
 * `data-home` on <html> is set before the first paint by a few lines in
 * the page's own head, from what this reader chose last time: "ask" for
 * somebody who has not chosen, else "simple" or "detailed". The markup
 * ships the detailed page visible and the other two `hidden`, so a reader
 * with no JavaScript -- and a crawler -- never sees the door at all and
 * gets the page that was always there.
 */
html[data-home="ask"] #homeDetailed,
html[data-home="ask"] #homeSimple,
html[data-home="simple"] #homeDetailed,
html[data-home="detailed"] #homeSimple { display: none; }
html[data-home="simple"] #homeSimple { display: block; }
html[data-home="ask"] #firstRun { display: block; }

/*
 * The door: one screen, almost empty, with one small switch on it.
 *
 * The whole surface is a button, because "press anywhere" has to be
 * reachable by a keyboard as well as a thumb, and a button that fills its
 * container is both. The switch sits above it in the stacking order and a
 * little right of and below the middle -- late in the eye's path across
 * the screen, which is where the smaller of two choices belongs.
 */
.first-run {
  position: relative;
  /* The screen under the sticky header, less what main puts around it. */
  min-height: calc(100dvh - var(--header-h) - 108px);
  margin: -8px 0 0;
}
.first-run-rest {
  position: absolute;
  inset: 0;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 10px;
  width: 100%;
  padding: 24px;
  border: 0;
  border-radius: var(--radius-lg);
  background: radial-gradient(90% 70% at 50% 42%, var(--blue-soft), transparent 70%);
  color: inherit;
  text-align: center;
  cursor: pointer;
}
.first-run-rest:hover { background: radial-gradient(90% 70% at 50% 42%, var(--blue-soft), transparent 60%); }
.first-run-kicker { font-size: 12.5px; font-weight: 600; letter-spacing: .08em; text-transform: uppercase; color: var(--blue); }
.first-run-title { font-size: clamp(24px, 4vw, 38px); font-weight: 700; line-height: 1.15; letter-spacing: -.02em; max-width: 16em; }
.first-run-note { font-size: 15px; color: var(--muted); }
.first-run-toggle {
  position: absolute;
  top: calc(50% + 86px);
  inset-inline-start: calc(50% + 96px);
  display: inline-flex;
  align-items: center;
  gap: 9px;
  width: auto;
  padding: 9px 16px 9px 12px;
  border: 1px solid var(--border-strong);
  border-radius: 999px;
  background: var(--card);
  color: var(--text-soft);
  font-size: 14px;
  font-weight: 600;
  box-shadow: var(--shadow);
  cursor: pointer;
}
.first-run-toggle:hover { color: var(--text); border-color: var(--blue); }
/* A switch, drawn: the one control on the screen should look like one. */
.first-run-switch {
  width: 34px;
  height: 20px;
  border-radius: 999px;
  background: var(--bg-sunken);
  border: 1px solid var(--border-strong);
  position: relative;
  flex: none;
}
.first-run-switch::after {
  content: "";
  position: absolute;
  top: 2px;
  inset-inline-start: 2px;
  width: 14px;
  height: 14px;
  border-radius: 50%;
  background: var(--muted);
  transition: inset-inline-start .15s ease, background .15s ease;
}
.first-run-toggle:hover .first-run-switch { border-color: var(--blue); }
.first-run-toggle:hover .first-run-switch::after { inset-inline-start: 16px; background: var(--blue); }
@media (max-width: 639px) {
  /* Still off-centre, but by a thumb's width rather than a mouse's. */
  .first-run-toggle { top: calc(50% + 72px); inset-inline-start: calc(50% + 24px); }
}

/*
 * The short story: five steps, one under the other.
 *
 * Narrower than the page it sits in. The detailed page is wide because it
 * has a picture in it that needs the width; this is prose, and prose read
 * across 1120px is prose nobody finishes.
 */
.home-simple { max-width: 920px; margin: 0 auto; }
.home-simple .hero { margin-bottom: 34px; }
/* The short story's first heading. An h2 in the markup, a title on the screen. */
.simple-h1 {
  font-size: clamp(30px, 5vw, 44px);
  line-height: 1.12;
  max-width: 20em;
  letter-spacing: -.03em;
  margin: 0 0 14px;
}
.simple-steps { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: 34px; }
.simple-steps > li { margin: 0; }
.simple-steps h2 { font-size: 21px; margin: 0 0 6px; }
.simple-steps > li > p { max-width: 62ch; color: var(--text-soft); margin: 0 0 14px; }
.simple-steps .illus { max-width: 620px; }
.simple-keeps { margin-top: 34px; }

/*
 * The short page's one picture: what you say on the left, the member's own
 * brand in the middle, what happens on the right -- three pairs, lit one
 * at a time.
 *
 * One at a time is the whole design. Three arrows moving together say
 * "things happen"; three moving in turn say which sentence caused which
 * result, which is the only thing this drawing is for. The die is placed
 * across all three rows rather than drawn three times, because there is
 * one brand and everything runs through it.
 */
.sim-flow { padding: 26px 24px; margin-top: 30px; }
.sim-ends {
  display: flex;
  justify-content: space-between;
  gap: 12px;
  margin-bottom: 16px;
  font-size: 12px;
  font-weight: 600;
  letter-spacing: .05em;
  text-transform: uppercase;
  color: var(--muted);
}
.sim-ends-out { color: var(--blue); text-align: end; }

.sim-grid {
  display: grid;
  grid-template-columns: minmax(0, 1fr) 36px max-content 36px minmax(0, 1fr);
  gap: 12px;
  align-items: center;
}
.sim-said {
  margin: 0;
  border: 1px solid var(--border-strong);
  border-radius: var(--radius);
  background: var(--bg);
  padding: 11px 13px;
  font-size: 14px;
  line-height: 1.45;
}
.sim-shown {
  border: 1px solid var(--border);
  border-radius: var(--radius);
  background: var(--card);
  padding: 11px 13px;
  display: flex;
  flex-direction: column;
  gap: 3px;
}
.sim-shown strong { display: flex; align-items: center; gap: 8px; font-size: 14px; }
.sim-shown .muted { font-size: 12.5px; line-height: 1.45; }

/*
 * The brand. A dashed square rather than our mark: on a white-label host
 * brand.js replaces it with that customer's own logo (applyLook), so the
 * middle of the picture becomes theirs without this page knowing.
 */
.sim-die {
  grid-column: 3;
  grid-row: 1 / 4;
  align-self: center;
  justify-self: center;
  width: 196px;
  border: 1px solid var(--blue-line);
  border-radius: var(--radius-lg);
  background: var(--card);
  padding: 20px 14px;
  text-align: center;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 8px;
}
.sim-die b { font-size: 17px; line-height: 1.2; }
.sim-die small { font-size: 11.5px; line-height: 1.45; color: var(--muted); }
.sim-die .brand-mark.is-placeholder {
  width: 32px;
  height: 32px;
  border: 2px dashed var(--blue-line);
  border-radius: 9px;
  background: none;
  -webkit-mask: none;
  mask: none;
}
.sim-die .brand-mark.is-placeholder::before,
.sim-die .brand-mark.is-placeholder::after { content: none; }

/* A cord, and the spark that runs down it when its pair's turn comes. */
.sim-cord { position: relative; height: 3px; border-radius: 2px; background: var(--blue-line); }
.sim-cord > i { position: absolute; top: -2px; width: 7px; height: 7px; border-radius: 50%; background: var(--blue); opacity: 0; }
.sim-run-1 > i { animation: sim-run 18s cubic-bezier(.45, 0, .55, 1) infinite; animation-delay: 1s; }
.sim-run-2 > i { animation: sim-run 18s cubic-bezier(.45, 0, .55, 1) infinite; animation-delay: 7s; }
.sim-run-3 > i { animation: sim-run 18s cubic-bezier(.45, 0, .55, 1) infinite; animation-delay: 13s; }
@keyframes sim-run {
  0%        { left: 0; opacity: 0; }
  1%        { opacity: 1; }
  8%        { left: calc(100% - 7px); opacity: 1; }
  10%, 100% { left: calc(100% - 7px); opacity: 0; }
}

.sim-said-1 { animation: sim-said 18s ease-out infinite; animation-delay: .4s; }
.sim-said-2 { animation: sim-said 18s ease-out infinite; animation-delay: 6.4s; }
.sim-said-3 { animation: sim-said 18s ease-out infinite; animation-delay: 12.4s; }
@keyframes sim-said {
  0%        { border-color: var(--border-strong); background: var(--bg); }
  4%, 16%   { border-color: var(--blue); background: var(--card); }
  22%, 100% { border-color: var(--border-strong); background: var(--bg); }
}

.sim-shown-1 { animation: sim-shown 18s ease-out infinite; animation-delay: 1.8s; }
.sim-shown-2 { animation: sim-shown 18s ease-out infinite; animation-delay: 7.8s; }
.sim-shown-3 { animation: sim-shown 18s ease-out infinite; animation-delay: 13.8s; }
@keyframes sim-shown {
  0%        { border-color: var(--border); background: var(--card); transform: translateX(0); }
  5%        { border-color: var(--blue); background: var(--blue-soft); transform: translateX(4px); }
  14%, 100% { border-color: var(--blue-line); background: var(--card); transform: translateX(0); }
}

/* The three notes under the picture, side by side where there is room. */
.sim-three { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: 24px; margin-top: 34px; }
.sim-three h2 { font-size: 19px; }
.sim-three > li > p { margin-bottom: 0; }

/*
 * Narrow: the pairs stack and the cords go. A cord drawn between two
 * boxes that are one above the other is pointing at nothing, and the
 * pairing is carried by the order instead.
 */
@media (max-width: 860px) {
  .sim-grid { grid-template-columns: minmax(0, 1fr); }
  .sim-cord { display: none; }
  .sim-die { grid-column: auto; grid-row: auto; order: -1; width: 100%; max-width: 320px; }
  .sim-shown { margin-bottom: 8px; }
  .sim-three { grid-template-columns: minmax(0, 1fr); gap: 26px; }
  .sim-ends-out { display: none; }
}

@media (prefers-reduced-motion: reduce) {
  .sim-cord > i { animation: none; opacity: 0; }
  .sim-said-1, .sim-said-2, .sim-said-3 { animation: none; }
  .sim-shown-1, .sim-shown-2, .sim-shown-3 { animation: none; border-color: var(--blue-line); }
}
.simple-cta { margin-top: 30px; text-align: center; }
.simple-cta .hero-cta { justify-content: center; }
.simple-cta .hero-price { margin-top: 6px; }
/* A button that is a sentence, not a control: the way back to the long page. */
.linklike {
  width: auto;
  padding: 0;
  border: 0;
  background: none;
  color: var(--blue);
  font: inherit;
  text-decoration: underline;
  cursor: pointer;
}
