/* ==========================================================================
   Bid Budi — application layout.

   tokens.css owns colour, type, space and the component primitives. This file
   owns layout only, and every value in it resolves to a token. A raw hex or a
   raw px size here means a token is missing — add it to tokens.css instead.

   Built at 390px first, then widened. Breakpoints:
     (base)   390px  phone
     >= 700px        large phone / small tablet
     >= 1024px       iPad landscape — the auction detail becomes two columns
     >= 1280px       desktop — 880 / 448 as designed
   ========================================================================== */

/* --- Skeleton ------------------------------------------------------------ */

html { -webkit-text-size-adjust: 100%; }

body { min-height: 100vh; display: flex; flex-direction: column; }

main { flex-grow: 1; }

.bb-shell {
  width: 100%;
  max-width: var(--bb-content-max);
  margin: 0 auto;
  padding: 0 var(--bb-space-4);
}
@media (min-width: 700px)  { .bb-shell { padding: 0 var(--bb-space-6); } }
@media (min-width: 1280px) { .bb-shell { padding: 0 var(--bb-space-7); } }

.bb-stack     { display: flex; flex-direction: column; gap: var(--bb-space-4); }
.bb-stack--sm { gap: var(--bb-space-2); }
.bb-stack--lg { gap: var(--bb-space-6); }
.bb-row       { display: flex; align-items: center; gap: var(--bb-space-3); }
.bb-row--wrap { flex-wrap: wrap; }
.bb-row--top  { align-items: flex-start; }
.bb-row--end  { align-items: flex-end; }
.bb-spacer    { flex-grow: 1; }
/* Vertical only, and it has to stay that way. Nearly every page writes
   `class="bb-shell bb-section"` on one element, and the shorthand here — same
   specificity as .bb-shell, further down the file — was winning the horizontal
   half of it and zeroing the shell's gutter. The page then sat flush against
   the viewport edge while a bare .bb-shell above it (the flash slip) kept its
   34px, which is how the misalignment showed itself. padding-block cannot
   reach the sides, so the two classes stop fighting. */
.bb-section   { padding-block: var(--bb-space-5); }
@media (min-width: 1024px) { .bb-section { padding-block: var(--bb-space-6) var(--bb-space-7); } }

/* --- Header -------------------------------------------------------------- */

/* The header genuinely does float above the page, so it casts a shadow that
   says so. Without it a sticky bar and a painted stripe look identical the
   moment content scrolls under it. */
.bb-header {
  background: var(--bb-surface);
  border-bottom: 1px solid var(--bb-line);
  position: sticky;
  top: 0;
  z-index: 40;
  box-shadow: var(--bb-lift-1);
}
.bb-header__inner {
  display: flex;
  align-items: center;
  gap: var(--bb-space-4);
  min-height: 58px;
}
.bb-logo {
  font-family: var(--bb-font-display);
  font-size: var(--bb-size-h1);
  letter-spacing: -0.01em;
  color: var(--bb-ink);
  text-decoration: none;
  white-space: nowrap;
}
.bb-logo:hover { color: var(--bb-ink); text-decoration: none; }
/* An inline anchor's box is only its line height. The wordmark is a tap target
   on a phone, so give it the real one. */
.bb-logo { display: inline-flex; align-items: center; gap: 9px; min-height: var(--bb-tap-min); }

/* The mark and the wordmark are one lock-up, not an image with a caption.
   The mark alone does not say the name, and the name alone is the same
   typography as everything else on the page, so neither travels without the
   other — which is why the alt text is empty: the anchor already reads "Bid
   Budi" to a screen reader, and an alt of "Bid Budi logo" would say it twice.

   The height is fixed and the width is not: the artwork is 166x120 and letting
   it set its own width keeps it from being squashed if the ratio is ever
   re-cut. It ships at that size rather than at the 1254px the master is drawn
   at, for the same reason the mail templates resample it — see Html.php. */
.bb-logo__mark { height: 26px; width: auto; flex: none; display: block; }
@media (min-width: 700px) { .bb-logo__mark { height: 30px; } }

.bb-nav { display: none; gap: var(--bb-space-5); }
.bb-nav a {
  color: var(--bb-ink-muted);
  font-size: var(--bb-size-body);
  padding: 19px 0;
  border-bottom: 2px solid transparent;
  white-space: nowrap;
}
.bb-nav a:hover { color: var(--bb-ink); text-decoration: none; }
.bb-nav a.bb-nav__iconed { display: inline-flex; align-items: center; gap: 6px; }
.bb-nav a[aria-current="page"] {
  color: var(--bb-ink);
  font-weight: 600;
  border-bottom-color: var(--bb-brass);
}
@media (min-width: 1024px) { .bb-nav { display: flex; } }

.bb-header__id { display: none; align-items: center; gap: var(--bb-space-2); }
@media (min-width: 700px) { .bb-header__id { display: flex; } }

.bb-avatar {
  width: 32px; height: 32px;
  background: var(--bb-ink);
  color: var(--bb-ink-on-dark);
  display: flex; align-items: center; justify-content: center;
  font-size: var(--bb-size-fine); font-weight: 600;
  flex-shrink: 0;
  box-shadow: var(--bb-emboss);
}

/* --- Account menu --------------------------------------------------------
   A first name on its own reads as a label, not a control, so nobody found
   the account page or the sign-out behind it. This is a <details>: it works
   closed-to-open with no script, and the caret says out loud that there is
   something under it. app.js only closes it on Escape or a click outside. */
.bb-account { position: relative; }
.bb-account__trigger {
  display: flex; align-items: center; gap: var(--bb-space-2);
  min-height: var(--bb-tap-min);
  padding: 0 var(--bb-space-2);
  /* A resting border, not only one on hover: at rest is exactly when someone
     has to be able to tell this is a control and not a caption. */
  border: 1px solid var(--bb-line);
  background: var(--bb-surface);
  color: var(--bb-ink);
  font-size: var(--bb-size-small);
  cursor: pointer;
  list-style: none;               /* Firefox marker */
  user-select: none;
}
.bb-account__trigger::-webkit-details-marker { display: none; }
.bb-account__trigger:hover { border-color: var(--bb-line-strong); background: var(--bb-surface-sunk); }
.bb-account__trigger:focus-visible { outline: 2px solid var(--bb-brass); outline-offset: 2px; }
.bb-account[open] > .bb-account__trigger { border-color: var(--bb-line-strong); background: var(--bb-surface-sunk); }
.bb-account__name { font-weight: 600; white-space: nowrap; max-width: 12ch; overflow: hidden; text-overflow: ellipsis; }
.bb-account__caret { color: var(--bb-ink-muted); transition: transform 120ms ease; flex-shrink: 0; }
.bb-account[open] > .bb-account__trigger .bb-account__caret { transform: rotate(180deg); }

/* Unread sits on the trigger as well as on the link inside, because the whole
   point is that you can see there is something waiting without opening it. */
.bb-account__count {
  min-width: 18px; height: 18px; padding: 0 5px;
  display: inline-flex; align-items: center; justify-content: center;
  /* Brass as a fill would be 3.7:1 against its own label. Brass as an outline
     with brass text on the soft tint is 5.2:1 and just as loud. */
  background: var(--bb-brass-soft); border: 1px solid var(--bb-brass);
  color: var(--bb-brass-text);
  font-family: var(--bb-font-mono); font-size: var(--bb-size-label); font-weight: 500;
  line-height: 1; flex-shrink: 0;
}

.bb-account__panel {
  position: absolute;
  top: calc(100% + 6px);
  right: 0;
  min-width: 244px;
  background: var(--bb-surface);
  border: var(--bb-border);
  box-shadow: var(--bb-lift-2);
  z-index: 50;
}
.bb-account__who {
  display: flex; flex-direction: column; gap: 2px;
  padding: var(--bb-space-3);
  border-bottom: var(--bb-border-hair);
  background: var(--bb-surface-sunk);
}
.bb-account__whoname { font-size: var(--bb-size-small); font-weight: 600; color: var(--bb-ink); }
.bb-account__roles {
  list-style: none;
  margin: 0;
  padding: var(--bb-space-3);
  border-bottom: var(--bb-border-hair);
  display: flex; flex-direction: column; gap: var(--bb-space-2);
}
.bb-account__roles li { display: flex; flex-direction: column; gap: 1px; }
.bb-account__rolename {
  font-family: var(--bb-font-mono);
  font-size: var(--bb-size-meta);
  letter-spacing: 0.06em;
  color: var(--bb-ink-body);
}
/* Written out in full here — this is where the header's ellipsis is resolved,
   so wrapping is the point and nowrap would defeat it. */
.bb-account__roles .bb-fine { white-space: normal; overflow-wrap: anywhere; }

.bb-account__links { display: flex; flex-direction: column; }
.bb-account__links a {
  display: flex; align-items: center; gap: var(--bb-space-2);
  min-height: var(--bb-tap-min);
  padding: var(--bb-space-2) var(--bb-space-3);
  border-bottom: var(--bb-border-hair);
  color: var(--bb-ink-body);
  font-size: var(--bb-size-small);
}
.bb-account__links a:hover { background: var(--bb-surface-sunk); color: var(--bb-ink); text-decoration: none; }
.bb-account__out { padding: var(--bb-space-2); }
.bb-account__signout {
  display: flex; align-items: center; justify-content: center; gap: var(--bb-space-2);
  width: 100%;
  min-height: var(--bb-tap-min);
  background: var(--bb-surface);
  border: 1px solid var(--bb-ink);
  color: var(--bb-ink);
  font-family: inherit; font-size: var(--bb-size-small); font-weight: 600;
  cursor: pointer;
}
.bb-account__signout:hover { background: var(--bb-ink); color: var(--bb-ink-on-dark); }
.bb-account__signout:focus-visible { outline: 2px solid var(--bb-brass); outline-offset: 2px; }

/* --- Header bell (0065) ---------------------------------------------------
   What you have to do, and what has happened to you. A <details> like the
   account menu, so app.js's [data-bb-menu] handling covers it. Outside
   .bb-header__id because that is hidden on a phone and this must not be. */
.bb-bell { position: relative; }
.bb-bell__trigger {
  position: relative;
  display: flex; align-items: center; justify-content: center;
  min-width: var(--bb-tap-min); min-height: var(--bb-tap-min);
  border: 1px solid var(--bb-line);
  background: var(--bb-surface);
  color: var(--bb-ink);
  cursor: pointer;
  list-style: none;
  user-select: none;
}
.bb-bell__trigger::-webkit-details-marker { display: none; }
.bb-bell__trigger:hover,
.bb-bell[open] > .bb-bell__trigger { border-color: var(--bb-line-strong); background: var(--bb-surface-sunk); }
.bb-bell__trigger:focus-visible { outline: 2px solid var(--bb-brass); outline-offset: 2px; }
/* Alert, not brass: the account count was "something to read", this is
   "something to do", and a facility owed money should not look like a newsletter. */
.bb-bell__count {
  position: absolute; top: -6px; right: -6px;
  min-width: 18px; height: 18px; padding: 0 5px;
  display: inline-flex; align-items: center; justify-content: center;
  background: var(--bb-alert-soft); border: 1px solid var(--bb-alert);
  color: var(--bb-alert);
  font-family: var(--bb-font-mono); font-size: var(--bb-size-label); font-weight: 600;
  line-height: 1;
}
.bb-bell__panel {
  position: absolute;
  top: calc(100% + 6px);
  right: 0;
  width: 360px;
  max-height: min(70vh, 560px);
  overflow-y: auto;
  background: var(--bb-surface);
  border: var(--bb-border);
  box-shadow: var(--bb-lift-2);
  z-index: 50;
}
/* On a phone the bell is not at the right edge (the burger is), so a panel
   anchored to it would run off the left. Anchored to the sticky header instead. */
@media (max-width: 699px) {
  .bb-bell { position: static; }
  .bb-bell__panel { top: 100%; left: var(--bb-space-4); right: var(--bb-space-4); width: auto; }
}
.bb-bell__head {
  display: flex; align-items: baseline; justify-content: space-between; gap: var(--bb-space-2);
  margin: 0;
  padding: var(--bb-space-2) var(--bb-space-3);
  background: var(--bb-surface-sunk);
  border-bottom: var(--bb-border-hair);
  font-family: var(--bb-font-mono); font-size: var(--bb-size-meta);
  letter-spacing: 0.06em; text-transform: uppercase;
  color: var(--bb-ink-body);
}
.bb-bell__head .bb-fine { text-transform: none; letter-spacing: 0; }
.bb-bell__list { list-style: none; margin: 0; padding: 0; }
.bb-bell__item {
  display: flex; flex-direction: column; gap: 2px;
  min-height: var(--bb-tap-min);
  padding: var(--bb-space-2) var(--bb-space-3);
  border-bottom: var(--bb-border-hair);
  border-left: 3px solid transparent;
  color: var(--bb-ink-body);
  text-decoration: underline; text-decoration-color: transparent;
}
.bb-bell__item:hover,
.bb-bell__item:focus-visible { background: var(--bb-surface-sunk); text-decoration-color: currentColor; }
.bb-bell__item--alert  { border-left-color: var(--bb-alert); }
.bb-bell__item--info   { border-left-color: var(--bb-brass); }
.bb-bell__item--update { border-left-color: var(--bb-line-strong); }
.bb-bell__title  { font-size: var(--bb-size-small); font-weight: 600; color: var(--bb-ink); }
.bb-bell__detail { font-size: var(--bb-size-small); color: var(--bb-ink-body); }
.bb-bell__when   { font-family: var(--bb-font-mono); font-size: var(--bb-size-meta); color: var(--bb-ink-muted); }
.bb-bell__empty  { margin: 0; padding: var(--bb-space-4) var(--bb-space-3); font-size: var(--bb-size-small); color: var(--bb-ink-body); }
/* A row a bell link points at (/seller/money#settlement-42). */
.bb-row-target:target > td { background: var(--bb-brass-soft); }
.bb-row-target:target > td:first-child { box-shadow: inset 3px 0 0 var(--bb-brass); }
.bb-bell__all {
  display: flex; align-items: center; justify-content: center;
  min-height: var(--bb-tap-min);
  font-size: var(--bb-size-small); font-weight: 600;
  text-decoration: underline;
}

.bb-burger {
  display: inline-flex; align-items: center; justify-content: center;
  width: var(--bb-tap-min); height: var(--bb-tap-min);
  margin-right: calc(var(--bb-space-3) * -1);
  background: none; border: none; color: var(--bb-ink); cursor: pointer;
}
@media (min-width: 1024px) { .bb-burger { display: none; } }

.bb-drawer {
  display: none;
  border-top: 1px solid var(--bb-line);
  background: var(--bb-surface);
  box-shadow: var(--bb-lift-2);   /* it slides out over the page, not into it */
}
.bb-drawer[data-open="true"] { display: block; }
@media (min-width: 1024px) { .bb-drawer[data-open="true"] { display: none; } }
.bb-drawer a {
  display: flex; align-items: center; gap: var(--bb-space-2);
  min-height: var(--bb-tap-min);
  padding: var(--bb-space-3) 0;
  border-bottom: var(--bb-border-hair);
  color: var(--bb-ink-body);
  font-size: var(--bb-size-h3);
}
.bb-drawer a:last-child { border-bottom: none; }

/* --- Reference bar ------------------------------------------------------- */

.bb-refbar {
  background: var(--bb-ink);
  color: var(--bb-ink-on-dark-muted);
  font-family: var(--bb-font-mono);
  font-size: var(--bb-size-meta);
  letter-spacing: 0.06em;
  box-shadow: var(--bb-emboss);
}
.bb-refbar__inner {
  display: flex; flex-wrap: wrap; align-items: center;
  gap: var(--bb-space-2) var(--bb-space-3);
  padding: 9px 0;
  min-height: 42px;
}
.bb-refbar strong { color: var(--bb-ink-on-dark); font-weight: 500; letter-spacing: 0.09em; }
/* A text token, not the divider one. --bb-line-dark exists for rules drawn on
   an ink ground and the rebrand darkened it; borrowing it for a character put
   this separator at 3.2:1. Anything a reader reads takes an ink-on-dark token. */
.bb-refbar__sep { color: var(--bb-ink-on-dark-muted); }
.bb-refbar__rules { width: 100%; }
/* A link while the Rules of auction tab is hidden. A full-height tap target on
   the bar; not underlined at rest (owner's request, 2026-10-10) — the brighter
   ink-on-dark colour and the hover underline mark it as a link. */
a.bb-refbar__rules {
  display: inline-flex; align-items: center; min-height: var(--bb-tap-min);
  color: var(--bb-ink-on-dark);
  text-decoration: none; text-underline-offset: 3px;
}
a.bb-refbar__rules:hover { text-decoration: underline; }
a.bb-refbar__rules:focus-visible { outline: 2px solid var(--bb-brass); outline-offset: 2px; }
@media (min-width: 1024px) {
  .bb-refbar__inner { flex-wrap: nowrap; padding: 0; }
  .bb-refbar__rules { width: auto; margin-left: auto; }
}

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

/* A heading with no class still looks like a heading of this site, never the
   browser's. :where() has no weight, so every class below (and .bb-lp on the
   landing page) still wins. */
:where(h1, h2, h3, h4) { font-family: var(--bb-font-display); letter-spacing: -0.02em; margin: 0; }

.bb-display { font-family: var(--bb-font-display); font-size: var(--bb-size-display); line-height: 1.1; margin: 0; }
.bb-h1      { font-family: var(--bb-font-display); font-size: var(--bb-size-h1); line-height: 1.15; margin: 0; }
.bb-h2      { font-size: var(--bb-size-h2); font-weight: 600; margin: 0; }
.bb-h3      { font-size: var(--bb-size-h3); font-weight: 600; margin: 0; }
.bb-body    { font-size: var(--bb-size-body); line-height: var(--bb-leading-body); color: var(--bb-ink-body); margin: 0; text-wrap: pretty; }
.bb-small   { font-size: var(--bb-size-small); color: var(--bb-ink-muted); margin: 0; }
.bb-fine    { font-size: var(--bb-size-fine); color: var(--bb-ink-muted); margin: 0; }
.bb-muted   { color: var(--bb-ink-muted); }
.bb-faint   { color: var(--bb-ink-faint); }
.bb-verify-text { color: var(--bb-verify); }
.bb-alert-text  { color: var(--bb-alert); }
.bb-notrec  { font-family: var(--bb-font-mono); color: var(--bb-ink-faint); letter-spacing: 0.06em; }
.bb-mono    { font-family: var(--bb-font-mono); }
.bb-nowrap  { white-space: nowrap; }
/* A fact that is not ours and may be any length — a requested URL, a pasted
   reference. Breaks anywhere rather than pushing the page sideways. */
.bb-break   { overflow-wrap: anywhere; }

/* --- Cards & panels ------------------------------------------------------ */

.bb-card__body--tight { padding: var(--bb-space-3) var(--bb-space-4); }
.bb-sunk { background: var(--bb-surface-sunk); border: var(--bb-border); padding: var(--bb-space-3) var(--bb-space-4); box-shadow: var(--bb-sink); }

.bb-divider { border: none; border-top: var(--bb-border-hair); margin: 0; }
.bb-divider--dashed { border-top: 1px dashed var(--bb-line); }

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

.bb-btn--inline { display: inline-flex; align-items: center; justify-content: center; gap: var(--bb-space-2); width: auto; }
.bb-btn--sm { min-height: var(--bb-tap-min); padding: 10px 16px; font-size: var(--bb-size-small); }

.bb-btnrow { display: flex; flex-direction: column; gap: var(--bb-space-2); }
@media (min-width: 700px) { .bb-btnrow--wide { flex-direction: row; } .bb-btnrow--wide > * { flex: 1 1 0; } }

/* Facility console: buttons are as wide as their label, everywhere in /seller.
   fit-content rather than auto, because auto still stretches inside a flex
   column (.bb-stack, .bb-btnrow) or a grid cell. */
.bb-main--seller .bb-btn { width: fit-content; max-width: 100%; }
.bb-main--seller .bb-btnrow { align-items: flex-start; }
@media (min-width: 700px) { .bb-main--seller .bb-btnrow--wide > * { flex: 0 0 auto; } }

/* Search field with its own submit. Stacked on a phone so neither is cramped;
   the field takes the width and the button takes what it needs above 700px. */
.bb-searchrow { display: grid; grid-template-columns: minmax(0, 1fr); gap: var(--bb-space-2); }
@media (min-width: 700px) { .bb-searchrow { grid-template-columns: minmax(0, 1fr) auto; } }
.bb-searchrow .bb-btn { min-width: 140px; }

/* Segmented control. Selected is ALWAYS a dark fill, never a heavier border. */
.bb-segment { display: flex; border: var(--bb-border); background: var(--bb-surface); box-shadow: var(--bb-lift-1); }
.bb-segment__option { flex: 1 1 0; display: flex; }
.bb-segment__option input { position: absolute; opacity: 0; pointer-events: none; }
.bb-segment__option label,
.bb-segment > a {
  flex: 1 1 0;
  min-height: var(--bb-tap-min);
  display: flex; align-items: center; justify-content: center;
  padding: 12px var(--bb-space-3);
  font-size: var(--bb-size-body); font-weight: 600;
  color: var(--bb-ink-muted);
  cursor: pointer;
  text-align: center;
  border-right: var(--bb-border-hair);
}
.bb-segment__option:last-child label, .bb-segment > a:last-child { border-right: none; }
/* Selected is a dark fill, and now also physically pressed in. The fill still
   carries the meaning; the depth just makes it unmistakable which one is down. */
.bb-segment__option input:checked + label,
.bb-segment > a[aria-current="true"] {
  background: var(--bb-ink);
  color: var(--bb-ink-on-dark);
  box-shadow: var(--bb-press);
}
.bb-segment__option input:focus-visible + label { outline: 2px solid var(--bb-brass); outline-offset: -2px; }

/* Quick-amount chips — 44px minimum, always. */
.bb-chips { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: var(--bb-space-2); }
@media (min-width: 700px) { .bb-chips { grid-template-columns: repeat(4, minmax(0, 1fr)); } }
.bb-chip {
  min-height: var(--bb-tap-min);
  display: flex; align-items: center; justify-content: center;
  background: var(--bb-surface);
  border: var(--bb-border);
  font-family: var(--bb-font-mono);
  font-size: var(--bb-size-body);
  color: var(--bb-ink);
  cursor: pointer;
  box-shadow: var(--bb-lift-1);
  transition: box-shadow var(--bb-motion), transform var(--bb-motion);
}
.bb-chip:hover { box-shadow: var(--bb-lift-2); transform: translateY(calc(var(--bb-rise) * -1)); }
/* Pressed stays pressed — a chip you have chosen is a key held down.
   Two attributes, because there are two kinds of chip and they are not
   interchangeable: a chip that toggles a value in place is a <button> and
   carries aria-pressed; a chip that navigates to a filtered URL is an <a> and
   carries aria-current. aria-pressed on a link is a critical WCAG failure, so
   the styling has to follow the correct attribute rather than force the markup
   to use the wrong one. */
.bb-chip[aria-pressed="true"],
.bb-chip[aria-current="true"] {
  background: var(--bb-ink); color: var(--bb-ink-on-dark); border-color: var(--bb-ink);
  box-shadow: var(--bb-press);
  transform: translateY(0);
}

/* --- Forms --------------------------------------------------------------- */

.bb-field { display: flex; flex-direction: column; gap: 6px; }
.bb-field__hint { font-size: var(--bb-size-fine); color: var(--bb-ink-faint); }
.bb-field__error { font-size: var(--bb-size-fine); color: var(--bb-alert); }

/* A field is cut into the sheet, not stuck on top of it — the same way a
   printed form has a box you write inside. */
.bb-input, .bb-select, .bb-textarea {
  width: 100%;
  min-height: var(--bb-tap-min);
  padding: 12px var(--bb-space-3);
  background: var(--bb-surface);
  border: var(--bb-border);
  color: var(--bb-ink);
  font-family: var(--bb-font-ui);
  font-size: var(--bb-size-h3);
  box-shadow: var(--bb-sink);
  transition: box-shadow var(--bb-motion), border-color var(--bb-motion);
}
.bb-input--mono, .bb-select--mono { font-family: var(--bb-font-mono); }
.bb-textarea { min-height: 100px; resize: vertical; line-height: var(--bb-leading-body); }
.bb-input:focus-visible, .bb-select:focus-visible, .bb-textarea:focus-visible {
  outline: 2px solid var(--bb-brass); outline-offset: -1px; border-color: var(--bb-ink);
}
.bb-input[aria-invalid="true"] { border-color: var(--bb-alert); }

/* Show / hide on a password field. The button sits inside the box at the right
   and the input gives up that much room, so typed characters never run under
   it. 44px square, because it is a control (--bb-tap-min). */
.bb-password { position: relative; }
.bb-password .bb-input { padding-right: calc(var(--bb-tap-min) + var(--bb-space-2)); }
.bb-password__reveal {
  position: absolute; top: 50%; right: 2px; transform: translateY(-50%);
  display: inline-flex; align-items: center; justify-content: center;
  width: var(--bb-tap-min); height: var(--bb-tap-min);
  padding: 0; background: transparent; border: 0; border-radius: var(--bb-radius-sm);
  color: var(--bb-ink-faint); cursor: pointer;
}
.bb-password__reveal[hidden] { display: none; }
.bb-password__reveal:hover { color: var(--bb-ink); }
.bb-password__reveal:focus-visible { outline: 2px solid var(--bb-brass); outline-offset: -2px; color: var(--bb-ink); }
.bb-password__eye-off { display: none; }
.bb-password__reveal[aria-pressed="true"] .bb-password__eye { display: none; }
.bb-password__reveal[aria-pressed="true"] .bb-password__eye-off { display: block; }

.bb-check { display: flex; align-items: flex-start; gap: var(--bb-space-3); min-height: var(--bb-tap-min); padding: 10px 0; cursor: pointer; }

/* The BOX is the tap target and the MARK is what you see.
   --bb-tap-min says "enforce on every control", and a checkbox is a control:
   this pattern had a 22px input inside a 44px label, so the label cleared the
   minimum and the thing you actually aim at did not. undersizedTapTargets()
   measures the input, correctly, and caught it the first time this pattern
   landed on a page that sweep covers.
   Grown by giving the input a 44px box rather than a 44px checkbox — the drawn
   mark stays 22px, so /clearance and /account/record look exactly as they did
   and every one of them is now aimable. appearance:none is what makes that
   separation possible; everything below it redraws what the native control was
   drawing, because turning appearance off turns ALL of it off. */
.bb-check input {
  appearance: none; -webkit-appearance: none;
  width: var(--bb-tap-min); height: var(--bb-tap-min);
  /* The 44px box grew 11px on every side of the old 22px one. Pulling it back by
     11px left and by 11-2 top leaves the drawn mark exactly where it has always
     sat, inside the card's padding — the target got bigger, the layout did not
     move. */
  margin: -9px 0 0 -11px; flex-shrink: 0;
  display: grid; place-content: center;
  background: none; border: 0; padding: 0; cursor: pointer;
}
.bb-check input::before {
  content: ""; width: 22px; height: 22px;
  border: var(--bb-border); border-radius: 5px;
  background: var(--bb-paper);
  box-shadow: var(--bb-sink);
  transition: background var(--bb-motion), border-color var(--bb-motion);
}
.bb-check input:checked::before {
  background: var(--bb-ink); border-color: var(--bb-ink);
  /* The tick, drawn rather than a glyph so it cannot go missing with a font. */
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3E%3Cpath fill='none' stroke='%23FFFFFF' stroke-width='2.5' stroke-linecap='round' stroke-linejoin='round' d='M3.5 8.5l3 3 6-6'/%3E%3C/svg%3E");
  background-repeat: no-repeat; background-position: center; background-size: 16px 16px;
}
.bb-check input:focus-visible::before { outline: 2px solid var(--bb-brass); outline-offset: 2px; }
.bb-check__text { font-size: var(--bb-size-body); line-height: 1.5; color: var(--bb-ink-body); }

.bb-formgrid { display: grid; grid-template-columns: minmax(0, 1fr); gap: var(--bb-space-4); }

@media (min-width: 700px) { .bb-formgrid--2 { grid-template-columns: repeat(2, minmax(0, 1fr)); } }

/* Amount input with steppers */
.bb-amountbox { display: flex; gap: var(--bb-space-2); }
.bb-amountbox__field {
  flex-grow: 1;
  display: flex; align-items: center; gap: var(--bb-space-2);
  border: var(--bb-border-ink);
  background: var(--bb-surface);
  padding: 0 var(--bb-space-3);
  min-height: var(--bb-tap-min);
  box-shadow: var(--bb-sink);
}
.bb-amountbox__prefix { font-family: var(--bb-font-mono); font-size: var(--bb-size-h3); color: var(--bb-ink-faint); }
.bb-amountbox__input {
  flex-grow: 1; width: 100%;
  border: none; background: none; outline: none;
  font-family: var(--bb-font-mono); font-size: var(--bb-size-h2); font-weight: 500;
  color: var(--bb-ink);
  padding: 10px 0;
  -moz-appearance: textfield;
}
.bb-amountbox__input::-webkit-outer-spin-button,
.bb-amountbox__input::-webkit-inner-spin-button { -webkit-appearance: none; margin: 0; }
.bb-amountbox__step {
  width: var(--bb-tap-min); min-height: var(--bb-tap-min);
  display: flex; align-items: center; justify-content: center;
  background: var(--bb-surface); border: var(--bb-border); color: var(--bb-ink);
  cursor: pointer; flex-shrink: 0;
  box-shadow: var(--bb-lift-1);
  transition: box-shadow var(--bb-motion), transform var(--bb-motion);
}
.bb-amountbox__step:hover  { box-shadow: var(--bb-lift-2); transform: translateY(calc(var(--bb-rise) * -1)); }
.bb-amountbox__step:active { box-shadow: var(--bb-press); transform: translateY(var(--bb-rise)); }

/* --- Auction grid -------------------------------------------------------- */

.bb-grid { display: grid; grid-template-columns: minmax(0, 1fr); gap: var(--bb-space-4); }
@media (min-width: 700px)  { .bb-grid { grid-template-columns: repeat(2, minmax(0, 1fr)); } }
@media (min-width: 1024px) { .bb-grid { grid-template-columns: repeat(3, minmax(0, 1fr)); } }
@media (min-width: 1280px) { .bb-grid { grid-template-columns: repeat(4, minmax(0, 1fr)); } }

/* A tile is a filed sheet. It lifts off the stack when you reach for it — the
   border darkening alone read as a colour change, not as a thing you can pick
   up. One pixel of travel; the shadow does the rest of the work. */
.bb-tile {
  position: relative;
  display: flex; flex-direction: column;
  background: var(--bb-surface);
  border: var(--bb-border);
  box-shadow: var(--bb-lift-1);
  transition: box-shadow var(--bb-motion), transform var(--bb-motion), border-color var(--bb-motion);
}
.bb-tile:hover, .bb-tile:has(.bb-tile__link:focus-visible) {
  border-color: var(--bb-ink);
  box-shadow: var(--bb-lift-2);
  transform: translateY(calc(var(--bb-rise) * -1));
  text-decoration: none;
}
.bb-tile:active { box-shadow: var(--bb-lift-1); transform: translateY(0); }
.bb-tile__photo { position: relative; aspect-ratio: 4 / 3; }
/* The header bar above the photograph: unit reference left, countdown right,
   both in the clock's face and size so the bar reads as one line. The tile's
   overflow:hidden rounds its top corners. */
.bb-tile__head {
  display: flex; align-items: center; justify-content: space-between;
  gap: var(--bb-space-2);
  min-height: 40px;
  padding: 8px 12px;
  background: var(--bb-ink); color: var(--bb-ink-on-dark);
}
.bb-tile__badge {
  min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
  font-family: var(--bb-font-display); font-weight: 700;
  font-size: var(--bb-size-h3); line-height: 1.1;
  letter-spacing: var(--bb-track-display);
  color: inherit;
}
.bb-tile__body { padding: var(--bb-space-3) var(--bb-space-4) var(--bb-space-4); display: flex; flex-direction: column; gap: 6px; flex-grow: 1; }
.bb-tile__title { font-family: var(--bb-font-display); font-size: var(--bb-size-h2); line-height: 1.2; color: var(--bb-ink); }

/* The title's link covers the whole sheet, so the tile still opens on a click
   anywhere. The focus ring goes round the sheet, not round the title text. */
.bb-tile__link { color: inherit; text-decoration: none; }
.bb-tile__link::after { content: ""; position: absolute; inset: 0; z-index: 1; }
.bb-tile__link:focus-visible { outline: none; }
.bb-tile:has(.bb-tile__link:focus-visible) { outline: 2px solid var(--bb-brass); outline-offset: 2px; }

/* Watch, filed into the bottom-right of the photograph — the one corner the
   unit badge and the clock chip leave free — and lit the way they are. It sits
   above the stretched link, so pressing it watches rather than navigates.
   Watching fills the heart with the logo orange: a fill, never a text colour. */
.bb-tile__watch { position: absolute; right: var(--bb-space-2); bottom: var(--bb-space-2); z-index: 2; margin: 0; }
.bb-tile__watch-btn {
  display: inline-flex; align-items: center; justify-content: center;
  width: 44px; height: 44px; padding: 0;
  background: var(--bb-ink); color: var(--bb-ink-on-dark);
  border: 0; cursor: pointer;
  box-shadow: var(--bb-lift-2), var(--bb-emboss);
  transition: transform var(--bb-motion);
}
.bb-tile__watch-btn:hover { transform: scale(1.08); }
.bb-tile__watch-btn:focus-visible { outline: 2px solid var(--bb-brass); outline-offset: 2px; }
.bb-tile__watch-btn[aria-pressed="true"] svg { fill: var(--bb-brass-bright); stroke: var(--bb-brass-bright); }
.bb-tile__watch-btn[aria-busy="true"] { opacity: 0.6; }

/* The facility line. Pushed to the bottom of the body with the foot, so the
   mark sits on the price rule rather than floating in whatever slack a short
   title left behind — every tile in a row lines its logos up with every other.
   The name is truncated rather than wrapped: a second line here would make one
   long trading name taller than the tile beside it. */
.bb-tile__seller { margin-top: auto; padding-top: var(--bb-space-3); display: flex; align-items: center; gap: var(--bb-space-3); min-width: 0; }
.bb-tile__seller-who { min-width: 0; }
.bb-tile__seller-name {
  font-size: var(--bb-size-small);
  font-weight: 600;
  color: var(--bb-ink);
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}

.bb-tile__foot { padding-top: var(--bb-space-3); border-top: var(--bb-border-hair); display: flex; align-items: flex-end; justify-content: space-between; gap: var(--bb-space-3); }

/* --- Auction detail ------------------------------------------------------ */


.bb-detail { display: grid; grid-template-columns: minmax(0, 1fr); gap: var(--bb-space-5); align-items: start; }
@media (min-width: 1024px) { .bb-detail { grid-template-columns: minmax(0, 1fr) 400px; gap: var(--bb-space-6); } }
@media (min-width: 1280px) { .bb-detail { grid-template-columns: minmax(0, 880px) 448px; gap: 32px; } }

.bb-detail__side { display: flex; flex-direction: column; gap: var(--bb-space-3); }
@media (min-width: 1024px) { .bb-detail__side { position: sticky; top: 74px; } }

.bb-gallery__main { position: relative; aspect-ratio: 4 / 3; }
@media (min-width: 1024px) { .bb-gallery__main { aspect-ratio: 22 / 10; } }
.bb-gallery__count {
  position: absolute; left: 0; top: 0;
  background: var(--bb-ink); color: var(--bb-ink-on-dark);
  font-family: var(--bb-font-mono); font-size: var(--bb-size-meta); letter-spacing: 0.08em;
  padding: 6px 10px;
  box-shadow: var(--bb-lift-2), var(--bb-emboss);
}
.bb-gallery__thumbs {
  display: grid; grid-auto-flow: column; grid-auto-columns: 78px;
  gap: var(--bb-space-2); overflow-x: auto; padding-bottom: var(--bb-space-1);
  scrollbar-width: thin;
}
@media (min-width: 1024px) {
  .bb-gallery__thumbs { grid-auto-flow: row; grid-template-columns: repeat(8, minmax(0, 1fr)); grid-auto-columns: auto; overflow: visible; }
}
.bb-gallery__thumb {
  position: relative;
  aspect-ratio: 4 / 3;
  border: var(--bb-border);
  background: none;
  padding: 0;
  cursor: pointer;
}
.bb-gallery__thumb[aria-current="true"] { border: 2px solid var(--bb-ink); }

.bb-gallery__main[data-inspectable="true"] img.bb-photo { cursor: zoom-in; }
.bb-gallery__inspect {
  position: absolute; right: var(--bb-space-3); top: var(--bb-space-3);
  display: inline-flex; align-items: center; gap: 8px;
  min-height: 48px; padding: 0 18px 0 14px;
  background: var(--bb-ink); color: var(--bb-ink-on-dark);
  border: 0; border-radius: var(--bb-radius-pill);
  font-family: var(--bb-font-ui); font-size: var(--bb-size-body); font-weight: 600;
  box-shadow: var(--bb-lift-2);
  cursor: pointer;
}
.bb-gallery__inspect[hidden] { display: none; }
.bb-gallery__inspect:focus-visible { outline: 2px solid var(--bb-brass); outline-offset: 2px; }

/* Photo inspector. Dark all over so the photograph is the only light thing on
   screen, and so lifted shadows read against true black rather than the page. */
.bb-inspector {
  width: 100vw; height: 100vh; height: 100dvh;
  max-width: none; max-height: none;
  margin: 0; padding: 0; border: 0;
  background: var(--bb-ink); color: var(--bb-ink-on-dark);
  font-family: var(--bb-font-ui);
}
.bb-inspector[open] { display: grid; grid-template-rows: auto minmax(0, 1fr) auto; }
.bb-inspector::backdrop { background: var(--bb-ink); }
.bb-inspector:fullscreen { width: 100%; height: 100%; }

.bb-inspector__bar {
  display: flex; align-items: center; gap: var(--bb-space-3);
  padding: var(--bb-space-2) 16px;
  border-bottom: 1px solid var(--bb-line-dark);
}
.bb-inspector__count { font-family: var(--bb-font-mono); font-size: var(--bb-size-meta); letter-spacing: 0.08em; }
.bb-inspector__angle {
  flex: 1 1 auto; min-width: 0;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
  font-size: var(--bb-size-small); color: var(--bb-ink-on-dark-muted);
}

.bb-inspector__stage { position: relative; overflow: hidden; }
.bb-inspector__view {
  position: absolute; inset: 0;
  display: flex; align-items: center; justify-content: center;
  touch-action: none; user-select: none;
  cursor: zoom-in;
}
.bb-inspector__view[data-zoomed="true"] { cursor: grab; }
.bb-inspector__view[data-zoomed="true"]:active { cursor: grabbing; }
.bb-inspector__img {
  display: block;
  max-width: 100%; max-height: 100%;
  object-fit: contain;
  transform-origin: center center;
  -webkit-user-drag: none;
}

.bb-inspector__nav {
  position: absolute; top: 50%; transform: translateY(-50%);
  width: var(--bb-tap-min); height: var(--bb-tap-min);
  display: grid; place-items: center;
  background: rgba(var(--bb-ink-rgb), 0.72); color: var(--bb-ink-on-dark);
  border: 1px solid var(--bb-line-dark); border-radius: var(--bb-radius-pill);
  cursor: pointer;
}
.bb-inspector__nav[hidden] { display: none; }
.bb-inspector__nav--prev { left: 16px; }
.bb-inspector__nav--next { right: 16px; }

.bb-inspector__tools {
  display: flex; flex-wrap: wrap; align-items: center; gap: var(--bb-space-2) var(--bb-space-4);
  padding: var(--bb-space-3) 16px;
  border-top: 1px solid var(--bb-line-dark);
}
.bb-inspector__group { display: flex; align-items: center; gap: var(--bb-space-1); }
.bb-inspector__btn {
  min-width: var(--bb-tap-min); min-height: var(--bb-tap-min); padding: 0 12px;
  display: inline-flex; align-items: center; justify-content: center;
  background: transparent; color: var(--bb-ink-on-dark);
  border: 1px solid var(--bb-line-dark); border-radius: var(--bb-radius-sm);
  font: inherit; font-size: var(--bb-size-small);
  cursor: pointer;
}
.bb-inspector__btn[hidden] { display: none; }
.bb-inspector__btn[aria-pressed="true"] { background: var(--bb-ink-on-dark); color: var(--bb-ink); }
.bb-inspector__btn:disabled { opacity: 0.45; cursor: not-allowed; }
.bb-inspector__btn:focus-visible,
.bb-inspector__nav:focus-visible,
.bb-inspector__slider input:focus-visible { outline: 2px solid var(--bb-brass); outline-offset: 2px; }
.bb-inspector__readout {
  min-width: 52px; text-align: center;
  font-family: var(--bb-font-mono); font-size: var(--bb-size-meta);
}
.bb-inspector__slider {
  display: flex; align-items: center; gap: var(--bb-space-2);
  font-size: var(--bb-size-small);
}
.bb-inspector__slider input { width: 140px; accent-color: var(--bb-brass); }
.bb-inspector__note {
  flex: 1 1 100%; margin: 0;
  font-size: var(--bb-size-fine); color: var(--bb-ink-on-dark-muted);
}
.bb-inspector__note[data-active="true"] { color: var(--bb-ink-on-dark); }
@media (max-width: 699px) {
  .bb-inspector__slider { flex: 1 1 100%; justify-content: space-between; }
  .bb-inspector__slider input { flex: 1 1 auto; max-width: 220px; }
}

/* The placeholder fills whichever aspect-ratio box it is standing in for, so a
   real <img> can replace it later without the layout moving. */
.bb-tile__photo > .bb-photo-placeholder,
.bb-gallery__main > .bb-photo-placeholder,
.bb-gallery__thumb > .bb-photo-placeholder {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
}

/* Evidence rail — one column on a phone, two on a tablet, three on desktop.
   It is never in a tab and never collapsed. */
.bb-evidence__grid { grid-template-columns: minmax(0, 1fr); }
.bb-evidence__cell { border-right: none; border-top: var(--bb-border-hair); }
.bb-evidence__cell:first-child { border-top: none; }
@media (min-width: 700px) {
  .bb-evidence__grid { grid-template-columns: repeat(2, minmax(0, 1fr)); }
  .bb-evidence__cell:nth-child(odd) { border-right: var(--bb-border-hair); }
  .bb-evidence__cell:nth-child(-n+2) { border-top: none; }
}
@media (min-width: 1024px) {
  .bb-evidence__grid { grid-template-columns: repeat(3, minmax(0, 1fr)); }
  .bb-evidence__cell { border-right: var(--bb-border-hair); border-top: var(--bb-border-hair); }
  .bb-evidence__cell:nth-child(3n) { border-right: none; }
  .bb-evidence__cell:nth-child(-n+3) { border-top: none; }
}
.bb-evidence__head { min-height: var(--bb-tap-min); }
.bb-evidence__head a {
  display: inline-flex;
  align-items: center;
  min-height: var(--bb-tap-min);
  color: var(--bb-brass-on-ink);
  font-family: var(--bb-font-ui);
  font-size: var(--bb-size-fine);
  letter-spacing: normal;
  text-transform: none;
}

/* Tabs — horizontally scrollable on a phone rather than wrapping.

   Every tab is a real URL rendering the same frame, so the strip sticks under
   the header and stays put: the tabs have to feel like one screen, because
   they are one screen. */
.bb-tabs {
  display: flex; gap: var(--bb-space-5);
  border-bottom: 1px solid var(--bb-line);
  overflow-x: auto; scrollbar-width: none;
  position: sticky; top: 59px; z-index: 20;
  background: var(--bb-paper);
  padding-top: 2px;
}
.bb-tabs::-webkit-scrollbar { display: none; }
/* More tabs off to the right: say so. The strip fades at its right edge on a
   phone, and the padding lets the last tab scroll clear of the fade. The
   gradient is an alpha mask, so the token's colour is irrelevant — only its
   opacity is read. */
@media (max-width: 1023px) {
  .bb-tabs {
    -webkit-mask-image: linear-gradient(to right, var(--bb-ink) 85%, transparent);
            mask-image: linear-gradient(to right, var(--bb-ink) 85%, transparent);
    padding-right: var(--bb-space-8);
  }
}
.bb-tabs a {
  padding: 12px 0; white-space: nowrap;
  font-size: var(--bb-size-body); color: var(--bb-ink-muted);
  border-bottom: 2px solid transparent;
  min-height: var(--bb-tap-min); display: flex; align-items: center; gap: 7px;
}
.bb-tabs a:hover { color: var(--bb-ink); text-decoration: none; }
.bb-tabs a[aria-current="page"] { color: var(--bb-ink); font-weight: 600; border-bottom-color: var(--bb-ink); }
.bb-tabs__count {
  font-family: var(--bb-font-mono); font-size: var(--bb-size-meta);
  color: var(--bb-ink-faint); background: var(--bb-surface-sunk);
  border: var(--bb-border-hair); padding: 1px 6px;
}

/* The panel under the tab strip. The one thing that changes between tabs, so
   it is the one thing given a shared frame — every tab opens the same way. */
.bb-panel { display: flex; flex-direction: column; gap: var(--bb-space-4); scroll-margin-top: 120px; }
.bb-panel__note {
  font-size: var(--bb-size-small); color: var(--bb-ink-muted);
  max-width: 78ch; margin: 0;
}
.bb-prose { white-space: pre-wrap; max-width: 78ch; }

/* --- A question, and the answer under it ---------------------------------
   A <details>, not a scripted accordion, and the reasoning is the account
   menu's one level up: it opens and closes with no JavaScript, it is
   focusable and operable from the keyboard without a role, an aria-expanded
   or a keydown handler to get wrong, and the browser's own find-in-page
   searches inside a CLOSED one and opens it. A div toggled by a class does
   none of that — and on a page whose whole job is answering a question
   somebody arrived with, find-in-page is the feature.

   Everything is closed on load. An FAQ where one answer is already open
   reads as though that question mattered more, and pushes the rest of the
   list below the fold on a phone; the list of questions IS the content. */
.bb-qa { border-bottom: var(--bb-border-hair); }
.bb-qa:last-child { border-bottom: 0; }
.bb-qa__q {
  display: flex; align-items: center; gap: var(--bb-space-3);
  min-height: var(--bb-tap-min);
  padding: var(--bb-space-3) 0;
  cursor: pointer;
  list-style: none;               /* Firefox marker */
  user-select: none;
}
.bb-qa__q::-webkit-details-marker { display: none; }
.bb-qa__q:focus-visible { outline: 2px solid var(--bb-brass); outline-offset: 2px; }
/* The heading is the control's label, so it carries the whole hit area rather
   than sitting inside it — a summary you can only open by hitting the words
   exactly is a 44px target on paper and a 17px one in the hand. */
.bb-qa__title {
  flex: 1; margin: 0;
  font-family: var(--bb-font-display); letter-spacing: -0.02em;
  font-size: var(--bb-size-body); font-weight: 600; line-height: 1.35;
  color: var(--bb-ink);
}
.bb-qa__q:hover .bb-qa__title { color: var(--bb-brass-text); }
.bb-qa__mark {
  flex-shrink: 0; color: var(--bb-ink-muted);
  transition: transform var(--bb-motion);
}
.bb-qa[open] > .bb-qa__q .bb-qa__mark { transform: rotate(180deg); }
.bb-qa[open] > .bb-qa__q .bb-qa__title { color: var(--bb-ink); }
.bb-qa__body {
  display: flex; flex-direction: column; gap: var(--bb-space-3);
  padding: 0 0 var(--bb-space-4);
  max-width: 74ch;
}
/* Printing an FAQ with every answer collapsed prints a table of contents.
   Nothing else on this site hides content from print, and this must not
   either — so a closed one is forced open on paper.

   Three declarations because browsers hide a closed details three different
   ways: a UA display:none on the non-summary children, content-visibility on
   the newer engines, and ::details-content where that pseudo has landed. Each
   is its own rule, so an engine that does not know the pseudo drops that rule
   and keeps the two it does understand. */
@media print {
  .bb-qa__body { display: flex !important; content-visibility: visible !important; }
  .bb-qa { content-visibility: visible; }
  .bb-qa::details-content { content-visibility: visible; block-size: auto; }
  .bb-qa__mark { display: none; }
}


.bb-bidamount { font-family: var(--bb-font-display); font-size: 40px; line-height: 1.05; margin-top: 2px; }
@media (min-width: 700px) { .bb-bidamount { font-size: 44px; } }

.bb-costline { display: flex; justify-content: space-between; gap: var(--bb-space-3); font-size: var(--bb-size-small); color: var(--bb-ink-muted); padding: 3px 0; }

/* What winning costs, under the photograph (partials/price_summary). Phones and
   tablets only: from 1024px the bid panel sits beside the photographs and says
   the same thing, and one screen must not say it twice. */
.bb-pricesum {
  display: flex; flex-direction: column; gap: var(--bb-space-2);
  padding: var(--bb-space-4);
  border: 1px solid var(--bb-brass-line);
  border-radius: var(--bb-radius-lg);
  background: var(--bb-brass-soft);
}
.bb-pricesum__total { font-family: var(--bb-font-display); font-size: var(--bb-size-amount); line-height: 1.1; color: var(--bb-ink); }
@media (min-width: 1024px) { .bb-pricesum { display: none; } }

/* Before you bid, know this (partials/know_before). A list a buyer can scan,
   one condition per line. */
/* How buying works (pages/how_it_works). A numbered list a beginner reads
   top to bottom; the numbers are drawn, the list stays an <ol> for readers
   that announce it. */
.bb-howsteps { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: var(--bb-space-3); }
.bb-howsteps__no {
  flex: 0 0 auto; display: inline-flex; align-items: center; justify-content: center;
  width: var(--bb-tap-min); height: var(--bb-tap-min); border-radius: var(--bb-radius-pill);
  background: var(--bb-brass-soft); border: 1px solid var(--bb-brass-line);
  color: var(--bb-brass-text); font-family: var(--bb-font-display); font-size: var(--bb-size-h2);
}

/* Pay page: refund bank details only matter for Instant EFT and PayShap. */
form:has(input[name="method"][value="card"]:checked) .bb-bankfields { display: none; }

.bb-know { padding-top: var(--bb-space-3); border-top: 1px dashed var(--bb-line); }
.bb-checklist { display: flex; flex-direction: column; gap: var(--bb-space-2); margin: 0; padding-left: var(--bb-space-5); }
.bb-checklist li { font-size: var(--bb-size-small); color: var(--bb-ink-body); }

/* The way to the bid form, kept in reach on a one-column page. Below 1024px
   only; the page gets room at the foot so the bar never sits over the last
   lines of content. */
.bb-bidbar { display: none; }
@media (max-width: 1023px) {
  .bb-bidbar {
    display: flex; align-items: center; justify-content: space-between; gap: var(--bb-space-3);
    position: fixed; left: var(--bb-space-3); right: var(--bb-space-3); bottom: var(--bb-space-3); z-index: 40;
    min-height: var(--bb-tap-min); padding: var(--bb-space-2) var(--bb-space-2) var(--bb-space-2) var(--bb-space-4);
    border-radius: var(--bb-radius-lg);
    background: var(--bb-ink); color: var(--bb-ink-on-dark);
    box-shadow: var(--bb-shadow-pop);
    font-size: var(--bb-size-small);
  }
  .bb-bidbar:hover { text-decoration: none; color: var(--bb-ink-on-dark); }
  .bb-bidbar__go {
    display: inline-flex; align-items: center; min-height: 36px; padding: 0 var(--bb-space-4);
    border-radius: var(--bb-radius-sm);
    background: var(--bb-brass); color: var(--bb-ink-on-dark); font-weight: 600;
  }
  body:has(.bb-bidbar) { padding-bottom: calc(var(--bb-tap-min) + var(--bb-space-7)); }
  /* The bar must not cover the form it points at. */
  #bb-bid, #bb-know { scroll-margin-bottom: calc(var(--bb-tap-min) + var(--bb-space-7)); }
}
.bb-costline--total { font-size: var(--bb-size-body); font-weight: 600; color: var(--bb-ink); padding-top: 7px; border-top: var(--bb-border-hair); margin-top: 6px; }
.bb-costline span:last-child { font-family: var(--bb-font-mono); }

/* --- The bid card (partials/bid_panel) ------------------------------------
   One card, divided by hairlines, never by boxes inside boxes. It used to
   stack six bordered blocks — cancel, bid, proxy, alerts, recent bids — and
   ran past two screens on a phone. The order is the bidder's: clock, price,
   where I stand, what I bid; the things set once fold into the foot. */
.bb-bidcard__head {
  display: flex; flex-wrap: wrap; align-items: center; justify-content: space-between;
  gap: var(--bb-space-2) var(--bb-space-3);
}
.bb-bidcard__status { display: inline-flex; align-items: center; gap: 6px; }
/* The audience strip lives in the header here, so it drops its own rule. */
.bb-bidcard__head .bb-audience { padding: 0; border-top: 0; gap: 4px var(--bb-space-3); }

.bb-bidcard__body { display: flex; flex-direction: column; gap: var(--bb-space-4); }
@media (min-width: 1024px) {
  .bb-bidcard__body { padding: var(--bb-space-5); }
  .bb-bidcard .bb-clock--panel { margin: calc(var(--bb-space-5) * -1) calc(var(--bb-space-5) * -1) 0; padding: var(--bb-space-4) var(--bb-space-5); }
}
/* The countdown's soft-close link is a footnote to the clock: smaller type,
   and it gives back the slack under it. Its 44px target stays. */
.bb-bidcard .bb-clock--panel .bb-softclose { font-size: var(--bb-size-fine); margin-bottom: -10px; }

.bb-bidcard__price { display: flex; align-items: flex-end; justify-content: space-between; gap: var(--bb-space-3); }
.bb-bidcard__tally { display: flex; flex-direction: column; align-items: flex-end; gap: 2px; padding-bottom: 6px; }

/* Where I stand: a tinted slip, the colour carrying the meaning. */
.bb-bidcard__you {
  display: flex; flex-direction: column; gap: var(--bb-space-1);
  padding: var(--bb-space-3);
  border-radius: var(--bb-radius-md);
  background: var(--bb-surface-sunk);
}
.bb-bidcard__you--ahead  { background: var(--bb-verify-soft); }
.bb-bidcard__you--behind { background: var(--bb-alert-soft); }

/* Everything that acts: the form, or the way into it. */
.bb-bidcard__act {
  display: flex; flex-direction: column; gap: var(--bb-space-3);
  padding-top: var(--bb-space-4);
  border-top: var(--bb-border-hair);
}
.bb-bidcard__ask { display: flex; align-items: baseline; justify-content: space-between; gap: var(--bb-space-2); flex-wrap: wrap; margin-bottom: var(--bb-space-2); }
.bb-bidcard__cost .bb-costline--total { border-top: 0; margin-top: 0; padding-top: 0; }
.bb-bidcard__cost .bb-fine { margin-top: 2px; }
.bb-bidcard__btns { display: grid; grid-template-columns: minmax(0, 1fr); gap: var(--bb-space-2); }
@media (min-width: 420px) { .bb-bidcard__btns { grid-template-columns: minmax(0, 1.6fr) minmax(0, 1fr); } }
.bb-bidcard__btns > .bb-btn { width: 100%; }
.bb-bidcard__hint { margin-top: calc(var(--bb-space-1) * -1); }
.bb-bidcard .bb-check { padding: 2px 0; min-height: 0; }
.bb-bidcard .bb-check__text { font-size: var(--bb-size-small); }

.bb-bidcard__foot { border-top: var(--bb-border-hair); padding: 0 var(--bb-space-4); }
@media (min-width: 1024px) { .bb-bidcard__foot { padding: 0 var(--bb-space-5); } }
.bb-bidcard__first {
  padding: var(--bb-space-3) var(--bb-space-4);
  border-top: var(--bb-border-hair);
  background: rgba(var(--bb-ink-rgb), 0.02);
}
@media (min-width: 1024px) { .bb-bidcard__first { padding: var(--bb-space-3) var(--bb-space-5); } }

/* A fold: a <details> for a control set once or used rarely, so it costs one
   line until it is wanted. Same reasoning as .bb-qa — works without script,
   and the summary is the whole hit area. */
.bb-fold__q {
  display: flex; align-items: center; gap: var(--bb-space-2);
  min-height: var(--bb-tap-min);
  cursor: pointer; list-style: none; user-select: none;
  color: var(--bb-ink-body);
}
.bb-fold__q::-webkit-details-marker { display: none; }
.bb-fold__q:focus-visible { outline: 2px solid var(--bb-brass); outline-offset: 2px; }
.bb-fold__title { flex: 1; font-size: var(--bb-size-small); font-weight: 600; }
.bb-fold__q:hover .bb-fold__title { color: var(--bb-brass-text); }
.bb-fold__mark { flex-shrink: 0; color: var(--bb-ink-muted); transition: transform var(--bb-motion); }
.bb-fold[open] > .bb-fold__q .bb-fold__mark { transform: rotate(180deg); }
.bb-fold__body { display: flex; flex-direction: column; gap: var(--bb-space-2); padding-bottom: var(--bb-space-3); }
/* Inside the position slip it is a quiet secondary line, not a heading. */
.bb-fold--inline .bb-fold__q { margin: -6px 0 -10px; }
.bb-fold--inline .bb-fold__title { flex: 0 1 auto; font-weight: 500; color: var(--bb-ink-muted); text-decoration: underline; text-underline-offset: 3px; }
.bb-fold--inline[open] .bb-fold__body { padding-top: var(--bb-space-2); }

/* The audience strip lives in the header here, so it drops its own rule. */
.bb-bidcard__head .bb-audience { padding: 0; border-top: 0; gap: 4px var(--bb-space-3); }

.bb-bidcard__body { display: flex; flex-direction: column; gap: var(--bb-space-4); }
@media (min-width: 1024px) {
  .bb-bidcard__body { padding: var(--bb-space-5); }
  .bb-bidcard .bb-clock--panel { margin: calc(var(--bb-space-5) * -1) calc(var(--bb-space-5) * -1) 0; padding: var(--bb-space-4) var(--bb-space-5); }
}
/* The countdown's soft-close link is a footnote to the clock: smaller type,
   and it gives back the slack under it. Its 44px target stays. */
.bb-bidcard .bb-clock--panel .bb-softclose { font-size: var(--bb-size-fine); margin-bottom: -10px; }

.bb-bidcard__price { display: flex; align-items: flex-end; justify-content: space-between; gap: var(--bb-space-3); }
.bb-bidcard__tally { display: flex; flex-direction: column; align-items: flex-end; gap: 2px; padding-bottom: 6px; }

/* Where I stand: a tinted slip, the colour carrying the meaning. */
.bb-bidcard__you {
  display: flex; flex-direction: column; gap: var(--bb-space-1);
  padding: var(--bb-space-3);
  border-radius: var(--bb-radius-md);
  background: var(--bb-surface-sunk);
}
.bb-bidcard__you--ahead  { background: var(--bb-verify-soft); }
.bb-bidcard__you--behind { background: var(--bb-alert-soft); }

/* Everything that acts: the form, or the way into it. */
.bb-bidcard__act {
  display: flex; flex-direction: column; gap: var(--bb-space-3);
  padding-top: var(--bb-space-4);
  border-top: var(--bb-border-hair);
}
.bb-bidcard__ask { display: flex; align-items: baseline; justify-content: space-between; gap: var(--bb-space-2); flex-wrap: wrap; margin-bottom: var(--bb-space-2); }
.bb-bidcard__cost .bb-costline--total { border-top: 0; margin-top: 0; padding-top: 0; }
.bb-bidcard__cost .bb-fine { margin-top: 2px; }
.bb-bidcard__btns { display: grid; grid-template-columns: minmax(0, 1fr); gap: var(--bb-space-2); }
@media (min-width: 420px) { .bb-bidcard__btns { grid-template-columns: minmax(0, 1.6fr) minmax(0, 1fr); } }
.bb-bidcard__btns > .bb-btn { width: 100%; }
.bb-bidcard__hint { margin-top: calc(var(--bb-space-1) * -1); }
.bb-bidcard .bb-check { padding: 2px 0; min-height: 0; }
.bb-bidcard .bb-check__text { font-size: var(--bb-size-small); }

.bb-bidcard__foot { border-top: var(--bb-border-hair); padding: 0 var(--bb-space-4); }
@media (min-width: 1024px) { .bb-bidcard__foot { padding: 0 var(--bb-space-5); } }
.bb-bidcard__first {
  padding: var(--bb-space-3) var(--bb-space-4);
  border-top: var(--bb-border-hair);
  background: rgba(var(--bb-ink-rgb), 0.02);
}
@media (min-width: 1024px) { .bb-bidcard__first { padding: var(--bb-space-3) var(--bb-space-5); } }

/* A fold: a <details> for a control set once or used rarely, so it costs one
   line until it is wanted. Same reasoning as .bb-qa — works without script,
   and the summary is the whole hit area. */
.bb-fold__q {
  display: flex; align-items: center; gap: var(--bb-space-2);
  min-height: var(--bb-tap-min);
  cursor: pointer; list-style: none; user-select: none;
  color: var(--bb-ink-body);
}
.bb-fold__q::-webkit-details-marker { display: none; }
.bb-fold__q:focus-visible { outline: 2px solid var(--bb-brass); outline-offset: 2px; }
.bb-fold__title { flex: 1; font-size: var(--bb-size-small); font-weight: 600; }
.bb-fold__q:hover .bb-fold__title { color: var(--bb-brass-text); }
.bb-fold__mark { flex-shrink: 0; color: var(--bb-ink-muted); transition: transform var(--bb-motion); }
.bb-fold[open] > .bb-fold__q .bb-fold__mark { transform: rotate(180deg); }
.bb-fold__body { display: flex; flex-direction: column; gap: var(--bb-space-2); padding-bottom: var(--bb-space-3); }
/* Inside the position slip it is a quiet secondary line, not a heading. */
.bb-fold--inline .bb-fold__q { margin: -6px 0 -10px; }
.bb-fold--inline .bb-fold__title { flex: 0 1 auto; font-weight: 500; color: var(--bb-ink-muted); text-decoration: underline; text-underline-offset: 3px; }
.bb-fold--inline[open] .bb-fold__body { padding-top: var(--bb-space-2); }

/* The latest bids, under the unit (pages/auction/show). A ledger, not a
   table: three facts a row, the top bid marked, the reader's own in bold. */
.bb-bidlog { margin-top: var(--bb-space-6); }
.bb-bidlog__head { display: flex; align-items: baseline; justify-content: space-between; gap: var(--bb-space-3); padding-bottom: var(--bb-space-2); border-bottom: var(--bb-border-hair); }
.bb-bidlog__head .bb-label { margin: 0; }
.bb-bidlog__list { list-style: none; margin: 0; padding: 0; }
.bb-bidlog__row {
  display: grid; grid-template-columns: minmax(0, 1fr) auto auto; align-items: baseline;
  gap: var(--bb-space-3);
  padding: 10px 0;
  border-bottom: var(--bb-border-hair);
  font-size: var(--bb-size-small); color: var(--bb-ink-body);
}
.bb-bidlog__who { min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.bb-bidlog__when { font-size: var(--bb-size-meta); color: var(--bb-ink-faint); }
.bb-bidlog__amt { font-family: var(--bb-font-mono); font-variant-numeric: tabular-nums; color: var(--bb-ink); text-align: right; min-width: 6ch; }
.bb-bidlog__row--top .bb-bidlog__amt { color: var(--bb-brass-text); font-weight: 600; }
.bb-bidlog__row--self .bb-bidlog__who { font-weight: 600; color: var(--bb-ink); }

/* The audience strip — views, watchers, and who is on the page right now.
   Sits between the bid figures and the bid form, hairline-ruled off both,
   because it is the one block in the panel that is not about money.

   Set in mono at meta size: these are checkable quantities, not headline
   figures, and giving them the display face would put them in competition
   with the bid amount directly above. The strip wraps rather than scrolls —
   at 320px three items become two lines, which is fine; a horizontally
   scrolling row of three short facts is not. */
.bb-audience {
  display: flex; flex-wrap: wrap; align-items: center;
  gap: 6px var(--bb-space-3);
  margin: 0; padding: var(--bb-space-2) 0 0;
  list-style: none;
  border-top: var(--bb-border-hair);
}
/* On a tile the strip is the last line in the body, under the price rule, and
   it earns its space by being the one thing on a tile that answers "is anyone
   else interested in this?" — which is the question a grid is scanned with.
   Tighter than the panel's, because a tile is already four stacked facts. */
.bb-audience--tile {
  gap: 4px var(--bb-space-3);
  margin-top: 10px; padding-top: 9px;
}
/* Four figures (views, watching, here now, bids) in a quarter-width tile:
   number over word, each centred, with equal gaps between and at the ends, so
   none wraps onto a second line. A held-back "here now" drops out and the
   other three re-space themselves. */
.bb-audience--tile {
  flex-wrap: nowrap; justify-content: space-evenly;
  gap: 0 6px;
}
.bb-audience--tile .bb-audience__item {
  display: grid; grid-template-columns: auto auto; justify-content: center;
  align-items: center; column-gap: 4px; min-width: 0;
}
.bb-audience--tile .bb-audience__label {
  grid-column: 1 / -1; text-align: center; white-space: nowrap;
}

.bb-audience__item { display: inline-flex; align-items: center; gap: 6px; }
/* An author display: rule beats the UA stylesheet's display:none, so a tile
   holding back its presence figure would render it anyway — hidden in name and
   on screen in fact. (The legal-basis form's [data-bb-when] wrapper had the same
   trap and the same fix, until 0038 removed the form.) */
.bb-audience__item[hidden] { display: none; }
/* The icon carries no information the words do not — it is a landmark for
   somebody scanning the panel, and hidden from assistive technology. */
.bb-audience__item svg { flex-shrink: 0; }
.bb-audience__n {
  font-family: var(--bb-font-mono);
  font-size: var(--bb-size-meta);
  font-variant-numeric: tabular-nums;
  color: var(--bb-ink);
}
/* Tabular figures matter here: 'viewing now' repaints every 25 seconds off the
   clock sync, and proportional digits make the label beside it jump. */
.bb-audience__label { font-size: var(--bb-size-meta); color: var(--bb-ink-faint); }

.bb-steps { display: flex; flex-direction: column; gap: 11px; }
.bb-step { display: flex; gap: 11px; align-items: flex-start; }
.bb-step__no { font-family: var(--bb-font-mono); font-size: var(--bb-size-meta); color: var(--bb-brass-text); padding-top: 2px; }

/* Role chip. Deliberately neutral: green means verified and red means time
   running out, so a role gets neither. */
.bb-pill--role {
  flex-shrink: 0;
  background: var(--bb-surface-sunk);
  border: 1px solid var(--bb-line-strong);
  color: var(--bb-ink-muted);
  white-space: nowrap;
  /* Between 700 and 1023 the primary nav is behind the burger, so the bar has
     room the phone does not. */
  max-width: 46vw;
  min-width: 0;
  overflow: hidden;
}
/* The ellipsis has to live on the text itself. .bb-pill is a flex container, so
   text-overflow on it never applied to the bare label inside — a facility name
   past the cap was simply chopped, mid-word, with nothing to say so. */
.bb-pill__text {
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
/* A scoped role is two parts: the role, which holds its width, then the
   facility, which is what gives way to the ellipsis. Same divider as +N. */
.bb-pill__role { flex-shrink: 0; white-space: nowrap; }
.bb-pill__role + .bb-pill__text {
  padding-left: 6px;
  margin-left: 2px;
  border-left: 1px solid var(--bb-line-strong);
  color: var(--bb-ink);
}
.bb-pill__more {
  padding-left: 6px;
  margin-left: 2px;
  border-left: 1px solid var(--bb-line-strong);
  color: var(--bb-ink-faint);
}
/* Tight on a phone, where it shares the bar with the wordmark and the burger. */
@media (max-width: 699px) {
  .bb-pill--role { max-width: 34vw; font-size: var(--bb-size-label); }
  /* About 130px: the role alone. The facility is written out in the drawer. */
  .bb-header__inner .bb-pill__role { flex-shrink: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; }
  .bb-header__inner .bb-pill__role + .bb-pill__text { display: none; }
}
/* 22ch cut "Alpha Storage — Alpha Storage Main" down to "Alpha Storage — A"
   on a 1440px header with 600px of empty space beside it. Take the room that
   is there, in proportion to the window, and keep the cap for the window that
   has none. */
/* The role now sits in front of the facility, so the cap grew to match. */
@media (min-width: 1024px) { .bb-pill--role { max-width: min(44vw, 84ch); } }
/* The bell (0065) took 60px of a bar that had none to spare. Below 1024px the
   chip is what gives way — it already ellipsises, and the drawer and the
   account menu write it out in full. The bell never shrinks: a count you
   cannot see is not a count. */
@media (max-width: 1023px) { .bb-header__inner .bb-pill--role { flex-shrink: 1; } }
.bb-bell { flex-shrink: 0; }

/* In the drawer the chip is on its own line, so nothing is gained by capping
   it against the viewport. */
.bb-drawer .bb-pill--role { max-width: 100%; }
/* Role and facility together outrun a phone's width, so in the drawer the chip
   may wrap rather than cut either. */
.bb-drawer .bb-pill--role { flex-wrap: wrap; row-gap: 2px; white-space: normal; }
.bb-drawer .bb-pill__text { white-space: normal; overflow: visible; }

/* --- Countdown ----------------------------------------------------------- */

.bb-countdown {
  font-family: var(--bb-font-mono);
  font-size: 38px; font-weight: 500; line-height: 1.1;
  margin-top: 6px; color: var(--bb-alert);
  font-variant-numeric: tabular-nums;
}
@media (min-width: 700px) { .bb-countdown { font-size: 44px; } }
/* --- Trend bar -----------------------------------------------------------
   There is no build step and no charting library, and none may be added. A
   twelve-month trend is a table of months with a bar beside each row, sized as
   a percentage of the tallest month by an inline width.

   BLUE, NOT TEAL AND NOT CORAL. UI.md §1 rule 3: teal means verified and coral
   means time running out, and neither may be borrowed to mean "big" or "small".
   Growth is not a verified fact and a decline is not a deadline, so the bar
   takes the wayfinding hue, which carries no state at all.

   The bar is never the only way to read the figure — every amount is printed in
   mono in the same row and repeated in the bar's accessible name. A trend
   nobody can read the numbers off is decoration. */
.bb-bar { height: 8px; min-width: 60px; background: var(--bb-surface-sunk); border-radius: var(--bb-radius-pill); overflow: hidden; }
.bb-bar__fill { height: 8px; background: var(--bb-blue); border-radius: var(--bb-radius-pill); }

.bb-track { height: 5px; background: var(--bb-alert-track); margin-top: var(--bb-space-3); }
.bb-track__fill { height: 5px; background: var(--bb-alert); transition: width 1s linear; }

/* --- Auction clock -------------------------------------------------------
   The countdown that runs on every live auction. Red is reserved for time
   actually running out, so a seven-day auction is ink; it goes brass in the
   last hour and red inside the closing window, where a bid still moves the
   deadline. Colour is never the only signal — the words change with it. */

.bb-clock { display: flex; flex-direction: column; gap: 2px; }
.bb-clock__value {
  font-family: var(--bb-font-mono);
  font-variant-numeric: tabular-nums;
  font-feature-settings: "tnum" 1;
  color: var(--bb-ink);
  line-height: 1.1;
}
.bb-clock__at { font-family: var(--bb-font-mono); font-size: var(--bb-size-meta); color: var(--bb-ink-muted); }

.bb-clock[data-urgency="soon"]  .bb-clock__value { color: var(--bb-brass-text); }
.bb-clock[data-urgency="final"] .bb-clock__value { color: var(--bb-alert); }
.bb-clock[data-urgency="over"]  .bb-clock__value { color: var(--bb-ink-muted); }

/* Panel — the auction detail bid card. The one place it is the loudest thing
   on the screen, because it is the thing the buyer is racing. */
.bb-clock--panel {
  gap: var(--bb-space-2);
  padding: var(--bb-space-3) var(--bb-space-4);
  margin: calc(var(--bb-space-4) * -1) calc(var(--bb-space-4) * -1) 0;
  border-bottom: var(--bb-border-hair);
  background: var(--bb-surface-sunk);
}
.bb-clock--panel .bb-clock__value { font-size: 34px; font-weight: 500; }
@media (min-width: 700px) { .bb-clock--panel .bb-clock__value { font-size: 40px; } }
.bb-clock--panel .bb-clock__eyebrow { white-space: nowrap; }
.bb-clock--panel .bb-clock__at { text-align: right; }
.bb-clock--panel[data-urgency="final"] { background: var(--bb-alert-soft); border-bottom-color: var(--bb-alert-line); }
.bb-clock--panel[data-urgency="final"] .bb-clock__eyebrow { color: var(--bb-alert); }
.bb-clock__note { margin: 0; }

/* Every auction closes softly. Said up front, with the way to find out more,
   rather than only once the late-bid window has opened. */
.bb-softclose {
  display: inline-flex; align-items: center; gap: 8px;
  min-height: var(--bb-tap-min);
  font-size: var(--bb-size-small); color: var(--bb-ink-body);
  text-decoration: none;
}
.bb-softclose svg { flex: none; color: var(--bb-brass); }
.bb-softclose__more { color: var(--bb-brass-text); text-decoration: underline; text-underline-offset: 3px; }
.bb-softclose:hover .bb-softclose__more { text-decoration-thickness: 2px; }
.bb-softclose:focus-visible { outline: 2px solid var(--bb-brass); outline-offset: 2px; }

/* The elapsed run of the auction, not an invented deadline. It fills toward
   the close so a glance reads as "how much is left". */
.bb-clock__track { height: 4px; background: var(--bb-line); }
.bb-clock__fill  { height: 4px; background: var(--bb-ink); transition: width 1s linear; }
.bb-clock[data-urgency="soon"]  .bb-clock__fill { background: var(--bb-brass); }
.bb-clock[data-urgency="final"] .bb-clock__fill { background: var(--bb-alert); }

/* Chip — the countdown on a browse tile, at the right of the black header bar
   (.bb-tile__head) opposite the unit reference. On a grid the two facts that
   drive a click are time left and price, so the clock takes the contrast.

   The absolute close time is not lost — the tile foot carries it. */
.bb-clock--chip {
  flex-shrink: 0;
  flex-direction: row; align-items: center; gap: 7px;
  background: var(--bb-ink); color: var(--bb-ink-on-dark);
}
/* In the final minutes the chip turns red; padding gives that red a shape. */
.bb-clock--chip[data-urgency="final"] { padding: 3px 9px; margin-right: -9px; }
.bb-clock--chip .bb-clock__icon { flex-shrink: 0; opacity: 0.7; }
.bb-clock--chip .bb-clock__value {
  font-size: var(--bb-size-h3);
  font-weight: 600;
  letter-spacing: 0.02em;
  color: inherit;   /* the base rule hard-sets ink, which is invisible here */
}

/* Escalation inverts the whole chip rather than tinting the number, so it reads
   across a grid at a glance. These selectors are deliberately one class heavier
   than the generic .bb-clock[data-urgency] rules above, which resolve to colours
   chosen for a light ground and fail contrast on this one. */
.bb-clock--chip[data-urgency="soon"]  .bb-clock__value { color: var(--bb-brass-on-ink); }
.bb-clock--chip[data-urgency="final"] { background: var(--bb-alert); }
.bb-clock--chip[data-urgency="final"] .bb-clock__value { color: var(--bb-ink-on-dark); }
.bb-clock--chip[data-urgency="over"]  .bb-clock__value { color: var(--bb-ink-on-dark-muted); }

/* Row — inside a table cell, under the status. */
.bb-clock--row .bb-clock__value { font-size: var(--bb-size-meta); font-weight: 600; }

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

/* A table wider than the phone scrolls inside this box rather than taking the
   page sideways with it. Anything that scrolls has to be reachable from a
   keyboard — app.js gives it a tabindex when, and only when, it actually
   overflows — and a focus ring, because otherwise the tab stop is invisible. */
.bb-tablewrap { overflow-x: auto; }
.bb-tablewrap:focus-visible {
  outline: 2px solid var(--bb-ink);
  outline-offset: 2px;
}
.bb-table { width: 100%; border-collapse: collapse; font-size: var(--bb-size-small); }
.bb-table th {
  text-align: left; padding: 10px var(--bb-space-3);
  font-family: var(--bb-font-mono); font-size: var(--bb-size-label);
  letter-spacing: var(--bb-track-label); text-transform: uppercase;
  color: var(--bb-ink-faint); border-bottom: var(--bb-border);
  white-space: nowrap;
}
.bb-table td { padding: 11px var(--bb-space-3); border-bottom: var(--bb-border-hair); color: var(--bb-ink-body); vertical-align: top; }
.bb-table tr:last-child td { border-bottom: none; }
.bb-table td.bb-num, .bb-table th.bb-num { text-align: right; font-family: var(--bb-font-mono); white-space: nowrap; }
.bb-table--fixed { table-layout: fixed; }

/* A cell that holds a sentence rather than a value. Without a measure it sets
   the width of the whole table to the length of its longest explanation, and
   one wordy row pushes every column off the side of a phone; without a floor it
   collapses to one word per line beside a short neighbour. Both bounds, and
   normal wrapping, because the default here is a value column's nowrap. */
.bb-table td.bb-say { min-width: 20ch; max-width: 44ch; white-space: normal; }

/* --- Timeline (chain of custody) ----------------------------------------- */

.bb-timeline { display: flex; flex-direction: column; }
.bb-event {
  display: grid;
  grid-template-columns: minmax(0, 1fr);
  gap: 2px var(--bb-space-4);
  padding: 11px 0;
  border-bottom: var(--bb-border-hair);
}
@media (min-width: 700px) {
  .bb-event { grid-template-columns: 168px minmax(0, 1fr) 180px; align-items: baseline; }
}
.bb-event:last-child { border-bottom: none; }
.bb-event__when  { font-family: var(--bb-font-mono); font-size: var(--bb-size-meta); color: var(--bb-ink-faint); }
.bb-event__what  { font-size: var(--bb-size-small); color: var(--bb-ink-body); }
.bb-event__who   { font-family: var(--bb-font-mono); font-size: var(--bb-size-meta); color: var(--bb-ink-muted); }
@media (min-width: 700px) { .bb-event__who { text-align: right; } }

.bb-integrity { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); }
@media (min-width: 700px) { .bb-integrity { grid-template-columns: repeat(4, minmax(0, 1fr)); } }
.bb-integrity__cell { padding: var(--bb-space-4); border-right: var(--bb-border-hair); border-top: var(--bb-border-hair); }
.bb-integrity__cell:nth-child(-n+2) { border-top: none; }
.bb-integrity__cell:nth-child(2n) { border-right: none; }
@media (min-width: 700px) {
  .bb-integrity__cell:nth-child(-n+4) { border-top: none; }
  .bb-integrity__cell:nth-child(2n) { border-right: var(--bb-border-hair); }
  .bb-integrity__cell:nth-child(4n) { border-right: none; }
}
.bb-integrity__value { font-family: var(--bb-font-mono); font-size: var(--bb-size-amount); line-height: 1.1; margin-top: var(--bb-space-1); }

/* --- Comparison (joint opening) ------------------------------------------ */

.bb-compare { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: var(--bb-space-2); }
.bb-compare__pane { display: flex; flex-direction: column; gap: 6px; }
.bb-compare__photo { aspect-ratio: 4 / 3; }

.bb-progressrow {
  display: flex; align-items: center; gap: var(--bb-space-3);
  padding: var(--bb-space-3) var(--bb-space-4);
  background: var(--bb-surface-sunk); border: var(--bb-border);
  border-radius: var(--bb-radius-md);
  box-shadow: var(--bb-sink);
}

/* --- Signature ----------------------------------------------------------- */

.bb-sigpad {
  width: 100%; height: 160px;
  background: var(--bb-surface);
  border: var(--bb-border-ink);
  touch-action: none; cursor: crosshair;
  display: block;
  box-shadow: var(--bb-sink);
}

/* --- Flash & callouts ---------------------------------------------------- */

.bb-flash { display: flex; flex-direction: column; gap: var(--bb-space-2); padding-top: var(--bb-space-4); }
/* Redirects land on #bb-flash, and the header is sticky over it. */
.bb-flash { scroll-margin-top: 74px; }
.bb-flash__item { padding: 13px var(--bb-space-4); border-left: 3px solid var(--bb-ink); background: var(--bb-surface); font-size: var(--bb-size-small); color: var(--bb-ink-body); box-shadow: var(--bb-lift-1); }
.bb-flash__item--success { border-left-color: var(--bb-verify); }
.bb-flash__item--warn    { border-left-color: var(--bb-brass); }

/* A refusal is not a notice.
   It is the reason the thing the reader just pressed did not happen, and it
   has to answer that press at a glance — the same weight as the button that
   failed, not a tinted hairline of the same small grey text as everything
   else on the page. An approve that is refused and reads as though nothing
   occurred is how a reviewer concludes the button is broken. */
.bb-flash__item--error,
.bb-flash__item--outbid {
  display: flex;
  align-items: flex-start;
  gap: var(--bb-space-3);
  padding: var(--bb-space-4);
  background: var(--bb-alert-soft);
  border: 1px solid var(--bb-alert-line);
  border-left: 4px solid var(--bb-alert);
  color: var(--bb-ink);
}
.bb-flash__mark {
  font-family: var(--bb-font-mono);
  font-size: 15px;
  line-height: 1.3;
  color: var(--bb-alert);
  flex: none;
}
.bb-flash__title {
  margin: 0;
  font-size: var(--bb-size-fine);
  font-weight: 600;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--bb-alert);
}
.bb-flash__text {
  margin: 4px 0 0;
  font-size: var(--bb-size-body);
  line-height: var(--bb-leading-body);
  color: var(--bb-ink);
}
/* It is focused on arrival, and a focus ring is the only thing that tells the
   reader why the keyboard is suddenly here rather than where they left it. */
.bb-flash__item--error:focus { outline: 2px solid var(--bb-alert); outline-offset: 2px; }

/* Outbid on arrival: the refusal's weight, a heavier bar and a larger
   headline, because the price on the page has just moved past the buyer and
   the only useful next step is the button inside the message. */
.bb-flash__item--outbid { border-left-width: 6px; }
.bb-flash__item--outbid .bb-flash__mark { font-size: 20px; font-weight: 700; }
.bb-flash__item--outbid .bb-flash__title { font-size: var(--bb-size-body); }
.bb-flash__item--outbid:focus { outline: 2px solid var(--bb-alert); outline-offset: 2px; }
.bb-flash__action { margin: var(--bb-space-3) 0 0; }

/* The reason a control is shut, sitting against that control.
   --bb-ink-faint at 12px is right for a hint about how to fill a field in and
   wrong for the sentence explaining why the button will not move: a reviewer
   reads the row of buttons, not the fine print under it, so this is body-sized
   and carries the alert mark the checklist above uses for the same failure. */
.bb-blocked {
  display: flex;
  align-items: flex-start;
  gap: var(--bb-space-2);
  margin: 0;
  font-size: var(--bb-size-small);
  line-height: var(--bb-leading-body);
  color: var(--bb-ink-body);
}
.bb-blocked::before {
  content: "\2715";
  font-family: var(--bb-font-mono);
  color: var(--bb-alert);
  flex: none;
}
.bb-blocked a { font-weight: 600; }

.bb-gapnote {
  border: 1px dashed var(--bb-line-strong);
  background: var(--bb-surface-sunk);
  padding: var(--bb-space-3) var(--bb-space-4);
  font-size: var(--bb-size-fine);
  line-height: 1.55;
  color: var(--bb-ink-muted);
}
.bb-gapnote code, .bb-gapnote b { font-family: var(--bb-font-mono); color: var(--bb-ink-body); font-weight: 500; }

.bb-empty { padding: var(--bb-space-8) var(--bb-space-4); text-align: center; color: var(--bb-ink-muted); border: 1px dashed var(--bb-line-strong); }

/* --- Configuration setting ----------------------------------------------
   One business rule on /admin/config: its plain name, the control, what it
   does, and the key underneath for whoever is reconciling against the code.

   The old screen was a four-column table, which forced the explanation into a
   cell the width of a phrase and put the key first — the reader met
   "auction.antisnipe_window_minutes" before anything told them what it was
   for. Stacking gives the sentence its full measure and lets the name lead. */

.bb-cfg + .bb-cfg { border-top: 1px solid var(--bb-line-hair); padding-top: var(--bb-space-5); }
.bb-cfg { display: flex; flex-direction: column; gap: var(--bb-space-2); }

.bb-cfg__head { display: flex; flex-wrap: wrap; align-items: baseline; gap: var(--bb-space-2) var(--bb-space-3); }
.bb-cfg__head .bb-label { margin: 0; }
.bb-cfg__chips { display: flex; flex-wrap: wrap; gap: var(--bb-space-2); }

/* Not a bb-row: the control, its unit and the Save button have to wrap as a
   group on a narrow screen without the unit ending up orphaned on its own
   line above the button it belongs beside. */
.bb-cfg__set { display: flex; flex-wrap: wrap; align-items: center; gap: var(--bb-space-2) var(--bb-space-3); margin: 0; }
.bb-cfg__control { width: 100%; max-width: 220px; }
.bb-cfg__unit { font-size: var(--bb-size-fine); color: var(--bb-ink-muted); }
/* The cents-to-rand echo. Loud enough to be read before the digits it
   translates, which are a hundred times the amount they stand for. */
.bb-cfg__rand { font-family: var(--bb-font-mono); color: var(--bb-ink-body); white-space: nowrap; }

.bb-cfg__what { margin: 0; font-size: var(--bb-size-small); line-height: var(--bb-leading-body); color: var(--bb-ink-body); max-width: 68ch; }
.bb-cfg__meta { margin: 0; font-size: var(--bb-size-meta); color: var(--bb-ink-faint); }

/* A setting that does not do what its name says. The words carry the meaning —
   "WORDING ONLY" says it without the colour — so this is emphasis on a message
   already written, never the message itself. */
.bb-cfg__chip--warn {
  color: var(--bb-alert);
  background: var(--bb-alert-soft);
  border: 1px solid var(--bb-alert-line);
  padding: 2px 6px;
}

/* --- Footer -------------------------------------------------------------- */

.bb-footer { background: var(--bb-ink); color: var(--bb-ink-on-dark-muted); margin-top: var(--bb-space-8); box-shadow: var(--bb-emboss); }
.bb-footer__inner { padding: var(--bb-space-6) 0; display: grid; grid-template-columns: minmax(0, 1fr); gap: var(--bb-space-5); }
@media (min-width: 700px) { .bb-footer__inner { grid-template-columns: repeat(2, minmax(0, 1fr)); } }
@media (min-width: 1024px) { .bb-footer__inner { grid-template-columns: 2fr 1fr 1fr 1fr; } }
.bb-footer a { color: var(--bb-ink-on-dark); }
.bb-footer .bb-label { color: var(--bb-ink-on-dark-muted); }
.bb-footer__links { display: flex; flex-direction: column; font-size: var(--bb-size-small); }
/* A column of links is a column of tap targets, not a paragraph. */
.bb-footer__links a { display: flex; align-items: center; min-height: var(--bb-tap-min); }

/* --- Utility ------------------------------------------------------------- */

.bb-hide-phone { display: none; }
@media (min-width: 700px) { .bb-hide-phone { display: initial; } }
.bb-only-phone { display: initial; }
@media (min-width: 700px) { .bb-only-phone { display: none; } }

.bb-sr {
  position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px;
  overflow: hidden; clip: rect(0, 0, 0, 0); white-space: nowrap; border: 0;
}

/* --- Motion ---------------------------------------------------------------
   The depth is structural and stays. The one-pixel travel that goes with it is
   decoration, so anyone who has asked for less motion keeps the shadows and
   loses the movement. */

@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after {
    transition-duration: 0.01ms !important;
    animation-duration: 0.01ms !important;
    animation-iteration-count: 1 !important;
    scroll-behavior: auto !important;
  }
  .bb-btn:hover:not(:disabled),
  .bb-btn:active:not(:disabled),
  .bb-tile:hover, .bb-tile:has(.bb-tile__link:focus-visible),
  .bb-tile__watch-btn:hover,
  .bb-chip:hover,
  .bb-amountbox__step:hover,
  .bb-amountbox__step:active { transform: none; }
}

/* Depth is a screen affordance. On paper the ink is the record and a printed
   drop shadow is just toner — every sheet is already physically a sheet. */
@media print {
  .bb-header, .bb-footer, .bb-tabs, .bb-btn, .bb-burger { display: none !important; }
  /* A printed chain of custody is the record, not the screen it was read on. */
  .bb-detail__side { display: none !important; }
  .bb-detail { display: block !important; }
  body { background: #fff; }
  .bb-card, .bb-evidence { break-inside: avoid; }
  *, *::before, *::after { box-shadow: none !important; transform: none !important; }
}

/* --- Facility mark and the "sold by" row ---------------------------------
   A facility logo is the only image on the site the seller controls, so it is
   boxed to a fixed square and object-fit: contain'ed rather than trusted to
   size itself. A tall logo, a wide logo and no logo at all occupy exactly the
   same space, which is what stops one facility's upload from reflowing a page
   that every other facility shares. Square corners: nothing here is round. */
.bb-facilitymark {
  flex-shrink: 0;
  display: block;
  object-fit: contain;
  width: 52px;
  height: 52px;
  padding: 5px;
  background: var(--bb-surface);
  border: var(--bb-border);
  box-shadow: var(--bb-sink);
}

/* The fallback is not an empty box. Initials in the same square keep the row
   identical whether or not this particular facility uploaded anything. */
.bb-facilitymark--initials {
  display: flex;
  align-items: center;
  justify-content: center;
  background: var(--bb-placeholder);
  color: var(--bb-ink-on-fill);
  font-family: var(--bb-font-mono);
  font-size: var(--bb-size-fine);
  letter-spacing: 0.04em;
  box-shadow: none;
}

/* A browse tile gives the mark a line rather than a row, so it is a shade
   smaller than the one on the detail page — but only a shade. At 26px a real
   logo is a smudge: most facility marks are wordmarks or a device with fine
   detail, and shrinking one to the height of the text beside it means nobody
   recognises the facility they dealt with last time, which is the whole reason
   it is on the grid. 44px keeps the detail legible at arm's length. */
.bb-facilitymark--sm { width: 44px; height: 44px; padding: 4px; }
/* Beside the auction heading: the seller's mark leads the page. */
.bb-facilitymark--lg { width: 88px; height: 88px; padding: 8px; }
.bb-facilitymark--lg.bb-facilitymark--initials { font-size: var(--bb-size-h1); }
@media (min-width: 1024px) { .bb-facilitymark--lg { width: 120px; height: 120px; padding: 10px; } }
.bb-facilitymark--sm.bb-facilitymark--initials { font-size: var(--bb-size-fine); }

.bb-soldby {
  display: flex;
  align-items: center;
  gap: var(--bb-space-3);
  background: var(--bb-surface-sunk);
  border: var(--bb-border);
  padding: var(--bb-space-3) var(--bb-space-4);
  border-radius: var(--bb-radius-md);
  box-shadow: var(--bb-sink);
}
.bb-soldby__who { min-width: 0; }

/* The clearance record, joined to the block above it rather than floating under
   it as a second card: the facility and what it is like to deal with are one
   statement, and a gap between them reads as two unrelated claims. */
.bb-soldby__more {
  background: var(--bb-surface-sunk);
  border: var(--bb-border);
  border-top: 0;
  padding: var(--bb-space-3) var(--bb-space-4);
  box-shadow: var(--bb-sink);
}
.bb-soldby__name {
  color: var(--bb-ink);
  font-weight: 600;
  overflow-wrap: anywhere;
}
h2.bb-soldby__name { margin: 0; }

/* Plain text whose typed line breaks are part of what it says. */
.bb-lines { white-space: pre-line; }

/* An auction description as the facility formatted it (Core\RichText).
   The editor makes every Enter a paragraph, so a paragraph is a line: no
   space between them, or one Enter reads as a blank line. A blank line the
   facility did type is stored as an empty paragraph and still shows. Set to
   read exactly like .bb-lines under it, the same text typed as plain lines. */
.bb-richtext p { margin: 0; }
.bb-richtext ul, .bb-richtext ol { margin: 0; padding-left: 1.4em; }
.bb-richtext li { margin: 2px 0; }
.bb-richtext strong { color: var(--bb-ink); font-weight: 600; }

/* The editor over a rich textarea (partials/field 'rich'). Quill's snow theme,
   brought onto the form's own surface and type. */
.bb-richedit .ql-toolbar.ql-snow { border: var(--bb-border); border-radius: var(--bb-radius-md) var(--bb-radius-md) 0 0; background: var(--bb-surface-sunk); }
.bb-richedit .ql-container.ql-snow { border: var(--bb-border); border-top: 0; border-radius: 0 0 var(--bb-radius-md) var(--bb-radius-md); background: var(--bb-surface); font: inherit; font-size: var(--bb-size-body); }
.bb-richedit .ql-editor { min-height: 140px; color: var(--bb-ink-body); }
.bb-richedit .ql-editor:focus-visible { outline: 2px solid var(--bb-brass); outline-offset: -2px; }

/* The Sold by tab (pages/auction/seller): the facility's address and how to
   reach it, under its mark. Label over value on a phone, side by side above. */
.bb-sellerinfo { display: grid; gap: var(--bb-space-3); margin: var(--bb-space-4) 0 0; }
.bb-sellerinfo__row { display: grid; gap: 2px; }
.bb-sellerinfo__row dt { margin: 0; }
.bb-sellerinfo__row dd { margin: 0; overflow-wrap: anywhere; }
@media (min-width: 700px) {
  .bb-sellerinfo__row { grid-template-columns: 180px minmax(0, 1fr); gap: var(--bb-space-4); align-items: baseline; }
}

/* A file input styled as far as a file input can be. The button inside it is
   the browser's and cannot be replaced, only made to sit quietly. */
.bb-input--file {
  padding: var(--bb-space-2);
  font-size: var(--bb-size-small);
  cursor: pointer;
}
.bb-input--file::file-selector-button {
  font: inherit;
  margin-right: var(--bb-space-3);
  padding: 6px var(--bb-space-3);
  background: var(--bb-surface);
  color: var(--bb-ink);
  border: 1px solid var(--bb-ink);
  cursor: pointer;
}

/* --- Console section bar -------------------------------------------------
   Inside /seller or /admin, the sections of that console. The header nav is
   the buyer's on every page in the product, so this strip is the only thing
   on a console page that says where you are and offers a way back that is not
   the account menu.

   Mobile first: the tabs scroll sideways inside their own track rather than
   wrapping into a block that pushes the page down, and the strip itself never
   widens the document. Square corners and a brass underline for the current
   tab, matching .bb-nav — a second navigation that looked like a different
   product would be its own kind of lost. */

.bb-console {
  background: var(--bb-surface-sunk);
  border-bottom: 1px solid var(--bb-line);
  box-shadow: var(--bb-lift-1);
}

.bb-console__crumb {
  padding-top: var(--bb-space-2);
}
.bb-console__crumb ol {
  list-style: none;
  margin: 0;
  padding: 0;
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  gap: 0 var(--bb-space-2);
  font-size: var(--bb-size-fine);
}
.bb-console__crumb li {
  display: flex;
  align-items: center;
  gap: var(--bb-space-2);
  min-width: 0;
}
/* The separator is drawn, not typed, so it is never read aloud as a word and
   never selected when the reader copies the trail. */
.bb-console__crumb li + li::before {
  content: "";
  width: 5px;
  height: 5px;
  border-right: 1px solid var(--bb-line-strong);
  border-top: 1px solid var(--bb-line-strong);
  transform: rotate(45deg);
  flex-shrink: 0;
  align-self: center;
}
/* A breadcrumb is how someone goes back, so it is a tap target and docs/UI.md §8
   applies to it like any other. The type stays at --bb-size-fine — the trail is
   meant to be quiet — and only the hit area grows to --bb-tap-min. The li is
   centred rather than baseline-aligned to match, which the drawn separator was
   already centring itself against. */
.bb-console__crumb a {
  color: var(--bb-ink-muted);
  display: inline-flex;
  align-items: center;
  min-height: var(--bb-tap-min);
}
.bb-console__crumb a:hover { color: var(--bb-ink); }
.bb-console__crumb [aria-current="page"] {
  color: var(--bb-ink);
  font-weight: 500;
  /* One auction title is any length and the bar is not. */
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.bb-console__tabs {
  display: flex;
  gap: var(--bb-space-5);
  overflow-x: auto;
  overflow-y: hidden;
  /* The track scrolls; the document does not. */
  scrollbar-width: none;
  -webkit-overflow-scrolling: touch;
}
.bb-console__tabs::-webkit-scrollbar { display: none; }
.bb-console__tabs a {
  display: flex;
  align-items: center;
  min-height: var(--bb-tap-min);
  padding: var(--bb-space-2) 0;
  border-bottom: 2px solid transparent;
  color: var(--bb-ink-muted);
  font-size: var(--bb-size-body);
  white-space: nowrap;
  flex-shrink: 0;
}
.bb-console__tabs a:hover { color: var(--bb-ink); text-decoration: none; }
.bb-console__tabs a[aria-current="page"] {
  color: var(--bb-ink);
  font-weight: 600;
  border-bottom-color: var(--bb-brass);
}

/* The second row: the children of the section you are standing in. Same scroll
   track as the row above it, because nine money screens do not fit in 390px
   either and the document must still never scroll sideways.

   Read as subordinate rather than as a second set of equals: one step down the
   type scale, a hairline above it, and the current item marked by a filled pill
   instead of the underline the row above uses. Two rows of identical underlined
   tabs would leave no way to tell which level you were reading. */
.bb-console__subtabs {
  display: flex;
  gap: var(--bb-space-3);
  padding-top: var(--bb-space-2);
  margin-top: var(--bb-space-1);
  border-top: var(--bb-border-hair);
  overflow-x: auto;
  overflow-y: hidden;
  scrollbar-width: none;
  -webkit-overflow-scrolling: touch;
}
.bb-console__subtabs::-webkit-scrollbar { display: none; }
.bb-console__subtabs a {
  display: flex;
  align-items: center;
  min-height: var(--bb-tap-min);
  padding: 0 var(--bb-space-3);
  border-radius: var(--bb-radius-pill);
  color: var(--bb-ink-muted);
  font-size: var(--bb-size-small);
  white-space: nowrap;
  flex-shrink: 0;
}
.bb-console__subtabs a:hover { color: var(--bb-ink); background: var(--bb-surface-sunk); text-decoration: none; }
.bb-console__subtabs a:focus-visible { outline: 2px solid var(--bb-brass); outline-offset: 2px; }
.bb-console__subtabs a[aria-current="page"] {
  color: var(--bb-ink);
  font-weight: 600;
  background: var(--bb-surface);
  box-shadow: var(--bb-lift-1);
}

@media print {
  .bb-console__tabs,
  .bb-console__subtabs { display: none; }
}

/* Capture set — take a picture, or upload one --------------------------------
   docs/UI.md §8: build the 390px layout first and widen. On a phone the two
   controls stack full width, because that is the hand holding the phone at a
   unit door and a half-width button is a missed tap. From 480px up there is
   room for them side by side, which is where "upload" starts being the likelier
   of the two.

   Each control is a <label> wrapping a hidden file input, so it must be given
   back everything .bb-btn assumes about a <button>: the flex centring for the
   icon, and a focus ring, since the thing that takes focus is the label and not
   the input inside it. */
.bb-captureset {
  /* Side by side at their own width, not stretched across the card (owner's
     request, 2026-10-19: no full-width buttons on the listing screen). */
  display: flex;
  flex-wrap: wrap;
  gap: var(--bb-space-2);
}

.bb-capture {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: var(--bb-space-2);
  /* .bb-btn sets a min-height; a label needs the box model spelled out or the
     icon and the text sit on different baselines. */
  line-height: 1.2;
  margin: 0;
}

/* The input is the mechanism, never the control. display:none would take it out
   of the accessibility tree and stop .click() working in some browsers, so it is
   hidden by size instead and the label carries the name. */
.bb-capture input[type="file"] {
  position: absolute;
  width: 1px;
  height: 1px;
  padding: 0;
  margin: -1px;
  overflow: hidden;
  clip: rect(0 0 0 0);
  clip-path: inset(50%);
  white-space: nowrap;
  border: 0;
}

/* The label is focusable via tabindex, so it needs the same ring every other
   control gets — without it the keyboard path is invisible. */
.bb-capture:focus-visible {
  outline: 2px solid var(--bb-ink);
  outline-offset: 2px;
}

/* Set while the file goes up. Not disabled — a label cannot be — so the pointer
   is refused and the intent is announced. */
.bb-capture[aria-disabled="true"] {
  opacity: 0.55;
  pointer-events: none;
}

/* A real photograph, in the box the placeholder was holding open. object-fit
   keeps a portrait phone shot from stretching in a landscape frame; the unit
   has to be the shape it actually is. */
.bb-photo {
  display: block;
  width: 100%;
  height: 100%;
  object-fit: cover;
  background: var(--bb-fill-muted, var(--bb-surface));
}
.bb-tile__photo > .bb-photo,
.bb-gallery__main > .bb-photo,
.bb-gallery__thumb > .bb-photo {
  position: absolute;
  inset: 0;
}

/* The seller's own contact sheet. Square, so a row of mixed orientations still
   scans as a column. */
.bb-thumb {
  display: block;
  width: 56px;
  height: 56px;
  object-fit: cover;
  border: var(--bb-border-hair);
  background: var(--bb-surface);
}
.bb-thumb--empty {
  background: repeating-linear-gradient(
    45deg,
    var(--bb-surface),
    var(--bb-surface) 5px,
    var(--bb-line, #ddd) 5px,
    var(--bb-line, #ddd) 6px
  );
}

/* Available to a screen reader, invisible on the page. Used where a cell is
   carrying meaning as a picture and needs to say the same thing in words. */
.bb-vh {
  position: absolute;
  width: 1px;
  height: 1px;
  padding: 0;
  margin: -1px;
  overflow: hidden;
  clip: rect(0 0 0 0);
  clip-path: inset(50%);
  white-space: nowrap;
  border: 0;
}

/* --- Submission checklist ------------------------------------------------
   Everything outstanding on a unit, at once, rather than one refusal per
   attempt. Done rows stay visible and legible — a checklist that hides what
   you have finished never lets you see that you are nearly there — so the
   done state is carried by the mark and the ink, not by removal.

   Green means verified and is not spent here on "form field completed": a
   finished checklist row uses the muted ink and a tick, and the card border
   is the only place the verify colour appears, once, when every row is done. */

.bb-checklist { list-style: none; margin: 0; padding: 0; }
.bb-checklist__item {
  display: flex;
  gap: var(--bb-space-3);
  align-items: baseline;
  padding: var(--bb-space-3) 0;
  border-bottom: var(--bb-border-hair);
}
.bb-checklist__item:last-child { border-bottom: none; }
.bb-checklist__mark {
  font-family: var(--bb-font-mono);
  font-size: var(--bb-size-small);
  line-height: 1;
  color: var(--bb-brass-text);
  flex-shrink: 0;
  width: 1em;
  text-align: center;
}
.bb-checklist__item--done .bb-checklist__mark { color: var(--bb-verify-text); }
.bb-checklist__what {
  font-size: var(--bb-size-body);
  color: var(--bb-ink);
  font-weight: 500;
}
.bb-checklist__item--done .bb-checklist__what {
  color: var(--bb-ink-muted);
  font-weight: 400;
}

/* An outstanding row links to the section that fixes it, so it is a navigation
   control and docs/UI.md §8's 44px floor applies — it was rendering at 18px,
   which is a target a thumb misses. The type size does not change; only the hit
   area. inline-flex rather than block so the link does not take the full width
   of the row and swallow taps meant for the row itself. */
.bb-checklist__what a {
  display: inline-flex;
  align-items: center;
  min-height: var(--bb-tap-min);
}

/* A standing register entry, marked where the reader will see it and linked to
   the note itself further down. Brass, not red: an open question of law is not
   an alarm, and red is reserved for time running out. */
.bb-gapmark {
  font-family: var(--bb-font-mono);
  font-size: var(--bb-size-meta);
  letter-spacing: 0.06em;
  color: var(--bb-brass-text);
  border-bottom: 1px solid var(--bb-brass-line);
  white-space: nowrap;
}
.bb-gapmark:hover { text-decoration: none; border-bottom-color: var(--bb-brass); }

/* ==========================================================================
   Shape
   --------------------------------------------------------------------------
   The rebrand's radius layer, grouped rather than scattered.

   Every rule below assigns a corner from the four steps in tokens.css and does
   nothing else. They are collected here on purpose: a radius system is only
   coherent if it can be read in one place, and thirty of these buried across
   1300 lines is how a fifth radius quietly appears. If a component needs a
   corner, it gets one HERE, from the scale — not inline, and not a new value.

   Nothing in this section may set colour, spacing or type.
   ========================================================================== */

/* Sheets --------------------------------------------------------------- */
.bb-tile,
.bb-drawer,
.bb-account__panel,
.bb-bell__panel,
.bb-console,
.bb-gallery__main,
.bb-compare__photo,
.bb-sigpad         { border-radius: var(--bb-radius-lg); }

/* The tile's top edge is square: the black header bar and the photograph
   under it run straight to the corners, and only the foot keeps the radius. */
.bb-tile           { overflow: hidden; border-top-left-radius: 0; border-top-right-radius: 0; }
.bb-gallery__main  { overflow: hidden; }

/* Inset and quiet panels ------------------------------------------------ */
.bb-sunk,
.bb-empty,
.bb-gapnote,
.bb-integrity,
.bb-capture,
.bb-flash__item,
.bb-flash__item--error,
.bb-flash__item--outbid,
.bb-panel__note,
.bb-checklist__item { border-radius: var(--bb-radius-md); }

/* A left-bordered slip keeps its bar square on the leading edge — rounding it
   turns a 3px rule into a lozenge and the meaning-carrying colour thins out. */
.bb-note,
.bb-flash__item {
  border-top-left-radius: 0;
  border-bottom-left-radius: 0;
}

/* Controls -------------------------------------------------------------- */
.bb-input,
.bb-select,
.bb-textarea,
.bb-amountbox__field,
.bb-amountbox__step,
.bb-burger,
.bb-account__trigger,
.bb-bell__trigger,
.bb-segment,
.bb-chip,
.bb-avatar,
.bb-facilitymark,
.bb-thumb,
.bb-gallery__thumb  { border-radius: var(--bb-radius-sm); }

/* The segment is one control, so only its outer corners round. */
.bb-segment { overflow: hidden; }
.bb-segment__option { border-radius: 0; }

/* Badges, chips and bars ------------------------------------------------- */
.bb-tile__badge,
.bb-tile__watch-btn,
.bb-gallery__count,
.bb-clock--chip,
.bb-tabs__count,
.bb-account__count,
.bb-bell__count,
.bb-pill--role,
.bb-notrec          { border-radius: var(--bb-radius-pill); }

.bb-track,
.bb-track__fill,
.bb-clock__track,
.bb-clock__fill     { border-radius: var(--bb-radius-pill); }

/* Media ------------------------------------------------------------------ */
.bb-photo,
.bb-thumb--empty    { border-radius: var(--bb-radius-sm); }

/* Display type ------------------------------------------------------------
   The old display face was a serif and needed no tracking. The grotesque that
   replaced it does, at every size it is set. */
.bb-display,
.bb-h1,
.bb-logo,
.bb-bidamount,
.bb-countdown,
.bb-clock__value,
.bb-integrity__value {
  font-family: var(--bb-font-display);
  font-weight: 700;
  letter-spacing: var(--bb-track-display);
}

.bb-h2, .bb-h3 { font-family: var(--bb-font-display); letter-spacing: -0.02em; }

/* Mono is exempt. It sets facts, and a fact set in the display face stops
   looking like something you can check. */
.bb-fact, .bb-mono, .bb-label, .bb-notrec { letter-spacing: revert; }
.bb-label { letter-spacing: var(--bb-track-label); }
.bb-fact, .bb-mono { font-family: var(--bb-font-mono); }

/* ==========================================================================
   Console nav
   --------------------------------------------------------------------------
   The second nav group in the header, pushed right by the spacer and sitting
   beside the avatar. Same anatomy as the primary nav, so it inherits the
   underline-on-current treatment; what it adds is a rule on its leading edge,
   because two runs of links with nothing between them read as one run.
   ========================================================================== */
.bb-nav--console {
  gap: var(--bb-space-4);
  padding-left: var(--bb-space-5);
  margin-left: var(--bb-space-2);
  border-left: 1px solid var(--bb-line);
}
@media (min-width: 1024px) { .bb-nav--console { display: flex; } }
.bb-nav--console a { white-space: nowrap; }

/* ==========================================================================
   Reputation (GAP-38)
   --------------------------------------------------------------------------
   A score, its stars and the arithmetic that produced it. The stars are drawn
   as a pair of stacked glyphs per star rather than as three separate
   characters, because a half star has no reliable glyph across the fonts this
   product ships: the filled one is clipped to a percentage width over the
   empty one, so a 3.5 sits exactly halfway and nothing depends on a codepoint
   the reader may not have.

   Everything meaningful is also text. The row carries role="img" and a label,
   the numeral sits beside it, and the breakdown underneath is a plain list —
   so nothing here is conveyed by colour or by shape alone (WCAG 1.4.1).
   ========================================================================== */
.bb-rep {
  display: flex;
  flex-direction: column;
  gap: var(--bb-space-2);
}

.bb-rep__head {
  display: flex;
  align-items: baseline;
  flex-wrap: wrap;
  gap: var(--bb-space-2) var(--bb-space-3);
}

.bb-rep__stars { display: inline-flex; gap: 2px; }

.bb-rep__star {
  position: relative;
  display: inline-block;
  width: 1em;
  height: 1em;
  line-height: 1;
  font-size: 17px;
}
.bb-rep__star-off,
.bb-rep__star-on {
  position: absolute;
  inset: 0;
  overflow: hidden;
  white-space: nowrap;
  line-height: 1;
}
.bb-rep__star-off { color: var(--bb-line-strong); }
.bb-rep__star-on  { color: var(--bb-amber); width: 0; }

.bb-rep__score {
  font-family: var(--bb-font-display);
  font-size: var(--bb-size-h1);
  font-weight: 700;
  letter-spacing: var(--bb-track-display);
  line-height: 1;
  color: var(--bb-ink);
}
.bb-rep__outof {
  font-size: var(--bb-size-small);
  font-weight: 400;
  color: var(--bb-ink-muted);
}

.bb-rep__band {
  font-family: var(--bb-font-mono);
  font-size: var(--bb-size-meta);
  text-transform: uppercase;
  letter-spacing: var(--bb-track-label);
  color: var(--bb-ink-body);
  background: var(--bb-surface-sunk);
  border: 1px solid var(--bb-line);
  border-radius: var(--bb-radius-pill);
  padding: 2px var(--bb-space-2);
}
/* No score is a statement, not an absence, so it is set like one. */
.bb-rep__band--none { color: var(--bb-ink-muted); }

.bb-rep__lines {
  list-style: none;
  margin: 0;
  padding: 0;
  display: flex;
  flex-direction: column;
  gap: var(--bb-space-1);
  border-top: 1px solid var(--bb-line-hair);
  padding-top: var(--bb-space-2);
}
.bb-rep__line {
  display: flex;
  align-items: baseline;
  gap: var(--bb-space-3);
  font-size: var(--bb-size-fine);
  color: var(--bb-ink-body);
}
.bb-rep__line-label { flex: 1 1 auto; min-width: 0; }
.bb-rep__line-count { flex: 0 0 auto; white-space: nowrap; }
/* Context lines are shown and deliberately not scored — see FacilityReputation. */
.bb-rep__line--context { color: var(--bb-ink-muted); font-style: italic; }

/* In a table cell the eyebrow and the explanatory line belong to the screen
   rather than to every row, so compact drops both and keeps the score. */
.bb-rep--compact { gap: 0; }
.bb-rep--compact .bb-rep__score { font-size: var(--bb-size-h3); }
.bb-rep--compact .bb-rep__star { font-size: var(--bb-size-small); }
.bb-rep--compact .bb-rep__head { gap: var(--bb-space-1) var(--bb-space-2); }

/* "Sold by <facility> · Follow" under the auction title (2026-10-10). Follow
   lives here, beside the name it acts on, rather than beside Watch, which is
   about the unit. A pill, not a .bb-btn, so it reads as part of the seller's
   line; ::after extends the hit area to the 44px tap minimum. */
.bb-sellerline { display: flex; flex-wrap: wrap; align-items: center; gap: var(--bb-space-2) var(--bb-space-3); }
.bb-sellerline form { display: contents; }
.bb-follow {
  position: relative;
  display: inline-flex; align-items: center; gap: 6px;
  min-height: 32px; padding: 0 12px;
  font: inherit; font-size: var(--bb-size-meta); font-weight: 600;
  color: var(--bb-ink); background: var(--bb-surface);
  border: 1px solid var(--bb-line-strong); border-radius: var(--bb-radius-pill);
  cursor: pointer; text-decoration: none;
}
.bb-follow::after { content: ''; position: absolute; inset: -6px 0; }
.bb-follow:hover { border-color: var(--bb-ink); }
.bb-follow:focus-visible { outline: 2px solid var(--bb-brass); outline-offset: 2px; }
.bb-follow--on { color: var(--bb-ink-muted); background: var(--bb-surface-sunk); }

/* A modal sheet (admin/seller) -------------------------------------------
   Deliberately NOT .bb-inspector, which is a full-bleed dark stage built for
   one photograph. This is a sheet of paper lifted off the page — the same
   vocabulary as .bb-card, one elevation higher — because what is on it is a
   form, and a form on a black ground reads as a different application.

   The sheet scrolls, the dialog does not: a worked example plus two fields
   plus a reason box is taller than a phone in landscape, and a dialog that
   clips is a button nobody can reach. */
.bb-modal {
  width: calc(100% - var(--bb-space-4) * 2);
  max-width: 620px;
  max-height: 88dvh;
  margin: auto;
  padding: 0;
  border: var(--bb-border);
  border-radius: var(--bb-radius-lg);
  background: var(--bb-surface);
  color: var(--bb-ink);
  box-shadow: var(--bb-lift-3);
  overflow: hidden;
}
/* Every value in this file resolves to a token (README). The scrim is the
   ink colour at reduced opacity rather than a hand-mixed black: a literal
   rgba() is how the old violet survived a palette change, because a hex grep
   does not find a colour hiding inside a function call. */
.bb-modal::backdrop { background: var(--bb-ink); opacity: 0.55; }
.bb-modal__sheet {
  max-height: 88dvh;
  overflow-y: auto;
  padding: var(--bb-space-4);
  overscroll-behavior: contain;
}
.bb-modal__close {
  flex-shrink: 0;
  width: var(--bb-tap-min); height: var(--bb-tap-min);
  border: 0; border-radius: var(--bb-radius-sm);
  background: transparent; color: var(--bb-ink-muted);
  font-size: var(--bb-size-h2); line-height: 1;
  cursor: pointer;
}
.bb-modal__close:hover { color: var(--bb-ink); background: var(--bb-surface-sunk); }
.bb-modal__close:focus-visible { outline: 2px solid var(--bb-brass); outline-offset: 2px; }

/* KPI block and gauge (admin dashboard, Phase 2) -------------------------
   Server-rendered, because /admin/finance/revenue's trend already is and
   because app.js is progressive enhancement — a canvas library would make the
   first block of the console blank with scripting off. The bars are .bb-bar,
   reused rather than reinvented; only the layout and the dial are new. */
.bb-kpi {
  display: grid;
  grid-template-columns: 1fr;
  gap: var(--bb-space-4);
}
@media (min-width: 768px)  { .bb-kpi { grid-template-columns: repeat(2, minmax(0, 1fr)); } }
@media (min-width: 1024px) { .bb-kpi { grid-template-columns: repeat(4, minmax(0, 1fr)); } }
.bb-kpi__fig { display: flex; flex-direction: column; gap: 6px; min-width: 0; }
.bb-kpi__fig .bb-bar { min-width: 0; }

/* A half-dial. The arc carries pathLength="100" so the dash is the percentage
   itself and nothing in the template does trigonometry. aria-hidden, with the
   figures beside it carrying the meaning in words — a dial a screen reader
   announces as a path has told nobody anything. */
.bb-gauge {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--bb-space-4);
}
.bb-gauge__dial { width: 160px; height: 90px; flex-shrink: 0; }
.bb-gauge__track,
.bb-gauge__value {
  fill: none;
  stroke-width: 10;
  stroke-linecap: round;
}
.bb-gauge__track { stroke: var(--bb-surface-sunk); }
.bb-gauge__value { stroke: var(--bb-brass); }
.bb-gauge__read { flex: 1 1 14rem; min-width: 0; display: flex; flex-direction: column; gap: 4px; }

/* A count that is a question, so it is a link. The cell keeps its size — the
   link fills it rather than shrinking to the digits, so the tap target is the
   whole figure and not the two pixels of a "2". */
.bb-integrity__link {
  display: inline-flex;
  align-items: center;
  min-height: var(--bb-tap-min);
  color: inherit;
  text-decoration: none;
}
.bb-integrity__link:hover { color: var(--bb-brass-text); text-decoration: underline; text-underline-offset: 4px; }
.bb-integrity__link:focus-visible { outline: 2px solid var(--bb-brass); outline-offset: 2px; }

/* Campaign previews (admin/marketing/campaign, 0055) --------------------
   Two iframes of the SAME URL at two widths, because the preview has to be
   the real document rendered by the browser rather than a drawing of it. The
   phone pane is fixed at 390px — the width docs/UI.md builds for first — so
   what is checked is the width that actually matters. */
.bb-preview {
  display: grid;
  grid-template-columns: 1fr;
  gap: var(--bb-space-4);
}
@media (min-width: 1024px) { .bb-preview { grid-template-columns: minmax(0, 1fr) 390px; } }
.bb-preview__pane { display: flex; flex-direction: column; gap: 6px; min-width: 0; }
.bb-preview__frame {
  width: 100%;
  height: 520px;
  border: var(--bb-border);
  border-radius: var(--bb-radius-md);
  background: var(--bb-surface);
}
.bb-preview__frame--phone { max-width: 390px; }

/* The vendored editor, given the platform's own field vocabulary so it does
   not read as a third-party control dropped into the page. Quill's own
   stylesheet supplies the rest; these are the four joins. */
.ql-toolbar.ql-snow,
.ql-container.ql-snow { border-color: var(--bb-line); }
.ql-toolbar.ql-snow { border-radius: var(--bb-radius-sm) var(--bb-radius-sm) 0 0; background: var(--bb-surface-sunk); }
.ql-container.ql-snow { border-radius: 0 0 var(--bb-radius-sm) var(--bb-radius-sm); background: var(--bb-surface); }
.ql-editor { min-height: 18rem; font-family: var(--bb-font-ui); font-size: var(--bb-size-body); color: var(--bb-ink-body); }
.ql-snow .ql-stroke { stroke: var(--bb-ink-muted); }
.ql-snow .ql-picker { color: var(--bb-ink-muted); }

/* --- Page header ----------------------------------------------------------
   One anatomy for the top of every page, rendered by partials/page_head.php:
   back link, eyebrow, title, lede, actions. Two title tiers only — .bb-display
   on public reading pages and the auction, .bb-h1 everywhere else. */
.bb-pagehead { display: flex; flex-direction: column; gap: var(--bb-space-2); }
.bb-pagehead__row { display: flex; flex-wrap: wrap; align-items: center; gap: var(--bb-space-3); }
.bb-pagehead__row > h1 { flex: 1 1 auto; min-width: 0; }
.bb-pagehead__back {
  display: inline-flex; align-items: center; gap: var(--bb-space-1);
  align-self: flex-start;
  min-height: var(--bb-tap-min);
  font-size: var(--bb-size-small); color: var(--bb-ink-muted);
}
.bb-pagehead__back:hover { color: var(--bb-ink); }
.bb-pagehead__lede {
  margin: 0; max-width: 68ch;
  font-size: var(--bb-size-body); line-height: var(--bb-leading-body);
  color: var(--bb-ink-muted); text-wrap: pretty;
}
.bb-pagehead__actions { display: flex; flex-wrap: wrap; gap: var(--bb-space-2); }

/* --- Utilities that replaced repeated inline styles ----------------------- */
/* Compound selectors on purpose: .bb-table td sets its own size and glass.css
   (loaded after this file) its own card border, and the inline styles these
   replace beat both. */
.bb-meta, .bb-table td.bb-meta { font-size: var(--bb-size-meta); }
.bb-status { font-weight: 600; }
.bb-label.bb-label--alert { color: var(--bb-alert); }
.bb-label.bb-label--brass { color: var(--bb-brass-text); }
.bb-card.bb-card--alert { border-color: var(--bb-alert-line); }
.bb-card.bb-card--brass { border-color: var(--bb-brass-line); }
.bb-input--sm { min-height: 36px; padding: 6px 10px; font-size: var(--bb-size-fine); }
.bb-narrow { width: 100%; max-width: 640px; margin-inline: auto; }
.bb-narrow--sm { max-width: 480px; }
.bb-narrow--lg { max-width: 720px; }
.bb-chip--inline { width: auto; padding: 0 14px; }
.bb-tile__amount { font-size: var(--bb-size-h1); }
.bb-progressrow__count { font-size: var(--bb-size-h2); }
.bb-drawer__signout { margin: 12px 0 16px; }
