/* The base browser, on top of style.css. style.css is the store's own, copied byte for byte out of
   tebex/paynow/template, so the tokens, the buttons, the bar, the footer and the link accounts
   panel are the store's; only what the browser adds is here, in the same look: dark only, the
   white button for the one thing to do, cards on the border with no drop, no blue and no glow.

   Everything on a page is scoped under .bb because style.css already owns .card, .badge, .chip and
   .note, and a page here means something different by each of them. The overlays sit outside the
   page, so they hang off their ids instead. */

.site-content.bb { position: relative; padding-bottom: 8px; }

/* the store's square grid across the top, as behind its lander, drawn by the store's own main.js
   with the lander's mask: brightest top right and clear where the heading sits. edge to edge from
   the bar down, thinning out as the page gets busy. behind everything but the page itself */
.bb .top-fx {
  --tail: calc(0.55 * clamp(320px, 40vw, 520px));
  position: absolute; z-index: -1; top: 0; left: 50%; display: block;
  width: 100vw; height: calc(clamp(320px, 40vw, 520px) + var(--tail)); margin-left: -50vw; pointer-events: none;
  -webkit-mask-image: var(--fade-out);
  mask-image: var(--fade-out);
}

/* ---------------------------------------------------------------- browse */

/* each tab has its own blurb, one line to four, so the lead keeps the room of the longest or the
   tabs and everything under them jump on every switch */
.bb .browse .page-lead { min-height: 2lh; }

/* the tabs on the left, Import a base at the far end of their row, under them on a phone */
.bb .tabs-row { display: flex; flex-wrap: wrap; align-items: center; justify-content: space-between; gap: 12px; margin-bottom: 28px; }
/* the four tabs as the store's switch over its plans: a track with the picked tab raised on a thumb,
   which browse.js lays onto the tab and slides across to the next one */
.bb .tabs {
  position: relative; isolation: isolate; display: flex; gap: 4px; width: fit-content; max-width: 100%;
  padding: 4px; border: 1px solid var(--border); border-radius: 12px; background: var(--surface);
  overflow-x: auto; scrollbar-width: none;
}
.bb .tabs::-webkit-scrollbar { display: none; }
.bb .tabs a {
  flex: none; padding: 6px 14px; border-radius: var(--r);
  color: var(--muted); font-size: 14px; font-weight: 500; line-height: 20px; text-align: center; white-space: nowrap;
  transition: color var(--motion);
}
.bb .tabs a:hover, .bb .tabs a.on { color: var(--ink); }
/* inside the track, or overflow cuts the ring's top off */
.bb .tabs a:focus-visible { outline-offset: -2px; }
.bb .tabs::before {
  content: ""; position: absolute; z-index: -1; top: 4px; bottom: 4px; left: 0;
  width: var(--tab-w, 0); border-radius: var(--r); background: var(--surface-3);
  translate: var(--tab-x, 0) 0;
}
/* only once it has been placed, or the first paint slides it in from the left */
.bb .tabs.slide::before { transition: translate 0.35s var(--ease), width 0.35s var(--ease); }

/* the FILTERS panel down the left, as the in-game browser has it, on a card */
.bb .browse-body { display: grid; grid-template-columns: 288px minmax(0, 1fr); gap: 24px; align-items: start; }
.bb .filters {
  position: sticky; top: calc(var(--nav-h) + 16px);
  max-height: calc(100vh - var(--nav-h) - 32px); overflow-y: auto; scrollbar-width: thin;
  padding: 16px 16px 20px; border: 1px solid var(--border); border-radius: var(--r-card); background: var(--surface);
}
.bb .filters-head { display: flex; align-items: center; justify-content: space-between; margin-bottom: 4px; }
.bb .filters-head h2 { margin: 0; color: var(--ink); font-size: 16px; font-weight: 600; line-height: 22px; }
.bb .filters:not(.active) #clear { visibility: hidden; }
.bb #filters-done { display: none; margin-left: 6px; }
.bb .filters-head #clear { margin-left: auto; }
.bb .fsec { padding-top: 16px; margin-top: 16px; border-top: 1px solid var(--border); }
.bb .fsec h3 { margin: 0 0 12px; color: var(--ink); font-size: 14px; font-weight: 500; line-height: 20px; }
.bb .fnote { margin: -8px 0 12px; color: var(--muted); font-size: 13px; line-height: 18px; }
.bb .trow + .trow { margin-top: 12px; }
.bb .trow > span { display: block; margin-bottom: 6px; color: var(--faint); font-size: 12px; font-weight: 500; line-height: 16px; }
.bb .res { display: grid; grid-template-columns: 24px minmax(0, 1fr) auto; align-items: center; gap: 4px 10px; }
.bb .res + .res { margin-top: 14px; }
.bb .res img { width: 24px; height: 24px; }
.bb .res span { color: var(--muted); font-size: 14px; line-height: 20px; }
.bb .res output { width: 76px; text-align: right; color: var(--faint); font-size: 12px; font-weight: 500; line-height: 16px; font-variant-numeric: tabular-nums; }
.bb .res.set output { color: var(--ink); }

/* a 4px rail with the range filled up to a 16px thumb on the page colour, ringed in the fill. the
   fill runs to the thumb's centre, which travels 16px short of the rail, hence the calc. grey while
   it says Any, white once it caps something, so a slider nobody has touched does not look on */
.bb .res input[type=range] {
  --p: 1;
  --fill: var(--surface-4);
  grid-column: 1 / -1; width: 100%; height: 16px; margin: 0; padding: 0;
  border: 0; border-radius: 0; background: transparent; cursor: pointer;
  -webkit-appearance: none; appearance: none;
}
.bb .res.set input[type=range] { --fill: var(--ink); }
.bb .res input[type=range]::-webkit-slider-runnable-track {
  height: 4px; border-radius: 2px;
  background: linear-gradient(var(--fill), var(--fill)) left / calc(8px + (100% - 16px) * var(--p)) 100% no-repeat, var(--surface-3);
}
.bb .res input[type=range]::-moz-range-track { height: 4px; border-radius: 2px; background: var(--surface-3); }
.bb .res input[type=range]::-moz-range-progress { height: 4px; border-radius: 2px; background: var(--fill); }
.bb .res input[type=range]::-webkit-slider-thumb {
  -webkit-appearance: none; width: 16px; height: 16px; margin-top: -6px;
  border: 1px solid var(--fill); border-radius: 50%; background: var(--bg);
}
.bb .res input[type=range]::-moz-range-thumb {
  box-sizing: border-box; width: 16px; height: 16px;
  border: 1px solid var(--fill); border-radius: 50%; background: var(--bg);
}
.bb .res input[type=range]:focus-visible { outline: none; }
.bb .res input[type=range]:focus-visible::-webkit-slider-thumb { box-shadow: 0 0 0 2px var(--bg), 0 0 0 4px var(--ink); }
.bb .res input[type=range]:focus-visible::-moz-range-thumb { box-shadow: 0 0 0 2px var(--bg), 0 0 0 4px var(--ink); }
.bb .filters-toggle { display: none; min-height: 36px; }

.bb .controls { display: flex; flex-wrap: wrap; gap: 10px; align-items: center; margin-bottom: 16px; }
/* the store's field with the magnifier in front: the label is the box, the input inside it bare */
.bb .search {
  flex: 1 1 320px; display: flex; align-items: center; gap: 8px;
  height: 36px; padding: 0 12px; border: 1px solid var(--border-strong); border-radius: var(--r);
  background: var(--wash); cursor: text;
}
.bb .search:focus-within { outline: 2px solid var(--ink); outline-offset: 1px; }
.bb .search input { flex: 1; min-width: 0; height: auto; padding: 0; border: 0; outline: 0; background: transparent; color: var(--ink); font: inherit; font-size: 14px; }
.bb .search input:focus-visible { outline: none; }
.bb .search input::-webkit-search-cancel-button { filter: invert(1) opacity(0.5); }
.bb .search svg { flex: none; width: 16px; height: 16px; fill: none; stroke: var(--faint); stroke-width: 1.5; stroke-linecap: round; }
@media (max-width: 640px) {
  .bb .search input { font-size: 16px; }
}

/* one width for the select and the dropdown common.js swaps in for it, so the swap moves nothing */
.bb .pick { flex: none; width: 176px; height: 36px; }

/* the dropdown: its trigger is the store's field with a chevron, its list a raised card like the
   bar's panels, rows 6px round with the picked one on the fill */
.bb .dd { position: relative; }
.bb .dd-btn {
  display: flex; align-items: center; justify-content: space-between; gap: 8px;
  width: 100%; height: 100%; padding: 0 10px 0 12px;
  border: 1px solid var(--border-strong); border-radius: var(--r); background: var(--wash);
  color: var(--ink); font: inherit; font-size: 14px; font-weight: 400; line-height: 20px; text-align: left; cursor: pointer;
  transition: background-color var(--motion);
}
.bb .dd-btn:hover { background: var(--wash-hover); }
.bb .dd-text { min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.bb .dd-chev { flex: none; width: 10px; height: 6px; fill: none; stroke: var(--muted); stroke-width: 1.5; stroke-linecap: round; stroke-linejoin: round; transition: transform 0.2s var(--std); }
.bb .dd-btn[aria-expanded="true"] .dd-chev { transform: rotate(180deg); }
.bb .dd-list {
  position: absolute; top: calc(100% + 4px); left: 0; right: 0; z-index: 30;
  display: flex; flex-direction: column;
  margin: 0; padding: 4px; list-style: none;
  border: 1px solid var(--border-strong); border-radius: var(--r-lg); background: var(--surface-2);
  transform-origin: top left;
  transition: opacity 0.15s var(--std), translate 0.15s var(--std), display 0.15s allow-discrete;
}
.bb .dd-list[hidden] { display: none; opacity: 0; translate: 0 -4px; }
@starting-style {
  .bb .dd-list:not([hidden]) { opacity: 0; translate: 0 -4px; }
}
.bb .dd-list li { display: flex; align-items: center; gap: 8px; padding: 6px 8px; border-radius: var(--r-sm); color: var(--text); font-size: 14px; line-height: 20px; cursor: pointer; transition: background-color var(--motion); }
.bb .dd-list li.active { background: var(--surface-3); }
.bb .dd-list li[aria-selected="true"] { color: var(--ink); font-weight: 500; }

/* the store's filter pills: a hairline round pill, picked onto the fill */
.bb .tags { display: flex; flex-wrap: wrap; gap: 6px; }
.bb .tag {
  display: inline-flex; align-items: center; height: 26px; padding: 0 10px;
  border: 1px solid var(--border); border-radius: var(--r-pill);
  background: transparent; color: var(--muted);
  font: inherit; font-size: 12px; font-weight: 500; line-height: 16px; white-space: nowrap; cursor: pointer;
  transition: color var(--motion), background-color var(--motion), border-color var(--motion);
}
.bb .tag:hover { color: var(--ink); }
.bb .tag.on { border-color: var(--border-strong); background: var(--surface-3); color: var(--ink); }

/* a screen tall at least, so the footer never shows under a half-loaded or short grid and then
   gets shoved off when the cards land */
.bb .grid { display: grid; gap: 16px; grid-template-columns: repeat(auto-fill, minmax(250px, 1fr)); align-content: start; min-height: 100vh; margin-bottom: 48px; }

/* style.css's .card is the store's padded card, so everything it sets is set again here. a base is
   a card with the picture on top and the caption under it */
.bb .card {
  display: flex; flex-direction: column; padding: 0; overflow: hidden;
  border: 1px solid var(--border); border-radius: var(--r-card); background: var(--surface); color: inherit;
  transition: background-color var(--motion);
}
.bb a.card:hover { background: var(--surface-2); }

.bb .shot { position: relative; aspect-ratio: 16 / 10; overflow: hidden; border-bottom: 1px solid var(--border); background: var(--surface-2); }
.bb .shot img { display: block; width: 100%; height: 100%; object-fit: cover; }
.bb .shot .none { display: grid; place-items: center; height: 100%; color: var(--faint); font-size: 14px; }
/* height and object-fit spelled out: the rule above sets both, and a footprint left at 100%
   tall is a narrow slice down the middle of it */
.bb .shot .fp {
  position: absolute; right: 10px; bottom: 10px;
  width: 60px; height: 60px; object-fit: contain;
  padding: 3px; border-radius: var(--r);
  /* the footprint is drawn on black, so it wants a dark tile under it rather than a light one,
     which turns it into a black square sitting on the photo */
  background: var(--surface-2);
}
/* over a picture, so solid: the store's white for featured, the page colour under the red for one
   nobody can find in the grid */
.bb .badge { position: absolute; left: 10px; top: 10px; background: var(--pop); color: var(--pop-ink); }
.bb .badge.draft { background: rgba(9, 9, 11, 0.85); color: var(--danger-text); }

.bb .card .body { display: flex; flex-direction: column; gap: 4px; flex: 1; padding: 14px 20px 18px; }
.bb .card .body h3 { margin: 0; font-size: 16px; font-weight: 500; line-height: 22px; }
.bb a.card:hover .body h3 { text-decoration: underline; text-decoration-color: var(--faint); text-underline-offset: 3px; }
.bb .by { color: var(--muted); font-size: 14px; line-height: 20px; }
.bb .desc {
  margin: 2px 0 0; color: var(--muted); font-size: 14px; line-height: 20px;
  display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; overflow: hidden;
}
.bb .facts { display: flex; flex-wrap: wrap; align-items: center; gap: 4px 14px; margin-top: auto; padding-top: 10px; color: var(--muted); font-size: 13px; line-height: 18px; }
.bb .facts b { color: var(--ink); font-weight: 500; font-variant-numeric: tabular-nums; }
.bb .code { color: var(--faint); font-family: var(--mono); font-size: 12px; letter-spacing: 0.06em; }
.bb .facts .code { margin-left: auto; }

/* back and next at either end, the pages between them quiet until hovered and the current one on
   the fill, as the store's switch picks a tab. on a phone back and next drop to their arrows */
.bb .pager { display: flex; justify-content: space-between; align-items: center; gap: 8px; padding-bottom: 56px; }
.bb .pager .btn svg { flex: none; width: 16px; height: 16px; fill: none; stroke: currentColor; stroke-width: 1.25; stroke-linecap: round; stroke-linejoin: round; }
.bb .pg-list { display: flex; gap: 2px; }
.bb .pg, .bb .pg-gap {
  display: inline-grid; place-items: center; min-width: 32px; height: 32px; padding: 0 4px; border-radius: var(--r);
  font: inherit; font-size: 14px; font-weight: 500; line-height: 20px; font-variant-numeric: tabular-nums;
}
.bb .pg { border: 0; background: none; color: var(--muted); cursor: pointer; transition: background-color var(--motion), color var(--motion); }
.bb .pg:hover { background: var(--surface-3); color: var(--ink); }
.bb .pg[aria-current] { background: var(--surface-3); color: var(--ink); cursor: default; }
.bb .pg-gap { color: var(--faint); }
@media (max-width: 640px) {
  .bb .pager .btn { width: 32px; padding: 0; }
  .bb .pager .btn span { position: absolute; width: 1px; height: 1px; overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; }
}
/* nothing here: the store's empty cart, a dashed hairline round the words */
.bb .empty { padding: 64px 24px; border: 1px dashed var(--border-strong); border-radius: var(--r-card); background: rgba(255, 255, 255, 0.012); color: var(--muted); font-size: 14px; line-height: 20px; text-align: center; }
.bb .empty-art { display: block; width: 120px; height: 86px; margin: 0 auto 20px; overflow: visible; fill: none; stroke-width: 1; }
.bb .empty-art .plot { stroke: var(--border-strong); }
.bb .empty-art .cell { fill: var(--wash); stroke: var(--faint); stroke-dasharray: 2 3; }
.bb .empty-art .drop { stroke: var(--faint); stroke-dasharray: 2 3; }
.bb .empty-art .cube path { stroke: var(--bg); stroke-linejoin: round; }
.bb .empty-art .t { fill: var(--ink); }
.bb .empty-art .l { fill: var(--muted); }
.bb .empty-art .r { fill: #5d5d66; }
.bb .empty-art .cube { animation: bb-hover 2.4s ease-in-out infinite; }
@keyframes bb-hover { 50% { transform: translateY(-5px); } }
.bb .empty h2 { margin: 0 0 4px; color: var(--ink); font-family: var(--display); font-size: 20px; font-weight: 600; line-height: 26px; }

/* no room for a column beside the grid, so the panel waits behind a Filters button and opens over
   the page below the nav. opening it in the flow shoved the whole grid down by its height */
@media (max-width: 900px) {
  .bb .browse-body { grid-template-columns: minmax(0, 1fr); }
  .bb .filters {
    display: none; position: fixed; inset: var(--nav-h) 0 0; z-index: 90;
    max-height: none; border-width: 1px 0 0; border-radius: 0; overscroll-behavior: contain;
  }
  .bb .filters.open { display: block; }
  .bb .filters-toggle, .bb #filters-done { display: inline-flex; }
  /* wide enough for "Filters (9)", so a count arriving does not push the sort along */
  .bb .filters-toggle { min-width: 120px; }
}

@media (max-width: 640px) {
  /* the four tabs across the screen, as the store's account tabs go on a phone */
  .bb .tabs { display: grid; grid-auto-flow: column; grid-auto-columns: minmax(0, 1fr); width: 100%; }
  .bb .tabs a { padding: 7px 6px; }
  .bb .pick, .bb .filters-toggle { flex: 1 1 0; }
  .bb .grid { grid-template-columns: minmax(0, 1fr); }
  .bb .browse .page-lead { min-height: 4lh; }
}

.bb .skel { background: var(--surface-3); animation: bb-pulse 1.6s ease-in-out infinite; }
@keyframes bb-pulse { 50% { opacity: 0.5; } }

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

/* base.js draws the whole page in one go once the base arrives; until then the footer sat right
   under the crumbs and got thrown off the screen */
.bb #page { min-height: 100vh; }

/* the way back: muted links going to the ink on hover, a chevron between, and the page you are on
   in the ink */
.bb .crumbs {
  display: flex; align-items: center; gap: 8px; margin: clamp(40px, 5vw, 64px) 0 20px;
  color: var(--ink); font-size: 14px; line-height: 20px; white-space: nowrap;
}
.bb .crumbs a { flex: none; color: var(--muted); transition: color var(--motion); }
.bb .crumbs a:hover { color: var(--ink); }
.bb .crumbs svg { flex: none; width: 14px; height: 14px; fill: none; stroke: var(--faint); stroke-width: 1.5; stroke-linecap: round; stroke-linejoin: round; }
.bb .crumbs span { min-width: 0; overflow: hidden; text-overflow: ellipsis; }

/* the heading across the top, the code and cost beside the pictures, then details, the owner's
   panel and the feedback each the full width under them. base.js builds a left column and a
   side column, so both are display: contents and their children take areas of this grid. the
   side panels carry no class, so each is known by what it holds. rows are spaced by margins,
   so a missing description, owner panel or thread leaves an empty row costing nothing */
.bb .base {
  display: grid; grid-template-columns: minmax(0, 1fr) 340px; column-gap: 16px;
  grid-template-areas: "title title" "by by" "desc desc" "shots code" "shots cost" "facts facts" "owner owner" "notes notes";
  align-items: start; padding-bottom: 56px;
}
.bb .base-main, .bb .base .side { display: contents; }
.bb .base-main > .title { grid-area: title; }
.bb .base-main > p:nth-of-type(1) { grid-area: by; }
.bb .base-main > p:nth-of-type(2) { grid-area: desc; }
/* the pictures and the build cost end on one line (jack, 2026-10-09): the cost card stretches down
   to the pictures with its Full list kept at the foot, and the pictures down to the side panels with
   what they hold centred */
.bb .base-main > .shots { grid-area: shots; align-self: stretch; display: flex; flex-direction: column; justify-content: center; }
.bb .side > .panel { margin-top: 16px; }
.bb .side > .panel:has(> .codebox) { grid-area: code; margin-top: 40px; }
.bb .side > .panel:has(> .costs) { grid-area: cost; align-self: stretch; display: flex; flex-direction: column; }
.bb .side > .panel:has(> .bill[hidden]) > .costs { margin-bottom: auto; }
.bb .side > .panel:has(> .rows) { grid-area: facts; }
.bb .side > .panel:not(:has(> .codebox, > .costs, > .rows)) { grid-area: owner; }
.bb .base .notes { grid-area: notes; }
/* with the full list open, details move up under the pictures and the list runs on down beside them
   (jack, 2026-10-09). the list's last stretch is a 1fr row only it crosses: an auto row it shared
   with details took a share of its height and pushed details 270 to 500px down. the cost card keeps
   its own height while open, or it jumped to the foot of details as the list began to unroll */
@media (min-width: 901px) {
  .bb .base:has(.bill:not([hidden])) {
    grid-template-areas: "title title" "by by" "desc desc" "shots code" "shots cost" "facts cost" ". cost" "owner owner" "notes notes";
    grid-template-rows: repeat(6, auto) 1fr auto auto;
  }
  .bb .base:has(.bill:not([hidden])) .side > .panel:has(> .costs) { align-self: start; }
}
@media (max-width: 900px) {
  .bb .base { grid-template-columns: minmax(0, 1fr); grid-template-areas: "title" "by" "desc" "shots" "code" "cost" "facts" "owner" "notes"; }
  .bb .side > .panel:has(> .codebox) { margin-top: 16px; }
}

/* the store's page title */
.bb .title { display: flex; flex-wrap: wrap; align-items: center; gap: 14px; }
.bb .title h1 { margin: 0; font-family: var(--display); font-size: 48px; font-weight: 600; line-height: 1.04; letter-spacing: -0.02em; text-wrap: balance; }
@media (max-width: 640px) {
  .bb .title h1 { font-size: 36px; }
}
/* the store's badges that say something: green for featured, red for unlisted */
.bb .pill { display: inline-flex; align-items: center; height: 22px; padding: 0 9px; border-radius: var(--r-pill); background: var(--success-soft); color: var(--success); font-size: 12px; font-weight: 500; line-height: 16px; white-space: nowrap; }
.bb .pill.draft { background: var(--danger-soft); color: var(--danger-text); }
.bb .base-main > p { font-size: 16px; line-height: 24px; text-wrap: pretty; }
.bb .base-main > p a { color: var(--ink); text-decoration: underline; text-decoration-color: var(--faint); text-underline-offset: 3px; transition: text-decoration-color var(--motion); }
.bb .base-main > p a:hover { text-decoration-color: currentColor; }

/* a card 12px in, the picture 10px round in it and the strip under it */
.bb .shots { margin-top: 40px; padding: 12px; border: 1px solid var(--border); border-radius: var(--r-card); background: var(--surface); }
.bb .stage { position: relative; aspect-ratio: 16 / 9; overflow: hidden; border-radius: var(--r-lg); background: var(--surface-2); }
.bb .stage img { display: block; width: 100%; height: 100%; object-fit: contain; }
.bb .thumbs { display: flex; flex-wrap: wrap; gap: 8px; padding: 12px 0 0; }
.bb .thumbs button { width: 104px; aspect-ratio: 16 / 9; padding: 0; overflow: hidden; border: 1px solid var(--border); border-radius: var(--r); background: var(--surface-2); cursor: pointer; opacity: 0.6; transition: opacity var(--motion); }
.bb .thumbs button:hover, .bb .thumbs button.on { opacity: 1; }
.bb .thumbs button.on { border-color: var(--muted); }
.bb .thumbs img { display: block; width: 100%; height: 100%; object-fit: contain; }

/* the side panels are the store's cards: a muted label over what they hold, the tiles inside them
   a step up */
.bb .panel { padding: 20px; border: 1px solid var(--border); border-radius: var(--r-card); background: var(--surface); }
.bb .panel h2 { margin: 0 0 12px; color: var(--muted); font-size: 13px; font-weight: 500; line-height: 18px; }
.bb .rows { display: grid; }
.bb .rows .row { display: flex; justify-content: space-between; gap: 12px; padding: 10px 0; font-size: 14px; line-height: 20px; }
.bb .rows .row:first-child { padding-top: 0; }
.bb .rows .row span { color: var(--muted); }
.bb .rows .row b { color: var(--ink); font-weight: 500; font-variant-numeric: tabular-nums; }
.bb .actions { display: grid; gap: 8px; }
/* the owner's buttons sit in a row at their own widths: stretched across the full-width panel they
   were three long bars */
.bb .side > .panel:not(:has(> .codebox)) .actions { display: flex; flex-wrap: wrap; }

/* details run the full width, so each fact is a tile, name over value */
.bb .side > .panel:has(> .rows) .rows { grid-template-columns: repeat(auto-fit, minmax(120px, 1fr)); gap: 8px; }
.bb .side > .panel:has(> .rows) .rows .row { flex-direction: column; align-items: flex-start; justify-content: flex-start; gap: 2px; padding: 10px 12px; border-radius: var(--r); background: var(--surface-2); }
.bb .side > .panel:has(> .rows) .rows .row b { font-size: 16px; line-height: 22px; }
.bb .panel p { margin: 0; }
.bb .panel p a { color: var(--ink); text-decoration: underline; text-decoration-color: var(--faint); text-underline-offset: 3px; }
.bb .panel p a:hover { text-decoration-color: currentColor; }

/* the code in the mono on a tile, its copy button at the end */
.bb .codebox { display: flex; align-items: center; justify-content: space-between; gap: 10px; padding: 6px 6px 6px 14px; border-radius: var(--r); background: var(--surface-2); }
.bb .codebox .v { color: var(--ink); font-family: var(--mono); font-size: 22px; letter-spacing: 0.18em; }
.bb .codebox + .actions { margin-top: 12px; }

/* the four costs come in wood, stone, frags, hqm order from base.js, so the plugin's own
   resource icons can be hung on them by position without touching the script */
.bb .costs { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 8px; }
.bb .cost { position: relative; min-height: 56px; padding: 9px 10px 9px 48px; border-radius: var(--r); background: var(--surface-2); }
.bb .cost::before { content: ""; position: absolute; left: 10px; top: 50%; width: 28px; height: 28px; transform: translateY(-50%); background: center / contain no-repeat; }
.bb .cost:nth-child(1)::before { background-image: url("/assets/media/res-wood.png"); }
.bb .cost:nth-child(2)::before { background-image: url("/assets/media/res-stone.png"); }
.bb .cost:nth-child(3)::before { background-image: url("/assets/media/res-frags.png"); }
.bb .cost:nth-child(4)::before { background-image: url("/assets/media/res-hqm.png"); }
.bb .cost .k { overflow: hidden; color: var(--muted); font-size: 12px; font-weight: 500; line-height: 16px; text-overflow: ellipsis; white-space: nowrap; }
.bb .cost .v { color: var(--ink); font-size: 16px; font-weight: 500; line-height: 22px; font-variant-numeric: tabular-nums; }

/* the full list the costs open into, GET COST's three sections: a grade a line on a hairline with
   its pieces at the end, the materials and deployables as tiles below */
.bb .bill-btn { margin-top: 12px; }
.bb .bill-btn svg { width: 10px; height: 6px; fill: none; stroke: currentColor; stroke-width: 1.5; stroke-linecap: round; stroke-linejoin: round; transition: transform 0.2s var(--std); }
.bb .bill-btn[aria-expanded="true"] svg { transform: rotate(180deg); }
.bb .bill { display: grid; gap: 16px; margin-top: 16px; }
.bb .bill[hidden] { display: none; }
.bb .bill h3 { display: flex; justify-content: space-between; gap: 12px; margin: 0 0 4px; color: var(--muted); font-size: 13px; font-weight: 500; line-height: 18px; }
.bb .bill .line { display: flex; align-items: center; justify-content: space-between; gap: 12px; padding: 7px 0; border-top: 1px solid var(--border); font-size: 14px; line-height: 20px; }
.bb .bill .line span { min-width: 0; color: var(--text); overflow-wrap: anywhere; }
.bb .bill .line b { flex: none; color: var(--ink); font-weight: 500; font-variant-numeric: tabular-nums; }
.bb .bill-note { color: var(--muted); font-size: 14px; line-height: 20px; }
/* the materials and deployables as tiles, the icon a step up from the panel with its count in the
   bottom corner, shadowed so it reads over a light icon. the name is the store's tooltip, on hover
   or a tap, made only while it shows: there at rest it still widened a phone's page, and centred on
   the last tile of a row it ran past the edge, so base.js slides it back by --tip-x */
.bb .tiles { display: grid; grid-template-columns: repeat(auto-fill, minmax(52px, 1fr)); gap: 6px; padding-top: 8px; border-top: 1px solid var(--border); }
.bb .tile { position: relative; display: grid; place-items: center; aspect-ratio: 1; border-radius: var(--r); background: var(--surface-2); outline: none; }
.bb .tile:focus-visible { box-shadow: 0 0 0 2px var(--ink); }
.bb .tile img { width: 72%; height: 72%; object-fit: contain; }
.bb .tile span { padding: 4px; overflow: hidden; color: var(--muted); font-size: 10px; line-height: 12px; text-align: center; overflow-wrap: anywhere; }
.bb .tile b { position: absolute; right: 5px; bottom: 3px; color: var(--ink); font-size: 12px; font-weight: 500; line-height: 16px; font-variant-numeric: tabular-nums; text-shadow: 0 1px 2px #000; }
.bb .tile::before, .bb .tile::after { content: none; }
.bb .tile:hover::before, .bb .tile:focus::before { content: ""; }
.bb .tile:hover::after, .bb .tile:focus::after { content: attr(data-tip); translate: calc(-50% + var(--tip-x, 0px)) 0; }
.bb .tile:focus::before, .bb .tile:focus::after { opacity: 1; scale: 1; }

/* a base's tags: a hairline pill that does nothing, unlike the store's copy chip */
.bb .chips { display: flex; flex-wrap: wrap; gap: 6px; }
.bb .chip { height: 24px; padding: 0 10px; border: 1px solid var(--border); border-radius: var(--r-pill); background: transparent; color: var(--muted); font-family: var(--font); font-size: 12px; font-weight: 500; line-height: 16px; cursor: default; transition: none; }
.bb .chip:hover { background: transparent; }

/* the feedback thread: a card of tiles 8px apart, the name over the comment with the time small
   and grey beside it */
.bb .notes { display: grid; gap: 8px; margin-top: 16px; padding: 20px; border: 1px solid var(--border); border-radius: var(--r-card); background: var(--surface); }
.bb .notes h2 { margin: 0 0 4px; color: var(--ink); font-size: 16px; font-weight: 600; line-height: 22px; }
.bb .note { padding: 12px; border-radius: var(--r); background: var(--surface-2); color: var(--text); }
.bb .note .who { color: var(--ink); font-size: 14px; font-weight: 500; line-height: 20px; }
.bb .note .at { margin-left: 6px; color: var(--faint); font-size: 12px; font-weight: 500; line-height: 16px; }
.bb .note p { margin: 2px 0 0; color: var(--text); font-size: 14px; line-height: 20px; white-space: pre-wrap; overflow-wrap: anywhere; }

/* ---------------------------------------------------------------- 3d */

#viewer { position: fixed; inset: 0; z-index: 200; display: none; background: #325b9c; }
#viewer.on { display: block; }
/* style.css keeps a stable scrollbar gutter, which stays reserved under overflow hidden and
   leaves a strip of page down the right of the viewer wherever scrollbars take up room */
html.viewing { overflow: hidden; scrollbar-gutter: auto; }
#viewer canvas { display: block; width: 100%; height: 100%; touch-action: none; }

#viewer .hud {
  position: absolute; right: 0; top: 0;
  display: flex; align-items: center; gap: 8px; padding: 14px 18px;
  color: #fff; font-size: 14px;
}
/* the store's buttons in white over the render's blue, the one switched on in the page colour */
#viewer .hud button {
  min-height: 32px; padding: 0 12px; border: 0; border-radius: var(--r); cursor: pointer;
  background: rgba(250, 250, 250, 0.92); color: var(--pop-ink);
  font: inherit; font-size: 14px; font-weight: 500; line-height: 20px;
  transition: background-color var(--motion), color var(--motion);
}
#viewer .hud button:hover { background: var(--pop); }
#viewer .hud button[aria-pressed="true"] { background: var(--bg); color: var(--ink); }
#viewer .hud button[aria-pressed="true"]:hover { background: var(--surface-3); }

/* the cut sliders, floors over rows at one length, top left across from the buttons on a chip of
   their white. shadcn's slider in its light colours: a near-black fill to the thumb over a grey rail,
   the thumb white in a thin dark ring. the name, the rail and the reading sit in fixed columns, so a
   reading changing width never moves a rail under a dragging thumb */
#viewer .cuts {
  position: absolute; left: 18px; top: 14px; display: grid; gap: 2px; padding: 6px 12px;
  border-radius: var(--r); background: rgba(250, 250, 250, 0.92); color: var(--pop-ink);
  font-size: 13px; font-weight: 500; line-height: 16px;
}
#viewer .cuts:not(:has(.cut:not([hidden]))) { display: none; }
/* narrower, the buttons reach this corner: under them instead */
@media (max-width: 900px) { #viewer .cuts { top: 58px; } }
#viewer .cut { display: grid; grid-template-columns: 42px min(200px, calc(100vw - 174px)) 52px; align-items: center; gap: 10px; height: 28px; }
#viewer .cut[hidden] { display: none; }
#viewer .cut label { color: rgba(9, 9, 11, 0.55); }
#viewer .cut output { text-align: right; white-space: nowrap; font-variant-numeric: tabular-nums; }
#viewer .cut input { display: block; width: 100%; height: 20px; margin: 0; background: transparent; cursor: pointer; appearance: none; -webkit-appearance: none; }
#viewer .cut input:focus { outline: none; }
#viewer .cut input::-webkit-slider-runnable-track {
  height: 6px; border-radius: 3px;
  background: linear-gradient(to right, var(--pop-ink) var(--fill, 100%), rgba(9, 9, 11, 0.14) 0);
}
#viewer .cut input::-webkit-slider-thumb {
  box-sizing: border-box; width: 16px; height: 16px; margin-top: -5px;
  border: 1px solid rgba(9, 9, 11, 0.5); border-radius: 50%; background: #fff;
  box-shadow: 0 1px 2px rgba(0, 0, 0, 0.15); appearance: none; -webkit-appearance: none;
}
#viewer .cut input::-moz-range-track { height: 6px; border-radius: 3px; background: rgba(9, 9, 11, 0.14); }
#viewer .cut input::-moz-range-progress { height: 6px; border-radius: 3px; background: var(--pop-ink); }
#viewer .cut input::-moz-range-thumb {
  box-sizing: border-box; width: 16px; height: 16px;
  border: 1px solid rgba(9, 9, 11, 0.5); border-radius: 50%; background: #fff; box-shadow: 0 1px 2px rgba(0, 0, 0, 0.15);
}
#viewer .cut input:focus-visible::-webkit-slider-thumb { box-shadow: 0 0 0 3px rgba(9, 9, 11, 0.18); }
#viewer .cut input:focus-visible::-moz-range-thumb { box-shadow: 0 0 0 3px rgba(9, 9, 11, 0.18); }

#viewer .loading { position: absolute; inset: 0; display: grid; place-items: center; overflow: hidden; background: var(--bg); color: var(--ink); text-align: center; }
/* the words sit on a card over the squares */
#viewer .loading canvas { position: absolute; top: 0; left: 0; }
#viewer .loading .panel { position: relative; max-width: min(420px, calc(100vw - 32px)); padding: 16px 24px; border: 1px solid var(--border-strong); border-radius: var(--r-lg); background: var(--surface-2); font-size: 14px; line-height: 20px; }

/* ---------------------------------------------------------------- dialogs */

/* the store's gift dialog: 16px round, a step up from the page inside the stronger hairline, the
   page dimmed to 60% behind it, rising 6px into place as it fades over 180ms and back the same way.
   the close is an X in the corner, the muted grey going to the ink on hover */
#edit, #kill, #import {
  width: min(512px, calc(100vw - 32px)); max-height: calc(100dvh - 32px); padding: 0;
  border: 1px solid var(--border-strong); border-radius: 16px;
  background: var(--surface-2); color: var(--text);
  transition: opacity 0.18s var(--std), translate 0.18s var(--std), overlay 0.18s allow-discrete, display 0.18s allow-discrete;
}
#edit:not([open]), #kill:not([open]), #import:not([open]) { opacity: 0; translate: 0 6px; }
@starting-style {
  #edit[open], #kill[open], #import[open] { opacity: 0; translate: 0 6px; }
}
#edit::backdrop, #kill::backdrop, #import::backdrop { background: rgba(0, 0, 0, 0.6); transition: opacity 0.18s var(--std), overlay 0.18s allow-discrete, display 0.18s allow-discrete; }
#edit:not([open])::backdrop, #kill:not([open])::backdrop, #import:not([open])::backdrop { opacity: 0; }
@starting-style {
  #edit[open]::backdrop, #kill[open]::backdrop, #import[open]::backdrop { opacity: 0; }
}
#edit .dlg-head, #kill .dlg-head, #import .dlg-head { display: flex; align-items: center; justify-content: space-between; gap: 12px; padding: 24px 24px 12px; }
#edit .dlg-head h2, #kill .dlg-head h2, #import .dlg-head h2 { margin: 0; font-family: var(--display); font-size: 21px; font-weight: 600; line-height: 26px; letter-spacing: -0.01em; }
#edit .dlg-x, #kill .dlg-x, #import .dlg-x {
  flex: none; display: grid; place-items: center; width: 28px; height: 28px; padding: 0;
  border: 0; border-radius: var(--r-sm); background: none; color: var(--muted); cursor: pointer;
  transition: background-color var(--motion), color var(--motion);
}
#edit .dlg-x:hover, #kill .dlg-x:hover, #import .dlg-x:hover { background: var(--surface-3); color: var(--ink); }
#edit .dlg-x svg, #kill .dlg-x svg, #import .dlg-x svg { width: 16px; height: 16px; fill: none; stroke: currentColor; stroke-width: 1.5; stroke-linecap: round; }
#edit .dlg-body, #kill .dlg-body, #import .dlg-body { display: grid; gap: 16px; padding: 0 24px 16px; }
#edit .dlg-foot, #kill .dlg-foot, #import .dlg-foot { display: flex; justify-content: flex-end; gap: 8px; padding: 4px 24px 24px; }
#edit .dlg-note, #kill .dlg-note, #import .dlg-note { margin: 0; color: var(--muted); font-size: 14px; line-height: 20px; }
/* the store's field: its label, the box and the count 6px apart, the count in muted 12px */
#edit .field, #kill .field, #import .field { display: grid; gap: 6px; margin: 0; }
#edit .field label, #kill .field label, #import .field label { color: var(--ink); font-size: 14px; font-weight: 500; line-height: 20px; }
#edit .field .count, #kill .field .count { justify-self: end; color: var(--faint); font-size: 12px; font-weight: 500; line-height: 16px; font-variant-numeric: tabular-nums; }
#kill .code-input { font-family: var(--mono); letter-spacing: 0.14em; text-transform: uppercase; }
#edit .tags { display: flex; flex-wrap: wrap; gap: 6px; }
#edit .tag {
  height: 26px; padding: 0 10px; border: 1px solid var(--border); border-radius: var(--r-pill);
  background: transparent; color: var(--muted); font: inherit; font-size: 12px; font-weight: 500; line-height: 16px; cursor: pointer;
  transition: color var(--motion), background-color var(--motion), border-color var(--motion);
}
#edit .tag:hover { color: var(--ink); }
#edit .tag.on { border-color: var(--border-strong); background: var(--surface-3); color: var(--ink); }
#edit .err, #kill .err, #import .err { min-height: 18px; margin: 0; color: var(--danger-text); font-size: 13px; line-height: 18px; }
/* the import (2026-10-14): a drop zone in the site's own square grid until a file is picked, then
   one card, an icon tile, the name and a line under it, for the file and for where the import
   stands, its bar along the card's foot */
.bb .import-open { height: 36px; }
/* .btn's own display beats the hidden attribute, which is all that keeps this off a site with no rcon */
.bb .import-open[hidden] { display: none; }
#import .dlg-head { align-items: flex-start; }
#import .dlg-sub { margin: 4px 0 0; color: var(--muted); font-size: 14px; line-height: 20px; }
#import .imp-pick { position: relative; display: grid; gap: 16px; }
/* a .btn or a grid's own display beats the hidden attribute, and the dialog hides both as it goes */
#import [hidden] { display: none !important; }
#import .err:empty { display: none; }
/* out of sight but laid out: a label opens no picker for an input that is display none */
#import .imp-input { position: absolute; top: 0; left: 0; width: 1px; height: 1px; opacity: 0; pointer-events: none; }
#import .imp-drop {
  position: relative; isolation: isolate; display: grid; justify-items: center; gap: 2px;
  padding: 28px 16px 24px; border: 1px dashed var(--border-strong); border-radius: var(--r-lg);
  background: var(--wash); text-align: center; cursor: pointer;
  transition: background-color var(--motion);
}
/* the hero's 12px squares 16px apart, fading out from the middle, lit up while a file is over it */
#import .imp-drop::before {
  content: ""; position: absolute; inset: 0; z-index: -1; border-radius: inherit;
  background: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='16' height='16'%3E%3Crect x='2' y='2' width='12' height='12' rx='2' fill='%23fff'/%3E%3C/svg%3E") center / 16px 16px;
  opacity: 0.035; transition: opacity var(--motion);
  -webkit-mask: radial-gradient(closest-side, #000 25%, transparent); mask: radial-gradient(closest-side, #000 25%, transparent);
}
#import .imp-drop:hover, #import .imp-drop.over { background-color: var(--wash-hover); }
#import .imp-drop.over::before { opacity: 0.09; }
#import .imp-input:focus-visible + .imp-drop { outline: 2px solid var(--ink); outline-offset: 2px; }
#import .imp-drop .imp-tile { margin-bottom: 10px; }
#import .imp-drop-say { color: var(--ink); font-size: 14px; font-weight: 500; line-height: 20px; }
#import .imp-drop-say u { text-decoration-color: var(--border-strong); text-underline-offset: 3px; transition: text-decoration-color var(--motion); }
#import .imp-drop:hover .imp-drop-say u { text-decoration-color: var(--ink); }
#import .imp-drop-hint { color: var(--muted); font-size: 13px; line-height: 18px; }
#import .imp-tile {
  display: grid; place-items: center; flex: none; width: 36px; height: 36px;
  border: 1px solid var(--border); border-radius: var(--r); background: var(--surface-3); color: var(--ink);
}
#import .imp-tile svg { width: 18px; height: 18px; fill: none; stroke: currentColor; stroke-width: 1.75; stroke-linecap: round; stroke-linejoin: round; }
#import .imp-card {
  position: relative; display: flex; align-items: center; gap: 12px; padding: 12px;
  border: 1px solid var(--border); border-radius: var(--r-lg); background: var(--wash); overflow: hidden;
}
#import .imp-input:focus-visible ~ .imp-card .btn { outline: 2px solid var(--ink); outline-offset: 2px; }
#import .imp-what { display: grid; flex: 1; min-width: 0; }
#import .imp-what b { overflow: hidden; color: var(--ink); font-size: 14px; font-weight: 500; line-height: 20px; white-space: nowrap; text-overflow: ellipsis; }
#import .imp-what span { color: var(--muted); font-size: 13px; line-height: 18px; font-variant-numeric: tabular-nums; }
#import .imp-pct { color: var(--ink); font-size: 13px; font-weight: 500; line-height: 20px; font-variant-numeric: tabular-nums; }
#import .imp-job.is-ok .imp-tile { border-color: transparent; background: var(--success-soft); color: var(--success); }
#import .imp-job.is-bad .imp-tile { border-color: transparent; background: var(--danger-soft); color: var(--danger-text); }
#import .imp-job.is-bad .imp-what span { color: var(--text); }
#import .imp-bar { position: absolute; right: 0; bottom: 0; left: 0; height: 3px; background: var(--surface-3); overflow: hidden; }
#import .imp-bar i { display: block; width: 0; height: 100%; background: var(--ink); transition: width 0.2s var(--std); }
/* no share to give while the server reads or builds, so a piece of it slides across instead */
#import .imp-bar.busy i { width: 30%; animation: imp-busy 1.2s var(--std) infinite; }
@keyframes imp-busy { from { translate: -100% 0; } to { translate: 340% 0; } }
/* the buttons fill the foot, Cancel a quarter and the other three, either one alone the lot (jack,
   2026-10-14). a bare text Cancel stretched that wide reads as nothing, so it takes the grey fill */
#import .dlg-foot { display: grid; grid-template-columns: 1fr 3fr; }
#import .dlg-foot .btn { min-height: 36px; }
#import #i-cancel { grid-column: 1; padding: 0 12px; background: var(--surface-3); color: var(--ink); }
#import #i-cancel:hover { background: var(--surface-4); text-decoration: none; }
#import #i-go { grid-column: 2; }
#import .dlg-foot:has(#i-cancel[hidden]) #i-go, #import .dlg-foot:has(#i-go[hidden]) #i-cancel { grid-column: 1 / -1; }
@media (prefers-reduced-motion: reduce) {
  #import .imp-bar.busy i { width: 100%; opacity: 0.35; animation: none; }
}

/* style.css animates its own toast in on insert; this one lives in the page from the start, as the
   store's does, rising in over 250ms and sinking out over 180ms */
#toast {
  opacity: 0; pointer-events: none; animation: none;
  transform: translateY(8px) scale(0.96);
  transition: opacity 0.18s cubic-bezier(0, 0, 0.58, 1), transform 0.18s cubic-bezier(0, 0, 0.58, 1);
}
#toast.on { opacity: 1; transform: none; transition-duration: 0.25s; }

@media (prefers-reduced-motion: reduce) {
  .bb .skel, .bb .empty-art .cube { animation: none; }
}
