/* ── The builder shell ───────────────────────────────────────────────────────
   One chrome for all four editors: the admin column-layout builder, the admin
   free canvas, the customer's Quick Build and the customer's free canvas.

   The shape is always the same — a thin toolbar across the top, a fixed-width
   sidebar of collapsible panels down the left, and the real newspaper filling
   whatever is left. Controls live in the sidebar, so the page you are actually
   judging gets the room.

   Everything is a `bx-` class driven by the custom properties below, which is
   what lets the admin (dark) and the customer (light) share one stylesheet:
   only the tokens change, never the rules. Add `.bx-dark` for the admin.

   Loaded on every page by `stylesheet_link_tag :app`, so every selector here is
   namespaced under a `bx-` class and can't leak into the rest of the site.
   ────────────────────────────────────────────────────────────────────────── */

.bx-shell {
  /* The pop look, as the storefront wears it: cream and white panels, 2px ink
     edges, butter for the thing you press, sky for the thing that's on, hard
     offset shadows under anything that floats or can be pressed. */
  /* Surfaces */
  --bx-bg: #fff6e5;
  --bx-panel: #fff;
  --bx-sunken: #fff6e5;
  --bx-mat: #f3e4c4;
  /* Lines: a soft divider inside a panel, and the 2px ink edge everything you
     can touch wears. */
  --bx-line: rgba(18, 18, 18, 0.14);
  --bx-line-strong: #121212;
  --bx-edge: #121212;
  --bx-field: #fff;
  /* Ink: body, secondary, faint labels */
  --bx-ink: #121212;
  --bx-ink-2: #3b3833;
  --bx-ink-3: #635e55;
  /* Accent: the link/hover colour (cobalt), and the tint behind a hover. */
  --bx-accent: #1f3fd1;
  --bx-accent-ink: #1f3fd1;
  --bx-accent-soft: rgba(143, 179, 255, 0.28);
  /* What's on: a sky fill with ink on it. Toggles that are on get butter. */
  --bx-sel: #8fb3ff;
  --bx-on-sel: #121212;
  --bx-sel-soft: #fff0b8;
  /* The button you press: butter with ink on it. */
  --bx-primary: #ffd445;
  --bx-on-primary: #121212;
  /* What reads on top of a solid accent. */
  --bx-on-accent: #121212;
  /* Destructive: a pink fill with ink on it; the text-only red is for a line
     of warning with no fill behind it. */
  --bx-danger: #b0124a;
  --bx-danger-fill: #ff9ec0;
  --bx-danger-soft: rgba(255, 158, 192, 0.35);
  /* The hard, offset shadow — and the ring a focused field wears. */
  --bx-shadow: #121212;
  --bx-ring: rgba(143, 179, 255, 0.85);
  --bx-spin: #1f3fd1;

  --bx-font: "Bricolage Grotesque", "Avenir Next", "Helvetica Neue", system-ui, sans-serif;
  --bx-mono: "DM Mono", ui-monospace, "SFMono-Regular", monospace;

  --bx-sidebar: 306px;
  --bx-radius: 10px;
  /* What's already pinned above the shell (the site header) and how tall the
     shell's own toolbar is. Measured by builder_shell_controller; the fallbacks
     are what a shell with no JavaScript yet gets. */
  --bx-top: 0px;
  --bx-toolbar-h: 3.3rem;

  display: flex;
  flex-direction: column;
  position: relative;
  background: var(--bx-bg);
  color: var(--bx-ink);
  font-family: var(--bx-font);
}

/* The admin's builders: the same pop, after dark. Ink ground, panels a step
   lighter, cream type, butter for what you press and what's on. */
.bx-shell.bx-dark {
  --bx-bg: #121212;
  --bx-panel: #1c1c1c;
  --bx-sunken: #262626;
  --bx-mat: #2c2c2c;
  --bx-line: rgba(255, 246, 229, 0.12);
  --bx-line-strong: #3a3a3a;
  --bx-edge: #3a3a3a;
  --bx-field: #262626;
  --bx-ink: #fff6e5;
  --bx-ink-2: rgba(255, 246, 229, 0.78);
  --bx-ink-3: rgba(255, 246, 229, 0.58);
  --bx-accent: #8fb3ff;
  --bx-accent-ink: #8fb3ff;
  --bx-accent-soft: rgba(143, 179, 255, 0.14);
  --bx-sel: #ffd445;
  --bx-on-sel: #121212;
  --bx-sel-soft: rgba(255, 212, 69, 0.14);
  --bx-primary: #ffd445;
  --bx-on-primary: #121212;
  --bx-on-accent: #121212;
  --bx-danger: #ff9ec0;
  --bx-danger-fill: #ff9ec0;
  --bx-danger-soft: rgba(255, 158, 192, 0.16);
  --bx-shadow: #000;
  --bx-ring: rgba(255, 212, 69, 0.55);
  --bx-spin: #ffd445;
}

/* A builder that owns the whole window (the customer editors) rather than
   sitting inside a page of admin chrome. */
.bx-shell.bx-full { min-height: 100vh; }
/* A builder embedded in a page of other chrome (the admin) gets a card of its
   own. The corners are rounded on the children rather than clipped on the
   shell: an `overflow` here would make it the scroll container, and the sticky
   toolbar and sidebar inside would quietly stop sticking. */
.bx-shell.bx-boxed { border: 2px solid var(--bx-edge); border-radius: 18px; box-shadow: 6px 6px 0 var(--bx-shadow); }
.bx-boxed > .bx-toolbar { border-radius: 16px 16px 0 0; }
.bx-boxed > .bx-body > .bx-sidebar,
.bx-boxed > .bx-body > .bx-rail { border-bottom-left-radius: 16px; }
.bx-boxed > .bx-body > .bx-stage { border-bottom-right-radius: 16px; }

/* ── Top toolbar ─────────────────────────────────────────────────────────────
   Deliberately thin: the mode switch, how the save is going, and the one
   destructive button. Everything else belongs in the sidebar. */
.bx-toolbar {
  display: flex; align-items: center; justify-content: space-between;
  gap: 0.9rem; flex-wrap: wrap;
  padding: 0.55rem 0.95rem;
  background: var(--bx-panel);
  border-bottom: 2px solid var(--bx-edge);
}
.bx-toolbar-group { display: flex; align-items: center; gap: 0.55rem; min-width: 0; }
.bx-toolbar-group.is-tight { gap: 0.3rem; }
.bx-toolbar-sep { width: 2px; align-self: stretch; margin: 0.1rem 0.2rem; background: var(--bx-line); }
.bx-toolbar-label {
  display: block; font-family: var(--bx-mono); font-size: 0.6rem; letter-spacing: 0.14em;
  text-transform: uppercase; color: var(--bx-ink-3);
}
.bx-toolbar-name {
  min-width: 11rem; padding: 2px 0;
  border: 0; border-bottom: 2px solid var(--bx-line); background: transparent;
  font-family: var(--bx-font); font-weight: 800; letter-spacing: -0.02em;
  font-size: 1.1rem; color: var(--bx-ink);
}
.bx-toolbar-name:focus { outline: none; border-bottom-color: var(--bx-accent); }
.bx-back {
  display: inline-flex; align-items: center; justify-content: center;
  width: 2.2rem; height: 2.2rem; flex: none;
  border: 2px solid var(--bx-line-strong); border-radius: 999px;
  background: var(--bx-field); color: var(--bx-ink); font-size: 1.05rem; font-weight: 700; text-decoration: none;
  box-shadow: 2px 2px 0 var(--bx-shadow);
  transition: transform 0.12s, background 0.12s;
}
.bx-back:hover { background: var(--bx-primary); color: var(--bx-on-primary); transform: translateY(-1px); }

/* Which editor you're in. Two ways of building the same paper, so they read as
   one control with a side lit rather than as two rival buttons. */
.bx-modes {
  display: inline-flex; align-items: center; gap: 2px; padding: 2px;
  border: 2px solid var(--bx-line-strong); border-radius: 999px; background: var(--bx-sunken);
}
.bx-modes form { display: contents; }
.bx-mode {
  border: 0; border-radius: 999px; background: none;
  padding: 0.34rem 0.8rem;
  font: inherit; font-size: 0.75rem; font-weight: 600; line-height: 1.2; color: var(--bx-ink-2);
  white-space: nowrap; cursor: pointer; transition: background 0.12s, color 0.12s;
}
.bx-mode:hover:not(.is-on) { color: var(--bx-accent-ink); }
.bx-mode, .bx-mode:hover { text-decoration: none; }
.bx-mode.is-on { background: var(--bx-sel); color: var(--bx-on-sel); font-weight: 700; cursor: default; }

.bx-status { font-family: var(--bx-mono); font-size: 0.68rem; letter-spacing: 0.04em; color: var(--bx-ink-2); white-space: nowrap; }

/* ── Buttons ──────────────────────────────────────────────────────────────
   Every button is a little sticker: a 2px edge and a hard shadow it lifts off
   when you point at it. */
.bx-btn {
  display: inline-flex; align-items: center; justify-content: center; gap: 0.35rem;
  border: 2px solid var(--bx-line-strong); border-radius: var(--bx-radius);
  background: var(--bx-field); color: var(--bx-ink);
  font: inherit; font-size: 0.8rem; font-weight: 600; line-height: 1.2; padding: 0.42rem 0.75rem;
  box-shadow: 2px 2px 0 var(--bx-shadow);
  cursor: pointer; transition: transform 0.12s, color 0.12s, background 0.12s;
}
.bx-btn:hover:not(:disabled) { transform: translateY(-1px); background: var(--bx-accent-soft); color: var(--bx-ink); }
.bx-btn:active:not(:disabled) { transform: translate(1px, 1px); box-shadow: 1px 1px 0 var(--bx-shadow); }
.bx-btn:disabled { opacity: 0.4; cursor: default; }
.bx-btn-block { width: 100%; }
.bx-btn-primary {
  border-color: var(--bx-line-strong); background: var(--bx-primary); color: var(--bx-on-primary); font-weight: 800;
}
.bx-btn-primary:hover:not(:disabled) { background: var(--bx-primary); color: var(--bx-on-primary); }
.bx-dark .bx-btn-primary { border-color: var(--bx-shadow); }
.bx-btn-go {
  border: 2px solid var(--bx-line-strong); border-radius: 14px;
  background: var(--bx-primary); color: var(--bx-on-primary);
  font-size: 0.86rem; font-weight: 800; letter-spacing: -0.01em; padding: 0.6rem 1.15rem;
  box-shadow: 4px 4px 0 var(--bx-shadow);
}
.bx-btn-go:hover:not(:disabled) { background: var(--bx-primary); color: var(--bx-on-primary); transform: translateY(-2px); }
.bx-dark .bx-btn-go { border-color: var(--bx-shadow); }
.bx-btn-danger:hover:not(:disabled) { background: var(--bx-danger-fill); color: #121212; }
/* The one button that empties a page: never a slip, so it says what it is. */
.bx-btn-wipe { background: var(--bx-danger-soft); }
.bx-btn-wipe:hover:not(:disabled) { background: var(--bx-danger-fill); color: #121212; }
.bx-icon-btn {
  min-width: 2.2rem; width: auto; height: 2.2rem; flex: none; padding: 0 0.45rem; gap: 0.25rem;
  display: inline-flex; align-items: center; justify-content: center;
  border: 2px solid var(--bx-line-strong); border-radius: var(--bx-radius);
  background: var(--bx-field); color: var(--bx-ink);
  font: inherit; font-size: 0.82rem; font-weight: 600; line-height: 1; white-space: nowrap; text-decoration: none;
  box-shadow: 2px 2px 0 var(--bx-shadow);
  cursor: pointer; transition: transform 0.12s, color 0.12s, background 0.12s, opacity 0.12s;
}
.bx-icon-btn:hover:not(:disabled) { transform: translateY(-1px); background: var(--bx-accent-soft); }
.bx-icon-btn:disabled { opacity: 0.35; cursor: default; }
/* Start over. Quiet until you mean it, and set apart from its neighbours so it
   isn't what you hit reaching for the button beside it. */
.bx-icon-btn.is-quiet { opacity: 0.7; margin: 0 0.15rem; box-shadow: none; }
.bx-icon-btn.is-quiet:hover, .bx-icon-btn.is-quiet:focus-visible {
  opacity: 1; box-shadow: 2px 2px 0 var(--bx-shadow); background: var(--bx-danger-fill); color: #121212;
}
.bx-link {
  border: 0; background: none; padding: 0; font: inherit; font-size: 0.76rem; font-weight: 600;
  color: var(--bx-ink-2); text-decoration: underline; text-decoration-thickness: 2px; text-underline-offset: 3px;
  cursor: pointer; white-space: nowrap;
}
.bx-link:hover { color: var(--bx-accent-ink); }
.bx-link-danger { color: var(--bx-danger); text-decoration-color: var(--bx-danger-fill); }

/* ── Body: fixed sidebar + everything else ───────────────────────────────────
   A fixed first column and minmax(0,1fr) for the rest: opening a panel or
   selecting a component can never shove the paper sideways. */
.bx-body {
  display: grid;
  grid-template-columns: var(--bx-sidebar) minmax(0, 1fr);
  align-items: start;
  flex: 1;
  min-height: 0;
}
/* The toolbar and the sidebar both stay put while the paper scrolls, each
   pinned below whatever is already stuck above it — so the save state and the
   controls are always to hand however far down the page you are. */
.bx-toolbar { position: sticky; top: var(--bx-top); z-index: 30; }
.bx-sidebar {
  position: sticky; top: calc(var(--bx-top) + var(--bx-toolbar-h)); align-self: start;
  width: var(--bx-sidebar);
  max-height: calc(100vh - var(--bx-top) - var(--bx-toolbar-h));
  overflow-y: auto; overscroll-behavior: contain;
  background: var(--bx-panel);
  border-right: 2px solid var(--bx-edge);
}
.bx-full .bx-sidebar { min-height: calc(100vh - var(--bx-top) - var(--bx-toolbar-h)); }
/* The phone sheet's furniture, built once by builder_shell and shown only by
   the phone media query — off a phone it isn't there at all. */
.bx-sheet-handle, .bx-sheet-scrim { display: none; }

/* ── The component palette ───────────────────────────────────────────────────
   One panel for every builder: type to filter, shelves you can scan, and what
   you reached for last at the top. Filtering hides tiles rather than redrawing
   them, so it never costs the focus or a flicker. */
.bx-pal-search { position: relative; margin-bottom: 0.55rem; }
.bx-pal-search-ico {
  position: absolute; left: 0.55rem; top: 50%; transform: translateY(-50%);
  font-size: 0.78rem; opacity: 0.55; pointer-events: none;
}
.bx-pal-input {
  width: 100%; padding: 0.5rem 0.65rem 0.5rem 1.9rem;
  border: 2px solid var(--bx-line-strong); border-radius: 999px;
  background: var(--bx-field); color: var(--bx-ink);
  font: inherit; font-size: 0.82rem;
}
.bx-pal-input::placeholder { color: var(--bx-ink-3); }
.bx-pal-input:focus { outline: none; box-shadow: 0 0 0 3px var(--bx-ring); }
.bx-pal-input::-webkit-search-cancel-button { cursor: pointer; }

.bx-pal-list { display: flex; flex-direction: column; gap: 0.5rem; }
.bx-pal-shelf.is-out { display: none; }
.bx-pal-shelf-head {
  display: flex; align-items: center; gap: 0.35rem;
  margin: 0 0 0.3rem; padding: 0 0.1rem;
  font-family: var(--bx-mono); font-size: 0.6rem; font-weight: 500; letter-spacing: 0.13em; text-transform: uppercase;
  color: var(--bx-ink-3);
}
.bx-pal-tiles { display: flex; flex-direction: column; gap: 0.25rem; }

.bx-pal-tile {
  display: flex; align-items: center; gap: 0.55rem; width: 100%;
  padding: 0.42rem 0.5rem;
  border: 2px solid transparent; border-radius: var(--bx-radius);
  background: none; color: var(--bx-ink); font: inherit; text-align: left;
  cursor: pointer; transition: background 0.12s, border-color 0.12s;
}
.bx-pal-tile:hover, .bx-pal-tile:focus-visible {
  background: var(--bx-accent-soft); border-color: var(--bx-line-strong); outline: none;
}
.bx-pal-tile.is-out { display: none; }
/* The icon in a tinted chip rather than loose against the text — it reads as a
   thing you can pick rather than a bullet. */
.bx-pal-ico {
  display: flex; align-items: center; justify-content: center; flex: none;
  width: 1.85rem; height: 1.85rem;
  border-radius: 8px; background: var(--bx-sunken); border: 2px solid var(--bx-line-strong);
  font-size: 0.92rem; line-height: 1;
}
.bx-pal-tile:hover .bx-pal-ico { background: var(--bx-primary); }
.bx-pal-text { display: flex; flex-direction: column; min-width: 0; }
.bx-pal-name { font-size: 0.8rem; font-weight: 600; line-height: 1.25; }
.bx-pal-desc {
  font-size: 0.67rem; line-height: 1.3; color: var(--bx-ink-3);
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.bx-pal-empty { margin: 0.4rem 0.1rem; font-size: 0.72rem; color: var(--bx-ink-3); }
.bx-pal-empty.hidden { display: none; }

/* "+£4.99" on the tile of a component that costs extra. Said on the palette, in
   the popover and nowhere else it could be missed: the couple should meet the
   price when they're choosing, not at the till. A pink sticker rather than the
   builder's sky — this is money, and it shouldn't read as "selected". */
.bx-pal-price {
  display: inline-block; margin-left: 0.3rem;
  padding: 0.02rem 0.32rem; border-radius: 999px;
  border: 1.5px solid #121212; background: #ff9ec0; color: #121212;
  font-family: var(--bx-mono); font-size: 0.6rem; font-weight: 500; letter-spacing: 0.02em; white-space: nowrap;
  vertical-align: 1px;
}
/* On a card the badge floats over the miniature's top-right rather than
   pushing the name onto a second line — there's one line of room there. */
.bx-pal-tile.is-card { position: relative; }
.bx-pal-tile.is-card .bx-pal-price {
  position: absolute; top: 0.3rem; right: 0.3rem; margin: 0;
  box-shadow: 1.5px 1.5px 0 #121212;
}

/* Zoom: only worth the room on a touch screen, where the page is small and
   there's no other way in. On a desktop the paper is already legible. */
.bx-zoom { display: none; }
@media (hover: none), (max-width: 1024px) {
  .bx-zoom {
    display: flex; align-items: center; gap: 0.3rem;
    margin: 0 0 0.5rem auto; padding: 0.2rem;
    border: 2px solid var(--bx-line-strong); border-radius: 999px;
    background: var(--bx-panel); width: fit-content;
    box-shadow: 3px 3px 0 var(--bx-shadow);
  }
  .bx-zoom button {
    min-width: 38px; min-height: 38px; padding: 0 0.5rem;
    border: 0; border-radius: 999px; background: none;
    font: inherit; font-size: 0.9rem; color: var(--bx-ink); cursor: pointer;
  }
  .bx-zoom [data-zoom="reset"] { font-size: 0.72rem; }
  .bx-zoom [data-zoom-level] { min-width: 3.1rem; text-align: center; font-family: var(--bx-mono); font-size: 0.72rem; color: var(--bx-ink-2); font-variant-numeric: tabular-nums; }
  .bx-zoom.is-zoomed { background: var(--bx-sel-soft); }
}

/* ── A collapsible sidebar panel ─────────────────────────────────────────────
   The head is the whole button; the body animates open on grid-template-rows,
   which is the one way to transition to a content-sized height. */
.bx-panel { border-bottom: 2px solid var(--bx-line); }
.bx-panel:last-child { border-bottom: 0; }
.bx-panel-head {
  display: flex; align-items: center; gap: 0.55rem; width: 100%;
  padding: 0.75rem 0.85rem;
  border: 0; background: none; cursor: pointer;
  font-family: var(--bx-mono); font-size: 0.66rem; font-weight: 500; letter-spacing: 0.14em; text-transform: uppercase;
  color: var(--bx-ink-2); text-align: left;
  transition: color 0.12s, background 0.12s;
}
.bx-panel-head:hover { color: var(--bx-ink); background: var(--bx-accent-soft); }
.bx-panel.is-open > .bx-panel-head { color: var(--bx-ink); }
.bx-panel-ico { flex: none; width: 1.05rem; text-align: center; font-size: 0.85rem; opacity: 0.9; }
.bx-panel-title { flex: 1; min-width: 0; }
/* A count or a one-word summary of what's inside, for a shut panel. */
.bx-panel-note {
  flex: none; font-size: 0.6rem; letter-spacing: 0.06em; text-transform: none;
  color: var(--bx-ink-3); max-width: 8rem; overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.bx-panel-chev { flex: none; font-size: 0.6rem; opacity: 0.6; transition: transform 0.24s ease; }
.bx-panel.is-open > .bx-panel-head .bx-panel-chev { transform: rotate(90deg); }
.bx-panel-body { display: grid; grid-template-rows: 0fr; transition: grid-template-rows 0.24s ease; }
.bx-panel.is-open > .bx-panel-body { grid-template-rows: 1fr; }
.bx-panel-clip { overflow: hidden; min-height: 0; }
.bx-panel-pad { padding: 0.15rem 0.85rem 1rem; }
/* The contextual panel: it only means anything with something selected. */
.bx-panel.is-idle > .bx-panel-head { opacity: 0.55; }
@media (prefers-reduced-motion: reduce) {
  .bx-panel-body { transition: none; }
  .bx-panel-chev { transition: none; }
}

/* ── Fields ──────────────────────────────────────────────────────────────── */
.bx-label {
  display: block; margin: 0.75rem 0 0.3rem;
  font-family: var(--bx-mono); font-size: 0.63rem; font-weight: 500; letter-spacing: 0.12em; text-transform: uppercase; color: var(--bx-ink-2);
}
.bx-label:first-child { margin-top: 0; }
.bx-label .bx-label-note { text-transform: none; letter-spacing: normal; opacity: 0.85; }
.bx-input, .bx-select, .bx-textarea {
  width: 100%; border: 2px solid var(--bx-line-strong); border-radius: var(--bx-radius);
  background: var(--bx-field); color: var(--bx-ink);
  font: inherit; font-size: 0.84rem; padding: 0.44rem 0.6rem;
}
.bx-input::placeholder, .bx-textarea::placeholder { color: var(--bx-ink-3); }
.bx-input:focus, .bx-select:focus, .bx-textarea:focus {
  outline: none; box-shadow: 0 0 0 3px var(--bx-ring);
}
.bx-textarea { line-height: 1.5; resize: vertical; }
.bx-select { cursor: pointer; }
.bx-row { display: flex; gap: 0.5rem; align-items: flex-end; }
.bx-row > * { flex: 1 1 0; min-width: 0; }
.bx-row > .bx-flex-none { flex: none; }
.bx-color {
  flex: none; width: 2.6rem; height: 2.3rem; padding: 2px; cursor: pointer;
  border: 2px solid var(--bx-line-strong); border-radius: var(--bx-radius); background: var(--bx-field);
  box-shadow: 2px 2px 0 var(--bx-shadow);
}
.bx-check {
  display: flex; align-items: flex-start; gap: 0.5rem; margin-bottom: 0.4rem;
  font-size: 0.79rem; line-height: 1.35; color: var(--bx-ink); cursor: pointer;
}
.bx-check input { flex: none; margin: 0.15rem 0 0; accent-color: var(--bx-ink); cursor: pointer; }
.bx-dark .bx-check input { accent-color: var(--bx-primary); }
.bx-check small { display: block; font-size: 0.69rem; color: var(--bx-ink-3); line-height: 1.35; }
.bx-checks { display: flex; flex-wrap: wrap; gap: 0.25rem 1rem; }
.bx-hint { margin: 0.3rem 0 0; font-size: 0.7rem; line-height: 1.45; color: var(--bx-ink-3); }
.bx-hint strong { color: var(--bx-ink-2); font-weight: 600; }
.bx-rule { height: 2px; margin: 0.95rem 0 0.15rem; background: var(--bx-line); }
.bx-group { margin-top: 0.9rem; padding-top: 0.85rem; border-top: 2px solid var(--bx-line); }
.bx-group:first-child { margin-top: 0; padding-top: 0; border-top: 0; }
/* A subtle heading inside a panel, for a second idea in the same section. */
.bx-subhead {
  display: flex; align-items: center; gap: 0.4rem; margin: 0 0 0.45rem;
  font-family: var(--bx-mono); font-size: 0.62rem; font-weight: 500; letter-spacing: 0.13em; text-transform: uppercase; color: var(--bx-ink-2);
}
.bx-step { display: flex; align-items: center; gap: 0.45rem; }
.bx-step button {
  width: 2.1rem; height: 2.1rem; flex: none; padding: 0;
  border: 2px solid var(--bx-line-strong); border-radius: var(--bx-radius);
  background: var(--bx-field); color: var(--bx-ink); font: inherit; font-size: 1rem; font-weight: 700; line-height: 1; cursor: pointer;
  box-shadow: 2px 2px 0 var(--bx-shadow);
}
.bx-step button:hover { background: var(--bx-primary); color: var(--bx-on-primary); }

.bx-step .bx-input { width: 4.4rem; flex: none; text-align: center; padding: 0.4rem 0.3rem; }
.bx-step-unit { font-size: 0.78rem; color: var(--bx-ink-3); }

/* ── The text toolbar ────────────────────────────────────────────────────────
   The couples' editors set a piece's type from one strip at the top of the
   panel, the way a word processor does: two small dropdowns for the face and
   the size, then buttons for everything with one answer — align, columns, the
   lines around a piece. It replaces eight labelled sections that between them
   filled the sidebar before the couple reached the words they came to type.

   Everything here is 1.85rem tall and joined into groups, so a row of ten
   buttons still fits the sidebar; rows wrap when it doesn't.
   ────────────────────────────────────────────────────────────────────────── */
.bx-tb { display: flex; flex-direction: column; gap: 0.3rem; margin-bottom: 0.8rem; }
.bx-tb-row, .bx-tb-place { display: flex; flex-wrap: wrap; align-items: center; gap: 0.3rem; }
/* Where a piece sits, on the line under the toolbar. */
.bx-tb-place { margin-bottom: 0.8rem; }
.bx-tb-select {
  flex: 1 1 4.5rem; min-width: 0; height: 1.95rem;
  border: 2px solid var(--bx-line-strong); border-radius: 8px;
  background: var(--bx-field); color: var(--bx-ink);
  font: inherit; font-size: 0.74rem; font-weight: 600; padding: 0 0.2rem 0 0.35rem; cursor: pointer;
}
.bx-tb-select:focus-visible { outline: none; box-shadow: 0 0 0 3px var(--bx-ring); }
.bx-tb-font { flex: 2 1 5.5rem; }
.bx-tb-size { flex: 1 1 4rem; }
/* Fine tune: smaller, the percentage, bigger — one joined control. */
.bx-tb-step {
  display: flex; align-items: center; flex: none; height: 1.95rem; overflow: hidden;
  border: 2px solid var(--bx-line-strong); border-radius: 8px; background: var(--bx-field);
}
.bx-tb-step button {
  width: 1.5rem; height: 100%; flex: none; padding: 0; border: 0;
  background: none; color: var(--bx-ink); font: inherit; font-size: 0.9rem; font-weight: 700; line-height: 1; cursor: pointer;
}
.bx-tb-step button:hover { background: var(--bx-primary); color: var(--bx-on-primary); }
.bx-tb-step span {
  min-width: 2.2rem; text-align: center; font-family: var(--bx-mono); font-size: 0.64rem; font-variant-numeric: tabular-nums;
  color: var(--bx-ink-2);
}

/* A segmented group: buttons that answer one question between them. */
.bx-seg {
  display: flex; flex: none; overflow: hidden;
  border: 2px solid var(--bx-line-strong); border-radius: 8px; background: var(--bx-field);
}
.bx-seg-btn {
  display: inline-flex; align-items: center; justify-content: center; flex: none;
  width: 1.75rem; height: 1.95rem; padding: 0;
  border: 0; border-left: 2px solid var(--bx-line);
  background: none; color: var(--bx-ink);
  font: inherit; font-size: 0.72rem; line-height: 1; cursor: pointer;
}
.bx-seg-btn:first-child { border-left: 0; }
.bx-seg-btn svg { display: block; pointer-events: none; }
.bx-seg-btn:hover { background: var(--bx-accent-soft); color: var(--bx-ink); }
.bx-seg-btn:focus-visible { outline: 2px solid var(--bx-accent); outline-offset: -2px; }
/* The one that's on, filled — the only colour in the strip. */
.bx-seg-btn[aria-pressed="true"] {
  background: var(--bx-sel); color: var(--bx-on-sel);
}

/* The selected component's name, and the one way to remove it. */
.bx-prop-head {
  display: flex; align-items: center; justify-content: space-between; gap: 0.5rem;
  margin-bottom: 0.7rem; padding-bottom: 0.6rem; border-bottom: 2px solid var(--bx-line);
}
.bx-prop-type { font-family: var(--bx-mono); font-size: 0.64rem; font-weight: 500; letter-spacing: 0.13em; text-transform: uppercase; color: var(--bx-accent-ink); }
.bx-empty { padding: 0.4rem 0 0.6rem; }
.bx-empty-title { margin: 0 0 0.3rem; font-family: var(--bx-font); font-weight: 800; letter-spacing: -0.02em; font-size: 1.05rem; color: var(--bx-ink); }
.bx-empty-desc { margin: 0; font-size: 0.76rem; line-height: 1.55; color: var(--bx-ink-3); }
.bx-empty-desc strong { color: var(--bx-ink-2); }
/* A keyboard shortcut, wherever one is worth mentioning. */
.bx-keys { display: flex; flex-wrap: wrap; gap: 0.3rem 0.5rem; margin-top: 0.6rem; font-size: 0.68rem; color: var(--bx-ink-3); }
.bx-key {
  display: inline-block; padding: 0.05rem 0.32rem; border: 1.5px solid var(--bx-line-strong); border-radius: 5px;
  background: var(--bx-field); font-family: var(--bx-mono); font-size: 0.62rem; color: var(--bx-ink);
  box-shadow: 1px 1px 0 var(--bx-shadow);
}

/* ── A structured editor dropped into the panel ─────────────────────────────
   These partials are the order form's — one editor per component type in the
   whole app, so a couple fills a wedding party in the same way whichever screen
   they're on. But the order form is a wide cream column and this is a 306px
   sidebar in a different palette entirely, and the partials arrived here
   wearing the form's clothes: cream cards, rounded-xl corners, blush pills,
   sitting in a panel that uses none of those.

   So the partials lead every class string with a plain `rf-` hook (see
   OrdersHelper's field_*_classes) and the sidebar reskins the hooks against its
   own tokens below. The form is untouched; nothing here has to guess at a
   Tailwind utility, and a new editor built on those helpers lands looking like
   the rest of the panel without a line of CSS.

   `.bx-shell` prefixes are for specificity, not scope: Tailwind's stylesheet
   loads after this one, so a bare `.rf-field` would lose to `.bg-cream-50`.
   ────────────────────────────────────────────────────────────────────────── */
.bx-rich { margin-top: 0.15rem; }

/* Every field in an editor is the panel's own field: same ground, same border,
   same focus ring as a .bx-input beside it. */
.bx-shell .bx-rich .rf-field {
  width: 100%; border: 2px solid var(--bx-line-strong); border-radius: var(--bx-radius);
  background: var(--bx-field); color: var(--bx-ink);
  font: inherit; font-size: 0.82rem; line-height: 1.45; padding: 0.4rem 0.55rem;
  transition: box-shadow 0.12s ease;
}
.bx-shell .bx-rich .rf-field::placeholder { color: var(--bx-ink-3); }
.bx-shell .bx-rich .rf-field:focus {
  outline: none; box-shadow: 0 0 0 3px var(--bx-ring);
}
.bx-shell .bx-rich textarea.rf-field { resize: vertical; }
/* A figure, not a sentence — the stats editor's left-hand box. */
.bx-shell .bx-rich .rf-field-num { width: 3.6rem; flex: none; text-align: center; }

/* One row of a repeatable list, as a card the panel would have drawn: a
   hairline and the faintest wash, enough to group three fields as one thing
   without turning the sidebar into a stack of boxes. The wash is a percentage
   of the ink rather than a colour of its own, so it darkens the light theme and
   lightens the dark one from the same line. */
.bx-shell .bx-rich .rf-card {
  padding: 0.5rem; border: 2px solid var(--bx-line); border-radius: var(--bx-radius);
  background: var(--bx-sunken);
}
.bx-shell .bx-rich .rf-card.relative { padding-right: 1.9rem; }
/* Lists of rows sit closer together here than on the form. */
.bx-shell .bx-rich [data-rf-rows] > * + * { margin-top: 0.4rem; }

/* The ✕ on a row, and the dashed "add another" beneath the list. */
.bx-shell .bx-rich .rf-remove,
.bx-shell .bx-rich .rf-add,
.bx-shell .bx-rich .rf-pill,
.bx-shell .bx-rich .rf-action { cursor: pointer; font-family: inherit; }
.bx-shell .bx-rich .rf-remove {
  width: 1.5rem; height: 1.5rem; border-radius: 999px;
  background: transparent; color: var(--bx-ink-3); font-size: 0.78rem; line-height: 1;
}
.bx-shell .bx-rich .rf-remove:hover { background: var(--bx-danger-fill); color: #121212; }
.bx-shell .bx-rich .rf-add {
  margin-top: 0.5rem; padding: 0.32rem 0.75rem;
  border: 2px dashed var(--bx-line-strong); border-radius: 999px;
  background: transparent; color: var(--bx-ink);
  font-family: var(--bx-mono); font-size: 0.64rem; letter-spacing: 0.08em; text-transform: uppercase;
}
.bx-shell .bx-rich .rf-add:hover { border-style: solid; background: var(--bx-primary); color: var(--bx-on-primary); }
.bx-shell .bx-rich .rf-handle { color: var(--bx-ink-3); font-size: 0.95rem; padding: 0; }
.bx-shell .bx-rich .rf-handle:hover { color: var(--bx-ink-2); }

/* An editor's own words: how to work it (rf-note), the name over a field
   (rf-sublabel) or over a list (rf-grouplabel). All three read as the panel's
   .bx-hint and .bx-label rather than as the form's. */
.bx-shell .bx-rich .rf-note {
  margin: 0 0 0.5rem; font-size: 0.7rem; line-height: 1.45; color: var(--bx-ink-3);
}
.bx-shell .bx-rich .rf-sublabel, .bx-shell .bx-rich .rf-choice-label {
  display: block; margin: 0 0 0.25rem;
  font-family: var(--bx-mono); font-size: 0.6rem; letter-spacing: 0.12em; text-transform: uppercase; color: var(--bx-ink-2);
}
.bx-shell .bx-rich .rf-choice-label { display: inline-block; margin: 0 0.3rem 0 0; }
.bx-shell .bx-rich .rf-grouplabel {
  margin: 0.6rem 0 0.35rem; font-size: 0.72rem; font-weight: 700; color: var(--bx-ink);
}

/* A sunken panel inside an editor — the puzzle previews, the map's frame. */
.bx-shell .bx-rich .rf-panel {
  padding: 0.5rem; border: 2px solid var(--bx-line); border-radius: var(--bx-radius);
  background: var(--bx-sunken);
}
/* Either/or choices: the crossword's three looks, the map's two outlines. */
.bx-shell .bx-rich .rf-pill {
  padding: 0.2rem 0.65rem; border: 2px solid var(--bx-line-strong); border-radius: 999px;
  background: var(--bx-field); color: var(--bx-ink); font-size: 0.7rem; font-weight: 600;
}
.bx-shell .bx-rich .rf-pill:hover { background: var(--bx-accent-soft); color: var(--bx-ink); }
.bx-shell .bx-rich .rf-pill-on,
.bx-shell .bx-rich .rf-pill-on:hover { border-color: var(--bx-line-strong); background: var(--bx-sel); color: var(--bx-on-sel); }
/* The map's "＋ Add pin", and its search suggestions. */
.bx-shell .bx-rich .rf-action {
  padding: 0.3rem 0.7rem; border: 2px solid var(--bx-line-strong); border-radius: 999px;
  background: var(--bx-primary); color: var(--bx-on-primary); font-size: 0.72rem; font-weight: 700;
  box-shadow: 2px 2px 0 var(--bx-shadow);
}
.bx-shell .bx-rich .rf-action:hover { background: var(--bx-primary); color: var(--bx-on-primary); transform: translateY(-1px); }
.bx-shell .bx-rich .rf-menu {
  border: 2px solid var(--bx-line-strong); border-radius: var(--bx-radius); background: var(--bx-panel);
  box-shadow: 4px 4px 0 var(--bx-shadow);
}
/* A word inside an editor that acts as a button — the dateline's "type it
   instead" switch. The panel's own .bx-link, without importing the class. */
.bx-shell .bx-rich .rf-link { color: var(--bx-accent-ink); font-size: 0.7rem; font-weight: 600; }

/* Fields inside an editor that AREN'T on the hooks yet (a partial's own
   one-off input) still shouldn't tower over the ones that are. */
.bx-rich input[type="text"], .bx-rich input[type="date"],
.bx-rich select, .bx-rich textarea { font-size: 0.8rem; padding: 0.35rem 0.5rem; }
.bx-rich .rf-icon-swatch { width: 2rem; height: 2rem; }
/* The partials lay their fields out two-up for the order form's wide column.
   The sidebar is half that, and Tailwind's `sm:` is viewport-based so it can't
   know — stack them instead. Specificity beats the utility rather than
   !important, since Tailwind's stylesheet loads after this one. */
.bx-shell .bx-rich [data-rf-row] .grid { grid-template-columns: 1fr; }
/* …and the spans with it: a child still asking for two columns makes the grid
   grow an implicit second one, which is what kept the fields side by side even
   with a single-track template above. */
.bx-shell .bx-rich [data-rf-row] .grid > * { grid-column: auto; }
/* The dateline's venue and date are the same two-up pair, without a row around
   them — the venue needs the whole width of the panel to be readable. */
.bx-shell .bx-rich [data-dateline-fields] { grid-template-columns: 1fr; }
.bx-rich .rf-icon-menu { grid-template-columns: repeat(5, 1fr); width: 12rem; }
/* The puzzle editor builds its clue rows for the order form's wide column:
   a fixed-width answer, the clue filling the rest, then the remove button. At
   sidebar width that adds up to more than the row, and the button — the only
   way to delete a clue — gets pushed off the edge. Let the row wrap, let the
   inputs shrink (an input won't go below its intrinsic width without this),
   and drop the clue onto its own line. */
.bx-rich [data-role="rows"] > div { flex-wrap: wrap; }
.bx-rich [data-role="rows"] input { min-width: 0; }
.bx-rich [data-role="rows"] [data-role="answer"] { width: auto; flex: 1 1 6rem; }
.bx-rich [data-role="rows"] [data-role="clue"] { order: 3; flex: 1 1 100%; }
/* The panel scrolls, so an absolutely-positioned icon menu must sit above the
   fields under it rather than being clipped by the row it belongs to. */
.bx-rich [data-rf-icon-control] { position: relative; z-index: 5; }

/* A photo waiting to be uploaded, and the one that has been. */
.bx-photo {
  display: flex; align-items: center; justify-content: center;
  height: 7rem; overflow: hidden;
  border: 2px dashed var(--bx-line-strong); border-radius: var(--bx-radius);
  background: var(--bx-sunken); color: var(--bx-ink-2); font-size: 0.75rem;
}
.bx-photo img { width: 100%; height: 100%; object-fit: cover; }

/* ── Photo filters ────────────────────────────────────────────────────────
   Six swatches of the couple's own photograph, each wearing the filter it
   offers, so the look is chosen by eye rather than by reading six words. */
.bx-filters { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: 0.3rem; }
.bx-filter {
  display: flex; flex-direction: column; align-items: stretch; gap: 0.25rem;
  padding: 0.25rem; border: 2px solid var(--bx-line); border-radius: 9px;
  background: var(--bx-field); color: var(--bx-ink-2);
  font: inherit; font-size: 0.66rem; text-align: center; cursor: pointer;
  transition: border-color 0.12s, color 0.12s, background 0.12s;
}
.bx-filter:hover { border-color: var(--bx-line-strong); color: var(--bx-ink); background: var(--bx-accent-soft); }
.bx-filter:focus-visible { outline: 2px solid var(--bx-accent); outline-offset: 1px; }
.bx-filter[aria-pressed="true"] {
  border-color: var(--bx-line-strong); background: var(--bx-sel); color: var(--bx-on-sel); font-weight: 700;
  box-shadow: 2px 2px 0 var(--bx-shadow);
}
.bx-filter-swatch {
  display: block; overflow: hidden; border-radius: 4px; aspect-ratio: 4 / 3; background: var(--bx-line);
}
.bx-filter-swatch img { display: block; width: 100%; height: 100%; object-fit: cover; }
/* No photo uploaded yet — a stand-in with enough colour and enough tonal range
   that every filter still reads differently on it. */
.bx-filter-stand {
  display: block; width: 100%; height: 100%;
  background:
    linear-gradient(160deg, rgba(255, 255, 255, 0.85) 0%, rgba(255, 255, 255, 0) 45%),
    linear-gradient(30deg, #2b2118 0%, #b76e79 42%, #d8c3a5 72%, #f7f2e8 100%);
}
.bx-filter-name { display: block; line-height: 1.2; }

/* ── Layout cards ─────────────────────────────────────────────────────────
   A shape you pick once, so they're small: three across, a thumbnail-sized
   diagram, and a name clamped to two lines so every card is the same height
   however long the layout is called. */
.bx-cards { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: 0.3rem; }
.bx-card {
  display: flex; flex-direction: column; align-items: center; gap: 0.25rem;
  padding: 0.3rem 0.25rem;
  border: 2px solid var(--bx-line); border-radius: 9px;
  background: var(--bx-field); color: var(--bx-ink-2);
  font-family: inherit;
  cursor: pointer; transition: border-color 0.12s, color 0.12s, background 0.12s;
}
.bx-card:hover { border-color: var(--bx-line-strong); color: var(--bx-ink); background: var(--bx-accent-soft); }
.bx-card.is-active {
  border-color: var(--bx-line-strong); color: var(--bx-on-sel); background: var(--bx-sel);
  box-shadow: 2px 2px 0 var(--bx-shadow);
}
.bx-card-name {
  font-size: 0.53rem; line-height: 1.15; text-align: center;
  display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical;
  overflow: hidden; min-height: 1.22rem;
}
.bx-card-diagram { width: 100%; height: 24px; }
.bx-card .pl-diagram-slot { background: currentColor; opacity: 0.32; border-radius: 2px; }
.bx-card.is-active .pl-diagram-slot { opacity: 0.6; }
/* A component card in the free canvas's "add" list — a wide row, since these
   have a name and a line about what they are. */
.bx-tiles { display: flex; flex-direction: column; gap: 0.35rem; }
.bx-tile {
  display: flex; align-items: center; gap: 0.55rem; width: 100%; text-align: left;
  padding: 0.45rem 0.55rem;
  border: 2px solid var(--bx-line-strong); border-radius: var(--bx-radius);
  background: var(--bx-field); color: var(--bx-ink); font-family: inherit;
  box-shadow: 2px 2px 0 var(--bx-shadow);
  cursor: pointer; transition: border-color 0.12s, background 0.12s, transform 0.12s;
}
.bx-tile:hover { background: var(--bx-accent-soft); transform: translateY(-1px); }
.bx-tile-ico { flex: none; width: 1.5rem; text-align: center; font-size: 1.1rem; line-height: 1; }
.bx-tile-text { display: flex; flex-direction: column; min-width: 0; }
.bx-tile-name { font-size: 0.79rem; font-weight: 600; line-height: 1.25; }
.bx-tile-desc { font-size: 0.67rem; line-height: 1.25; color: var(--bx-ink-3); }
.bx-more {
  width: 100%; margin-top: 0.5rem; padding: 0.45rem;
  border: 2px dashed var(--bx-line-strong); border-radius: var(--bx-radius);
  background: none; color: var(--bx-ink); font: inherit; font-size: 0.76rem; font-weight: 600; cursor: pointer;
}
.bx-more:hover { border-style: solid; background: var(--bx-primary); color: var(--bx-on-primary); }

/* ── Toggle pills: the chrome bands and the rules ───────────────────────────
   Each is one switch with its own line about what it does, stacked so the
   sidebar reads as a list of decisions rather than a wall of pills. */
.bx-toggles { display: flex; flex-direction: column; gap: 0.35rem; }
.bx-toggle {
  display: flex; align-items: flex-start; gap: 0.5rem;
  padding: 0.45rem 0.6rem;
  border: 2px solid var(--bx-line); border-radius: var(--bx-radius);
  background: var(--bx-field); color: var(--bx-ink-2);
  font-size: 0.75rem; line-height: 1.3; cursor: pointer;
  transition: border-color 0.12s, color 0.12s, background 0.12s;
}
.bx-toggle:hover { border-color: var(--bx-line-strong); }
.bx-toggle:focus-within { border-color: var(--bx-line-strong); box-shadow: 0 0 0 3px var(--bx-ring); }
.bx-toggle input { flex: none; margin: 0.16rem 0 0; accent-color: var(--bx-ink); cursor: pointer; }
.bx-dark .bx-toggle input { accent-color: var(--bx-primary); }
.bx-toggle strong { display: block; font-weight: 700; color: var(--bx-ink); }
.bx-toggle small { display: block; font-size: 0.68rem; opacity: 0.9; }
.bx-toggle.is-on { border-color: var(--bx-line-strong); background: var(--bx-sel-soft); box-shadow: 2px 2px 0 var(--bx-shadow); }
.bx-toggle.is-on strong { color: var(--bx-ink); }

/* ── Page thumbnails ─────────────────────────────────────────────────────────
   A strip of small sheets rather than a grid of large ones: which page you're
   on is a glance, not a gallery. They run along one line and scroll sideways
   when an edition has more than the sidebar can hold. */
.bx-thumbs {
  display: flex; gap: 0.3rem; align-items: flex-start;
  overflow-x: auto; overscroll-behavior-x: contain;
  padding-bottom: 0.2rem;
  scrollbar-width: thin;
}
.bx-thumb {
  position: relative; display: block; flex: 0 0 auto; width: 44px; padding: 0; overflow: hidden;
  border: 2px solid var(--bx-line); border-radius: 6px;
  background: var(--bx-field); color: var(--bx-ink-2);
  cursor: pointer; text-decoration: none; text-align: center; font-family: inherit;
  transition: border-color 0.12s, box-shadow 0.12s, color 0.12s;
}
.bx-thumb:hover { border-color: var(--bx-line-strong); color: var(--bx-ink); }
.bx-thumb.is-active { border-color: var(--bx-line-strong); color: var(--bx-ink); }
.bx-thumb.is-active .bx-thumb-name { background: var(--bx-sel); color: var(--bx-on-sel); font-weight: 700; }
.bx-thumb-sheet {
  /* A span, so it has to be told to be a block — an inline box ignores
     aspect-ratio and the sheet collapses to the height of its contents. */
  display: block; aspect-ratio: 1 / 1.414; padding: 2px;
  background: var(--bx-panel); border-bottom: 2px solid var(--bx-line);
}
.bx-thumb-sheet .bx-card-diagram { height: 100%; }
.bx-thumb-sheet .pl-diagram-slot { background: currentColor; opacity: 0.3; border-radius: 1.5px; }
.bx-thumb.is-active .bx-thumb-sheet .pl-diagram-slot { opacity: 0.55; }
/* Just the page number under each sheet — the full name is on the tooltip.
   At this size a name would only ever be three ellipsised letters. */
.bx-thumb-name {
  display: block; padding: 0.1rem 0.15rem;
  font-family: var(--bx-mono); font-size: 0.55rem; line-height: 1.25;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.bx-thumb-add {
  display: flex; align-items: center; justify-content: center;
  flex: 0 0 auto; width: 44px; aspect-ratio: 1 / 1.414;
  border: 2px dashed var(--bx-line-strong); border-radius: 6px;
  background: none; color: var(--bx-ink-2); font-size: 0.95rem; font-weight: 700; cursor: pointer;
}
.bx-thumb-add:hover { border-style: solid; background: var(--bx-primary); color: var(--bx-on-primary); }
/* Removing a page sits beside adding one, so it says which it is on hover. */
.bx-thumb-add.is-danger:hover { background: var(--bx-danger-fill); color: #121212; }
/* Page count: how many sheets the edition runs to. */
.bx-counts { display: flex; gap: 0.4rem; }
.bx-count {
  width: 2.3rem; height: 2.3rem;
  border: 2px solid var(--bx-line-strong); border-radius: var(--bx-radius);
  background: var(--bx-field); color: var(--bx-ink); font: inherit; font-size: 0.92rem; font-weight: 700; cursor: pointer;
  box-shadow: 2px 2px 0 var(--bx-shadow);
}
.bx-count:hover { background: var(--bx-accent-soft); }
.bx-count.is-active { background: var(--bx-sel); color: var(--bx-on-sel); }


/* ── Stage: the paper, and the pages it's one of ─────────────────────────── */
.bx-stage { display: flex; flex-direction: column; min-width: 0; padding: 1.1rem 1.2rem 1.4rem; }

/* ── The guided steps ─────────────────────────────────────────────────────
   The walkthrough, as a row of chips above the paper: your names → your words
   → your photos → the fun stuff → the details. It answers the two questions a
   fill-in-the-blanks form answered and an editor doesn't — what is there to do,
   and how much of it is left — without taking the editor away to do it.

   Above the page tabs on purpose: a step spans the edition (your photos are on
   every page), so it's the outer of the two rows. */
.bx-guide { display: flex; flex-direction: column; gap: 0.4rem; margin-bottom: 0.7rem; }
.bx-guide[hidden] { display: none; }
.bx-guide-row {
  display: flex; align-items: stretch; gap: 0.35rem;
  overflow-x: auto; scrollbar-width: thin; padding: 0.1rem 0.15rem;
}
.bx-guide-step {
  flex: 0 0 auto;
  display: inline-flex; align-items: center; gap: 0.4rem;
  padding: 0.4rem 0.8rem;
  border: 2px solid var(--bx-line-strong); border-radius: 999px;
  background: var(--bx-panel); color: var(--bx-ink);
  font: inherit; font-size: 0.78rem; font-weight: 600; line-height: 1.3; white-space: nowrap;
  cursor: pointer; transition: background 0.12s, color 0.12s, transform 0.12s;
}
.bx-guide-step:hover { background: var(--bx-accent-soft); color: var(--bx-ink); }
/* The step you're in. Filled rather than merely ringed — with a row of chips
   this size, an outline alone doesn't carry across the width of the page. */
.bx-guide-step.is-on {
  background: var(--bx-primary); color: var(--bx-on-primary); font-weight: 800;
  box-shadow: 2px 2px 0 var(--bx-shadow);
}
.bx-guide-step.is-on .bx-guide-count { background: #121212; color: #ffd445; }
/* A step with nothing left to write. Quiet, not celebratory: it's a state, and
   the couple are looking at the ones that aren't. */
.bx-guide-step.is-done { color: var(--bx-ink-2); }
.bx-guide-step.is-done:not(.is-on) { background: var(--bx-sunken); border-color: var(--bx-line); }
.bx-guide-icon { font-size: 0.9rem; line-height: 1; }
.bx-guide-count {
  display: inline-flex; align-items: center; justify-content: center;
  min-width: 1.6rem; padding: 0 0.3rem; height: 1.2rem;
  border-radius: 999px; background: var(--bx-sunken); color: var(--bx-ink);
  border: 1.5px solid var(--bx-line);
  font-family: var(--bx-mono); font-size: 0.62rem; font-weight: 500; font-variant-numeric: tabular-nums;
}
/* How the whole paper is going, in one line of plain counting. */
.bx-guide-progress {
  margin: 0; padding: 0 0.2rem;
  font-family: var(--bx-mono); font-size: 0.68rem; letter-spacing: 0.03em; color: var(--bx-ink-2);
}
@media (max-width: 900px) {
  .bx-guide-name { display: none; }
  .bx-guide-step.is-on .bx-guide-name { display: inline; }
}

/* The page tabs, sitting right above the paper. Which page you're on is the
   move you make most often, so it's a named tab at the top of the work rather
   than a thumbnail down the sidebar. Both builders draw this same bar. */
.bx-tabs {
  display: flex; align-items: flex-end; gap: 0.3rem;
  margin-bottom: -2px; padding: 0 0.6rem;
  overflow-x: auto; scrollbar-width: thin;
  position: relative; z-index: 1;
}
.bx-tab {
  flex: 0 0 auto; max-width: 15rem;
  display: flex; align-items: center; gap: 0.4rem;
  padding: 0.45rem 0.85rem;
  border: 2px solid var(--bx-edge); border-bottom-color: transparent;
  border-radius: 10px 10px 0 0;
  background: var(--bx-panel); color: var(--bx-ink-2);
  font: inherit; font-size: 0.8rem; font-weight: 600; line-height: 1.3; white-space: nowrap;
  overflow: hidden; text-overflow: ellipsis;
  cursor: pointer; transition: background 0.12s, color 0.12s;
}
.bx-tab:hover { background: var(--bx-accent-soft); color: var(--bx-ink); }
/* The page you're on reads as the front sheet of the pile: it carries the
   butter number, and its bottom edge is open onto the mat below. */
.bx-tab.is-active {
  background: var(--bx-mat); color: var(--bx-ink);
  border-bottom-color: var(--bx-mat);
  font-weight: 800;
}
/* The number, so a named page still says where it falls in the edition. */
.bx-tab-n {
  display: inline-flex; align-items: center; justify-content: center;
  min-width: 1.25rem; height: 1.25rem; padding: 0 0.3rem;
  border-radius: 999px; border: 1.5px solid var(--bx-line); background: var(--bx-sunken); color: var(--bx-ink);
  font-family: var(--bx-mono); font-size: 0.62rem; font-weight: 500; font-variant-numeric: tabular-nums;
}
.bx-tab.is-active .bx-tab-n { background: var(--bx-primary); color: var(--bx-on-primary); border-color: #121212; }
.bx-tab-add { border-style: dashed; color: var(--bx-ink-2); font-size: 0.9rem; padding: 0.45rem 0.7rem; }
.bx-tab-add:hover { background: var(--bx-primary); color: var(--bx-on-primary); }
.bx-viewport {
  border: 2px solid var(--bx-edge); border-radius: 18px; background: var(--bx-mat); padding: 16px; overflow: hidden;
}
.bx-viewport.is-roomy { display: flex; justify-content: center; padding: 20px; }
/* The stage the open paper sits on. Flex-centred so the spread stays on the
   middle of the desk while it's being measured — at full size it's wider than
   the stage, and it's that full size the scale is worked out from. */
.bx-canvas { position: relative; width: 100%; display: flex; justify-content: center; align-items: flex-start; }
.bx-canvas .np-paper { box-shadow: 0 10px 34px rgba(0, 0, 0, 0.22); margin: 0 auto; }
/* Black & white, as the press prints it. The filter goes on the whole sheet
   rather than on the photographs alone, because that's what "mono" means to a
   printer: the paper's own colour, the rules, a coloured border and the pictures
   all come off the same plate. The PDF greys itself the same way (see
   newspapers/_grid_print), so what's on the stage is what arrives.
   The editor's own handles are inside the paper and grey with it — still plain
   against the newsprint, and worth it for a preview that doesn't lie. */
.bx-canvas.is-mono .np-paper { filter: grayscale(1); }

/* ── The paper, open ─────────────────────────────────────────────────────────
   The editor shows the edition the way it's read: two facing pages at a time,
   turned by hand. Not a preview of the work — it IS the work, so both pages
   carry the builder's handles and either can be edited where it lies.

   The leaves, the crease and the turn are the book viewer's (book.css); this
   is the same object with the handles left on. --bx-book-scale is set by the
   controller once it knows how much room the spread has. */
.bx-book {
  position: relative;
  display: flex;
  /* Sized by the pages it holds, not by the stage it's on. Left to fill the
     stage it reports the stage's width, the scale works out as 1, and the
     spread runs off both sides at full size. */
  width: max-content;
  flex: 0 0 auto;
  align-items: flex-start;
  justify-content: center;
  transform: scale(var(--bx-book-scale, 1));
  transform-origin: top center;
  transform-style: preserve-3d;
}
.bx-leaf {
  position: relative;
  flex: 0 0 auto;
  transform-style: preserve-3d;
}
.bx-leaf .np-paper { box-shadow: 0 18px 45px -20px rgba(0, 0, 0, 0.5); }

/* The fold. Two facing pages of one folded sheet curve away into the spine — so
   it is drawn only where there are two of them. A page with nothing to face
   (the cover, a last odd page, one page to a screen) is a single leaf sitting
   centred on the stage, with no spine to fall into. */
.bx-book:not(.is-solo):not(.is-single) .bx-leaf::after {
  content: "";
  position: absolute;
  top: 0; bottom: 0;
  width: 7%;
  opacity: 0.5;
  pointer-events: none;
  z-index: 5;
}
.bx-book:not(.is-solo):not(.is-single) .bx-leaf-l::after {
  right: 0;
  background: linear-gradient(to right, rgba(29, 24, 18, 0) 0%, rgba(29, 24, 18, 0.14) 100%);
}
.bx-book:not(.is-solo):not(.is-single) .bx-leaf-r::after {
  left: 0;
  background: linear-gradient(to left, rgba(29, 24, 18, 0) 0%, rgba(29, 24, 18, 0.14) 100%);
}

/* The turn, hinged on the spine — the same movement as the reader's, and the
   same keyframes (book.css), so the two can't drift apart. */
.bx-leaf.bk-turn-away-fwd, .bx-leaf.bk-turn-in-fwd,
.bx-leaf.bk-turn-away-back, .bx-leaf.bk-turn-in-back { z-index: 6; }

/* Turning the page, from either side of the paper. */
.bx-page-arrow {
  position: absolute;
  top: 50%;
  z-index: 7;
  display: flex; height: 2.4rem; width: 2.4rem;
  transform: translateY(-50%);
  align-items: center; justify-content: center;
  border-radius: 999px;
  border: 2px solid var(--bx-line-strong);
  background: var(--bx-panel);
  color: var(--bx-ink);
  font-weight: 800;
  box-shadow: 3px 3px 0 var(--bx-shadow);
  cursor: pointer;
}
.bx-page-arrow:hover:not(:disabled) { background: var(--bx-primary); color: var(--bx-on-primary); }
.bx-page-arrow:disabled { opacity: 0; pointer-events: none; }
.bx-page-arrow-prev { left: 0.35rem; }
.bx-page-arrow-next { right: 0.35rem; }

/* Which pages are open, under the paper. The tabs above it still name every
   page; this says what you're looking at. */
.bx-spread-label {
  margin: 0.85rem 0 0;
  text-align: center;
  font-family: var(--bx-mono);
  font-size: 0.68rem;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: var(--bx-ink-2);
}
.bx-canvas-note { padding: 3rem 1rem; text-align: center; font-size: 0.84rem; font-weight: 600; color: var(--bx-ink-2); }
.bx-canvas-note.is-error { color: var(--bx-danger); }
.bx-stage-hint { margin: 0.75rem 0 0; text-align: center; font-size: 0.72rem; line-height: 1.5; color: var(--bx-ink-2); }
/* Content that won't fit its column. A slot clips whatever runs past its bottom
   edge, so this is the only warning between a too-long paragraph and a sign-off
   sliced in half on the printed paper. */
.bx-warning {
  margin: 0 0 0.75rem;
  padding: 0.6rem 0.85rem;
  border: 2px solid #121212; border-radius: 12px;
  background: #ffd445; color: #121212;
  font-size: 0.78rem; font-weight: 600; line-height: 1.45;
  box-shadow: 3px 3px 0 var(--bx-shadow);
}
.bx-warning.hidden { display: none; }
.bx-stage-hint strong { color: var(--bx-ink-2); font-weight: 600; }
.bx-notice {
  display: none;
  padding: 0.65rem 1.1rem; background: #ffd445; color: #121212;
  border-bottom: 2px solid #121212; font-size: 0.8rem; font-weight: 600;
}

/* ── Dragging a component between slots ──────────────────────────────────────
   The page renderer draws the boxes; these are the states the builder puts on
   them. A grabbed component fades and tilts a little so it reads as picked up,
   the slot under the pointer lights, and a solid line shows the exact place it
   will land — the drop used to be a guess until you let go. */
.np-lay-item.sb-dragging { opacity: 0.35; transform: rotate(-0.6deg) scale(0.985); }
.np-lay-slot.sb-drop-over { background: var(--np-drop-tint, rgba(31, 63, 209, 0.1)); outline: 2px dashed #1f3fd1; outline-offset: 2px; }
.sb-drop-line {
  position: relative; height: 0; margin: 1px 0; pointer-events: none;
  border-top: 2px solid #1f3fd1; border-radius: 2px;
  box-shadow: 0 0 0 3px rgba(31, 63, 209, 0.18);
}
.sb-drop-line::before, .sb-drop-line::after {
  content: ""; position: absolute; top: -3px; width: 5px; height: 5px; border-radius: 50%; background: #1f3fd1;
}
.sb-drop-line::before { left: -2px; }
.sb-drop-line::after { right: -2px; }

/* ── The "what goes here?" picker ────────────────────────────────────────────
   Opened from a slot's ＋ (or the free canvas's category rows) and appended to
   the shell, so it takes the shell's theme wherever it's used. */
.bx-shell .sb-picker {
  position: absolute; z-index: 60; width: 300px;
  display: flex; flex-direction: column; gap: 8px; padding: 10px;
  border: 2px solid var(--bx-line-strong); border-radius: 16px;
  background: var(--bx-panel); color: var(--bx-ink);
  box-shadow: 6px 6px 0 var(--bx-shadow);
}
.bx-shell .sb-picker.hidden { display: none; }
.bx-shell .sb-picker-head { padding: 2px 4px 0; font-size: 0.8rem; font-weight: 700; color: var(--bx-ink); }
.bx-shell .sb-picker-search {
  width: 100%; padding: 8px 12px; outline: none;
  border: 2px solid var(--bx-line-strong); border-radius: 999px;
  background: var(--bx-field); color: var(--bx-ink); font: inherit; font-size: 0.82rem;
}
.bx-shell .sb-picker-search:focus { box-shadow: 0 0 0 3px var(--bx-ring); }
.bx-shell .sb-picker-list { display: flex; flex-direction: column; gap: 2px; max-height: 320px; overflow-y: auto; }
.bx-shell .sb-picker-group {
  padding: 8px 8px 4px; font-family: var(--bx-mono); font-size: 0.6rem; letter-spacing: 0.14em; text-transform: uppercase; color: var(--bx-ink-2);
}
.bx-shell .sb-picker-item {
  display: flex; align-items: center; gap: 10px; width: 100%; padding: 7px 8px; text-align: left;
  border: 2px solid transparent; border-radius: 10px; background: none; color: var(--bx-ink); font: inherit; cursor: pointer;
}
.bx-shell .sb-picker-item:hover, .bx-shell .sb-picker-item.is-active { background: var(--bx-accent-soft); border-color: var(--bx-line-strong); }

.bx-shell .sb-picker-item.hidden, .bx-shell .sb-picker-grp.hidden, .bx-shell .sb-picker-empty.hidden { display: none; }
.bx-shell .sb-picker-empty { padding: 14px 8px; text-align: center; font-size: 0.76rem; color: var(--bx-ink-3); }
.bx-shell .sb-picker-ico { flex: none; width: 22px; text-align: center; font-size: 1rem; }
.bx-shell .sb-picker-text { display: flex; flex-direction: column; min-width: 0; }
.bx-shell .sb-picker-name { font-size: 0.8rem; font-weight: 600; line-height: 1.2; }
.bx-shell .sb-picker-desc {
  font-size: 0.68rem; line-height: 1.2; color: var(--bx-ink-3);
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}

/* Something the builder wants you to look at — the layout picker after a page
   has been cleared, say. Lights briefly, then gets out of the way. */
.bx-flash { animation: bxflash 0.9s ease-out; border-radius: 8px; }
@keyframes bxflash {
  0%, 45% { background: var(--bx-accent-soft); box-shadow: 0 0 0 1px var(--bx-accent); }
  100% { background: transparent; box-shadow: none; }
}

/* ── Phones and tablets ─────────────────────────────────────────────────────
   A column of controls beside the paper doesn't survive 375px: half the screen
   goes to the sidebar and the paper is too small to read. So below the desktop
   width the paper takes the whole screen and the sidebar becomes a sheet at the
   bottom of it, pulled up when it's wanted and pushed down when it isn't. Same
   panels, same order — only where they sit changes.

   It covers tablets too. They used to lay the sidebar out as a band above the
   paper, which meant scrolling past every control to reach the page you were
   trying to judge.

   The sheet is `fixed`, so it rides above the paper rather than pushing it, and
   selecting a component brings it up by itself (see builder_shell). */
@media (max-width: 1024px) {
  .bx-body { grid-template-columns: minmax(0, 1fr); }
  .bx-stage { padding: 0.6rem 0.5rem calc(var(--bx-sheet-peek, 3.1rem) + 1rem); }
  .bx-toolbar { position: sticky; top: var(--bx-top, 0px); z-index: 30; }
  .bx-viewport { padding: 8px; border-radius: 12px; }
  .bx-viewport.is-roomy { padding: 8px; }
  .bx-viewport.is-roomy .bx-canvas { max-width: none; }

  .bx-sidebar {
    position: fixed; left: 0; right: 0; bottom: 0; top: auto;
    width: auto; max-width: none; z-index: 60;
    max-height: 85vh; min-height: 0;
    border: 0; border-top: 2px solid var(--bx-edge);
    border-radius: 20px 20px 0 0;
    background: var(--bx-panel);
    box-shadow: 0 -4px 0 var(--bx-primary);
    /* Shut, only the handle shows. */
    transform: translateY(calc(100% - var(--bx-sheet-peek, 3.1rem)));
    transition: transform 0.24s ease;
    overscroll-behavior: contain;
  }
  .bx-sidebar.is-sheet-open { transform: translateY(0); }

  /* The grab bar: the whole thing is the tap target, and it says what's in
     there rather than being a bare grey pill. The sidebar is already its own
     scroller, so the handle just sticks to the top of it and the panels run
     underneath — no extra wrapper, and desktop is untouched. */
  .bx-sheet-handle {
    position: sticky; top: 0; z-index: 1;
    display: flex; align-items: center; justify-content: center; gap: 0.5rem;
    width: 100%; min-height: var(--bx-sheet-peek, 3.1rem); padding: 0.5rem 1rem;
    border: 0; border-radius: 20px 20px 0 0; background: var(--bx-panel);
    font: inherit; font-size: 0.84rem; color: var(--bx-ink); cursor: pointer;
    touch-action: none;
  }
  .bx-sheet-grip { position: absolute; top: 6px; left: 50%; transform: translateX(-50%); width: 38px; height: 5px; border-radius: 999px; background: var(--bx-ink); }
  .bx-sheet-label { font-weight: 800; }
  .bx-sheet-hint { color: var(--bx-ink-2); font-family: var(--bx-mono); font-size: 0.68rem; }
  /* Tapping off the sheet puts it away — the usual way out of a sheet. */
  .bx-sheet-scrim {
    display: block;
    position: fixed; inset: 0; z-index: 55;
    background: rgba(18, 18, 18, 0.36);
    opacity: 0; pointer-events: none; transition: opacity 0.24s ease;
  }
  .bx-sheet-scrim.is-on { opacity: 1; pointer-events: auto; }

  /* Touch targets. A 44px minimum is the difference between a toolbar you can
     use with a thumb and one you keep missing. */
  .bx-toolbar { gap: 0.5rem; padding: 0.5rem 0.6rem; }
  /* Both halves of the bar wrap rather than run off the side of a phone: the
     name and the mode switch on one line, the save state on the next, and the
     buttons — checkout last and biggest — under it. */
  .bx-toolbar-group { flex-wrap: wrap; row-gap: 0.45rem; }
  .bx-toolbar > .bx-toolbar-group:last-child { flex: 1 1 100%; justify-content: flex-end; }
  .bx-toolbar > .bx-toolbar-group:last-child .bx-status { flex: 1 1 auto; }
  .bx-btn-go { white-space: nowrap; }
  .bx-toolbar .bx-btn,
  .bx-toolbar .bx-icon-btn,
  .bx-back { min-height: 44px; min-width: 44px; }
  .bx-toolbar .bx-btn { padding: 0.5rem 0.8rem; font-size: 0.82rem; }
  .bx-mode { min-height: 40px; padding: 0.5rem 0.85rem; font-size: 0.8rem; }
  .bx-toolbar-name { min-width: 0; width: 100%; font-size: 1rem; }
  .bx-toolbar-label { font-size: 0.54rem; }
  /* The status text is the first thing worth losing when the bar is tight. */
  .bx-status { font-size: 0.7rem; }

  /* Page tabs: one row, swiped through, never wrapped. */
  .bx-tabs { -webkit-overflow-scrolling: touch; scroll-snap-type: x proximity; padding-bottom: 2px; }
  .bx-tab { scroll-snap-align: start; min-height: 40px; font-size: 0.8rem; }

  /* Panel innards get the full width, and anything that was two-up goes to one. */
  .bx-panel-pad { padding: 0.7rem 0.85rem 0.95rem; }
  .bx-cards { grid-template-columns: repeat(auto-fill, minmax(140px, 1fr)); gap: 0.4rem; }
  .bx-card-diagram { height: 34px; }
  .bx-card-name { font-size: 0.62rem; }
  .bx-thumb { width: 52px; }
  .bx-row { flex-wrap: wrap; }
  .bx-input, .bx-select, .bx-textarea { font-size: 16px; min-height: 42px; }
  .bx-btn { min-height: 40px; }
  /* The toolbar keeps its shape, at a size a thumb can hit — and 16px on its
     selects for the same reason as above. */
  .bx-tb-select { font-size: 16px; height: 40px; }
  .bx-tb-step, .bx-tb-step button, .bx-seg-btn { height: 40px; }
  .bx-tb-step button, .bx-seg-btn { width: 2.2rem; }
  /* 16px on inputs is deliberate: iOS zooms the page in on any smaller font
     when a field takes focus, and never zooms back out. */

  /* Rich editors: their rows are laid out for a sidebar twice this wide. */
  .bx-rich [data-rf-row] .grid,
  .bx-rich [data-role="rows"] > div { grid-template-columns: 1fr; }
  .bx-rich [data-role="rows"] > div { flex-wrap: wrap; gap: 0.4rem; }
  .bx-rich input, .bx-rich textarea, .bx-rich select { min-width: 0; width: 100%; font-size: 16px; }
  /* 16px again, at the specificity the reskin above uses — anything smaller and
     iOS zooms the page in on focus and never zooms back out. */
  .bx-shell .bx-rich .rf-field { font-size: 16px; }
  .bx-shell .bx-rich .rf-field-num { width: 4.5rem; }
  .bx-rich table { display: block; overflow-x: auto; }
}

/* ══ New editor UX (iteration) ═══════════════════════════════════════════════
   Clicking the paper is the primary move in both editors now: the panels react
   to what's selected rather than being the place you go first. Everything in
   this block is behind an opt-in class so the admin builders are untouched.
   ────────────────────────────────────────────────────────────────────────── */

/* A contextual panel with nothing selected simply isn't there. It used to sit
   dimmed at the foot of the sidebar, which reads as "broken" rather than
   "waiting". */
.bx-shell .bx-panel.is-vacant { display: none; }

/* ── The picker, at card density ────────────────────────────────────────────
   Same buttons, same search, same shelves as the full palette — small cards
   instead of full-width rows, each led by a miniature of what the piece prints.
   Both customer editors use this density, so adding a piece looks the same in
   each.

   It replaced a row of name-only pills. A pill told you a component was called
   "The Hit Parade" or "By the Numbers", which is no help at all if you have
   never seen one: the thing a couple needs to know is what it will look like on
   their paper, and that is a picture, not a name. */
/* flex-direction has to be reset, not just wrap added: the full-density list is
   a column, and a wrapping column still stacks one per line and stretches each
   to the full width — which is the full-width row this density exists to
   replace. Cards keep their width as the panel widens (the sheet on a phone,
   the rail on a desktop) and gain a column instead of stretching, so the
   miniature inside always sits at the scale it was drawn for. */
.bx-pal-list.is-compact .bx-pal-tiles {
  display: grid; grid-template-columns: repeat(auto-fill, minmax(7rem, 1fr)); gap: 0.35rem;
}
.bx-pal-tile.is-card {
  display: flex; flex-direction: column; align-items: stretch; gap: 0.3rem;
  width: 100%; padding: 0.3rem;
  border: 2px solid var(--bx-line-strong); border-radius: var(--bx-radius);
  background: var(--bx-field); color: var(--bx-ink);
  font: inherit; font-size: 0.68rem; line-height: 1.2; text-align: left;
  box-shadow: 2px 2px 0 var(--bx-shadow);
  cursor: pointer; transition: transform 0.12s, background 0.12s, color 0.12s;
}
.bx-pal-tile.is-card:hover, .bx-pal-tile.is-card:focus-visible {
  border-color: var(--bx-line-strong); color: var(--bx-ink); background: var(--bx-accent-soft); outline: none;
  transform: translateY(-1px);
}
.bx-pal-tile.is-card .bx-pal-name {
  display: flex; align-items: center; gap: 0.25rem; min-width: 0;
  font-size: 0.7rem; font-weight: 600; line-height: 1.25;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
/* The icon loses its tinted chip at this density — the miniature above it is
   already the picture, so the emoji is only there to be recognised by. */
.bx-pal-tile.is-card .bx-pal-ico {
  width: auto; height: auto; flex: none;
  border: 0; background: none; border-radius: 0;
  font-size: 0.72rem; line-height: 1;
}
.bx-pal-tile.is-out { display: none; }

/* The 40px crop of the sketch that sits on a card. The miniature is drawn at a
   fixed width (see .bxp) and scaled to fit, so what you see here is the top of
   exactly the same drawing the popover shows in full — a thumbnail can't show
   something the preview doesn't. */
.bxp-thumb {
  display: block; position: relative; overflow: hidden;
  height: 40px; width: 100%;
  border-radius: 6px; border: 1.5px solid var(--bx-line);
  background: #fffdf8;
}
.bxp-thumb > .bxp {
  position: absolute; top: 0; left: 50%;
  transform: translateX(-50%) scale(0.62); transform-origin: top center;
  pointer-events: none;
}
/* The crop is a hard edge across the middle of a word otherwise — this fades
   the drawing out into the paper at the foot of the frame. */
.bxp-thumb::after {
  content: ""; position: absolute; inset: auto 0 0 0; height: 14px;
  background: linear-gradient(to bottom, rgba(255, 253, 248, 0), #fffdf8);
}

/* ── The preview beside the palette ─────────────────────────────────────────
   Fixed to the viewport rather than absolute inside the panel: the palette
   lives in a scrolling column, which would clip anything positioned within it.
   component_palette places it on the far side of that column — over the paper,
   never over the tiles you are choosing between.

   Every colour here is written out rather than taken from a --bx- token. The
   popover is appended to document.body, OUTSIDE .bx-shell, so the shell's
   tokens aren't in scope: var(--bx-panel) resolved to nothing and left the card
   see-through with the paper showing straight through the text. Like the sketch
   inside it, this card fixes its own paper and ink — it floats over a page it
   isn't part of, and has to stand off whatever happens to be behind it. */
.bx-pal-pop {
  position: fixed; z-index: 60; width: 19rem;
  padding: 0.6rem; pointer-events: none;
  border: 2px solid #121212; border-radius: 16px;
  background: #fff;
  color: #121212;
  font-family: "Bricolage Grotesque", "Avenir Next", "Helvetica Neue", system-ui, sans-serif;
  box-shadow: 6px 6px 0 #121212;
}
.bx-pal-pop[hidden] { display: none; }
.bx-pal-pop-head { display: flex; align-items: flex-start; gap: 0.4rem; margin-bottom: 0.45rem; }
.bx-pal-pop-ico { flex: none; font-size: 0.92rem; line-height: 1.2; }
.bx-pal-pop-text { display: flex; flex-direction: column; min-width: 0; }
.bx-pal-pop-text b { font-size: 0.82rem; font-weight: 800; letter-spacing: -0.01em; line-height: 1.25; }
.bx-pal-pop-text i { font-size: 0.68rem; font-style: normal; line-height: 1.3; color: #4a463f; }
/* The sheet the miniature is printed on — always paper-white and ink-black,
   even in the admin's dark shell: it is a picture of a printed page, and a
   printed page does not go dark with the furniture around it. */
.bx-pal-pop-paper {
  display: flex; justify-content: center;
  padding: 0.5rem 0.35rem; border-radius: 8px;
  background: #fffdf8; border: 1.5px solid rgba(18, 18, 18, 0.18);
}
/* Printed larger than life. The sketch is drawn at one size and one size only
   (176px — see .bxp, where every measurement inside it is a px off that), so
   the way to read it more easily is to magnify the whole drawing rather than
   re-measure two hundred rules: 1.36 puts it at 240px, which is as wide as the
   popover holds and about as large as a sketch bears before its hairlines start
   to look drawn rather than printed.

   `zoom` and not `transform: scale`, because zoom is part of layout. A scaled
   drawing still occupies its unscaled 176×h box, so the paper round it would
   have to be told a height in advance — and the height is whatever that type's
   sketch happens to come to. Zoomed, the paper and the popover simply fit
   themselves to the drawing. */
.bx-pal-pop-paper > .bxp { zoom: 1.36; }
.bx-pal-pop-foot {
  margin: 0.45rem 0 0; font-family: "DM Mono", ui-monospace, monospace;
  font-size: 0.6rem; letter-spacing: 0.04em; line-height: 1.35; color: #4a463f;
}

/* ── The free canvas's three-column shell ───────────────────────────────────
   A slim rail of tools on the left, the paper in the middle, and the detail
   panel on the right — so the paper keeps the centre of the screen and the
   controls sit either side of it rather than in front of it. */
.bx-shell.bx-rails > .bx-body {
  grid-template-columns: var(--bx-rail) minmax(0, 1fr) var(--bx-props);
}
.bx-shell.bx-rails { --bx-rail: 62px; --bx-props: 288px; }

.bx-rail {
  position: sticky; top: calc(var(--bx-top) + var(--bx-toolbar-h)); align-self: start;
  display: flex; flex-direction: column; align-items: center; gap: 0.25rem;
  padding: 0.6rem 0.35rem;
  height: calc(100vh - var(--bx-top) - var(--bx-toolbar-h));
  overflow-y: auto;
  background: var(--bx-panel); border-right: 2px solid var(--bx-edge);
}
.bx-rail-btn {
  display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 3px;
  width: 100%; padding: 0.5rem 0.2rem;
  border: 2px solid transparent; border-radius: 12px;
  background: none; color: var(--bx-ink-2);
  font-family: var(--bx-mono); font-size: 0.54rem; font-weight: 500; letter-spacing: 0.06em; text-transform: uppercase;
  cursor: pointer; transition: background 0.12s, color 0.12s, border-color 0.12s;
}
.bx-rail-btn:hover { background: var(--bx-accent-soft); color: var(--bx-ink); }
.bx-rail-btn.is-on {
  background: var(--bx-sel); border-color: var(--bx-line-strong); color: var(--bx-on-sel);
  box-shadow: 2px 2px 0 var(--bx-shadow);
}
.bx-rail-ico { font-family: var(--bx-font); font-size: 1.1rem; font-weight: 700; line-height: 1; }

/* The detail panel on the right. Same panels as before, just re-homed. */
.bx-props {
  position: sticky; top: calc(var(--bx-top) + var(--bx-toolbar-h)); align-self: start;
  width: var(--bx-props);
  height: calc(100vh - var(--bx-top) - var(--bx-toolbar-h));
  overflow-y: auto; overscroll-behavior: contain;
  background: var(--bx-panel); border-left: 2px solid var(--bx-edge);
}
.bx-props-head {
  display: flex; align-items: baseline; justify-content: space-between; gap: 0.5rem;
  padding: 0.75rem 0.85rem 0.6rem;
  border-bottom: 2px solid var(--bx-line);
  font-family: var(--bx-mono); font-size: 0.64rem; font-weight: 500; letter-spacing: 0.13em; text-transform: uppercase; color: var(--bx-ink);
}
.bx-props-body { padding: 0.75rem 0.85rem 1.2rem; }
/* X / Y / W / H in one tidy block. */
.bx-quad { display: grid; grid-template-columns: 1fr 1fr; gap: 0.4rem; }

/* ── The floating toolbar on the selected box ───────────────────────────────
   The edits worth making without leaving the paper. It follows the selection
   and sits just above it, flipping below when there's no room. */
.bx-float {
  position: absolute; z-index: 40;
  display: flex; align-items: center; gap: 2px;
  padding: 3px;
  border: 2px solid var(--bx-line-strong); border-radius: 999px;
  background: var(--bx-panel);
  box-shadow: 3px 3px 0 var(--bx-shadow);
  font-family: var(--bx-font);
  white-space: nowrap;
}
.bx-float.hidden { display: none; }
.bx-float button, .bx-float select {
  border: 0; border-radius: 999px; background: none; color: var(--bx-ink);
  font: inherit; font-size: 0.75rem; font-weight: 600; line-height: 1;
  padding: 0.38rem 0.55rem; cursor: pointer;
}
.bx-float button:hover { background: var(--bx-accent-soft); color: var(--bx-ink); }
.bx-float select { max-width: 8.5rem; padding-right: 0.2rem; }
.bx-float-grip { cursor: move; opacity: 0.6; padding: 0.35rem 0.4rem; }
.bx-float-sep { width: 2px; align-self: stretch; margin: 0.15rem 1px; background: var(--bx-line); }
.bx-float-del:hover { background: var(--bx-danger-fill); color: #121212; }

/* Zoom, parked in the corner of the screen rather than on a line above the
   paper. Fixed, not absolute: the stage is a whole A4 sheet tall, so anything
   anchored to its bottom edge would sit a page and a half below the fold. */
.bx-stage { position: relative; }
/* On the free canvas zoom earns its place at every width — boxes are placed by
   hand there, so being able to pull back and see the whole sheet, or come in
   close to line two things up, is part of the job rather than a small-screen
   concession. The base .bx-zoom is desktop-hidden, so the styling is repeated
   here rather than inherited. */
.bx-zoom-corner {
  display: flex; align-items: center; gap: 0.3rem;
  position: fixed; right: calc(var(--bx-props) + 1.1rem); bottom: 1.4rem; z-index: 30;
  margin: 0; padding: 0.2rem; width: fit-content;
  border: 2px solid var(--bx-line-strong); border-radius: 999px;
  background: var(--bx-panel);
  box-shadow: 3px 3px 0 var(--bx-shadow);
}
.bx-zoom-corner button {
  min-width: 34px; min-height: 34px; padding: 0 0.5rem;
  border: 0; border-radius: 999px; background: none;
  font: inherit; font-size: 0.9rem; font-weight: 700; color: var(--bx-ink); cursor: pointer;
}
.bx-zoom-corner button:hover { background: var(--bx-primary); color: var(--bx-on-primary); }
.bx-zoom-corner [data-zoom="reset"] { font-size: 0.74rem; }
.bx-zoom-corner [data-zoom-level] {
  min-width: 3.1rem; text-align: center; font-family: var(--bx-mono); font-size: 0.7rem;
  color: var(--bx-ink-2); font-variant-numeric: tabular-nums;
}
.bx-zoom-corner.is-zoomed { background: var(--bx-sel-soft); }

@media (max-width: 1024px) {
  /* The rail and the detail panel fold back into the bottom sheet on a phone. */
  .bx-shell.bx-rails > .bx-body { grid-template-columns: minmax(0, 1fr); }
  .bx-rail { position: static; flex-direction: row; height: auto; border-right: 0;
             border-bottom: 2px solid var(--bx-edge); overflow-x: auto; }
  .bx-rail-btn { width: auto; min-width: 62px; }
  /* Stacked, the detail panel would sit below a full A4 sheet — a long scroll
     from the box you just tapped. So on a touch layout it comes up from the
     bottom instead, over the paper, and only when there's something to say. */
  .bx-props {
    position: fixed; left: 0; right: 0; bottom: 0; top: auto;
    width: auto; height: auto; max-height: 52vh; z-index: 45;
    border-left: 0; border-top: 2px solid var(--bx-edge);
    border-radius: 20px 20px 0 0;
    box-shadow: 0 -4px 0 var(--bx-primary);
    transform: translateY(101%); transition: transform 0.18s ease-out;
  }
  .bx-props.is-open { transform: none; }
  .bx-props-head { position: sticky; top: 0; z-index: 2; background: var(--bx-panel); border-radius: 20px 20px 0 0; }
  /* Room to scroll the paper clear of the sheet. */
  .bx-shell.bx-rails.has-sheet .bx-stage { padding-bottom: 52vh; }
  /* Stacked, so there's no panel to sit clear of. */
  .bx-zoom-corner { right: 1.1rem; }
}

/* ── "Moving your paper…" ────────────────────────────────────────────────────
   Switching editors saves, converts and then navigates, which takes a beat.
   That used to be announced by a line of text in the top-right corner of the
   toolbar, which is the last place anyone is looking — their eyes are on the
   paper. So the paper is what gets covered.

   Fixed rather than absolute: the stage is a whole A4 sheet tall, so an
   overlay filling it would centre its spinner somewhere below the fold. Fixed
   to the window, inset past the rail and the panel, it covers exactly the
   column the paper is in and centres in what the couple can actually see. */
.bx-busy {
  position: fixed;
  top: calc(var(--bx-top) + var(--bx-toolbar-h));
  left: var(--bx-rail, 0px);
  right: var(--bx-props, 0px);
  bottom: 0;
  z-index: 60;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 0.9rem;
  padding: 1.5rem;
  text-align: center;
  background: color-mix(in srgb, var(--bx-bg) 82%, transparent);
  backdrop-filter: blur(2px);
  animation: bx-busy-in 0.14s ease-out;
}
/* min(), not a flat 22rem: on a narrow phone a flat cap is wider than the
   space left after the padding, and the line runs off the edge. */
.bx-busy-text, .bx-busy-sub { max-width: min(22rem, 100%); margin: 0; }
.bx-busy.hidden { display: none; }
@keyframes bx-busy-in { from { opacity: 0 } to { opacity: 1 } }

.bx-busy-ring {
  width: 2.4rem; height: 2.4rem;
  /* The track is faint and the leading segment is the accent, so what the eye
     follows is the moving part rather than the ring. */
  border: 3px solid color-mix(in srgb, var(--bx-ink) 12%, transparent);
  border-top-color: var(--bx-spin);
  border-radius: 50%;
  animation: bx-spin 0.75s linear infinite;
}
@keyframes bx-spin { to { transform: rotate(360deg) } }

.bx-busy-text {
  font-family: var(--bx-font);
  font-weight: 800;
  letter-spacing: -0.02em;
  font-size: 1.25rem;
  color: var(--bx-ink);
}
.bx-busy-sub { font-family: var(--bx-mono); font-size: 0.7rem; letter-spacing: 0.04em; color: var(--bx-ink-2); }

/* A spinner that never stops is the one bit of motion worth turning off. */
@media (prefers-reduced-motion: reduce) {
  .bx-busy { animation: none; }
  .bx-busy-ring { animation-duration: 2.4s; }
}

/* On a touch layout the rail sits above the paper rather than beside it, and
   the toolbar wraps — so there's no reliable height to start below. The whole
   screen is covered instead, which is also the clearer thing on a phone: the
   page is about to be replaced either way. */
@media (max-width: 1024px) {
  .bx-busy { inset: 0; }
}

/* ── Focus mode ──────────────────────────────────────────────────────────────
   Click a piece on the paper and the editor gives you that piece and nothing
   else: it lights on the page and everything around it fades back. That is the
   whole of it — the dimming, the count in the steps above the paper, and the
   glow on the piece you haven't written yet. The form itself stays in the
   sidebar, where it is whether focus mode is on or not.

   There is no panel. There was one, docked over the foot of the window, and it
   covered the paper it was meant to be helping you read. Nothing here draws on
   top of the page. */

/* The paper, with one piece lit. The dimming is on the page rather than a
   scrim over it, so the faded pieces are still THERE — you can see where the
   one you're filling in sits in the edition, which is the difference between
   focus and a modal. */
.bx-canvas.qb-focus .np-lay-item:not(.sb-selected),
.bx-canvas.qb-focus .sb-slot-add,
.bx-canvas.qb-focus .np-spread-layer:not(.sb-selected) {
  opacity: 0.28;
  transition: opacity 0.18s ease;
}
/* The lit piece: full strength, and a wider halo than the ordinary selection
   so it reads as lifted off the page rather than merely ringed. */
.bx-canvas.qb-focus .np-lay-item.sb-selected {
  opacity: 1;
  box-shadow: 0 0 0 3mm color-mix(in srgb, #1f3fd1 12%, transparent);
  border-radius: 2px;
}
/* The rest of the step you're on: brighter than the page behind it, dimmer than
   the piece you're actually filling in. Clicking "Your photos" should show you
   your photos — all of them — not just the one that happens to be open. */
.bx-canvas.qb-focus .np-lay-item.qb-step-lit:not(.sb-selected) { opacity: 0.62; }

/* A ＋ you're pointing at is a ＋ you meant — adding a piece shouldn't need
   focus mode turned off first. */
.bx-canvas.qb-focus .sb-slot-add:hover,
.bx-canvas.qb-focus .sb-slot-add:focus-visible { opacity: 1; }
/* The next piece still waiting for words. A slow breath rather than a flash —
   it's a signpost you follow when you're ready, not an alarm. */
.bx-canvas.qb-focus .np-lay-item.qb-next {
  opacity: 0.75;
  outline: 1.5px dashed rgba(31, 63, 209, 0.85);
  outline-offset: 3px;
  border-radius: 3px;
  animation: qb-breathe 2.4s ease-in-out infinite;
}
@keyframes qb-breathe {
  0%, 100% { opacity: 0.55; outline-color: rgba(31, 63, 209, 0.4); }
  50%      { opacity: 0.95; outline-color: rgba(31, 63, 209, 0.95); }
}
@media (prefers-reduced-motion: reduce) {
  .bx-canvas.qb-focus .np-lay-item.qb-next { animation: none; opacity: 0.9; }
  .bx-canvas.qb-focus .np-lay-item:not(.sb-selected) { transition: none; }
}

/* "Done ✓" closes the panel and gives the page back undimmed — so the piece
   still waiting for words is pointed at HERE instead, on a page where the
   breathing glow above would have nothing to stand out against. Three beats
   and gone (see PULSE_MS): a signpost you're shown on the way out, not a mark
   left on the paper. */
.bx-canvas .np-lay-item.qb-pulse {
  position: relative;
  border-radius: 3px;
  animation: qb-pulse 1.1s ease-out 3;
}
@keyframes qb-pulse {
  0%   { box-shadow: 0 0 0 0 rgba(31, 63, 209, 0.55); }
  70%  { box-shadow: 0 0 0 4mm rgba(31, 63, 209, 0); }
  100% { box-shadow: 0 0 0 0 rgba(31, 63, 209, 0); }
}
/* Without the beat, the same thing said by standing still: it's still only up
   for the few seconds the pulse would have run. */
@media (prefers-reduced-motion: reduce) {
  .bx-canvas .np-lay-item.qb-pulse {
    animation: none;
    outline: 1.5px dashed rgba(31, 63, 209, 0.85);
    outline-offset: 3px;
  }
}

/* Done, Next and the count — at the TOP of the piece's form in the sidebar,
   above the boxes. The way out of a piece belongs where the eye already is:
   the same column the words are typed in. */
.bx-prop-acts {
  display: flex; align-items: center; justify-content: space-between; gap: 0.7rem;
  margin-bottom: 0.5rem;
}
.bx-prop-acts .bx-btn { flex: none; }
.bx-prop-next { font-size: 0.72rem; white-space: nowrap; text-align: right; }
.bx-prop-progress {
  margin: 0 0 0.7rem;
  padding-bottom: 0.7rem;
  border-bottom: 2px solid var(--bx-line);
  font-size: 0.72rem; line-height: 1.5; color: var(--bx-ink-2);
}
/* A phone has no Escape key, and the panel is a sheet there rather than a
   column — the count is still worth saying, the keystroke isn't. */
@media (max-width: 1024px) { .bx-desk-only { display: none; } }

/* ── How much room is left ───────────────────────────────────────────────────
   Under the field: what this piece is setting at now and what it has room for,
   measured off the page it's actually on (see page_fit.js). Advice, not a
   limit — the paper takes what it's given and says when it won't print. */
.bx-fit {
  margin: 0.35rem 0 0;
  font-size: 0.7rem; line-height: 1.45;
  color: var(--bx-ink-3);
}
.bx-fit[hidden] { display: none; }
.bx-fit.is-full { color: var(--bx-ink); font-weight: 600; }

.bx-fit.is-over { color: var(--bx-danger); font-weight: 600; }

/* ── The miniature of a component ────────────────────────────────────────────
   The sketch behind every palette thumbnail and hover preview, built in
   component_preview.js. One drawing serves both: the popover shows it whole and
   magnified, the card crops the top 40px of the same node scaled down.

   176px is the drawing unit, not a size a sketch is shown at — neither place
   uses it as-is. Everything below is measured against it, so changing it alone
   changes nothing about how large a sketch appears; the two places that show
   one set their own scale (.bxp-thumb, .bx-pal-pop-paper).

   Deliberately NOT the real renderer. The paper is drawn in Ruby, at A4, in the
   template's own faces, from the couple's own words — reproducing that here to
   fill a 176px box would be a second renderer to keep in step with the first.
   What a sketch has to get right is the SHAPE of a piece: a numbered chart, a
   two-column list, a grid of squares, a big figure in a seal. That is what
   tells a couple whether it's the thing they were after.

   Everything is `bxp-` and everything is sized in px off .bxp's own font-size,
   so a sketch measures the same whatever it's dropped into — and it fixes its
   own ink and paper rather than reading the shell's tokens, because it is a
   picture of a printed page and a printed page doesn't go dark when the
   furniture around it does. */
.bxp {
  --bxp-ink: #1d1812;
  --bxp-ink-2: #6b6255;
  --bxp-line: rgba(29, 24, 18, 0.24);
  --bxp-paper: #fffdf8;

  width: 176px; flex: none;
  font-family: "Playfair Display", Georgia, serif;
  font-size: 6px; line-height: 1.35; text-align: left;
  color: var(--bxp-ink); background: var(--bxp-paper);
  overflow-wrap: break-word;
}
.bxp * { box-sizing: border-box; }

/* Rules, at the weights a newspaper actually rules with. */
.bxp-rule { height: 1px; margin: 2.5px 0; background: var(--bxp-line); }
.bxp-rule.is-heavy { height: 2px; background: var(--bxp-ink); }
.bxp-rule.is-hair { height: 1px; background: rgba(29, 24, 18, 0.14); }
.bxp-rule.is-spaced { margin: 5px 0; }
.bxp-rule.is-short { width: 38%; margin: 3px auto; }

/* The small-caps line over a piece — its heading on the paper. */
.bxp-kicker {
  font-family: "Jost", system-ui, sans-serif;
  font-size: 5.5px; font-weight: 600; letter-spacing: 0.13em; text-transform: uppercase;
  text-align: center; color: var(--bxp-ink);
}

/* ── Words ─────────────────────────────────────────────────────────────── */
.bxp-mast {
  font-family: "Cloister Black", "UnifrakturMaguntia", "Playfair Display", serif;
  font-size: 21px; line-height: 1.05; text-align: center; margin: 1.5px 0;
}
.bxp-mast-strap {
  font-family: "Jost", system-ui, sans-serif;
  font-size: 4.6px; letter-spacing: 0.16em; text-transform: uppercase;
  text-align: center; color: var(--bxp-ink-2); margin-top: 1.5px;
}
.bxp-headline { font-size: 12px; font-weight: 700; line-height: 1.1; text-align: center; }
.bxp-standfirst {
  font-size: 7px; font-style: italic; line-height: 1.3; text-align: center; color: var(--bxp-ink-2);
}
.bxp-p { margin: 0 0 2.5px; }
.bxp-p:last-child { margin-bottom: 0; }
.bxp-p.is-justified { text-align: justify; hyphens: auto; }
.bxp-p.is-centred { text-align: center; }
.bxp-p.is-verse { font-style: italic; }
.bxp-p.is-lead { font-size: 7.5px; }
/* Running text is set in columns, because that is what makes a page of words
   read as a newspaper rather than as a letter. */
.bxp-cols { column-count: 2; column-gap: 7px; column-rule: 1px solid rgba(29, 24, 18, 0.14); }

.bxp-quote { text-align: center; font-size: 8px; font-style: italic; line-height: 1.3; }
.bxp-quote-mark { font-size: 13px; line-height: 0; vertical-align: -3px; opacity: 0.45; }
.bxp-attrib {
  margin-top: 2px; text-align: center;
  font-family: "Jost", system-ui, sans-serif; font-size: 5px;
  letter-spacing: 0.08em; color: var(--bxp-ink-2);
}
.bxp-dateline {
  display: flex; align-items: center; justify-content: center; gap: 3px;
  font-family: "Jost", system-ui, sans-serif;
  font-size: 5.2px; letter-spacing: 0.14em; text-transform: uppercase;
}
.bxp-dot { opacity: 0.5; }
.bxp-footer, .bxp-folio {
  font-family: "Jost", system-ui, sans-serif;
  font-size: 5px; letter-spacing: 0.1em; text-align: center; color: var(--bxp-ink-2);
}
.bxp-boxed { border: 1px solid var(--bxp-ink); padding: 4px 5px; }
.bxp-notice-head {
  margin-bottom: 2px; text-align: center;
  font-size: 8px; font-weight: 700; letter-spacing: 0.02em;
}
.bxp-dove { font-size: 11px; text-align: center; line-height: 1; margin-bottom: 1px; }
.bxp-name {
  text-align: center; font-size: 6.5px; font-weight: 600; letter-spacing: 0.04em;
}

/* ── Media ─────────────────────────────────────────────────────────────── */
.bxp-photo { margin: 0; }
.bxp-photo-img { display: block; width: 100%; height: auto; border: 1px solid var(--bxp-line); }
.bxp-caption {
  margin-top: 1.5px; text-align: center;
  font-family: "Jost", system-ui, sans-serif; font-size: 4.8px; color: var(--bxp-ink-2);
}

/* One grid serves the crossword, the word search, the bingo card and the QR
   code — they differ in what a cell holds, not in how the cells are laid. */
.bxp-grid {
  display: grid; grid-template-columns: repeat(var(--bxp-cols), 1fr);
  gap: 1px; margin: 3px auto;
}
.bxp-sq {
  display: flex; align-items: center; justify-content: center;
  aspect-ratio: 1; min-width: 0;
  border: 1px solid var(--bxp-line); background: var(--bxp-paper);
  font-size: 5px; line-height: 1; text-transform: uppercase;
}
.bxp-sq.is-ink { background: var(--bxp-ink); border-color: var(--bxp-ink); }
.bxp-qr { width: 46px; gap: 0; }
.bxp-qr .bxp-sq { border: 0; }
.bxp-xword { width: 74px; }
.bxp-wsearch { width: 100%; }
.bxp-wsearch .bxp-sq { border-color: transparent; font-size: 5.4px; }
.bxp-wsearch .bxp-sq.is-found {
  border-color: var(--bxp-line); border-radius: 50%; font-weight: 700;
}
.bxp-bingo { width: 100%; }
.bxp-bingo .bxp-sq {
  padding: 1px; font-size: 3.8px; line-height: 1.2; text-align: center;
  text-transform: none; overflow: hidden; hyphens: auto;
}
.bxp-bingo .bxp-sq.is-free { font-size: 9px; }
.bxp-clues { margin-top: 1px; }
.bxp-clue { font-size: 5px; line-height: 1.35; }
.bxp-clue b { font-weight: 700; }
.bxp-tags { display: flex; flex-wrap: wrap; gap: 2px; justify-content: center; margin-top: 2px; }
.bxp-tag {
  padding: 0.5px 3px; border: 1px solid var(--bxp-line); border-radius: 999px;
  font-family: "Jost", system-ui, sans-serif; font-size: 4.4px; letter-spacing: 0.06em;
}

.bxp-map { position: relative; }
.bxp-map-svg { display: block; width: 100%; height: auto; }
.bxp-pin { position: absolute; transform: translate(-50%, -50%); text-align: center; }
.bxp-pin i { display: block; width: 3px; height: 3px; margin: 0 auto; border-radius: 50%; background: var(--bxp-ink); }
.bxp-pin b {
  display: block; font-family: "Jost", system-ui, sans-serif;
  font-size: 4px; font-weight: 500; letter-spacing: 0.04em; white-space: nowrap;
}

/* ── Lists and schedules ───────────────────────────────────────────────── */
/* The love story: a thread down the left with a bead at each milestone. */
.bxp-thread { position: relative; padding-left: 8px; }
.bxp-thread::before {
  content: ""; position: absolute; left: 2.5px; top: 3px; bottom: 3px;
  width: 1px; background: var(--bxp-line);
}
.bxp-stop { position: relative; margin-bottom: 3px; }
.bxp-stop:last-child { margin-bottom: 0; }
.bxp-bead {
  position: absolute; left: -7px; top: 2px;
  width: 4px; height: 4px; border-radius: 50%;
  background: var(--bxp-paper); border: 1px solid var(--bxp-ink);
}
.bxp-when {
  display: block; font-family: "Jost", system-ui, sans-serif;
  font-size: 4.6px; letter-spacing: 0.12em; text-transform: uppercase; color: var(--bxp-ink-2);
}
.bxp-what { display: block; font-size: 6.5px; font-weight: 600; line-height: 1.2; }
.bxp-where { display: block; font-size: 5px; font-style: italic; color: var(--bxp-ink-2); }

/* The order of the day, running across the page rather than down it. */
.bxp-track { display: flex; gap: 2px; }
.bxp-tick { flex: 1; min-width: 0; text-align: center; }
.bxp-tick .bxp-bead { position: static; display: block; margin: 1.5px auto; }
.bxp-tick .bxp-what { font-size: 4.8px; font-weight: 600; }
.bxp-tick .bxp-when { font-size: 4.4px; }

/* A labelled list: the menu, the wedding party. Stacked puts the value under
   its label, which is how a menu sets a course and its dishes. */
.bxp-pairs { display: flex; flex-direction: column; gap: 2.5px; }
.bxp-pair { display: flex; align-items: baseline; gap: 3px; }
.bxp-pairs.is-stacked .bxp-pair { flex-direction: column; gap: 0; text-align: center; }
.bxp-pair-l {
  flex: none; font-family: "Jost", system-ui, sans-serif;
  font-size: 4.6px; letter-spacing: 0.12em; text-transform: uppercase; color: var(--bxp-ink-2);
}
.bxp-pair-v { font-size: 6.5px; line-height: 1.2; }

.bxp-drinks { display: flex; flex-direction: column; gap: 3px; }
.bxp-drink { text-align: center; }
.bxp-drink-n { display: block; font-size: 7px; font-style: italic; font-weight: 600; }
.bxp-drink-i { display: block; font-size: 5px; color: var(--bxp-ink-2); }

/* The Hit Parade prints as a chart, so the numbers carry it. */
.bxp-chart { display: flex; flex-direction: column; gap: 2px; }
.bxp-chart-row { display: flex; align-items: baseline; gap: 3px; }
.bxp-chart-n {
  flex: none; width: 9px; text-align: right;
  font-size: 9px; font-weight: 700; line-height: 1; color: var(--bxp-ink);
}
.bxp-chart-t { min-width: 0; }
.bxp-chart-t b { display: block; font-size: 6px; font-weight: 600; line-height: 1.2; }
.bxp-chart-t i {
  display: block; font-family: "Jost", system-ui, sans-serif; font-style: normal;
  font-size: 4.4px; letter-spacing: 0.12em; text-transform: uppercase; color: var(--bxp-ink-2);
}

.bxp-steps { display: flex; flex-direction: column; gap: 1.5px; }
.bxp-step { text-align: center; font-size: 6px; line-height: 1.25; }
.bxp-step b { font-weight: 600; }

.bxp-stats { display: grid; grid-template-columns: 1fr 1fr; gap: 3px 5px; }
.bxp-stat { text-align: center; }
.bxp-stat-n { display: block; font-size: 13px; font-weight: 700; line-height: 1; }
.bxp-stat-l {
  display: block; margin-top: 0.5px;
  font-family: "Jost", system-ui, sans-serif;
  font-size: 4.2px; letter-spacing: 0.1em; text-transform: uppercase; color: var(--bxp-ink-2);
}

/* ── Fun ───────────────────────────────────────────────────────────────── */
.bxp-quiz { display: flex; flex-direction: column; gap: 1px; }
.bxp-q { display: flex; gap: 2px; margin-top: 2px; }
.bxp-q-n { flex: none; font-weight: 700; font-size: 5.5px; }
.bxp-q-t { font-size: 5.5px; line-height: 1.25; }
.bxp-opts { display: flex; flex-wrap: wrap; gap: 1px 5px; padding-left: 7px; }
.bxp-opt {
  font-family: "Jost", system-ui, sans-serif; font-size: 4.6px; color: var(--bxp-ink-2);
}

.bxp-horo { margin-bottom: 2.5px; }
.bxp-horo:last-child { margin-bottom: 0; }
.bxp-sign {
  display: block; font-size: 6px; font-weight: 600; font-style: italic;
}
.bxp-reading { display: block; font-size: 5.2px; line-height: 1.3; color: var(--bxp-ink-2); }

.bxp-sun { font-size: 13px; line-height: 1; text-align: center; margin: 1px 0; }

.bxp-ads { display: flex; flex-direction: column; gap: 3px; }
.bxp-ad { padding-bottom: 2.5px; border-bottom: 1px solid rgba(29, 24, 18, 0.14); }
.bxp-ad:last-child { border-bottom: 0; padding-bottom: 0; }
.bxp-ad-h {
  display: block; font-family: "Jost", system-ui, sans-serif;
  font-size: 5px; font-weight: 600; letter-spacing: 0.1em; text-transform: uppercase;
}
.bxp-ad-b { display: block; font-size: 5.2px; line-height: 1.3; color: var(--bxp-ink-2); }

.bxp-hashtag {
  text-align: center; font-size: 15px; font-weight: 700; line-height: 1.1;
  letter-spacing: -0.01em; word-break: break-all;
}

.bxp-letter { margin-bottom: 3px; }
.bxp-letter:last-child { margin-bottom: 0; }
.bxp-letter-q { display: block; font-size: 5.4px; font-weight: 700; line-height: 1.25; }
.bxp-letter-a {
  display: block; font-size: 5.2px; font-style: italic; line-height: 1.3; color: var(--bxp-ink-2);
}

/* A telegram: the message in the wire's own monospaced capitals, the sender
   under it. */
.bxp-wire {
  margin-bottom: 3px; padding: 3px 4px;
  border: 1px solid var(--bxp-line); background: rgba(29, 24, 18, 0.02);
}
.bxp-wire:last-child { margin-bottom: 0; }
.bxp-wire-m {
  display: block; font-family: "Courier New", ui-monospace, monospace;
  font-size: 4.6px; line-height: 1.35; text-transform: uppercase; letter-spacing: 0.02em;
}
.bxp-wire-f {
  display: block; margin-top: 1px; text-align: right;
  font-size: 4.8px; font-style: italic; color: var(--bxp-ink-2);
}

.bxp-badge { display: flex; justify-content: center; }
.bxp-badge-svg { display: block; width: 74px; height: 74px; }
.bxp-badge-n {
  fill: #fffdf8; font-family: "Playfair Display", Georgia, serif;
  font-size: 26px; font-weight: 700; text-anchor: middle;
}
.bxp-badge-l {
  fill: #fffdf8; font-family: "Jost", system-ui, sans-serif;
  font-size: 7px; letter-spacing: 1.2px; text-anchor: middle;
}

/* ── Layout furniture ──────────────────────────────────────────────────── */
.bxp-vrule { display: flex; justify-content: center; height: 46px; }
.bxp-vrule span { width: 1px; height: 100%; background: var(--bxp-line); }
.bxp-flourish { text-align: center; font-size: 8px; letter-spacing: 0.1em; }
.bxp-spacer {
  display: flex; align-items: center; justify-content: center; height: 34px;
  border: 1px dashed var(--bxp-line);
  font-family: "Jost", system-ui, sans-serif; font-size: 4.6px;
  letter-spacing: 0.14em; text-transform: uppercase; color: var(--bxp-ink-2);
}
