/* The front-end editor, for a signed-in member of the society.
   Behaviour and reasoning: platform/assets/js/editor-client.js.

   WHY IT DOES NOT USE THE SITE'S OWN DESIGN TOKENS, which is the opposite of
   every other stylesheet here. This furniture belongs to US, not to the museum.
   A society whose brand colour is a pale sand would get a "Save" button that
   cannot be found and a warning that does not look like one — and the one place
   a volunteer must never misread a control is the one that changes their live
   website. So: fixed, high-contrast, deliberately not part of the site.

   It is also why nothing here is scoped to the site's typography. The site's
   font may be a display face chosen for headings; a form filled in by somebody
   with reading glasses on a laptop in a back office wants the system font at a
   size their operating system already knows they need. */

/* HIDDEN MEANS HIDDEN, ON ANYTHING THIS KIT OWNS.
 *
 * The user agent hides [hidden] with `display: none`, but that is the WEAKEST
 * possible rule: any author `display:` on the same element beats it. Every
 * layout rule in this file is therefore a loaded gun pointed at every
 * `el.hidden = true` in editor-client.js, and the two do not look related when
 * you read either one.
 *
 * It has fired twice, found by the verifier and by the audit that followed:
 *   .cb-cards { display: grid }  — the "More kinds" disclosure painted all
 *     eight blocks it claimed to be hiding, with aria-expanded="false"
 *     announced over content that was on screen and clickable.
 *   .cb-thumb { display: block } — "Remove picture" left the thumbnail of the
 *     removed picture sitting in the form.
 * Neither errored. Both looked exactly like working code.
 *
 * So this is a rule about the whole class rather than a patch per offender: the
 * next person to add a `display:` here will not know to check, and should not
 * have to. `!important` is deliberate — `.cb-field .cb-linkrow .cb-radio` and
 * `.cb-kit-tall .cb-kit-split .cb-kit-more` already out-specify a plain
 * attribute selector, so anything less would leave the same trap half-set.
 * There is no case where an element wants to carry [hidden] and be seen; that
 * is what the attribute MEANS, and the kit uses it as its single closed state.
 *
 * SCOPED TO `cb-` — WHICH IS NOT THE SAME AS "OURS ONLY". Read this before
 * writing a block that hides part of itself.
 *
 * A bare `[hidden]` rule here would reach the client's whole page, so the
 * `cb-` prefix is a real narrowing. But it does NOT confine the rule to this
 * kit's own furniture, and the first version of this comment claimed it did.
 * EDITOR_MARKS (editor-client.js) stamps `cb-spot`, `cb-bspot`, `cb-pending`
 * and their siblings onto CLIENT block roots while somebody is editing. Live
 * examples on CAMRO: `p-doors p-doors--three cb-spot cb-bspot`, and
 * `collection-grid collection-cards u-hug-t cb-spot cb-bspot`. So this rule
 * reaches a client's own elements, with !important, whenever the kit is on.
 *
 * Measured, with a `.slider-ready { display: block }` of the kind a block
 * ships:
 *     client rule + NO kit mark  ->  display: block   (their rule wins)
 *     client rule + kit mark     ->  display: none    (this rule wins)
 *
 * The mark only lands while editing, so THE SAME NODE BEHAVES DIFFERENTLY
 * SIGNED IN AND SIGNED OUT. A block that hides one of its own marked parts by
 * attribute and reveals it with a display rule would work perfectly for the
 * public and be invisible to the person editing it — the hardest fault to
 * reproduce and the first one a client meets.
 *
 * No block does that today: the three that use `hidden` neither hide their own
 * root nor reveal by adding a class while the attribute stays set, and ten
 * built pages carry no client class that matches. Verified rather than
 * assumed. If you are writing the block that WOULD do it, use a class to hide,
 * not the attribute. */
[class^="cb-"][hidden],
[class*=" cb-"][hidden] { display: none !important; }

/* THE KIT'S TEXT COLOUR IS NOT INHERITED, IT IS STATED.
 *
 * Inheritance has NO specificity, so a client's bare element selector beats it
 * every time. CAMRO's own stylesheet carries `strong { color: var(--mb-ink) }`,
 * and that is how the Page structure panel came to show near-black row titles
 * on a dark green ground — rgb(18,35,28) on rgb(11,79,58) — while its header
 * and close button, which name --cb-blue-ink explicitly, were correctly light.
 * The panel was right; the <strong> inside each row was reading the museum's
 * ink because nothing of ours had said otherwise.
 *
 * It is the same shape as the [hidden] rule above: we leaned on a default in a
 * document whose author has a rule. The kit is injected into somebody else's
 * page, so every default we lean on is theirs to overwrite, and the list of
 * elements they might style is not ours to predict — this page also colours
 * `a` and `h1..h4` by bare selector, and the kit renders both.
 *
 * So: inside a kit surface, text-level elements inherit their surface's colour
 * whatever the page says. `inherit` rather than a token, because the three
 * surfaces do not share one — .cb-panel is ink on paper while .cb-kit and
 * .cb-nav are light on the site's deep tone.
 *
 * `:where()`, NOT `:is()`, AND THE DIFFERENCE IS THE WHOLE SAFETY OF THIS RULE.
 * `:where()` contributes ZERO specificity, so each of these lands at (0,1,0) —
 * the weight of the surface class alone. That is enough to beat any bare
 * element selector a client's page can write (0,0,1), and low enough that every
 * colour the kit chooses for itself still wins:
 *
 *   .cb-nav-note, .cb-offnote, .cb-bad   (0,1,0) — <p> elements carrying a
 *     deliberate warning or error colour. With `:is()` this rule would have
 *     been (0,1,1) and SILENTLY TAKEN THE RED OUT OF EVERY ERROR MESSAGE in
 *     the kit. It is placed early, so at equal weight those later rules win.
 *   .cb-rich a, .cb-ground b, .cb-tile em (0,1,1) — win outright.
 *
 * <span> is deliberately NOT in the list. Nothing on this page styles a bare
 * span, and including it would out-rank single-class rules like
 * .cb-nav-queued, which is a span that inherits its colour on purpose. */
.cb-kit   :where(a, strong, b, em, i, u, s, small, code, kbd, mark, abbr,
              h1, h2, h3, h4, h5, h6, p, li, dt, dd, label, th, td,
              figcaption, blockquote),
.cb-nav   :where(a, strong, b, em, i, u, s, small, code, kbd, mark, abbr,
              h1, h2, h3, h4, h5, h6, p, li, dt, dd, label, th, td,
              figcaption, blockquote),
.cb-panel :where(a, strong, b, em, i, u, s, small, code, kbd, mark, abbr,
              h1, h2, h3, h4, h5, h6, p, li, dt, dd, label, th, td,
              figcaption, blockquote) { color: inherit; }

/* THE COLOURS ARE THE AGENCY'S, not the museum's, and that is the point rather
   than a side effect (Eric, 2026-08-15): "it should be really obvious the
   things that are part of the page versus the things that are for editing".
   A volunteer glancing at their screen can tell our tooling from their website
   by colour alone, which is what stops "is this on my site?" being a question
   at all. The pair below are the engine's DEFAULTS; a company's own pair comes
   in over the top from sites/company.json (`brand.kit_colors`) — see the
   white-label seam in includes/components/editor-kit.njk. */
:root {
  --cb-ink: #16130f;
  --cb-paper: #ffffff;
  --cb-line: #d6cec2;
  --cb-blue: #10466A;
  --cb-blue-deep: #0c3650;
  --cb-blue-ink: #ffffff;
  --cb-orange: #F55C00;
  --cb-orange-dark: #d24e00;
  --cb-bad: #8c2f2f;
  --cb-bad-bg: #fbf1ef;
  --cb-good: #2c6244;
  --cb-good-bg: #eef6f1;
  --cb-font: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
  /* Tints of the two brand colours: the hover wash on an editable spot, the
     highlight behind changed words, the pale focus ring on the kit's own dark
     buttons, the blue wash on a block. Literals here so an old browser still
     draws SOMETHING sensible; re-derived from the brand pair below, so a
     company's kit_colors re-brand every wash and ring, not just the solids. */
  --cb-focus: #ffd6b8;
  --cb-orange-wash: rgba(245, 92, 0, .07);
  --cb-orange-mark: rgba(245, 92, 0, .12);
  --cb-blue-wash: rgba(16, 70, 106, .06);
}
@supports (color: color-mix(in srgb, red 50%, white)) {
  :root {
    --cb-focus: color-mix(in srgb, var(--cb-orange) 25%, #fff);
    --cb-orange-wash: color-mix(in srgb, var(--cb-orange) 7%, transparent);
    --cb-orange-mark: color-mix(in srgb, var(--cb-orange) 12%, transparent);
    --cb-blue-wash: color-mix(in srgb, var(--cb-blue) 6%, transparent);
  }
}

/* Said to a screen reader and to nobody else. */
.cb-sr {
  position: absolute; width: 1px; height: 1px; overflow: hidden;
  clip: rect(0 0 0 0); clip-path: inset(50%); white-space: nowrap;
}

/* ── the corner kit ──────────────────────────────────────────────────────
   Top right, floating, ours. It is deliberately the ONLY editing control on
   the page: the old "Edit the words on this page" button sat inside the
   museum's own layout under their heading, where it read as part of their
   site and pushed their content down the page. */
.cb-kit-btn {
  font: inherit; font-weight: 500; cursor: pointer; border-radius: 8px;
  padding: .5rem .9rem; min-height: 38px; border: 1px solid transparent;
  background: rgba(255, 255, 255, .12); color: var(--cb-blue-ink);
}
.cb-kit-go { background: var(--cb-orange); border-color: var(--cb-orange); }
.cb-kit-go:hover { background: var(--cb-orange-dark); }
.cb-kit-done { background: var(--cb-paper); color: var(--cb-blue); }
.cb-kit-quiet { border-color: rgba(255, 255, 255, .4); }
.cb-kit-btn:focus-visible { outline: 3px solid var(--cb-focus); outline-offset: 2px; }
/* While editing is ON the kit says so without being read: an orange body under
   a white "Done editing". Colour is never the only signal — the button's words
   change too. */
/* THE GROUND IS THE SITE'S DEEP TONE; THE ACCENT IS THE ONE ACTION.
   Cam, 2026-09-07, on the live site: the editor "needs to be custom to the
   specific site". That settles what the ground should be as well as what
   colour family it comes from — the accent cannot mark the one consequential
   button while it is also painting the 580px bar behind it. So: deep tone
   here, accent on `.cb-kit-go` (Publish) alone, everything else neutral,
   and the row reads as one thing with one verb in it.
   Eric's rule that it must be obvious what is the page and what is the tool
   is carried by tone and shape — a floating dark bar over the page — rather
   than by a hue the site never uses. */
.cb-kit-live { background: var(--cb-blue-deep); }

/* On a phone the row cannot fit and wrapped into a ragged block hanging
   off the right edge. Span the width instead, buttons grouped right, so it
   reads as one strip. The stacked form below is unchanged. */

/* Stacked: the same bar as a column, switched from the gear ("Stack the
   bar") and kept per site in this browser. Buttons fill the column and read
   from the left, so the stack scans as a list, not a pile of pills. */
/* The two glyphs are NOT buttons in the stack (Cam, 2026-08-27: a gear
   stretched into a full-width pill "looks like a button going sideways ...
   it should just be a gear"). So: the gear compact and centred with no pill
   behind it, and the grip's dots turned to lie across the bar, matching the
   direction it now reads in. */
/* Same reasoning for the −: a glyph stretched to the column's full width
   reads as a button, which is exactly what it is not. */
/* Stacked, the head is a real row: the grip centred on the bar and the − in
   the top-right corner. A 1fr/auto/1fr grid rather than space-between, so
   the grip is centred on the BAR and not merely on what is left over once
   the − has taken its width — which also keeps it centred while EDITING,
   when there is no − to balance it against. */

.cb-kit-gear-label { display: none; }
/* ── the gear: the page's occasional controls, off the bar ─────────────── */
.cb-kit-more { position: relative; display: inline-flex; }
.cb-kit-gear { font-size: 1.15rem; line-height: 1; padding: .45rem .6rem; }

/* ── the − that puts the editor bar away (Cam, 2026-08-31) ──────────────
   Drawn like the gear, not like a verb: no pill, no border, dimmed until
   pointed at. The bar's buttons are things you came here to press; this and
   the gear are the bar's own furniture and should not compete with them. */
/* ── the split button: "Publish ▾" (Cam, 2026-08-31) ────────────────
   Two buttons that merely sat next to each other were two items in the bar's
   flex, so STACKING the bar put the caret on its own row underneath — "I do
   not want that. I want it to always be beside Publish and, honestly,
   to look like it's a part of the button."

   So the pair is wrapped in one element and drawn as one button split down
   the middle: shared silhouette, square where they meet, a hairline of the
   ink between them so the two hit areas are still findable by eye. Being ONE
   flex item is what makes it survive the stack; the rules under .cb-kit-tall
   below only stop the column rules from taking it apart again. */
.cb-kit-split { display: inline-flex; align-items: stretch; }
.cb-kit-split > .cb-kit-btn { border-radius: 6px 0 0 6px; }
.cb-kit-split .cb-kit-more { margin: 0; }
.cb-kit-split .cb-kit-gear {
  border-radius: 0 6px 6px 0; font-size: .8rem; padding: .45rem .55rem;
  box-shadow: inset 1px 0 0 rgba(0, 0, 0, .22);
}
/* Stacked, the split is the full-width row and Publish takes whatever
   the caret does not: the caret stays a caret rather than growing into half
   a bar. The tall rules blank the gear's fill for the standalone ⚙︎ — this
   one is a primary verb, so it keeps its colour. */
.cb-kit-menu {
  position: absolute; top: calc(100% + .45rem); right: 0; z-index: 2147483003;
  display: flex; flex-direction: column; gap: 2px; min-width: 11em;
  padding: .3rem; border-radius: 8px;
  background: var(--cb-blue); color: var(--cb-blue-ink);
  box-shadow: 0 6px 22px rgba(0, 0, 0, .32);
}
.cb-kit-menu[hidden] { display: none; }
.cb-kit-menu-item {
  font: inherit; font-weight: 600; cursor: pointer; text-align: left;
  padding: .45rem .8rem; min-height: 38px; border: 1px solid transparent;
  border-radius: 6px; background: rgba(255, 255, 255, .12); color: var(--cb-blue-ink);
  /* One line per choice: the menu sizes to its longest verb rather than
     breaking "Discard all changes" across two lines inside a 11em box. */
  white-space: nowrap;
}
.cb-kit-menu-item:hover { background: rgba(255, 255, 255, .24); }
.cb-kit-menu-item:focus-visible { outline: 3px solid var(--cb-focus); outline-offset: 2px; }

/* ── the page-structure navigator (Cam, 2026-08-27) ─────────────────────
   A floating list of the page's bands, dragged into a new order row by row.
   Our colours, like the kit, and for the same reason: it rearranges somebody's
   live website and must never read as part of it. */
.cb-nav {
  position: fixed; top: 4.5rem; left: .75rem; z-index: 2147483002;
  width: 17rem; max-width: calc(100vw - 1.5rem);
  padding: .5rem; border-radius: 9px;
  background: var(--cb-blue); color: var(--cb-blue-ink);
  font: 400 .875rem/1.3 var(--cb-font);
  box-shadow: 0 6px 22px rgba(0, 0, 0, .32);
}
.cb-nav-head { display: flex; align-items: center; cursor: grab; padding: 0 .1rem .35rem; }
.cb-nav-title { font-weight: 800; font-size: .8rem; letter-spacing: .08em; text-transform: uppercase; flex: 1; }
.cb-nav-close {
  font: inherit; cursor: pointer; border: 0; border-radius: 6px;
  background: rgba(255, 255, 255, .12); color: var(--cb-blue-ink);
  padding: .2rem .5rem;
}
.cb-nav-close:hover { background: rgba(255, 255, 255, .24); }
.cb-nav-close:focus-visible { outline: 3px solid var(--cb-focus); outline-offset: 2px; }
.cb-nav-help { margin: 0 .1rem .5rem; font-size: .78rem; opacity: .85; }
/* The LIST scrolls, not the panel, so the right-click menu can overhang the
   box instead of growing it a scrollbar. */
.cb-nav-list { display: flex; flex-direction: column; gap: 3px; max-height: calc(100vh - 14rem); overflow: auto; }
.cb-nav-row {
  display: flex; align-items: center; gap: .35rem;
  border-radius: 6px; background: rgba(255, 255, 255, .1); padding: .3rem .4rem;
}
.cb-nav-grip { cursor: grab; user-select: none; letter-spacing: -.15em; opacity: .7; padding: .2rem .15rem; border-radius: 4px; }
.cb-nav-grip:hover { opacity: 1; background: rgba(255, 255, 255, .15); }
.cb-nav-grip:focus-visible { outline: 3px solid var(--cb-focus); outline-offset: 2px; opacity: 1; }
.cb-nav-name {
  font: inherit; cursor: pointer; border: 0; background: none; color: inherit;
  flex: 1; min-width: 0; text-align: left; padding: .1rem 0;
  display: flex; flex-direction: column; gap: 1px;
}
.cb-nav-name strong { font-weight: 700; }
.cb-nav-name:focus-visible { outline: 3px solid var(--cb-focus); outline-offset: 2px; }
.cb-nav-words {
  font-size: .74rem; opacity: .75;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap; max-width: 12rem;
}
.cb-nav-queued {
  font-size: .68rem; font-weight: 800; letter-spacing: .06em; text-transform: uppercase;
  background: var(--cb-orange); border-radius: 4px; padding: .12rem .35rem;
}
.cb-nav-dragged { opacity: .45; }
.cb-nav-over { outline: 2px dashed var(--cb-orange); outline-offset: 1px; }
.cb-nav-menu { position: absolute; top: auto; right: auto; }
.cb-nav-danger { color: #ffc9b3; }
.cb-nav-note { margin: .5rem .1rem 0; font-size: .78rem; color: #ffc9b3; }
/* The flash when a row is clicked: the band answers from the page. */
.cb-nav-flash { outline: 3px solid var(--cb-orange); outline-offset: 4px; transition: outline-color .2s; }

/* A SECTION spot wraps the spots inside it. While the pointer is on one of
   them, the band's own outline stands down — one thing lit at a time. No
   background here either, for the reason at the spot-hover rule. */
.cb-bspot:has(.cb-spot:hover) { outline: none; }

/* The Advanced fold in the collection block's settings. */
.cb-adv { margin-top: .35rem; }
.cb-adv summary { cursor: pointer; font-size: .8rem; opacity: .85; }
.cb-adv input { width: 100%; margin-top: .35rem; }

/* ── what can be clicked while editing ─────────────────────────────────── */
.cb-spot { cursor: pointer; border-radius: 4px; }
.cb-spot:hover, .cb-spot:focus-visible {
  outline: 2px dashed var(--cb-orange); outline-offset: 5px;
}
.blk[data-bg]:not([data-bg="paper"]).cb-spot { outline-offset: -5px; }
/* The tint rides on SMALL spots only, and block/section spots are excluded in
   the SELECTOR rather than overridden with a background of their own: any
   `background` here — a wash, even `transparent` — REPLACES what the element
   paints for itself, which on a navy band meant the whole section going white
   the moment it was hovered (Cam, 2026-08-27, twice). The hover rules must
   simply never speak about a block spot's background. */
.cb-spot:hover:not(.cb-bspot), .cb-spot:focus-visible:not(.cb-bspot) {
  background: var(--cb-orange-wash);
}
.cb-spot:focus-visible { outline-style: solid; }
/* A picture is the one spot whose own edges are the thing being outlined, so
   it gets the tint on the frame rather than behind transparent text. */
.cb-spot:hover img, .cb-spot:focus-visible img { opacity: .9; }
@media (prefers-reduced-motion: no-preference) {
  .cb-spot { transition: background .1s linear; }
}

/* ── the light formatting box ──────────────────────────────────────────── */
.cb-marks {
  display: flex; gap: .3rem; flex-wrap: wrap;
  padding: .4rem; border: 1px solid var(--cb-line); border-bottom: 0;
  border-radius: 6px 6px 0 0; background: #f4f6f8;
}
.cb-mk {
  font: inherit; font-size: .95rem; cursor: pointer; min-width: 40px; min-height: 38px;
  border: 1px solid var(--cb-line); border-radius: 5px;
  background: var(--cb-paper); color: var(--cb-blue);
}
.cb-mk:hover { background: #e7edf3; }
.cb-mk-b { font-weight: 800; }
.cb-mk-i { font-style: italic; font-family: Georgia, serif; }
.cb-mk-s { text-decoration: line-through; }
.cb-mk-a { min-width: auto; padding: 0 .7rem; font-size: .85rem; font-weight: 600; }
.cb-mk:focus-visible { outline: 3px solid #2f6fbf; outline-offset: 2px; }

/* The box itself. Deliberately shaped like the textarea it replaced — a
   volunteer should not have to work out that this one is different, only that
   the buttons above it do something. */
.cb-rich {
  border: 1px solid var(--cb-line); border-radius: 0 0 6px 6px;
  padding: .6rem .65rem; min-height: 5.5rem; font-size: 1rem; line-height: 1.5;
  background: var(--cb-paper); color: var(--cb-ink); overflow-wrap: break-word;
}
.cb-rich:focus-visible { outline: 3px solid #2f6fbf; outline-offset: -1px; }
.cb-rich a { color: #1256a0; }
.cb-linkrow {
  margin: .9rem 0 0; padding: .75rem; border-radius: 6px;
  border: 1px solid var(--cb-line); background: #f4f6f8;
}
.cb-listrow { display: block; width: 100%; text-align: left; margin: 0 0 .4rem; }
.cb-thumb-wide { max-height: 220px; margin-bottom: .9rem; }
.cb-soon { margin-top: 1rem; padding-top: .8rem; border-top: 1px solid var(--cb-line); }

/* ── the bar ─────────────────────────────────────────────────────────────
   Fixed to the BOTTOM, not the top. A top bar covers the site's own header —
   which is the thing an editor is most often here to look at — and on a phone
   it fights the browser's address bar for the same forty pixels. */
/* ONE BAR (2026-09-08). The floating kit is gone; this strip carries the
   verbs it used to. Identity on the left (an account menu), what happens to
   changes, then the page's verbs, Dashboard, and ⋯. Same z-index, same
   ground, same shadow as before — it was always the same object. */
.cb-bar {
  position: fixed; inset: auto 0 0 0; z-index: 2147483000;
  display: flex; align-items: center; gap: .6rem; flex-wrap: wrap;
  padding: .5rem .9rem; background: var(--cb-blue); color: var(--cb-blue-ink);
  font: 400 .875rem/1.4 var(--cb-font);
  box-shadow: 0 -2px 12px rgba(0, 0, 0, .25);
}
.cb-bar-dot {
  width: .5rem; height: .5rem; border-radius: 50%; background: #7fd8a0;
  flex: 0 0 auto;
}
.cb-bar-who {
  display: inline-flex; align-items: center; gap: .4rem;
  font: inherit; font-weight: 600; color: inherit; cursor: pointer;
  background: none; border: 0; border-radius: 7px; padding: .35rem .5rem;
}
.cb-bar-who:hover { background: rgba(255, 255, 255, .10); }
.cb-bar-who:focus-visible { outline: 3px solid var(--cb-focus); outline-offset: 2px; }
.cb-bar-caret { opacity: .6; font-size: .7rem; }
.cb-bar-mode { opacity: .7; font-size: .82rem; }
.cb-bar-rule { width: 1px; align-self: stretch; margin: .35rem .1rem; background: rgba(255, 255, 255, .22); flex: 0 0 auto; }
.cb-bar-sp { flex: 1 1 auto; }
.cb-bar-verbs { display: inline-flex; align-items: center; gap: .5rem; }
.cb-bar-link {
  color: inherit; text-decoration: none; opacity: .92; white-space: nowrap;
  padding: .45rem .55rem; border-radius: 7px;
}
.cb-bar-link:hover { opacity: 1; background: rgba(255, 255, 255, .12); text-decoration: none; }
.cb-bar-dots { font-size: 1.05rem; letter-spacing: .08em; line-height: 1; background: none; border-color: transparent; }
.cb-bar-dots:hover { background: rgba(255, 255, 255, .12); }
/* Menus on a bar at the FOOT open upward. */
.cb-bar .cb-kit-menu { top: auto; bottom: calc(100% + .45rem); }
.cb-bar-whowrap .cb-kit-menu { right: auto; left: 0; }
/* A finger needs 44px. Measured on the first draft at 375: every control was
   31-36px. */
@media (pointer: coarse) {
  .cb-bar .cb-kit-btn, .cb-bar-who, .cb-bar-link, .cb-bar-dots { min-height: 44px; }
  .cb-bar .cb-kit-split .cb-kit-gear { min-width: 44px; }
}
/* The bar sits over the bottom of the page, so give the page somewhere to end.
   Without this the last line of every article is permanently behind it.

   EXACTLY THE BAR'S HEIGHT, MEASURED (Cam, 2026-09-08: "below the footer it
   seems like there's also some spacing there"). This was a flat 4.5rem — 72px
   against a bar that measures 53 at 1440. The 19px difference is page ground
   showing BELOW the footer and above the bar, which reads as a gap somebody
   left. Reserve the bar's own height and the bar covers the reserve exactly:
   footer, then bar, no band between them. The bar WRAPS on a narrow screen
   (73px at 375), which is the other reason this cannot be a constant.
   clearKitOfBar() in editor-client.js measures it; the fallback is what the
   old rule said, for the moment before script runs. */
html.cb-editing body { padding-bottom: var(--cb-bar-h, 4.5rem); }

/* ── the dialog ───────────────────────────────────────────────────────── */
.cb-backdrop {
  position: fixed; inset: 0; z-index: 2147483001;
  background: rgba(22, 19, 15, .55);
  display: flex; align-items: flex-start; justify-content: center;
  padding: 5vh 1rem; overflow-y: auto;
}
html.cb-edit-open { overflow: hidden; }
/* THE FLOATING KIT STANDS DOWN WHILE A DIALOG IS OPEN. It sits one above the
   backdrop on purpose - it is the thing that opened the dialog and must not
   vanish under its own click - but a dialog is modal and focus-trapped, so
   nothing in the kit can be reached while one is up, and it was covering the
   top-right corner of the wide "+" panel: the Laptop/Phone buttons were drawn
   underneath it and could not be pressed. Hidden, not moved, because there is
   no corner a floating thing can be put in that some panel does not want. */
html.cb-edit-open .cb-panel {
  background: var(--cb-paper); color: var(--cb-ink);
  border-radius: 12px; width: 100%; max-width: 34rem;
  font: 400 1rem/1.5 var(--cb-font);
  box-shadow: 0 24px 60px -12px rgba(0, 0, 0, .35);
}
.cb-panel-head {
  display: flex; align-items: center; justify-content: space-between;
  gap: 1rem; padding: 1rem 1.25rem; border-bottom: 1px solid var(--cb-line);
}
.cb-panel-title { margin: 0; font-size: 1.25rem; font-weight: 600; letter-spacing: -.01em; }
.cb-x {
  border: 0; background: none; font-size: 1.6rem; line-height: 1; cursor: pointer;
  color: var(--cb-ink); padding: 0 .25rem; border-radius: 4px;
}
.cb-panel-body { padding: 1.5rem 1.6rem; }

/* ── the form ─────────────────────────────────────────────────────────── */
.cb-form { display: block; }
.cb-lede { margin: 0 0 1rem; }
.cb-dim { opacity: .7; font-size: .9rem; }
.cb-field { margin: 0 0 1.1rem; }
.cb-label { display: block; font-weight: 600; margin: 0 0 .25rem; font-size: .95rem; }
.cb-help { margin: 0 0 .35rem; font-size: .85rem; opacity: .75; }

/* A row of buttons (2026-08-27): the "Button 1 / Button 2" group marks, and
   the placement radios on one line rather than stacked. */
.cb-btngroup-label { margin: 1.1rem 0 .5rem; padding-top: .9rem;
  border-top: 1px solid #d9d9d9; font-size: 1rem; }
.cb-form .cb-field:first-child .cb-btngroup-label {
  margin-top: 0; padding-top: 0; border-top: 0; }
.cb-field .cb-linkrow .cb-radio { display: inline-flex;
  margin: .2rem 1.2rem .2rem 0; }

/* 16px minimum on every control, and this is not a style preference: iOS
   ZOOMS THE WHOLE PAGE IN when a field smaller than 16px takes focus, and then
   leaves the volunteer scrolled sideways in a form they can no longer see. */
.cb-panel input, .cb-panel textarea, .cb-panel select {
  width: 100%; box-sizing: border-box; font: inherit; font-size: 1rem;
  padding: .55rem .65rem; border: 1px solid var(--cb-line); border-radius: 6px;
  background: var(--cb-paper); color: var(--cb-ink);
}
.cb-panel textarea { resize: vertical; min-height: 5.5rem; }
.cb-panel input:focus-visible, .cb-panel textarea:focus-visible,
.cb-panel select:focus-visible, .cb-btn:focus-visible, .cb-x:focus-visible {
  outline: 3px solid #2f6fbf; outline-offset: 2px;
}
/* The sign-in code, spaced out and large. It is read off a phone one digit at a
   time and typed by somebody who cannot see it and the keyboard at once. */
.cb-code {
  font-size: 1.6rem !important; letter-spacing: .4em; text-align: center;
  font-variant-numeric: tabular-nums;
}

.cb-btn {
  font: inherit; font-size: 1rem; cursor: pointer; border-radius: 6px;
  padding: .6rem 1.1rem; border: 1px solid var(--cb-line);
  background: var(--cb-paper); color: var(--cb-ink);
  /* Big enough to hit. 44px is the size a finger actually is. */
  min-height: 44px;
}
.cb-btn + .cb-btn { margin-left: .5rem; }
.cb-primary {
  background: var(--cb-blue); border-color: var(--cb-blue); color: var(--cb-blue-ink);
  font-weight: 600; width: auto; min-width: 10rem; margin-top: .35rem;
}
.cb-actions { display: flex; gap: .5rem; margin-top: 1.25rem; justify-content: flex-end; flex-wrap: wrap; }
.cb-actions .cb-primary { width: auto; margin-top: 0; }
.cb-actions .cb-btn + .cb-btn { margin-left: 0; }
.cb-quiet { background: var(--cb-paper); }
.cb-sm { min-height: 34px; padding: .3rem .7rem; font-size: .85rem; }
.cb-bar .cb-btn { background: transparent; color: var(--cb-blue-ink);
  border-color: rgba(255, 255, 255, .45); }
.cb-btn[disabled] { opacity: .6; cursor: default; }

/* `.cb-edit-btn` is gone. It was the "Edit this item" / "Edit the words on this
   page" button, placed under the page's own heading on the argument that it was
   where the eye already is. What that actually did was put OUR control inside
   the museum's layout, in the museum's colours, shifting their content down —
   the exact confusion the rest of this file exists to prevent. Everything is in
   the corner kit now (.cb-kit). */

/* ── saying something went wrong ──────────────────────────────────────────
   Never colour alone: a border and a word. Roughly one man in twelve cannot
   tell this red from this green, and a historical society's committee is not
   a demographic where that gets designed around. */
.cb-say {
  margin: 0 0 1rem; padding: .7rem .85rem; border-radius: 6px;
  border-left: 4px solid; font-size: .95rem;
}
.cb-bad { border-color: var(--cb-bad); background: var(--cb-bad-bg); color: var(--cb-bad); }
.cb-good { border-color: var(--cb-good); background: var(--cb-good-bg); color: var(--cb-good); }
.cb-info { border-color: var(--cb-blue); background: #eef2f8; color: var(--cb-blue); }

/* The picture control. The thumbnail is a fixed short box so a portrait scan
   cannot push the Save button below the fold of a phone. */
.cb-thumb {
  display: block; max-width: 100%; max-height: 160px; width: auto;
  border-radius: 6px; border: 1px solid var(--cb-line); margin: 0 0 .4rem;
}
.cb-file { font-size: 1rem; }
.cb-pic-actions { margin-top: .45rem; }
.cb-pic-actions .cb-btn { font-size: .9rem; min-height: 36px; padding: .4rem .8rem; }

@media (prefers-reduced-motion: no-preference) {
  .cb-panel { animation: cb-in .12s ease-out; }
  @keyframes cb-in { from { transform: translateY(-6px); opacity: 0; } }
}

/* ── the way to the dashboard: a link that looks like the other kit buttons ── */
.cb-kit-link { text-decoration: none; display: inline-flex; align-items: center; }
.cb-kit-link:hover { background: rgba(255, 255, 255, .22); }

/* ── the page's own settings, clickable like a paragraph (2026-08-17) ──────
   The title, the description under it and the featured picture. Same outline
   as a paragraph so there is ONE idea on the page: outlined = you can change
   it. The picture is outlined on its own edges. */
.cb-fspot { cursor: pointer; }
img.cb-fspot:hover, img.cb-fspot:focus-visible {
  outline: 3px dashed var(--cb-orange); outline-offset: 4px; opacity: .92;
}

/* ── the "+" between paragraphs (2026-08-17) ───────────────────────────────
   Quiet until the pointer is near it: a page with thirty paragraphs must not
   turn into a ladder of orange buttons the moment editing goes on. Full width
   of the column so it is easy to hit; a thin line with a plus in the middle,
   which swells and colours on hover and on keyboard focus. Only ever drawn
   while editing is on, and removed on Done. */
.cb-add {
  display: flex; align-items: center; gap: .5rem; width: 100%;
  margin: .15rem 0; padding: .1rem 0; min-height: 22px;
  background: transparent; border: 0; cursor: pointer;
  font: 700 1rem/1 var(--cb-font); color: var(--cb-orange);
  opacity: .35;
}
/* A hairline, not a dashed rule: with thirty of these on a page the dashed
   orange line was the loudest thing on it. The solid fallback is for a
   browser without color-mix, which would otherwise draw no line at all. */
.cb-add-line { flex: 1; height: 0; border-top: 1px solid var(--cb-orange); }
@supports (color: color-mix(in srgb, red 50%, white)) {
  .cb-add-line { border-top-color: color-mix(in srgb, var(--cb-orange) 55%, transparent); }
}
.cb-add-plus {
  display: inline-flex; align-items: center; justify-content: center;
  width: 22px; height: 22px; border-radius: 50%;
  border: 2px solid var(--cb-orange); background: #fff; color: var(--cb-orange);
}
.cb-add:hover, .cb-add:focus-visible { opacity: 1; }
.cb-add:hover .cb-add-plus, .cb-add:focus-visible .cb-add-plus {
  background: var(--cb-orange); color: #fff; transform: scale(1.15);
}
.cb-add:focus-visible { outline: 3px solid var(--cb-focus); outline-offset: 2px; }
@media (prefers-reduced-motion: no-preference) {
  .cb-add, .cb-add-plus { transition: opacity .12s linear, transform .12s ease-out; }
}
@media print { .cb-add { display: none; } }
/* On a touch screen the 22px row is under the 44px a finger needs. Padding
   grows the hit area and the negative margin gives the same space back, so
   the page's rhythm does not move; the extra 8-9px each side overlaps the
   neighbouring paragraphs' margins, not their words. The in-column "+"
   carries its own height and is left alone. */
@media (pointer: coarse) {
  .cb-add:not(.cb-add--cell) {
    min-height: 44px; padding: 11px 0; margin: calc(.15rem - 11px) 0;
  }
}

/* The "+" INSIDE a column (Cam, 2026-08-31: "the columns are really supposed
   to be for adding other things ... from there you can actually add things
   into it"). Two differences from the one that stands between paragraphs.

   It is always visible rather than resting at .45 opacity, because in an
   EMPTY column it is the only thing there: a control you have to hover to
   discover, in a box that is otherwise blank, is a box that looks broken.

   And it carries its own height, for the same reason — an empty cell is zero
   pixels tall, so without this the column a person just made would have
   nothing to click and nothing to see. The margin above separates it from
   whatever is already in the column; there is none below, because it sits at
   the foot of the cell. */
.cb-add--cell { opacity: .8; min-height: 30px; margin: .4rem 0 0; }
.cb-add--cell:hover, .cb-add--cell:focus-visible { opacity: 1; }

/* What to add: three tabs above the form. */
.cb-choice { display: flex; gap: .4rem; flex-wrap: wrap; margin: 0 0 1rem; }
.cb-choice .cb-btn[aria-selected="true"] {
  background: var(--cb-blue); color: var(--cb-blue-ink); border-color: var(--cb-blue);
}
.cb-choice .cb-btn + .cb-btn { margin-left: 0; }
.cb-radio { display: flex; align-items: center; gap: .45rem; margin: .3rem 0; cursor: pointer; }
.cb-radio input { margin: 0; width: 1.05rem; height: 1.05rem; }
.cb-help.cb-inline { display: inline; margin: 0; }

/* The thing just added, drawn where it will go, and honest about the fact
   that the public copy has not caught up yet. Same orange dashed frame as a
   spot on hover, so it reads as "ours, in progress" and not as the museum's
   finished page. */
.cb-pending {
  position: relative; outline: 2px dashed var(--cb-orange); outline-offset: 5px;
  background: var(--cb-orange-wash); border-radius: 4px;
}
figure.cb-pending { margin: 1rem 0; }
.cb-pending-img { display: block; max-width: 100%; height: auto; }
.cb-pending-small { width: 280px; }
.cb-pending-medium { width: 520px; }
.cb-pending-badge {
  display: block; margin-top: .3rem;
  font: 600 .72rem/1.2 var(--cb-font); letter-spacing: .04em; text-transform: uppercase;
  color: var(--cb-orange);
}
h2.cb-pending .cb-pending-badge { font-weight: 600; }

/* Words that were changed, drawn in place until the site catches up
   (2026-08-17): what came out crossed through and greyed, what went in
   highlighted. Not colour alone — the strike and the underline carry it for
   anyone who cannot tell the two greys apart, and the badge says it in words. */
.cb-changed del.cb-gone {
  text-decoration: line-through; text-decoration-thickness: 2px;
  color: #8a8a8a; opacity: .75;
}
/* A paragraph or picture TAKEN OFF the page (2026-08-17): the whole run
   struck through, a picture greyed, until the site catches up. */
.cb-run-gone { opacity: .7; }
.cb-run-gone img { filter: grayscale(1); opacity: .45; }
.cb-changed ins.cb-came {
  text-decoration: underline; text-decoration-color: var(--cb-orange);
  text-decoration-thickness: 2px; text-underline-offset: 3px;
  background: var(--cb-orange-mark); border-radius: 2px;
}

/* The grip the kit is dragged by (2026-08-17). Top right covers things on
   some sites, so the bar moves; the dots say so without a label. */
.cb-kit-grip:hover { opacity: 1; background: rgba(255, 255, 255, .15); }
.cb-kit-grip:focus-visible { outline: 3px solid var(--cb-focus); outline-offset: 2px; opacity: 1; }
.cb-kit-dragging { cursor: grabbing; box-shadow: 0 10px 30px rgba(0, 0, 0, .35); }

/* Choosing one of the site's own pictures (2026-08-17): a search box and a
   small scrolling grid inside the form. Thumbnails are the site's real files
   at their real address; the picked value is only ever the file's NAME. */
/* Adding a picture to a body that is written as markdown (2026-09-01). It sits
   UNDER the box it writes into, set apart by a rule rather than a border, so it
   reads as something you do to the writing above rather than as another setting
   among the settings. Everything inside it reuses the controls the image fields
   already use — thumbnail, file input, picker — so the two ways a picture gets
   onto a page look the same to the person doing it. */
.cb-bodypic { margin-top: .7rem; padding-top: .7rem; border-top: 1px solid #E6E1D8; }
.cb-bodypic .cb-field-head { margin-bottom: .4rem; }
.cb-bodypic .cb-thumb { margin-bottom: .45rem; }
.cb-bodypic .cb-btn { font-size: .9rem; min-height: 36px; padding: .4rem .8rem; }

.cb-picker { margin-top: .6rem; border: 1px solid #ddd; border-radius: 6px; padding: .6rem; background: #fafafa; }
.cb-picker-search { width: 100%; box-sizing: border-box; font: inherit; padding: .45rem .6rem; border: 1px solid #ccc; border-radius: 6px; }
.cb-picker-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(96px, 1fr)); gap: .5rem; max-height: 280px; overflow-y: auto; margin-top: .5rem; }
.cb-picker-item { font: inherit; padding: .3rem; border: 2px solid transparent; border-radius: 6px; background: #fff; cursor: pointer; text-align: center; }
.cb-picker-item:hover, .cb-picker-item:focus-visible { border-color: var(--cb-orange); outline: none; }
.cb-picker-item img { display: block; width: 100%; height: 72px; object-fit: cover; border-radius: 4px; background: #eee; }
.cb-picker-name { display: block; font-size: .68rem; color: #555; margin-top: .25rem; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
/* Chosen, and WHICH chosen: the number is the order the rows will be made in,
   shown before anything is pressed rather than discovered afterwards. */
.cb-picker-item { position: relative; }
.cb-picked { border-color: var(--cb-orange); background: #fff7f0; }
.cb-picker-order { position: absolute; top: .15rem; left: .15rem; min-width: 1.15rem;
  padding: 0 .2rem; border-radius: 999px; background: var(--cb-orange); color: #fff;
  font-size: .65rem; line-height: 1.15rem; text-align: center; font-weight: 600; }
.cb-bulk { margin-top: .8rem; padding-top: .8rem; border-top: 1px dashed #ddd; }
.cb-bulk .cb-file { margin-top: .3rem; }

/* ── the agency ADMIN tier (2026-08-17) ────────────────────────────────────
   A block outlines in BLUE where a paragraph outlines in orange, so an admin
   can tell "this whole gallery" from "this caption" at a glance. Same tint
   family; a different edge. NO tint behind it (2026-08-27): a block or
   section is a whole band of somebody's page, and the wash at that size
   whited out the entire section — the dashed edge alone says "one thing". */
/* INSET, and in the orange (Cam, 2026-08-27). Outside-the-box at +8px the
   ring was invisible on a full-bleed band: the sides fell beyond the viewport
   and the blue vanished against a navy ground. Drawn just inside the edge it
   is on screen whatever the band's width, and the orange reads on navy, mist
   and white alike. */
.cb-bspot:hover, .cb-bspot:focus-visible {
  outline: 3px dashed var(--cb-orange); outline-offset: -6px;
}
.cb-pending-block { outline: 3px solid var(--cb-blue); outline-offset: 8px; position: relative; }
.cb-pending-block::after {
  content: "Change waiting for preview"; position: absolute; top: -.9rem; right: .5rem;
  background: var(--cb-blue); color: #fff; font: 600 .7rem/1.6 system-ui, sans-serif;
  padding: 0 .5rem; border-radius: 3px; letter-spacing: .02em;
}
.cb-pending-gone { opacity: .45; }
.cb-pending-gone::after { content: "Removal waiting for preview"; }
.cb-fieldset { border: 1px solid var(--cb-line); border-radius: 6px; padding: .6rem .8rem .8rem; margin: 0 0 1rem; }
.cb-fieldset legend { font-weight: 700; padding: 0 .3rem; }
.cb-rows { display: grid; gap: .6rem; }
.cb-row { border: 1px solid var(--cb-line); border-radius: 6px; padding: .6rem .8rem; background: rgba(0, 0, 0, .015); }
.cb-row .cb-actions { margin-top: .5rem; }
.cb-h3 { margin: 1rem 0 .4rem; font-size: 1rem; }
.cb-wait { border-top: 1px solid var(--cb-line); padding: .5rem 0; }
.cb-wait p { margin: 0 0 .3rem; }
.cb-wait .cb-btn + .cb-btn, .cb-wait a + .cb-btn { margin-left: .4rem; }
.cb-danger { color: #8a1c00; }
.cb-primary.cb-danger { background: #8a1c00; color: #fff; }

/* ── what a client may work with, and how a block works (2026-08-20) ─────── */

/* The block's own explanation, collapsed above its form. `details` because it
   is genuinely optional reading: somebody changing a caption for the tenth time
   should not have to scroll past a paragraph about masonry to reach the box. */
.cb-howto { margin: 0 0 1rem; border: 1px solid var(--cb-line); border-radius: 6px;
  padding: .45rem .7rem; background: rgba(0, 0, 0, .015); }
.cb-howto > summary { cursor: pointer; font-weight: 600; font-size: .9rem; }
.cb-howto > summary:focus-visible { outline: 2px solid var(--cb-orange); outline-offset: 2px; }
.cb-howto[open] > summary { margin-bottom: .5rem; }
.cb-howto .cb-help:last-child { margin-bottom: 0; }
.cb-help-list { margin: 0 0 .35rem; padding-left: 1.1rem; }
.cb-help-list li { margin: 0 0 .2rem; }

/* The admin tick-list. The checkbox needs its width back: every control in a
   panel is 100% wide so a phone keyboard cannot shrink it, which is right for a
   text field and turns a checkbox into a grey stripe. */
.cb-ticks { display: grid; gap: .55rem; margin: 0 0 1rem; }
.cb-tick { display: grid; grid-template-columns: auto 1fr; gap: .1rem .55rem;
  align-items: baseline; border-top: 1px solid var(--cb-line); padding-top: .55rem; }
.cb-panel .cb-tick input[type="checkbox"] { width: auto; min-height: 0; margin: 0; }
.cb-tick label { font-weight: 600; }
.cb-tick .cb-help { grid-column: 2; margin: 0; }

/* A change that does NOT wait for a preview must not wear the badge that says
   it does. Same blue frame, honest words - this is the whole of Eric's "I
   wouldn't want them to do a thing they don't know is structural and all of a
   sudden this edit is in limbo", in the one place the page says it. */
.cb-pending-live::after { content: "Change saved"; }
.cb-pending-live.cb-pending-gone::after { content: "Removal saved"; }

/* ── the working copy (2026-08-31, twice in one day) ─────────────────────
   On a draft site (html.cb-hold, set from the relay's /blocks answer) the
   held badges first learned honest working-copy words — and then Cam read
   them on his page: "you shouldn't see 'added — goes public when you
   publish'. The person already knows their site is not ready yet. At the
   bottom, it says changes on your site's working copy until you publish...
   we want it to look exactly like how it will look when it's published,
   outside of the little additional things like the plus signs."

   So on a working copy there are NO per-block ribbons, no frames, and no
   ghost of a removed band — removed means gone here, exactly as Publish
   will mean it. The bottom bar says the state ONCE; a held block still
   answers a hover in editing mode like everything else editable. */
html.cb-hold .cb-pending-badge { display: none; }
html.cb-hold .cb-pending-block { outline: none; }
html.cb-hold .cb-pending-block::after,
html.cb-hold .cb-pending-gone::after { content: none; }
html.cb-hold .cb-pending-gone { display: none; }
/* The SAME rule, one level down (Cam, 2026-08-31): "if you edit something it
   has a little dash around it — we do not want that. We want to see the site
   how it will appear once we publish it." The block ribbons went at the band
   level; the dashed frame and the orange wash were still landing on every
   changed paragraph, heading, picture and newly added card, which is the
   marked-up-proof look the client is not supposed to see. On a working copy a
   changed thing is just the thing. (The hover outline is untouched — that is
   the editing affordance, not a mark on the page.) */
/* NOT on a block that brought its own ground (Cam, 2026-09-01): "whenever I
   click save ... the actual spot behind that has the words does not have the
   color." This rule is why. It is one step MORE specific than
   `.blk[data-bg]` in core.css, so on a working copy it stripped the ground
   off the dress wrapper itself — while the band's full-width paint, which is
   a box-shadow and not a background, went on showing. A coloured section
   came out as a navy stripe down each edge of the screen with a white hole
   where the words were.

   Same lesson as the `.cb-spot` note further up this file: never speak about
   the background of an element whose background belongs to somebody else.
   The wash it exists to remove is only (0,1,0) and already loses to the
   ground, so a dressed block needs nothing said about it here — only the
   mark taken off, and the 4px radius that was rounding the corners of a
   full-bleed band. */
html.cb-hold .cb-pending:not([data-bg]) { outline: none; background: none; }
html.cb-hold .cb-pending[data-bg] { outline: none; border-radius: 0; }
/* And a run taken off a working copy is GONE, not struck through and greyed —
   the same honesty as .cb-pending-gone above, since Publish will simply mean
   it. The <del> rule covers a diff drawn before the page knew it was held. */
html.cb-hold .cb-run-gone { display: none; }
html.cb-hold .cb-changed del.cb-gone { display: none; }
html.cb-hold .cb-changed ins.cb-came {
  text-decoration: none; background: none; border-radius: 0;
}
/* A pending block that can be clicked open reads like every other spot —
   and, like every other spot, ONLY while editing is on (2026-08-31). With
   the mode off it is just a band on the page: an accordion opens, a link
   looks like a link, nothing says "click me to change it". */
html.cb-hold.cb-editing-on .cb-pending-editable { cursor: pointer; }
html.cb-hold.cb-editing-on .cb-pending-editable:hover,
html.cb-hold.cb-editing-on .cb-pending-editable:focus-visible {
  outline: 3px dashed var(--cb-orange); outline-offset: -6px;
}

/* A picture written into the page's HTML — an <img> in a pattern, an inline
   <svg> — reads as clickable to an ADMIN while editing is on (2026-09-25):
   a click opens its replace panel (placedPicturePanel). */
html.cb-pics-on [data-cbpic] { cursor: pointer; }
html.cb-pics-on [data-cbpic]:hover {
  outline: 3px dashed var(--cb-blue, #2f6fbf); outline-offset: 2px;
}
.cb-svgprev { background: repeating-conic-gradient(#f1f1f1 0 25%, #fff 0 50%) 0 0 / 16px 16px;
  object-fit: contain; max-height: 12rem; }
/* The drawing as it is now: a clone of the page's own <svg>, drawn on the
   ground it sits on (set from the page in placedSvgPanel; neutral here). */
.cb-svgnow { max-height: 12rem; padding: .6rem; border-radius: 6px; margin-bottom: .6rem;
  background: #f4f4f2; display: flex; justify-content: center; }
.cb-svgnow svg { max-width: 100%; max-height: 10.8rem; height: auto; width: auto; }
.cb-svgsrc { font-family: ui-monospace, "SF Mono", Menlo, Consolas, monospace;
  font-size: .82rem; line-height: 1.4; width: 100%; }

/* The quiet save: the sentence lands, sits out of the way of the next edit,
   and leaves (savedQuiet removes it; the animation only carries it in). Same
   dress as the bar so it reads as the kit talking, bottom-centre so it never
   covers the block just changed at the top of a panel's scroll. */
.cb-toast {
  position: fixed; left: 50%; bottom: 18px; transform: translateX(-50%);
  z-index: 2147483646; max-width: min(92vw, 34rem);
  background: var(--cb-blue); color: var(--cb-blue-ink);
  font: 500 .88rem/1.45 var(--cb-font);
  padding: .65rem 1rem; border-radius: 8px;
  box-shadow: 0 8px 28px rgba(0, 0, 0, .3);
  animation: cb-toast-in .18s ease-out;
}
@keyframes cb-toast-in {
  from { opacity: 0; transform: translateX(-50%) translateY(8px); }
  to { opacity: 1; transform: translateX(-50%) translateY(0); }
}

/* ── the working-copy bar (2026-08-31) ───────────────────────────────────
   One banner for the whole page once the truth swap has run: the page IS
   the working copy, and per-block badges would re-claim a wait that is
   over. Same family as the toast, one step down in z so a transient toast
   can still land on top of it. */
.cb-workingcopy {
  position: fixed; left: 50%; bottom: 18px; transform: translateX(-50%);
  z-index: 2147483645; max-width: min(94vw, 40rem);
  display: flex; gap: .7rem; align-items: center; flex-wrap: wrap;
  background: var(--cb-blue); color: var(--cb-blue-ink);
  font: 500 .88rem/1.45 var(--cb-font);
  padding: .55rem .9rem; border-radius: 8px;
  box-shadow: 0 8px 28px rgba(0, 0, 0, .3);
  animation: cb-toast-in .18s ease-out;
}
/* .cb-btn is full-width because it lives in panels; in this one-line bar a
   100%-wide button shoves the sentence into a one-word-per-line column and
   hangs out of the box (Cam's screenshot, 2026-08-31). Buttons here are
   words, not rows. */
.cb-workingcopy .cb-btn { flex: none; width: auto; margin-top: 0; }
.cb-workingcopy .cb-btn + .cb-btn { margin-left: 0; }
.cb-workingcopy .cb-wc-note { opacity: .85; }
/* The × that closes it. Furniture, not a verb, so no pill: quiet until you
   are on it, and a full-size hit area even though the glyph is small. */
.cb-workingcopy .cb-wc-x {
  flex: none; margin-left: -.2rem; width: 1.7rem; height: 1.7rem;
  display: inline-flex; align-items: center; justify-content: center;
  background: none; border: 0; border-radius: 6px; padding: 0;
  color: inherit; opacity: .7; cursor: pointer;
  font: 400 1.15rem/1 var(--cb-font);
}
.cb-workingcopy .cb-wc-x:hover { opacity: 1; background: rgba(255, 255, 255, .14); }
.cb-workingcopy .cb-wc-x:focus-visible { opacity: 1; outline: 2px solid currentColor; outline-offset: 1px; }

/* ── the Custom HTML preview (2026-08-22) ────────────────────────────────

   Eric pasted a table into the HTML box and could not see it anywhere before
   publishing. The preview is an iframe carrying the SITE's stylesheets, so
   what is inside it must look like the museum's website and what is around it
   must look like ours — otherwise a person cannot tell which of the two they
   are judging. Hence the agency's frame and label around a white stage. */
.cb-preview { margin: 0 0 1rem; }
.cb-preview-head { display: flex; align-items: center; gap: .4rem; flex-wrap: wrap;
  margin: 0 0 .4rem; }
.cb-preview-label { font-weight: 700; font-size: .9rem; margin-right: auto; }
.cb-panel .cb-preview-head .cb-btn { width: auto; }
.cb-preview-head .cb-btn[aria-pressed="true"] { background: var(--cb-blue);
  color: var(--cb-blue-ink); border-color: var(--cb-blue); }
/* The stage is the paper the block sits on: white, whatever the panel is. */
.cb-preview-stage { border: 1px solid var(--cb-line); border-radius: 6px;
  background: #fff; overflow: hidden; }
/* The frame is a REAL 1280 or 390 pixels wide and the whole thing is SCALED
   down to fit, never narrowed — the workshop's "Desktop" pane was 610px
   against a 736px breakpoint once and showed the phone layout under a desktop
   label for every draft. The stage's height is set in script to the scaled
   height, because a transform changes nothing about layout. */
.cb-preview-stage { position: relative; }
.cb-preview-frame { position: absolute; top: 0; left: 0; border: 0;
  transform-origin: top left; background: #fff; }
.cb-preview-note { margin: .4rem 0 .35rem; }
/* A dialog holding a laptop-width preview needs more than 34rem. Added from
   script (htmlPreview) so nothing else widens by accident. */
.cb-panel.cb-panel-wide { max-width: 60rem; }

/* -- SETTINGS ON THE LEFT, THE BLOCK ON THE RIGHT (Cam, 2026-08-31) -------

   "I'd like for these settings to exist on the left side of this modal, and
   for this modal, obviously, to be much wider ... and then for an actual
   live demonstration of what it's gonna appear like on the right side."

   Only the "+" panel wears this, and it puts it on from script
   (addExamplePanel) for the same reason cb-panel-wide is put on from script:
   every other dialog in the kit is a column of fields and must stay one.

   The picture STICKS as the form scrolls. A settings column is taller than
   the screen on a block with rows in it, and a demonstration you have to
   scroll back up to see is not one.

   Under 60rem of room there is no room for two columns, so it becomes one
   and the picture goes FIRST: on a phone the thing being described should
   be above the boxes describing it, not below three screens of them. */
.cb-panel.cb-panel-split { max-width: 76rem; }
.cb-panel-split .cb-panel-body { padding: 0; }
.cb-split { display: grid; grid-template-columns: minmax(0, 25rem) minmax(0, 1fr); }
.cb-split-form { padding: 1.25rem; min-width: 0; }
.cb-split-shown { padding: 1.25rem; min-width: 0;
  border-left: 1px solid var(--cb-line); background: #f4f2ee; }
.cb-split-shown .cb-preview { position: sticky; top: 1.25rem; margin: 0; }
.cb-split-shown .cb-preview-stage { background: #fff; }
/* The form column carries its own fieldsets; a second frame around them at
   this width reads as a box inside a box. */
.cb-split-form .cb-form > .cb-actions { position: sticky; bottom: 0;
  background: var(--cb-paper); padding: .75rem 0 0; margin-bottom: 0;
  border-top: 1px solid var(--cb-line); }

@media (max-width: 62rem) {
  .cb-split { grid-template-columns: minmax(0, 1fr); }
  .cb-split-shown { border-left: 0; border-bottom: 1px solid var(--cb-line);
    order: -1; }
  .cb-split-shown .cb-preview { position: static; }
}

/* THE PANEL STOPS AT THE EDGE OF THE SCREEN, AND THE SETTINGS SCROLL INSIDE
   IT (Cam, 2026-09-01): adding a text block and then a background "makes me
   have to scroll to see all that stuff."

   Measured at a 720px viewport before this: the panel stood 1145px tall and
   the whole BACKDROP scrolled, so reaching the background swatches scrolled
   the demonstration off the top — the one thing the split panel exists to
   keep in view — and the sticky Save bar rode down the screen above the
   settings that were still below the fold, reading as the end of the form.

   Capping the panel at the viewport puts the scroll where it belongs: the
   settings column scrolls against a picture that stays put, which is what
   `position: sticky` on the preview was always for. The narrow layout above
   is untouched — under 62rem there is one column and the page scroll is
   right.

   THE BACKDROP IS HELD STILL ONLY WHEN IT IS HOLDING THIS PANEL, which is
   what the `:has()` is for and it is not decoration. The backdrop is shared
   by every dialog the kit opens, and its `overflow-y: auto` is the only
   thing that lets a TALL ORDINARY panel — a rows form with a dozen items —
   be scrolled at all. Hiding it for every dialog at this width would leave
   the bottom of one of those unreachable, which is a worse bug than the one
   this rule is fixing. */
@media (min-width: 62.01rem) {
  .cb-backdrop:has(.cb-panel-split) { overflow: hidden; }
  .cb-panel.cb-panel-split {
    max-height: 90vh; display: flex; flex-direction: column; min-height: 0;
  }
  .cb-panel-split .cb-panel-head { flex: 0 0 auto; }
  .cb-panel-split .cb-panel-body {
    flex: 1 1 auto; min-height: 0; display: flex; overflow: hidden;
  }
  .cb-panel-split .cb-split { flex: 1 1 auto; min-height: 0; }
  .cb-split-form, .cb-split-shown { overflow-y: auto; min-height: 0; }
}

/* ── our own cards on the client's dashboard (2026-08-22) ────────────────

   The three site-wide admin screens moved off the floating bar onto
   /admin/site/. They deliberately reuse the dashboard's OWN `adm-card` look
   (platform/admin/_shell.njk) rather than a Cuberis one, because on that
   screen they are cards among cards — the only thing that marks them as ours
   is the heading above them. All that is needed here is the button reset:
   `adm-card` was written for an <a>, and these are actions, not addresses. */
button.cb-adm-card { width: 100%; text-align: left; font: inherit; cursor: pointer; }
button.cb-adm-card:focus-visible { outline: 2px solid var(--cb-orange); outline-offset: 2px; }
#cb-adm-tools .cb-adm-lede { color: #666; max-width: 46rem; margin: 0 0 1rem; }

/* ── the watcher: a save you can watch land (2026-08-22) ─────────────────
   Draws in two places from one state — a strip in the editing bar, and the
   same thing inside the "Saved" dialog. Both are `.cb-watch`; the modifier
   only changes how it sits. Empty when there is nothing to watch, so it
   takes no room on a quiet bar (`:empty` rather than a hidden attribute the
   render would have to remember to set). */
.cb-watch { display: contents; }
.cb-watch:empty { display: none; }
.cb-watch-bar {
  display: flex; align-items: center; gap: .5rem;
  flex: 0 1 auto; min-width: 0;
}
.cb-watch-bar:empty { display: none; }
.cb-watch-panel {
  display: flex; align-items: center; gap: .5rem; flex-wrap: wrap;
  margin: 0 0 1rem; font-size: .95rem;
}
.cb-watch-msg { white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.cb-watch-panel .cb-watch-msg { white-space: normal; }
/* Tabular figures so a ticking clock does not shuffle the words beside it. */
.cb-watch-time { opacity: .75; font-variant-numeric: tabular-nums; }

/* The bar fills on a clock towards 90% and stops; only the relay's "it is
   published" takes it to 100%. See the comment in editor-client.js — the
   motion is an estimate, the completion is a fact. */
.cb-watch-track {
  flex: 0 0 auto; width: 7rem; height: 4px; border-radius: 4px;
  background: rgba(255, 255, 255, .28); overflow: hidden; position: relative;
}
.cb-watch-panel .cb-watch-track { background: var(--cb-line); width: 9rem; }
.cb-watch-track > i {
  display: block; height: 100%; width: 0; border-radius: 4px;
  background: #7fd8a0; transition: width .25s linear;
}
.cb-watch-panel .cb-watch-track > i { background: var(--cb-blue); }
/* Past the patience window nobody can say how far along it is, so the bar
   stops pretending to know and simply shows that it is still going. */
.cb-watch[data-state="slow"] .cb-watch-track > i {
  width: 40%; position: absolute; left: -40%;
  animation: cb-watch-slide 1.15s ease-in-out infinite;
}
@keyframes cb-watch-slide { 0% { left: -42%; } 100% { left: 100%; } }
.cb-watch[data-state="live"] .cb-watch-msg { font-weight: 600; }
/* Stopped asking, still talking (2026-08-24). Not the error colour: nothing
   has failed as far as this person is concerned, the change is queued and the
   unattended pass carries it. It just is not on the site YET, and a strip that
   removed itself at that moment was the only report anybody got on the day the
   publish really was broken. */
.cb-watch[data-state="stalled"] .cb-watch-msg { font-weight: 600; }
.cb-watch-panel[data-state="stalled"] .cb-watch-msg { color: #8a5a00; }
.cb-watch[data-state="error"] .cb-watch-msg { color: #ffd7d7; font-weight: 600; }
.cb-watch-panel[data-state="error"] .cb-watch-msg { color: #b3261e; }
.cb-watch-note { opacity: .75; flex-basis: 100%; }

@media (prefers-reduced-motion: reduce) {
  .cb-watch-track > i { transition: none; animation: none; }
  .cb-watch[data-state="slow"] .cb-watch-track > i { position: static; width: 100%; }
}

/* ── the "+" picker: what are you trying to add here? (2026-08-27) ────────
   A screen of cards — the thing's name over a little drawing of its shape —
   instead of tabs over a text box and a drop-down of block names. The
   drawings are inline SVG in currentColor, so a card colours its own
   portrait as it is hovered, focused or disabled. */
.cb-cards {
  display: grid; grid-template-columns: repeat(auto-fill, minmax(9.5rem, 1fr));
  gap: .8rem; margin: .25rem 0 .5rem;
}
.cb-card {
  display: flex; flex-direction: column; align-items: center; gap: .55rem;
  padding: 1.1rem .7rem 1rem; cursor: pointer;
  background: #fff; color: #234; border: 1px solid var(--cb-line); border-radius: 10px;
  font: inherit; font-size: .92rem; font-weight: 600; text-align: center;
}
.cb-card:hover, .cb-card:focus-visible { border-color: var(--cb-blue, #2f6fbf); color: var(--cb-blue-deep, #1c4d8f); background: var(--cb-blue-wash, #f4f8fc); }
.cb-card:focus-visible { outline: 3px solid var(--cb-blue, #2f6fbf); outline-offset: 1px; }
.cb-card:disabled { opacity: .45; cursor: default; }
.cb-card.cb-card-busy { opacity: 1; border-color: var(--cb-blue, #2f6fbf); }
.cb-card-pic { display: block; }
.cb-card-pic svg { display: block; width: 4rem; height: 2.9rem; }
.cb-card-name { line-height: 1.2; }

/* ── the pictures in the SITE'S dress (Cam, 2026-09-25) ──────────────────
   "Actual colored things ... part of the site." Each card leads with a
   160x100 picture from platform/assets/js/brandart.js, and every colour in
   it is one of the site's own tokens, mapped here — so a site spun up
   tomorrow draws its pictures in its own colours and heading face with
   nothing configured. The card frame round it stays the kit's own palette
   (editor-kit.njk, "THE KIT'S COLOURS"): the picture is the site, the card
   is the tooling. */
.cb-card:has(.cb-art) { align-items: stretch; gap: .5rem; padding: .45rem .45rem .7rem; }
.cb-card-pic.cb-art svg {
  width: 100%; height: auto; aspect-ratio: 16 / 10; border-radius: 6px;
  box-shadow: 0 0 0 1px rgba(20, 30, 40, .08);
  transition: transform .15s ease, box-shadow .15s ease;
}
.cb-card:hover .cb-art svg, .cb-card:focus-visible .cb-art svg {
  box-shadow: 0 0 0 1px rgba(20, 30, 40, .08), 0 6px 16px -8px rgba(20, 30, 40, .35);
}
@media (prefers-reduced-motion: no-preference) {
  .cb-card:hover .cb-art svg { transform: translateY(-1px); }
}
.cb-pending-blockcard .cb-card-pic.cb-art svg { width: 9rem; height: auto; }

.cbx-paper  { fill: var(--color-bg, #fff); }
.cbx-pri    { fill: var(--color-primary, #1f3a5f); }
.cbx-acc    { fill: var(--color-accent, #b08d57); }
.cbx-hfill  { fill: var(--heading-color, var(--color-primary, #1f3a5f)); }
.cbx-on-pri { fill: var(--color-bg, #fff); }
.cbx-on-acc { fill: var(--color-bg, #fff); }
.cbx-soft   { fill: color-mix(in srgb, var(--color-text, #1a1a1a) 24%, var(--color-bg, #fff)); }
.cbx-faint-bar { fill: color-mix(in srgb, var(--color-text, #1a1a1a) 13%, var(--color-bg, #fff)); }
.cbx-pri-tint { fill: color-mix(in srgb, var(--color-primary, #1f3a5f) 13%, var(--color-bg, #fff)); }
.cbx-acc-tint { fill: color-mix(in srgb, var(--color-accent, #b08d57) 24%, var(--color-bg, #fff)); }
.cbx-card {
  fill: var(--card-bg, var(--color-bg, #fff));
  stroke: color-mix(in srgb, var(--color-text, #1a1a1a) 16%, var(--color-bg, #fff));
  stroke-width: 1;
}
.cbx-line { stroke: color-mix(in srgb, var(--color-text, #1a1a1a) 15%, var(--color-bg, #fff)); }
.cbx-line-strong { stroke: color-mix(in srgb, var(--color-primary, #1f3a5f) 45%, var(--color-bg, #fff)); }
.cbx-ring { stroke: var(--color-bg, #fff); stroke-width: 2.5; }
.cbx-stroke-pri { stroke: var(--color-primary, #1f3a5f); }
.cbx-stroke-on-pri { stroke: var(--color-bg, #fff); }
.cbx-a90 { opacity: .9; }
.cbx-a55 { opacity: .55; }
.cbx-a35 { opacity: .35; }
.cbx text { font-weight: 650; letter-spacing: -.01em; }
.cbx-h { fill: var(--heading-color, var(--color-primary, #1f3a5f));
  font-family: var(--font-heading, Georgia, serif); }
.cbx-body { font-family: var(--font-body, system-ui, sans-serif); font-weight: 600; letter-spacing: 0; }
.cbx-acc-t { fill: var(--color-accent, #b08d57); }
.cbx-mono { font-family: ui-monospace, "SF Mono", Menlo, Consolas, monospace; }

/* ── the arrangement picker: shapes, not words (2026-08-31) ─────────────
   A structure is a COLUMN TEMPLATE, and the fastest way to say which one is
   to draw it. Each diagram is boxes in the declared proportions — the
   numbers on the pattern's own modifiers — so a structure added to a
   pattern.json draws itself here with no CSS of its own. */
.cb-diagram { display: flex; flex-direction: column; gap: 2px;
  width: 3.4rem; height: 2.5rem; }
.cb-diagram-row { display: flex; gap: 2px; flex: 1 1 0; min-height: 0; }
.cb-diagram-cell { flex: 1 1 0; min-width: 0; border-radius: 2px;
  background: currentColor; opacity: .28; }
.cb-shapes { display: flex; flex-wrap: wrap; gap: .5rem; margin: .4rem 0 0; }
.cb-shape { display: flex; flex-direction: column; align-items: center;
  gap: .35rem; width: 5.6rem; padding: .5rem .35rem; cursor: pointer;
  background: #fff; color: #234; font: inherit; font-size: .8rem;
  text-align: center; border: 1.5px solid var(--cb-line); border-radius: 6px; }
.cb-shape:hover { border-color: var(--cb-blue, #2f6fbf); }
.cb-shape:focus-visible { outline: 3px solid var(--cb-blue, #2f6fbf); outline-offset: 1px; }
.cb-shape[aria-checked="true"] { border-color: var(--cb-blue, #2f6fbf);
  background: #f4f8fc; color: var(--cb-blue-deep, #1c4d8f); }
.cb-shape[aria-checked="true"] .cb-diagram-cell { opacity: .55; }
.cb-shape-name { line-height: 1.2; }
/* One column's two boxes. The heading is optional and says so, because a
   column of plain words is the commonest thing anybody puts in one. */
.cb-cellfield { margin: 0 0 .9rem; }
.cb-cellfield strong { display: block; margin: 0 0 .25rem; }
.cb-cellfield label { display: block; font-size: .85rem; margin: .3rem 0 .15rem; }
.cb-cellfield input, .cb-cellfield textarea { width: 100%; }

/* The placed block, standing in until the build draws the real thing. */
.cb-pending-blockcard {
  display: flex; flex-direction: column; align-items: center; gap: .4rem;
  padding: 1.4rem 1rem; margin: 1rem auto; max-width: 30rem;
  border: 1.5px dashed #b8c4d2; border-radius: 8px; color: #234;
}
.cb-pending-blockcard .cb-card-pic svg { width: 3.4rem; height: 2.5rem; }

/* ── the prose box: writing, drawn the way it will read (2026-08-27) ─────
   The multi-paragraph sibling of .cb-rich: same dress, more room, and real
   headings inside — sized as steps of the box's own type, not the page's
   display faces, so Heading 2 next to Heading 3 reads as a choice. */
.cb-prose { min-height: 11rem; max-height: 24rem; overflow-y: auto; }
.cb-prose h1, .cb-prose h2, .cb-prose h3, .cb-prose h4, .cb-prose h5, .cb-prose h6 {
  margin: .8em 0 .35em; line-height: 1.2; font-family: inherit;
}
.cb-prose h1 { font-size: 1.55em; }
.cb-prose h2 { font-size: 1.35em; }
.cb-prose h3 { font-size: 1.2em; }
.cb-prose h4 { font-size: 1.08em; }
.cb-prose h5 { font-size: 1em; }
.cb-prose h6 { font-size: .92em; letter-spacing: .02em; }
.cb-prose p { margin: .5em 0; }
.cb-prose ul, .cb-prose ol { margin: .5em 0; padding-left: 1.4em; }
.cb-prose > :first-child { margin-top: 0; }
.cb-prosebar { flex-wrap: wrap; align-items: center; }
/* ── ONE WRITING SURFACE, not a labelled field (Cam, 2026-08-31) ─────────

   "For the text block, I would rather it just be, like, a really good looking
   WYSIWYG." The bar and the box were two stacked boxes among the settings;
   this makes them one bordered thing with the bar sitting on top of it, the
   way every editor a person has used looks — and drawBlockForm moves it to
   the front of the form, so opening the panel lands you in the writing.

   The bar STICKS to the top of the surface, because the box scrolls: writing
   four paragraphs used to scroll Bold and Heading out of reach. */
.cb-editor {
  border: 1px solid var(--cb-line); border-radius: 8px; overflow: hidden;
  background: var(--cb-paper);
}
.cb-editor:focus-within { border-color: var(--cb-blue); box-shadow: 0 0 0 3px var(--cb-focus); }
.cb-editor .cb-marks {
  position: sticky; top: 0; z-index: 1;
  border: 0; border-bottom: 1px solid var(--cb-line); border-radius: 0;
  gap: .25rem; padding: .35rem .4rem;
}
.cb-editor .cb-rich {
  border: 0; border-radius: 0; padding: .85rem .9rem;
}
.cb-editor .cb-rich:focus-visible { outline: 0; }
.cb-editor .cb-prose { min-height: 15rem; max-height: 30rem; }
/* Smaller, quieter keys than the standalone bar's: there are six of them in a
   25rem column and they must not read as the loudest thing on the screen. */
.cb-editor .cb-mk {
  min-width: 34px; min-height: 32px; border-color: transparent;
  background: transparent; font-size: .95rem;
}
.cb-editor .cb-mk:hover { background: #e7edf3; border-color: var(--cb-line); }
.cb-mk-sep {
  width: 1px; align-self: stretch; margin: .2rem .15rem;
  background: var(--cb-line);
}
/* The words go first and the settings under them, so the fieldset that
   follows needs the rule that separates them. */
.cb-words { margin-bottom: 1.1rem; }

.cb-prose-style {
  font: inherit; font-size: .88rem; padding: .15rem .3rem;
  border: 1px solid #c8d2dd; border-radius: 5px; background: #fff; color: #234;
}

/* ── the hug buttons (2026-08-31) ────────────────────────────────────────
   Two toggles on the whole-band panel: hug the top, hug the bottom. They are
   ON/OFF rather than a menu of sizes, so the pressed one has to LOOK pressed —
   the tick in the label says it too, for anyone who cannot tell two greys
   apart, and aria-pressed says it to a screen reader. */
.cb-hug { margin-top: 1.25rem; }
.cb-hug .cb-actions { margin-top: .35rem; }
.cb-hug .cb-help { margin-top: .5rem; }
.cb-btn.cb-on {
  background: var(--cb-blue); border-color: var(--cb-blue); color: var(--cb-blue-ink);
  font-weight: 600;
}

/* ── the four grounds, in the block form (2026-08-31) ───────────────────
   A background is not a colour a client picks, it is a VARIATION: the ink,
   the rules and the accent all move with it. So each swatch shows what that
   ground actually produces — a rule, a heading, a line of text and the accent
   in the colours that variation resolves to — rather than naming it and
   hoping "Sand" means something. Same reasoning as the drawn choices in the
   block picker: a picture of the result beats a word for it. */
/* The verbs on one element inside a column (2026-09-01): move it within its
   column, or take it out. Quiet buttons in a row, because they sit UNDER the
   words box and are the smaller act on this panel — the words are what the
   panel is for. A direction that is not available is ABSENT rather than
   disabled, so this row is one, two or three buttons wide and must not
   reserve space for the ones that are not drawn. */
.cb-partverbs { display: flex; flex-wrap: wrap; gap: .4rem; margin-top: .35rem; }

/* THE COLOURS HERE ARE THE SITE'S TOKENS, NOT A CLIENT'S HEX. This block and
   the tiles, switches and toggles below had CAMRO's navy #14294D and gold
   #B08E4F written into them literally — twelve declarations in a file every
   licensed site loads, so a museum's editor drew its selected state and its
   focus ring in an association's brand colours. The section's own comment
   already said it was "token-driven rather than CAMRO's navy and gold"; it
   was not. If a token is ever missing the fallback is a neutral, never a
   client's colour. */
.cb-grounds { display: grid; grid-template-columns: repeat(4, minmax(0, 1fr)); gap: .5rem; }
.cb-ground { cursor: pointer; background: none; border: 0; padding: 0; font: inherit; text-align: center; }
.cb-ground-face { display: block; height: 2.6rem; border: 1px solid #CFC9BA; position: relative; }
.cb-ground b { display: block; font-size: .68rem; font-weight: 600; margin-top: .3rem; color: #4B5563; }
.cb-ground.is-on .cb-ground-face { border: 2px solid var(--color-primary, #1f3a5f); }
.cb-ground.is-on .cb-ground-face::after { content: ""; position: absolute; top: -1px; right: -1px;
                                          width: .5rem; height: .5rem; background: var(--color-accent, #8a6d3b); }
.cb-ground.is-on b { color: var(--color-primary, #1f3a5f); }
.cb-ground:focus-visible .cb-ground-face { outline: 2px solid var(--color-accent, #8a6d3b); outline-offset: 2px; }
/* The four bars are a MINIATURE OF THE BLOCK, not a colour key: a full-width
   rule, the heading under it, a shorter line of text, and a short accent. If
   they are all the same length the swatch reads as four stripes and says
   nothing about what the ground does — which is what the first cut did. */
.cb-ground-face span { position: absolute; left: 5px; right: 5px; }
.cb-g-rule { top: 7px; height: 2px; }
.cb-g-head { top: 12px; height: 5px; right: 30%; }
.cb-g-text { top: 22px; height: 2px; right: 18%; }
.cb-g-acc  { top: 28px; height: 4px; right: 58%; }
/* Each ground's own colours ARE THE SITE'S (2026-09-25). They were the
   house navy-and-gold as literal hex, so every site's Style tab showed a navy
   "Navy" whatever that ground really was — on CAMRO it is pine green, and
   Mist is their light green. The kit now asks the page: it draws a hidden
   .blk[data-bg=…] and reads back the --blk-* it resolves to (groundSwatch in
   editor-client.js), which is core.css's token mixes on most sites and the
   site's own overrides on a site that has them. Those arrive here as
   --sw-*; the fallbacks are neutrals, never anybody's brand. */
.cb-ground-face { background: var(--sw-ground, #FFFFFF); }
.cb-g-rule, .cb-g-head { background: var(--sw-deep, #3F4652); }
.cb-g-text { background: var(--sw-quiet, #8A919C); }
.cb-g-acc  { background: var(--sw-accent, #6B7280); }

/* ── the drawn choices, and flags as switches (2026-08-31) ──────────────
   Cam: the options should have "custom SVGs that display what each of these
   things mean". A drop-down reading "media" or "wide" tells a city clerk
   nothing; a wireframe of the arrangement tells them everything.

   The current one takes a navy border and a gold square — the same mark the
   site's own blocks use for every current state, so the editor and the page
   speak the same language. Not a tick and not a ring. */
.cb-tiles { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: .5rem; }
.cb-tiles-2 { grid-template-columns: repeat(2, minmax(0, 1fr)); }
.cb-tile {
  display: flex; flex-direction: column; gap: .4rem; position: relative;
  border: 1px solid #CFC9BA; background: #FCFBF8; padding: .4rem .4rem .45rem;
  cursor: pointer; font: inherit; text-align: center;
}
.cb-tile svg { display: block; width: 100%; height: auto;
               background: #FFFFFF; border: 1px solid #EFEBE1; }
.cb-tile em { font-style: normal; font-size: .68rem; line-height: 1.25; color: #4B5563; }
.cb-tile.is-on { border: 2px solid var(--color-primary, #1f3a5f);
                 padding: calc(.4rem - 1px) calc(.4rem - 1px) calc(.45rem - 1px); }
.cb-tile.is-on em { color: var(--color-primary, #1f3a5f); font-weight: 600; }
.cb-tile.is-on::after { content: ""; position: absolute; top: -1px; right: -1px;
                        width: .5rem; height: .5rem; background: var(--color-accent, #8a6d3b); }
.cb-tile:focus-visible { outline: 3px solid var(--color-accent, #8a6d3b); outline-offset: 2px; }

/* Flags were a free-text box you had to know the words for, which is the
   opposite of the promise this editor makes. They are a set of on-or-offs,
   so they are drawn as switches — square, because nothing else here is
   round. */
.cb-switches { display: flex; flex-direction: column; }
.cb-switch { display: flex; align-items: center; gap: .7rem;
             padding: .45rem 0; border-top: 1px solid #F0ECE2; font-size: .9rem; }
.cb-switch:first-child { border-top: 0; }
.cb-switch-name { flex: 1; }
.cb-switch-box { flex: none; width: 2.1rem; height: 1.1rem; padding: 0;
                 border: 0; border-radius: 0; background: #DCD8CE;
                 position: relative; cursor: pointer; }
.cb-switch-box::after { content: ""; position: absolute; top: 2px; left: 2px;
                        width: calc(1.1rem - 4px); height: calc(1.1rem - 4px);
                        background: #FFFFFF; }
.cb-switch.is-on .cb-switch-box { background: var(--color-primary, #1f3a5f); }
.cb-switch.is-on .cb-switch-box::after { left: auto; right: 2px; }
.cb-switch-box:focus-visible { outline: 3px solid var(--color-accent, #8a6d3b); outline-offset: 2px; }

/* A setting you can turn off (2026-09-01). The same square switch as the
   flags above, moved onto the label's own line, because the question it asks
   is about the whole field beside it rather than about one value among
   several. Off GRAYS the field rather than removing it: the panel keeps its
   shape, so nothing slides out from under the pointer, and a setting somebody
   turned off is still sitting where they left it. */
.cb-field-head { display: flex; align-items: center; gap: .75rem; margin: 0 0 .25rem; }
.cb-field-head .cb-label { margin: 0; flex: 1; }
.cb-onoff { flex: none; width: 2.1rem; height: 1.1rem; padding: 0;
            border: 0; border-radius: 0; background: #DCD8CE;
            position: relative; cursor: pointer; }
.cb-onoff::after { content: ""; position: absolute; top: 2px; left: 2px;
                   width: calc(1.1rem - 4px); height: calc(1.1rem - 4px);
                   background: #FFFFFF; }
.cb-onoff[aria-pressed="true"] { background: var(--color-primary, #1f3a5f); }
.cb-onoff[aria-pressed="true"]::after { left: auto; right: 2px; }
.cb-onoff:focus-visible { outline: 3px solid var(--color-accent, #8a6d3b); outline-offset: 2px; }
.cb-field.is-off > .cb-field-head .cb-label,
.cb-field.is-off > .cb-help { opacity: .55; }
.cb-field.is-off input[type=text] { background: #F4F2EE; color: #9A948B;
                                    border-color: #E6E1D8; }
/* Said only while it is true, and only on the field it is true of: a save
   writes a tag with no keyword on it, so the words in a switched-off box do
   not come back the next time the block is opened. Whether it applies is JS's
   to decide — a switched-off field with an EMPTY box has no words to lose and
   must not warn about them — so this says nothing about display except when
   hidden, or the attribute would lose to the rule. */
.cb-offnote { margin: .35rem 0 0; font-size: .82rem; color: #8A8378; }
.cb-offnote[hidden] { display: none; }

/* ═══ THE SETTINGS PANEL, dressed as it was drawn ═══════════════════════
   Cam, 2026-08-31, on the block-library canvas: "you had a design there
   that looked really nice for picking the options … maybe just apply that
   same thing to our current settings."

   So this is that drawing, applied to the panel that actually opens. The
   structure is what carries it, and all of it is token-driven rather than
   CAMRO's navy and gold, so it reads as the site it is standing on:

     · a solid header in the site's primary, the block's name in the site's
       heading face, and what the panel IS in tracked capitals beside it
     · sections divided by hairlines rather than boxed in borders — a
       fieldset inside a panel is a box inside a box
     · every section label in small tracked capitals, the same register head
       the blocks themselves use, so the editor and the page speak once
     · square corners on the controls, because the tiles and switches above
       are square and a rounded input beside a square switch reads as two
       different toolkits

   Appended rather than woven in: another session is working in this file's
   editor-bar rules, and a block at the end is the one shape that cannot
   collide with an edit somewhere else in it. */

.cb-panel {
  border-radius: 0;
  border: 1px solid var(--cb-line);
}
.cb-panel-head {
  background: var(--color-primary, #1f3a5f);
  color: var(--color-bg, #fff);
  border-bottom: 0;
  align-items: baseline;
}
.cb-panel-title {
  font-family: var(--font-heading, Georgia, serif);
  font-weight: 500;
  font-size: 1.25rem; letter-spacing: -.01em;
  /* NAVY ON NAVY WITHOUT THIS. The head above sets its own text colour and
     the title inherits it - except the title is an <h2>, and a site whose
     stylesheet colours its headings (CAMRO: --color-primary, the same navy
     this bar is painted in) wins on specificity over an inherited value. The
     panel's own name disappeared on the site the skin was drawn for. */
  color: inherit;
}
/* "Block options" — what the panel is, beside what it is about. Drawn with
   ::after so no markup has to change. Only on the panels that ARE a block's
   options (the split panel a new block opens in): on every panel it said
   "Block options" over the sign-in card and over Page settings. */
/* The class is the QUESTION the panel answers, not its shape. Scoping this to
   .cb-panel-split read as the same thing while only one panel was a block's
   options, and stopped being true the moment you clicked a block ALREADY on
   the page: that panel is an ordinary-width form, so it lost the kicker while
   the add panel kept it, and the two disagreed about what they were. Naming it
   for the block means a panel gets the kicker by being about a block, whatever
   width it happens to be. */
.cb-panel-block .cb-panel-head::after {
  content: "Block options";
  order: 1; margin-left: auto; margin-right: .75rem;
  font-size: .62rem; font-weight: 700; letter-spacing: .16em;
  text-transform: uppercase;
  color: color-mix(in srgb, var(--color-bg, #fff) 70%, var(--color-primary, #1f3a5f));
}
.cb-panel-head .cb-x { color: var(--color-bg, #fff); order: 2; opacity: .8; }
.cb-panel-head .cb-x:hover { opacity: 1; }

/* Sections, not boxes. */
.cb-panel .cb-fieldset {
  border: 0; border-top: 1px solid var(--cb-line);
  border-radius: 0; padding: 1rem 0 .4rem; margin: 0;
}
.cb-panel .cb-form > .cb-fieldset:first-of-type { border-top: 0; padding-top: 0; }
/* `display: block`, NOT `float: left` (2026-08-31). The float was the old way
   to make a legend behave like a full-width label, and inside a <fieldset> it
   has a consequence nothing else does: the FLEX OR GRID box IMMEDIATELY AFTER
   the legend resolves its automatic width to ZERO. A fieldset sizes its
   anonymous content box specially, and a floated legend puts that sizing down
   the min-content path.

   THE PRECISION MATTERS, because the first version of this note said "every
   flex or grid child" and that is wrong in the direction that hides the bug.
   Only the first slot is affected. Measured both ways, toggling nothing but
   this property:

     a flex box inserted RIGHT AFTER the legend   float 0px   block 918px
     the same box APPENDED to that fieldset       float 918px block 918px

   So it is invisible to anyone who checks by adding a control at the END of a
   section, and it fires on whoever writes the FIRST row-shaped one. Block
   children are unaffected in either slot, which is why it hid for as long as
   every panel was a stack of fields.

   What it looked like: the columns picker's structure diagrams stacked into a
   one-wide strip running off the bottom of the panel, and the tick-boxes
   below them did the same — both sit in slot 1 of their fieldsets. Cam,
   looking at it: "seems like something broke here."

   It was NOT only the columns panel, and it was not the block panel's ground
   swatches either (those are wrapped in a .cb-field, so nothing puts them in
   slot 1). What it did reach: the first .cb-radio of a settings fieldset, and
   — latent, waiting — drawBlockForm appends a boolean argument's .cb-radio
   straight to the fieldset, so the first block.json to declare a boolean as
   its first argument would have drawn a zero-width control. None does today.

   `display: block` gives the legend the same full-width label it had and
   takes the sizing off that path. */
.cb-panel .cb-fieldset legend {
  padding: 0; display: block; width: 100%;
  font-size: .64rem; font-weight: 700; letter-spacing: .16em;
  text-transform: uppercase;
  color: color-mix(in srgb, var(--cb-ink) 58%, var(--cb-paper));
  margin-bottom: .7rem;
}
.cb-panel .cb-label {
  font-size: .64rem; font-weight: 700; letter-spacing: .16em;
  text-transform: uppercase;
  color: color-mix(in srgb, var(--cb-ink) 58%, var(--cb-paper));
  margin-bottom: .45rem;
}
.cb-panel .cb-help { font-size: .78rem; line-height: 1.45; opacity: 1;
                     color: color-mix(in srgb, var(--cb-ink) 55%, var(--cb-paper)); }

/* Square, to match the tiles and switches. */
.cb-panel input, .cb-panel textarea, .cb-panel select { border-radius: 0; }
.cb-panel input:focus-visible, .cb-panel textarea:focus-visible,
.cb-panel select:focus-visible {
  outline: 2px solid var(--color-accent, #b08d57); outline-offset: 1px;
}

/* ── sent: the receipt for a change on its way (2026-08-31) ────────────
 *
 * Cam asked for "a little animated check mark" over "your changes are on
 * the way", with the wait in parentheses underneath. It replaces a single
 * sentence that carried the count, the reassurance and the timing at once.
 *
 * The tick DRAWS rather than fading: a stroke that travels reads as "this
 * just happened", where an opacity fade reads as a thing that was always
 * there. Ring first, mark second, and the mark is the slower of the two so
 * the eye lands on it last. */
.cb-sent { text-align: center; padding: 22px 8px 10px; }
.cb-sent-tick svg { width: 54px; height: 54px; display: block; margin: 0 auto 12px; }
.cb-sent-tick circle {
  fill: none; stroke: var(--cb-good); stroke-width: 2; opacity: .3;
  stroke-dasharray: 145; stroke-dashoffset: 145;
  animation: cb-sent-ring .42s ease-out forwards;
}
.cb-sent-tick path {
  fill: none; stroke: var(--cb-good); stroke-width: 4;
  stroke-linecap: round; stroke-linejoin: round;
  stroke-dasharray: 40; stroke-dashoffset: 40;
  animation: cb-sent-mark .34s .26s cubic-bezier(.65, 0, .45, 1) forwards;
}
@keyframes cb-sent-ring { to { stroke-dashoffset: 0; } }
@keyframes cb-sent-mark { to { stroke-dashoffset: 0; } }
.cb-sent-say {
  margin: 0; font-size: 1.05rem; font-weight: 600; color: var(--cb-ink);
}
/* The wait is a footnote, not news: smaller, quieter, and it must not
 * compete with the sentence above it for the first read. */
.cb-sent-foot {
  margin: 4px 0 0; font-size: .85rem; opacity: .62; color: var(--cb-ink);
}
/* Drawn, not animated, for anyone who has asked for less movement. The
 * tick still ARRIVES - it is simply already finished. */
@media (prefers-reduced-motion: reduce) {
  .cb-sent-tick circle, .cb-sent-tick path {
    animation: none; stroke-dashoffset: 0;
  }
}

/* ── the menu builder (2026-09-02) ────────────────────────────────────────
   The kit's copy of the studio's menu list. Held flat with an indent class
   rather than as nested lists, because that is how the JavaScript holds it
   while it is being dragged — see menuPanel in editor-client.js. */
.cb-menu-list {
  margin: 0 0 .75rem;
  max-height: min(46vh, 26rem);
  overflow-y: auto;
  /* A menu is one column of short rows; a long one has to be able to scroll
     inside the panel rather than pushing Save off the bottom of the screen,
     which on a phone is the same as having no Save button. */
}
.cb-menu-row {
  display: flex;
  align-items: center;
  gap: .5rem;
  padding: .35rem .5rem;
  border: 1px solid var(--cb-line);
  border-radius: 4px;
  background: var(--cb-paper);
  margin-bottom: .25rem;
}
.cb-menu-kid { margin-left: 1.75rem; }
.cb-menu-dragging { opacity: .4; }
/* The drop marker is a SHADOW rather than a border, so a row does not change
   height as the pointer passes over it — a list that jumps while you are
   dragging is a list you cannot aim at. */
.cb-menu-over { box-shadow: 0 -2px 0 0 var(--cb-orange); border-color: var(--cb-orange); }
/* Moving DOWN lands below the target, so the marker is on its lower edge. */
.cb-menu-over.cb-menu-over-below { box-shadow: 0 2px 0 0 var(--cb-orange); }
/* And while the drop would NEST, the marker steps in by one level, so what
   will happen is visible before the button is let go. An inset shadow cannot
   be offset from the left, so the step is drawn as a gradient on the edge. */
.cb-menu-over.cb-menu-over-in {
  box-shadow: none;
  background:
    linear-gradient(var(--cb-orange), var(--cb-orange)) 1.75rem 0 / calc(100% - 1.75rem) 2px no-repeat,
    var(--cb-paper);
}
.cb-menu-over.cb-menu-over-in.cb-menu-over-below {
  background:
    linear-gradient(var(--cb-orange), var(--cb-orange)) 1.75rem 100% / calc(100% - 1.75rem) 2px no-repeat,
    var(--cb-paper);
}
/* THE HANDLE is a real button (arrow keys move the row), dressed as six dots. */
.cb-menu-grip {
  display: inline-flex; align-items: center; justify-content: center;
  flex: none; width: 1.6rem; height: 1.9rem; margin: -.2rem 0;
  padding: 0; border: 0; border-radius: 4px; background: none;
  color: #8f8f9c; fill: currentColor; cursor: grab; user-select: none;
}
.cb-menu-grip:hover { color: var(--cb-ink); background: var(--cb-blue-wash); }
.cb-menu-grip:active { cursor: grabbing; }
.cb-menu-grip:focus-visible { outline: 3px solid var(--cb-focus); outline-offset: 1px; color: var(--cb-ink); }
/* On a PAGE rather than in a dialog the list is the page's own content, so it
   does not scroll inside itself: a nested scroll area on a full screen is a
   list that fights the mouse wheel. */
.cb-menu-list.cb-menu-list-page { max-height: none; overflow: visible; }
.cb-menu-list-page .cb-menu-row { padding: .5rem .6rem; }
.cb-menu-label { font-weight: 600; }
.cb-menu-label[contenteditable] {
  outline: none;
  border-bottom: 1px dashed transparent;
  min-width: 3ch;   /* a label emptied to nothing must still be clickable */
}
.cb-menu-label[contenteditable]:focus { border-bottom-color: var(--cb-orange); }
.cb-menu-where {
  color: #6b6357;
  font-size: .85em;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  min-width: 0;
}
.cb-menu-head { color: #8a6620; font-style: italic; }
.cb-menu-acts { margin-left: auto; display: flex; gap: .25rem; flex: none; }
.cb-menu-add { display: flex; gap: .5rem; flex-wrap: wrap; align-items: center; margin-bottom: .5rem; }
.cb-menu-pick { max-width: min(22rem, 100%); flex: 1 1 12rem; }
.cb-menu-note { min-height: 1.2em; }

/* ── a page's settings: where it shows up in search (2026-09-25) ───────── */
.cb-page-where { margin: -.2rem 0 .9rem; }
.cb-page-where a { color: var(--cb-blue); word-break: break-all; }
.cb-seo { border: 1px solid var(--cb-line); border-radius: 6px; padding: .75rem .9rem .3rem; margin: 0 0 1rem; }
.cb-seo > legend { padding: 0 .35rem; }
/* The search result as a search engine draws it: the address, then the title
   as a link-blue line, then the description. Drawn in the kit's own system
   font on purpose - it is a picture of somebody else's page, not of ours. */
.cb-serp { display: flex; flex-direction: column; gap: .15rem; padding: .7rem .8rem; margin: .2rem 0 .8rem;
  background: var(--cb-paper); border: 1px solid var(--cb-line); border-radius: 6px; font-family: var(--cb-font); }
.cb-serp-url { color: #4d5156; font-size: .8rem; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.cb-serp-title { color: #1a0dab; font-size: 1.1rem; line-height: 1.3; }
.cb-serp-desc { color: #4d5156; font-size: .86rem; line-height: 1.45; }
.cb-serp-desc.cb-serp-none { font-style: italic; }
.cb-count { display: block; margin-top: .25rem; font-size: .8rem; color: #5f5f6b; }
.cb-count.cb-count-over { color: var(--cb-bad); }
.cb-btn.cb-tiny { padding: .15rem .45rem; font-size: .9em; line-height: 1.4; }
/* Quiet danger verbs get red ink. The primary one (the panel's red
   "Discard all changes" button) keeps its white ink — this rule used to win
   over .cb-primary.cb-danger on source order alone and painted red on red
   (Cam, 2026-09-09). */
.cb-btn.cb-danger:not(.cb-primary) { color: var(--cb-bad); }
@media (max-width: 30rem) {
  /* The address is the first thing to go: on a phone the row has to hold a
     name and three controls, and a truncated path helps nobody. */
  .cb-menu-where { display: none; }
  .cb-menu-head { display: inline; }
}

/* ── the phone, where a third of the screen was chrome (2026-09-07) ───────
   Measured on the demo at 375x812, on top of F4's kit work: the kit 94px, the
   admin bar 115px, and the card picker a single 1,915px column. 209px of 812
   is gone before a person has touched anything, and the picker is a scroll
   rather than a choice. B-30. */
@media (max-width: 30rem) {
  /* TWO CARDS ACROSS, and the old rule missed it by four pixels: a 9.5rem
     minimum plus the gap needs 317px and the panel body offers 311, so the
     grid fell to one column and sixteen cards became 1,915px. A phone is the
     one width where the count has to be SAID rather than computed - the cards
     are square-ish, so two of them at any phone width read better than one
     enormous one. */
  .cb-cards { grid-template-columns: repeat(2, minmax(0, 1fr)); gap: .55rem; }
  .cb-card { padding: .8rem .45rem .7rem; gap: .4rem; font-size: .86rem; }
  .cb-card-pic svg { width: 3.1rem; height: 2.25rem; }

  /* THE BAR SAYS THE SAME THINGS IN TWO ROWS, NOT FOUR. Who you are and the
     way out belong together on the first row; the mode sentence is the long
     one and gets the second to itself, clipped rather than wrapped. Nothing
     is hidden and no word is changed - this is which line each thing sits
     on. */
  /* On a phone: one row of who / Dashboard / ⋯, one row of verbs stretched
     across, then the sentence. Three tidy rows, never a column of buttons.
     Measured against the old pair: 402px of chrome (49% of 812) -> ~134px. */
  .cb-bar { gap: .4rem .5rem; padding: .5rem .6rem; }
  .cb-bar-rule { display: none; }
  .cb-bar-sp { flex: 1 1 auto; }
  .cb-bar-mode { flex: 1 0 100%; order: 5; font-size: .78rem; }
  .cb-bar-verbs { flex: 1 0 100%; order: 4; display: flex; gap: .4rem; }
  .cb-bar-verbs > .cb-kit-btn, .cb-bar-verbs > .cb-kit-split { flex: 1 1 auto; }
  .cb-bar-verbs > .cb-kit-split > .cb-kit-btn { flex: 1 1 auto; }
  .cb-bar-verbs > .cb-kit-btn { text-align: center; }
}


/* A second section inside one panel. Used by "What this site's people can
   edit", which asks two questions - which blocks, and what else - and must
   not run them together as one undifferentiated list of ticks. */
.cb-subhead {
  font-size: 1em;
  font-weight: 700;
  margin: 1.25rem 0 .25rem;
  padding-top: .75rem;
  border-top: 1px solid var(--cb-line);
}

/* ── files attached to a change request (2026-09-03) ──────────────────────
   A list of receipts, not a gallery: by the time a row is here the file is
   already on the relay's shelf and the only questions left are "is that the
   right one" and "how do I take it off". So: name, size, Remove — and the
   name is allowed to wrap, because "newsletter-spring-2026-final-v3.pdf" is
   what people actually attach and truncating it hides the one word that
   distinguishes it from the other three drafts on their desktop. */
.cb-files { list-style: none; margin: .5rem 0 .35rem; padding: 0; }
.cb-file {
  display: flex; align-items: baseline; gap: .6rem; flex-wrap: wrap;
  padding: .4rem .55rem; margin: 0 0 .35rem;
  border: 1px solid var(--cb-line); border-radius: 6px;
  background: #fff; font-size: .9rem;
}
.cb-file-name { flex: 1 1 12rem; word-break: break-word; }
.cb-file-size { opacity: .7; white-space: nowrap; }
.cb-file .cb-btn { margin-left: auto; }

/* ── ONE HOUSE KEYWORD OFF EVERY PANEL (2026-09-07) ──────────────────────
   Cam: "all of the settings seem to be everywhere". Eyebrow goes; Heading,
   Opening line and Background stay — he ruled Background up front
   explicitly, reversing the proposal to hide it.

   HIDDEN, AND STILL SUBMITTING. The field stays in the form and its reader is
   untouched, so a block that already carries an eyebrow keeps it and saves it
   back unchanged. That is why this is a stylesheet rule and not a cut to
   blockform.py's DRESS_FIELDS: removing it there would take the value away.

   `display: none` also takes it out of the tab order, which a visibility or
   opacity rule would not — a control a sighted person cannot see and a
   keyboard still reaches is worse than one that is merely there. */
.cb-panel .cb-field[data-dress="eyebrow"] { display: none; }

/* TEMPORARY (Cam, 2026-09-09, mid-presentation): "the 'How it will look'
   sections … don't look good right now." Every preview is hidden — the
   Custom HTML one under its box and the live one in the right-hand column of
   the "+" panel — and the split panel closes back to a single column of
   settings. The script still builds them; nothing else changes. DELETE THIS
   BLOCK to bring them back. */
.cb-panel .cb-preview { display: none !important; }
.cb-panel .cb-split-shown { display: none !important; }
.cb-panel .cb-split { grid-template-columns: minmax(0, 1fr) !important; }
.cb-panel.cb-panel-wide, .cb-panel.cb-panel-split { max-width: 34rem !important; }

/* ── editing in place (2026-09-25) ─────────────────────────────────────────
   The words being edited stay where they are, in the site's own type, with a
   solid outline instead of the hover's dashed one: this is the thing you are
   in, not a thing you might click. The bar floats just above it. */
.cb-inplace {
  outline: 2px solid var(--cb-orange) !important; outline-offset: 5px;
  cursor: text; background: var(--cb-orange-wash);
  border-radius: 3px; caret-color: currentColor;
}
.cb-inplace:focus { outline: 2px solid var(--cb-orange) !important; }
.cb-saving { opacity: .6; transition: opacity .15s linear; }
/* A change made this session: a quiet underline, not a crossed-out diff with a
   badge. The page shows what it will say; the bar counts what is waiting. */
.cb-edited { text-decoration: underline dotted var(--cb-orange) 2px; text-underline-offset: 4px; }

.cb-inbar {
  position: fixed; z-index: 2147483000;
  display: flex; align-items: center; gap: 2px; flex-wrap: wrap; max-width: min(92vw, 520px);
  padding: 4px; border-radius: 8px;
  background: var(--cb-blue-deep); box-shadow: 0 6px 22px rgba(0,0,0,.28);
  font-family: var(--cb-font); font-size: 14px; line-height: 1;
}
.cb-in-mk {
  font: inherit; font-size: 14px; cursor: pointer;
  min-width: 34px; height: 32px; padding: 0 8px;
  border: 0; border-radius: 5px; background: transparent; color: #fff;
}
.cb-in-mk:hover { background: rgba(255,255,255,.14); }
.cb-in-mk:focus-visible { outline: 2px solid #fff; outline-offset: 1px; }
.cb-in-mk.cb-mk-b { font-weight: 800; }
.cb-in-mk.cb-mk-i { font-style: italic; font-family: Georgia, serif; }
.cb-in-mk.cb-mk-s { text-decoration: line-through; }
.cb-in-link { font-size: 13px; font-weight: 600; }
/* A heading's size (2026-09-25): a quiet select in the bar's own colours. */
.cb-in-size {
  font: inherit; font-size: 13px; height: 32px; padding: 0 6px; cursor: pointer;
  border: 1px solid rgba(255,255,255,.35); border-radius: 5px;
  background: transparent; color: #fff;
}
.cb-in-size option { color: var(--cb-ink); background: var(--cb-paper); }
.cb-in-size:focus-visible { outline: 2px solid #fff; outline-offset: 1px; }
.cb-in-sep { width: 1px; height: 20px; margin: 0 4px; background: rgba(255,255,255,.25); }
.cb-in-done { background: var(--cb-orange); font-weight: 800; }
.cb-in-done:hover { background: var(--cb-orange-dark); }
/* The link row the Link button opens sits under the bar's buttons. */
.cb-inbar .cb-linkrow { flex-basis: 100%; display: flex; gap: 4px; flex-wrap: wrap; padding: 4px 2px 2px; }
.cb-inbar .cb-linkrow input { flex: 1 1 180px; min-width: 0; height: 30px; padding: 0 8px;
  border: 0; border-radius: 5px; font: inherit; font-size: 13px; }
.cb-inbar .cb-say { flex-basis: 100%; margin: 4px 2px 0; color: #fff; font-size: 12.5px; }

.cb-flash {
  position: fixed; z-index: 2147483000; pointer-events: none;
  padding: 6px 10px; border-radius: 6px;
  font-family: var(--cb-font); font-size: 13px; font-weight: 600; line-height: 1.3;
  color: #fff; background: var(--cb-good); box-shadow: 0 4px 14px rgba(0,0,0,.2);
  max-width: min(90vw, 420px);
}
.cb-flash-bad { background: var(--cb-bad); }
.cb-flash-out { opacity: 0; transition: opacity .35s linear; }

/* ── one calm bar (2026-09-25) ─────────────────────────────────────────────
   The bar keeps ONE row on a desktop: a status that gets long is cut with an
   ellipsis rather than pushing the buttons onto a second line, which is what
   made them jump every time something saved. No clock and no filling track in
   the bar — a small pulsing dot says "working", the words say what. */
@media (min-width: 761px) {
  .cb-bar { flex-wrap: nowrap; }
  .cb-bar > * { flex-shrink: 0; }
  .cb-bar-mode, .cb-bar .cb-watch-bar { flex: 0 1 auto; min-width: 0; overflow: hidden;
    white-space: nowrap; text-overflow: ellipsis; }
}
.cb-bar .cb-watch-time, .cb-bar .cb-watch-track { display: none; }
.cb-bar .cb-watch-bar[data-state="copy"]::before,
.cb-bar .cb-watch-bar[data-state="working"]::before,
.cb-bar .cb-watch-bar[data-state="slow"]::before {
  content: ""; width: .5rem; height: .5rem; border-radius: 50%; flex: 0 0 auto;
  background: #7fd8a0; animation: cb-dot-pulse 1.1s ease-in-out infinite;
}
@keyframes cb-dot-pulse { 0%, 100% { opacity: .35; } 50% { opacity: 1; } }
@media (prefers-reduced-motion: reduce) {
  .cb-bar .cb-watch-bar::before { animation: none; opacity: .8; }
}

/* ── bands: a faint line, and a handle (2026-09-25) ────────────────────────
   A section is a whole band of the page. A dashed box the size of the band on
   every hover, and one that stayed after its panel closed, was most of the
   "everything pops up" Cam described. The band now shows a faint line on
   hover and keeps its keyboard focus ring; its options are the handle. */
.cb-sect.cb-spot:hover { outline: 1px solid rgba(245, 92, 0, .28); outline-offset: -1px; }
.cb-sect.cb-spot:focus-visible { outline: 2px solid var(--cb-orange); outline-offset: -2px; }
.cb-sect.cb-spot:focus:not(:focus-visible) { outline: none; }
.cb-sect-handle {
  position: fixed; z-index: 2147482990;
  font: 600 12px/1 var(--cb-font); letter-spacing: .02em;
  padding: 6px 9px; border: 0; border-radius: 6px; cursor: pointer;
  background: var(--cb-blue-deep); color: #fff; box-shadow: 0 3px 12px rgba(0,0,0,.22);
}
.cb-sect-handle:hover { background: var(--cb-orange); }
.cb-sect-handle:focus-visible { outline: 2px solid #fff; outline-offset: 1px; }

/* ── CONTENT, STYLE, LAYOUT (2026-09-25) ─────────────────────────────────
 * A block's settings, behind its ⚙ tab. Words are changed on the page; these
 * are the parts it shows, how it looks and how it is laid out. */
.cb-blk-handle { background: var(--cb-ink); }
.cb-tabbed { margin: 0 0 1rem; }
.cb-tabs { display: flex; gap: 2px; margin: 0 0 .9rem; border-bottom: 1px solid var(--cb-line); }
.cb-tab {
  appearance: none; border: 0; background: transparent; cursor: pointer;
  font: 600 .92rem/1 var(--cb-font); color: #5d564c;
  padding: .65rem .9rem .6rem; margin-bottom: -1px; border-bottom: 3px solid transparent;
}
.cb-tab:hover { color: var(--cb-ink); }
.cb-tab[aria-selected="true"] { color: var(--cb-ink); border-bottom-color: var(--cb-orange); }
.cb-tab:focus-visible { outline: 2px solid var(--cb-orange); outline-offset: -2px; border-radius: 4px 4px 0 0; }
.cb-tabpane > .cb-fieldset:last-child { margin-bottom: 0; }
.cb-items { list-style: none; margin: 0 0 .7rem; padding: 0; counter-reset: cb-item; }
.cb-item { border: 1px solid var(--cb-line); border-radius: 6px; margin: 0 0 .4rem; background: #fff; }
.cb-item-head { display: flex; align-items: center; gap: .5rem; padding: .35rem .4rem .35rem .7rem; }
.cb-item-name { flex: 1 1 auto; min-width: 0; font-weight: 600; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.cb-item-name::before { counter-increment: cb-item; content: counter(cb-item) ". "; color: #8a8175; font-weight: 500; }
.cb-item-verbs { display: flex; gap: 2px; flex: none; }
.cb-item-verbs .cb-btn { min-width: 2rem; }
.cb-item-verbs .cb-btn:disabled { opacity: .35; cursor: default; }
.cb-item-more { padding: 0 .7rem .4rem; }
.cb-item-more > summary { cursor: pointer; font-size: .82rem; color: #6b6358; }
.cb-item-more[open] > summary { margin-bottom: .5rem; }
.cb-words-fold { padding: 0; margin: 0 0 1rem; }
/* ── one button's settings (buttonFields, 2026-09-25) ──────────────────────
   The looks are TILES, each holding a sample drawn with the site's own button
   CSS (lookSample reads it off a throwaway <a>), on the ground the real button
   sits on — so "Accent" shows this site's accent, not ours. */
.cb-bset + .cb-bset { border-top: 1px solid var(--cb-line); padding-top: 1rem; margin-top: 1rem; }
/* An even grid, not a wrapping row: four in a line where they fit, two by two
   on a phone, and the lines between them drawn by the gap so a wrap never
   leaves a half-bordered cell. */
.cb-seg { display: grid; grid-template-columns: repeat(auto-fit, minmax(6.4rem, 1fr));
  gap: 1px; margin: .2rem 0 .6rem; background: var(--cb-line); border: 1px solid var(--cb-line); }
.cb-seg-opt { position: relative; display: flex; align-items: center; justify-content: center;
  padding: .45rem .6rem; cursor: pointer; font-size: .9rem; color: var(--cb-ink);
  background: var(--cb-paper); text-align: center; }
.cb-panel .cb-seg-opt input { position: absolute; opacity: 0; width: 1px; height: 1px; margin: 0; }
.cb-seg-opt:has(input:checked) { background: var(--cb-blue); color: var(--cb-blue-ink); }
.cb-seg-opt:has(input:focus-visible) { outline: 3px solid var(--cb-focus); outline-offset: 1px; }
.cb-bset-where { margin-bottom: .2rem; }
.cb-bset-href { margin: .25rem 0 .5rem; overflow-wrap: anywhere; }
.cb-bset-pick { display: flex; align-items: center; gap: .6rem; margin-bottom: .4rem; }
.cb-bset-pick:empty { display: none; }
.cb-bset-pick img { width: 3.5rem; height: 2.6rem; object-fit: cover; }
.cb-looks { display: grid; grid-template-columns: repeat(auto-fill, minmax(8.5rem, 1fr));
  gap: .5rem; margin: .2rem 0 .8rem; }
.cb-look { display: flex; flex-direction: column; align-items: stretch; gap: .35rem;
  padding: .4rem; background: var(--cb-paper); border: 1px solid var(--cb-line);
  cursor: pointer; font: inherit; color: var(--cb-ink); text-align: center; }
.cb-look[aria-checked="true"] { border-color: var(--cb-blue); box-shadow: 0 0 0 2px var(--cb-blue); }
.cb-look:focus-visible { outline: 3px solid var(--cb-focus); outline-offset: 2px; }
.cb-look-stage { display: flex; align-items: center; justify-content: center; min-height: 3.2rem;
  padding: .5rem; background: var(--cb-look-ground, #fff); overflow: hidden; }
.cb-look-sample { display: inline-block; padding: .4rem .8rem; font-size: .8rem; line-height: 1.2;
  white-space: nowrap; pointer-events: none; }
.cb-look-name { font-size: .82rem; }
.cb-checks { display: flex; flex-wrap: wrap; gap: .4rem 1.4rem; margin: .1rem 0 .3rem; }
.cb-check { display: inline-flex; align-items: center; gap: .45rem; cursor: pointer; }
.cb-panel .cb-check input { width: 1.05rem; height: 1.05rem; margin: 0; }

/* ── one menu item's mega panel, as a form (2026-09-25) ───────────────────
   A column is a fieldset; a link is one row of page, words and an optional
   line under it. The rows wrap on a narrow panel rather than squeezing three
   inputs into a phone's width. */
.cb-menu-panel { white-space: nowrap; }
.cb-mega-col { border: 1px solid var(--cb-line); border-radius: 6px; padding: .6rem .8rem .4rem; margin: 0 0 .9rem;
  min-width: 0; /* a fieldset is min-content wide by default and would push past the panel */ }
.cb-mega-col > legend { padding: 0 .35rem; display: flex; align-items: center; gap: .4rem; }
.cb-mega-col .cb-field { margin-bottom: .6rem; }
.cb-mega-col input[type=text], .cb-mega-page { width: 100%; box-sizing: border-box; }
/* Two lines per link: which page across the top, then its words, the line
   under it and its buttons. Four inputs abreast did not fit the panel. */
.cb-mega-link { display: grid; grid-template-columns: minmax(0, 1fr) minmax(0, 1fr) auto;
  gap: .35rem .4rem; align-items: center; margin: 0 0 .7rem; padding-bottom: .6rem;
  border-bottom: 1px dashed var(--cb-line); }
.cb-mega-link > .cb-mega-page { grid-column: 1 / -1; }
.cb-mega-feat .cb-mega-link { grid-template-columns: minmax(0, 1fr) minmax(0, 1fr); border-bottom: 0; padding-bottom: 0; }
.cb-mega-feat .cb-mega-link > .cb-mega-page { grid-column: auto; }
.cb-mega-link-acts { display: flex; gap: .25rem; }
.cb-mega-more { display: flex; gap: .5rem; flex-wrap: wrap; margin: .3rem 0 .2rem; }
.cb-mega-wide { margin-top: .1rem; }

/* Under an item with a mega panel: what its child list does and does not do. */
.cb-menu-panelnote { margin: -.1rem 0 .35rem 1.75rem; font-size: .85em; }
.cb-linkbtn { font: inherit; color: var(--cb-blue); background: none; border: 0; padding: 0;
  text-decoration: underline; cursor: pointer; }
.cb-linkbtn:focus-visible { outline: 3px solid var(--cb-focus); outline-offset: 2px; }

/* "Draft it for me" above the panel form (relay ai.js). */
.cb-mega-draft { display: flex; gap: .45rem; flex-wrap: wrap; align-items: center; margin: 0 0 .4rem; }
.cb-mega-ask { flex: 1 1 14rem; min-width: 0; }
.cb-mega-draftnote:empty { display: none; }
.cb-mega-draftnote { margin: 0 0 .9rem; }

/* The menu as it will be (2026-09-25): an item whose saved state is not on
   the live site yet, and the line above the list saying why. */
.cb-menu-wait { flex: none; font-size: .75rem; font-weight: 600; color: #6b4e00; background: #fff3cd;
  border-radius: 999px; padding: .1rem .55rem; white-space: nowrap; }
.cb-menu-waitnote:empty { display: none; }
.cb-menu-waitnote { margin: 0 0 .6rem; }

/* A split header's six-item limit, said on the Menu screen (2026-09-25). */
.cb-menu-splitnote:empty { display: none; }
.cb-menu-splitnote { color: var(--cb-bad); background: var(--cb-bad-bg); border-radius: 6px; padding: .5rem .7rem; }

/* A picture in a text block's body opens its picture panel while editing
   (2026-09-25; editBlockPicture) — the same look as a raw-HTML picture. */
html.cb-editing-on .cb-bspot .b-text__inner img,
html.cb-editing-on .cb-pending-editable .b-text__inner img { cursor: pointer; }
html.cb-editing-on .cb-bspot .b-text__inner img:hover,
html.cb-editing-on .cb-pending-editable .b-text__inner img:hover {
  outline: 3px dashed var(--cb-blue, #2f6fbf); outline-offset: 2px;
}
