:root {
  --ink: #ffffff;
  --ink-dim: rgba(255, 255, 255, 0.55);
  --ink-faint: rgba(255, 255, 255, 0.34);
  --accent: #e8a848;
  --accent-soft: rgba(232, 168, 72, 0.12);

  /* one glass material, used by every piece of chrome */
  --glass-bg: rgba(15, 17, 20, 0.62);
  --glass-brd: rgba(255, 255, 255, 0.10);
  --glass-shadow: 0 8px 32px rgba(0, 0, 0, 0.25), inset 0 1px 0 rgba(255, 255, 255, 0.08);
  --radius: 16px;

  /* shared layout rhythm */
  --gutter: 30px;
  --panel-w: 320px;
  --bar-h: 56px;
  --bar-bottom: 28px;

  font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", "Inter", Roboto, sans-serif;
}
* { margin: 0; padding: 0; box-sizing: border-box; }
html, body { height: 100%; overflow: hidden; background: #0d0f13; }
#app { position: fixed; inset: 0; }
#scene { position: absolute; inset: 0; width: 100%; height: 100%; display: block; }
#ui-root { position: absolute; inset: 0; pointer-events: none; color: var(--ink); }
#ui-root > * { pointer-events: auto; }

.glass {
  background: var(--glass-bg);
  backdrop-filter: blur(24px) saturate(150%);
  -webkit-backdrop-filter: blur(24px) saturate(150%);
  border: 1px solid var(--glass-brd);
  box-shadow: var(--glass-shadow);
  border-radius: var(--radius);
}

/* ---- wordmark ----------------------------------------------------------- */
#ui-root > .top-scrim { pointer-events: none; }
/* ---- top bar ------------------------------------------------------------ */
/* Spans the viewport, so it never has to move when a sheet opens — which is
   what the old floating bottom bar and top-right tool cluster both had to do. */
.topbar {
  position: absolute; top: 0; left: 0; right: 0; height: var(--topbar-h);
  display: flex; align-items: center; gap: 14px;
  padding: 0 var(--gutter); border-radius: 0;
  border-left: 0; border-right: 0; border-top: 0;
  pointer-events: auto;
}
.wordmark { user-select: none; margin-right: auto; }
/* The wordmark is the way home now (ui.js wires it), so it has to behave like
   one: a pointer, a hover state, and a focus ring for the keyboard. */
.wordmark.is-link { cursor: pointer; border-radius: 4px; padding: 4px 6px; margin-left: -6px; transition: opacity 160ms ease; }
.wordmark.is-link:hover { opacity: .62; }
.wordmark.is-link:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
/* §14.4: 0.34em tracking at weight 800 is the loudest dated signal in a UI. */
.wordmark h1 { font-size: 17px; letter-spacing: 0.10em; font-weight: 700; color: #fff; }
.top-right { display: flex; align-items: center; gap: 10px; }

/* Save rig — present exactly when there is something unsaved (REDESIGN §6). */
.save-btn {
  height: 36px; padding: 0 16px; border-radius: 999px; border: none; cursor: pointer;
  background: var(--accent); color: #1a1207;
  font: inherit; font-size: 13px; font-weight: 700; white-space: nowrap;
  transition: filter .15s, background .2s, color .2s;
}
.save-btn[hidden], .save-dock[hidden] { display: none; }
.save-btn:hover { filter: brightness(1.06); }
/* Saved and unchanged: still there, no longer shouting. */
.save-btn.is-done {
  background: rgba(255, 255, 255, 0.09); color: rgba(255, 255, 255, 0.72);
  box-shadow: inset 0 0 0 1px rgba(255, 255, 255, 0.12);
}

/* `display` here outranks the UA stylesheet's `[hidden] { display: none }`, so
   without the second rule a build with accounts switched off draws an empty
   29px pill in the top bar — the label is hidden on a phone and only the
   status dot is left. */
.acct-btn[hidden] { display: none; }
.acct-btn {
  display: inline-flex; align-items: center; gap: 8px;
  height: 36px; padding: 0 14px; border-radius: 999px; cursor: pointer;
  border: 1px solid rgba(255, 255, 255, 0.12); background: rgba(255, 255, 255, 0.06);
  color: rgba(255, 255, 255, 0.86); font: inherit; font-size: 13px; font-weight: 600;
  max-width: 260px; white-space: nowrap;
}
.acct-btn:hover { background: rgba(255, 255, 255, 0.12); color: #fff; }
.acct-label { overflow: hidden; text-overflow: ellipsis; }
/* Signed out the dot is hollow, signed in it is lit. One glyph, two states,
   no words spent saying which. */
.acct-dot {
  width: 7px; height: 7px; border-radius: 50%; flex: none;
  box-shadow: inset 0 0 0 1.5px rgba(255, 255, 255, 0.45);
}
.acct-btn.is-in .acct-dot { background: #6fd39b; box-shadow: none; }

/* ---- left panel --------------------------------------------------------- */
.panel {
  position: absolute; top: calc(var(--topbar-h) + 12px); left: var(--gutter);
  width: var(--panel-w); max-height: calc(100vh - var(--topbar-h) - 36px);
  display: flex; flex-direction: column; overflow: hidden;
}

/* ---- sections of the left column ---------------------------------------- */
.nav-sec { display: flex; flex-direction: column; min-height: 0; }
.nav-sec + .nav-sec { border-top: 1px solid rgba(255, 255, 255, 0.08); }
.nav-sec-head { display: flex; align-items: center; gap: 8px; padding: 14px 16px 8px; }
.nav-sec-title {
  font-size: 10.5px; font-weight: 700; letter-spacing: 0.09em; text-transform: uppercase;
  color: rgba(255, 255, 255, 0.5); margin: 0; padding: 14px 16px 8px;
}
.nav-sec-head .nav-sec-title { padding: 0; margin-right: auto; }
.nav-sec-head .panel-count { padding: 0; }
.nav-actions { display: flex; gap: 8px; padding: 4px 12px 14px; }
.nav-actions > * { flex: 1; }
.appearance { display: flex; flex-direction: column; gap: 12px; padding: 0 16px 14px; }
.paint-group, .bottle-group { display: flex; align-items: center; gap: 10px; }
.group-label, .bottle-group .lbl {
  font-size: 10px; font-weight: 700; letter-spacing: 0.1em; text-transform: uppercase;
  color: rgba(255, 255, 255, 0.42); width: 46px; flex: none;
}
.panel-head {
  padding: 15px 16px 9px; display: flex; align-items: baseline; gap: 7px;
  font-size: 10.5px; letter-spacing: 0.18em; text-transform: uppercase; font-weight: 600;
}
.panel-title { color: rgba(255, 255, 255, 0.62); }
.panel-count { color: rgba(255, 255, 255, 0.55); font-weight: 600; letter-spacing: 0; }
/* The separator the parentheses used to be. */
.panel-count::before { content: '·'; margin: 0 6px 0 2px; opacity: .5; }

.bag-list {
  overflow-y: auto; padding: 2px 0 8px; scrollbar-width: thin;
  scrollbar-color: rgba(255, 255, 255, 0.18) transparent;
}
.bag-list::-webkit-scrollbar { width: 6px; }
.bag-list::-webkit-scrollbar-thumb { background: rgba(255, 255, 255, 0.16); border-radius: 3px; }
.bag-list::-webkit-scrollbar-track { background: transparent; }
.bag-list.fade-bottom {
  mask-image: linear-gradient(#000 calc(100% - 34px), transparent);
  -webkit-mask-image: linear-gradient(#000 calc(100% - 34px), transparent);
}

.empty-state {
  padding: 14px 8px 18px; font-size: 12.5px; line-height: 1.7;
  color: rgba(255, 255, 255, 0.6);
}
.empty-state span { color: var(--ink-faint); font-size: 11.5px; }

.bag-card {
  display: flex; align-items: center; gap: 11px;
  min-height: 56px; padding: 9px 10px; border-radius: 12px;
  border: 1px solid transparent; cursor: pointer;
  transition: background 0.15s, border-color 0.15s;
}
.bag-card:hover { background: rgba(255, 255, 255, 0.06); border-color: rgba(255, 255, 255, 0.09); }
.bag-sw {
  width: 34px; height: 34px; border-radius: 10px; flex: none;
  box-shadow: inset 0 0 0 1px rgba(255, 255, 255, 0.20), 0 2px 6px rgba(0, 0, 0, 0.35);
}
.bag-txt { flex: 1; min-width: 0; }
/* brand + mount: sentence case, wraps rather than truncating the brand */
.bag-meta {
  display: flex; flex-wrap: wrap; gap: 0 5px; align-items: baseline;
  font-size: 11.5px; color: rgba(255, 255, 255, 0.55); line-height: 1.35;
}
.bag-meta .sep, .bag-meta .m { color: rgba(255, 255, 255, 0.38); }
.bag-name-row { display: flex; align-items: baseline; gap: 8px; margin-top: 2px; }
.bag-model {
  flex: 1; min-width: 0;
  font-size: 14px; font-weight: 600; color: #fff; line-height: 1.28;
  display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical;
  line-clamp: 2; overflow: hidden;
}
.bag-liters {
  flex: none; font-size: 12.5px; font-weight: 600; color: rgba(255, 255, 255, 0.72);
  font-variant-numeric: tabular-nums;
}
.bag-size { font-weight: 500; color: rgba(255, 255, 255, 0.5); }
.bag-acts { flex: none; display: flex; gap: 4px; }
.bag-act {
  width: 22px; height: 22px; border: none; border-radius: 7px;
  background: rgba(255, 255, 255, 0.08); color: rgba(255, 255, 255, 0.6);
  font-size: 11px; line-height: 1; cursor: pointer;
  opacity: 0; transition: opacity 0.14s, background 0.14s, color 0.14s;
}
.bag-card:hover .bag-act, .bag-act:focus-visible { opacity: 1; }
.bag-act:hover { background: rgba(255, 255, 255, 0.2); color: #fff; }
.bag-act.rm:hover { background: rgba(226, 88, 72, 0.42); color: #fff; }
.bag-act.buy {
  display: inline-flex; align-items: center; justify-content: center;
  text-decoration: none; color: rgba(255, 255, 255, 0.55);
}
.bag-act.buy:hover { background: rgba(232, 168, 72, 0.22); color: var(--accent); }

.panel-foot { padding: 0 12px 10px; }
.kit-summary {
  display: none; align-items: baseline; justify-content: space-between;
  padding: 11px 2px 12px; border-top: 1px solid rgba(255, 255, 255, 0.09);
}
.panel-foot.has-kit .kit-summary { display: flex; }
.kit-label {
  font-size: 10px; font-weight: 600; letter-spacing: 0.14em; text-transform: uppercase;
  color: rgba(255, 255, 255, 0.45);
}
.kit-total { font-size: 14px; font-weight: 600; color: #fff; font-variant-numeric: tabular-nums; }

.share-kit {
  display: none; width: 100%; height: 32px; margin-top: 7px;
  align-items: center; justify-content: center; gap: 6px;
  border: 1px solid rgba(255, 255, 255, 0.10); border-radius: 9px;
  background: transparent; cursor: pointer;
  font-size: 11.5px; font-weight: 600; letter-spacing: 0.02em;
  color: rgba(255, 255, 255, 0.5); transition: color 0.15s, border-color 0.15s, background 0.15s;
}
.panel-foot.has-kit .share-kit { display: flex; }
.share-kit .ic { font-size: 12.5px; line-height: 1; }
.share-kit:hover {
  color: rgba(255, 255, 255, 0.9); border-color: rgba(255, 255, 255, 0.22);
  background: rgba(255, 255, 255, 0.06);
}
.share-kit.done { color: var(--accent); border-color: rgba(232, 168, 72, 0.4); }

.add-bag {
  width: 100%; height: 40px; border-radius: 10px; cursor: pointer;
  border: 1px solid rgba(255, 255, 255, 0.18); background: rgba(255, 255, 255, 0.10);
  color: #fff; font-size: 12.5px; font-weight: 600; letter-spacing: 0.03em;
  transition: border-color 0.15s, background 0.15s;
}
.add-bag .plus { font-size: 15px; font-weight: 500; margin-right: 3px; }
.add-bag:hover { background: rgba(255, 255, 255, 0.16); border-color: rgba(255, 255, 255, 0.3); }

/* ---- one bottom dock: actions left, environments right ------------------ */
/* `.bottom-bar` and `.dock` are gone — REDESIGN.md: the bike was boxed in on
   four sides by a left panel, a right sheet, a bottom bar and a top-right tool
   cluster. Two surfaces now: this top bar, and the left column. */
.envs { display: flex; align-items: center; gap: 3px; }

.btn {
  border: none; cursor: pointer; border-radius: 999px; white-space: nowrap; flex: none;
  padding: 10px 15px; font-size: 12.5px; font-weight: 700; letter-spacing: 0.03em;
  background: rgba(255, 255, 255, 0.09); color: var(--ink);
  transition: background 0.15s, transform 0.1s;
}
.btn:hover { background: rgba(255, 255, 255, 0.16); }
.btn:active { transform: scale(0.97); }
/* the one filled-amber element in the whole UI */
.btn.primary { background: var(--accent); color: #1a1207; }
.btn.primary:hover { background: #f4bb60; }
.btn .bolt { margin-right: 2px; color: #2b2113; }
.btn.ghost { background: transparent; color: rgba(255, 255, 255, 0.6); }
.btn.ghost:hover { background: rgba(226, 88, 72, 0.16); color: #ff9d92; }
.btn.ghost.confirm { background: rgba(226, 88, 72, 0.28); color: #ffc4bd; }

.divider { width: 1px; height: 28px; background: rgba(255, 255, 255, 0.10); flex: none; }
/* label sits inline with its swatches, matching every other group in the bar */

.group-label {
  font-size: 9.5px; font-weight: 600; letter-spacing: 0.18em; text-transform: uppercase;
  color: rgba(255, 255, 255, 0.4); line-height: 1;
}
.paints { display: flex; gap: 6px; }
.paint {
  width: 26px; height: 26px; border-radius: 50%; border: none; cursor: pointer; padding: 0;
  box-shadow: inset 0 0 0 1px rgba(255, 255, 255, 0.18);
  transition: transform 0.12s, box-shadow 0.12s;
}
.paint:hover { transform: scale(1.1); }
.paint.on {
  box-shadow: 0 0 0 2px #0f1114, 0 0 0 4px #fff;
}

.env-chip {
  display: flex; align-items: center; gap: 7px;
  border: none; cursor: pointer; border-radius: 10px;
  padding: 0 8px 0 4px; height: 44px; font-size: 11px; font-weight: 500;
  background: transparent; color: var(--ink-dim);
  transition: color 0.15s;
}
.env-thumb {
  position: relative; overflow: hidden; flex: none;
  width: 56px; height: 36px; border-radius: 8px;
  border: 2px solid transparent;
  box-shadow: inset 0 0 0 1px rgba(255, 255, 255, 0.14);
  transition: border-color 0.15s;
}
/* the mountain thumbnail gets a peak so it can't be mistaken for the lake */
.env-thumb .peak {
  position: absolute; left: 50%; bottom: 0; transform: translateX(-50%);
  width: 30px; height: 17px; background: rgba(255, 255, 255, 0.82);
  clip-path: polygon(50% 0, 100% 100%, 0 100%);
}
.env-label { white-space: nowrap; }
.env-chip:hover { color: var(--ink); }
.env-chip:hover .env-thumb { border-color: rgba(255, 255, 255, 0.35); }
.env-chip.on { color: #fff; }
.env-chip.on .env-label { font-weight: 600; }
.env-chip.on .env-thumb { border-color: #fff; }

/* ---- top-right camera tools --------------------------------------------- */
/* In the bar now, not floating over the top-right corner of the scene. */
.viewtools { display: flex; align-items: center; padding: 2px; gap: 2px;
  border-radius: 999px; background: rgba(255, 255, 255, 0.06); }
.tool-btn {
  width: 40px; height: 40px; border: none; border-radius: 12px; cursor: pointer;
  background: transparent; color: rgba(255, 255, 255, 0.85);
  font-size: 20px; line-height: 1; transition: background 0.15s, color 0.15s;
}
.tool-btn:hover { background: rgba(255, 255, 255, 0.09); color: #fff; }
.tool-btn.on, .tool-btn.on:hover { background: var(--accent); color: #1a1207; }
.tool-divider { width: 1px; height: 22px; background: rgba(255, 255, 255, 0.10); }

/* ---- picker overlay ----------------------------------------------------- */
/* REDESIGN.md §14.1: `.picker-veil` deleted. Every picker now lives in the one
   sheet shell, which owns its own width and never dims the scene behind it, so
   the picker itself is a plain flow child of `.sheet-body`. */
.picker { display: flex; flex-direction: column; min-width: 0; min-height: 0; width: 100%; }
/* The pk-head padding was tuned for a free-floating 880px card; inside the
   sheet the shell already pads, so only the bottom gap is still wanted. */
.sheet-body .pk-head { padding: 0 0 12px; }
.pk-head { padding: 18px 22px 14px; display: flex; align-items: center; gap: 12px; }
.pk-title { flex: 1; min-width: 0; }
.pk-title h2 { font-size: 17px; font-weight: 700; letter-spacing: 0.02em; }

.pk-crumbs { display: flex; align-items: center; gap: 5px; margin-bottom: 5px; flex-wrap: wrap; }
.crumb {
  border: none; background: none; padding: 0; cursor: pointer;
  font-size: 11.5px; color: rgba(255, 255, 255, 0.5); transition: color 0.15s;
}
.crumb:hover { color: #fff; text-decoration: underline; }
.crumb.cur { color: rgba(255, 255, 255, 0.38); cursor: default; }
.crumb-sep { font-size: 11px; color: rgba(255, 255, 255, 0.25); }
.pk-title p {
  font-size: 10.5px; letter-spacing: 0.14em; text-transform: uppercase;
  color: var(--ink-faint); margin-top: 4px; font-weight: 600;
}
.pk-back, .pk-close {
  flex: none; width: 32px; height: 32px; border-radius: 10px; cursor: pointer;
  border: 1px solid rgba(255, 255, 255, 0.10); background: rgba(255, 255, 255, 0.04);
  color: var(--ink-dim); font-size: 14px; line-height: 1;
  transition: background 0.15s, color 0.15s;
}
.pk-back:hover, .pk-close:hover { background: rgba(255, 255, 255, 0.12); color: var(--ink); }
.pk-remove {
  flex: none; border: 1px solid rgba(255, 255, 255, 0.12); background: transparent;
  color: var(--ink-dim); border-radius: 999px; padding: 7px 14px;
  font-size: 11.5px; font-weight: 600; cursor: pointer; transition: all 0.15s;
}
.pk-remove:hover { border-color: rgba(226, 88, 72, 0.6); color: #ff9d92; background: rgba(226, 88, 72, 0.12); }

/* mount-zone picker */
.zones { overflow-y: auto; padding: 2px 22px 22px; scrollbar-width: thin; }
.zone + .zone { margin-top: 18px; }
.zone-title {
  font-size: 10px; font-weight: 700; letter-spacing: 0.18em; text-transform: uppercase;
  color: var(--ink-faint); margin-bottom: 9px;
}
.zone-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(190px, 1fr)); gap: 8px; }
.mount-btn {
  display: flex; flex-direction: column; align-items: flex-start; gap: 3px;
  text-align: left; padding: 11px 13px; border-radius: 12px; cursor: pointer;
  border: 1px solid rgba(255, 255, 255, 0.10); background: rgba(255, 255, 255, 0.03);
  transition: border-color 0.15s, background 0.15s;
}
.mount-btn:hover:not(:disabled) { border-color: var(--accent); background: var(--accent-soft); }
.mount-btn:disabled { opacity: 0.35; cursor: not-allowed; }
.mount-btn.occupied { border-color: rgba(232, 168, 72, 0.35); }
.mb-name { font-size: 13px; font-weight: 600; color: #fff; }
.mb-sub {
  font-size: 11px; color: var(--ink-faint); max-width: 100%;
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.mount-btn.occupied .mb-sub { color: var(--accent); }

/* brand list */
.brand-list { overflow-y: auto; padding: 2px 20px 20px; scrollbar-width: thin; }
.brand-row {
  width: 100%; display: flex; align-items: center; gap: 12px;
  padding: 13px 14px; border-radius: 12px; cursor: pointer; text-align: left;
  border: 1px solid rgba(255, 255, 255, 0.10); background: rgba(255, 255, 255, 0.03);
  transition: border-color 0.15s, background 0.15s;
}
.brand-row + .brand-row { margin-top: 7px; }
.brand-row:hover { border-color: var(--accent); background: var(--accent-soft); }
.brand-row.on { border-color: var(--accent); background: rgba(232, 168, 72, 0.14); }
.br-name { font-size: 14px; font-weight: 600; color: #fff; flex: none; }
.br-palette { display: flex; gap: 3px; flex: 1; }
.br-sw {
  width: 13px; height: 13px; border-radius: 4px;
  box-shadow: inset 0 0 0 1px rgba(255, 255, 255, 0.22);
}
.br-count { font-size: 11.5px; color: var(--ink-faint); flex: none; }
.br-fitted {
  font-size: 10px; font-weight: 700; letter-spacing: 0.1em; text-transform: uppercase;
  color: var(--accent); flex: none;
}
.br-go { font-size: 16px; color: rgba(255, 255, 255, 0.35); flex: none; line-height: 1; }

/* product lines within one brand */
.lines { overflow-y: auto; padding: 2px 20px 22px; scrollbar-width: thin; }
.pline + .pline { margin-top: 18px; }
.pline-title {
  font-size: 10px; font-weight: 700; letter-spacing: 0.18em; text-transform: uppercase;
  color: var(--ink-faint); margin-bottom: 9px;
}
.card.has-sizes { cursor: default; }
.card.has-sizes:hover { border-color: rgba(255, 255, 255, 0.10); background: rgba(255, 255, 255, 0.03); }
.card.has-sizes.on:hover { border-color: var(--accent); background: rgba(232, 168, 72, 0.14); }
.sizes { display: flex; flex-wrap: wrap; gap: 6px; margin-top: 10px; }
.size-chip {
  border: 1px solid rgba(255, 255, 255, 0.16); background: rgba(255, 255, 255, 0.05);
  color: rgba(255, 255, 255, 0.86); border-radius: 8px; cursor: pointer;
  padding: 6px 11px; font-size: 12px; font-weight: 600;
  font-variant-numeric: tabular-nums; transition: all 0.15s;
}
.size-chip:hover { border-color: var(--accent); background: rgba(232, 168, 72, 0.18); color: #fff; }
.size-chip.on { border-color: var(--accent); background: var(--accent); color: #1a1207; }

/* product cards */
.cards {
  overflow-y: auto; padding: 4px 20px 22px; scrollbar-width: thin;
  /* auto-FIT, not auto-fill: a brand with one bag for this mount used to get a
     half-width card marooned beside an empty track. Fit collapses the empty
     tracks so the row it does have fills the sheet. */
  display: grid; grid-template-columns: repeat(auto-fit, minmax(240px, 1fr)); gap: 10px;
}
.card {
  border: 1px solid rgba(255, 255, 255, 0.10); border-radius: 12px; padding: 13px 14px;
  cursor: pointer; background: rgba(255, 255, 255, 0.03);
  transition: border-color 0.15s, background 0.15s;
}
.card:hover { border-color: var(--accent); background: var(--accent-soft); }
.card.on { border-color: var(--accent); background: rgba(232, 168, 72, 0.14); }
.card .brand {
  font-size: 10px; letter-spacing: 0.14em; text-transform: uppercase;
  color: var(--accent); font-weight: 700;
}
.card .name {
  font-size: 13.5px; font-weight: 600; margin: 0; line-height: 1.3;
  display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical;
  line-clamp: 2; overflow: hidden;
}
.card .meta {
  font-size: 11px; color: var(--ink-dim); display: flex; align-items: center;
  gap: 9px; margin-top: 9px; flex-wrap: wrap;
}
.card .liters { font-weight: 600; font-variant-numeric: tabular-nums; }
.card .chips { display: flex; gap: 4px; }
.card .chip { width: 12px; height: 12px; border-radius: 4px; box-shadow: inset 0 0 0 1px rgba(255, 255, 255, 0.22); }
.card .est { font-style: italic; color: var(--ink-faint); }
.card .fitted {
  color: var(--accent); font-weight: 700;
  font-size: 10px; letter-spacing: 0.1em; text-transform: uppercase;
}

/* link out to the maker's own product page */
.buy-link {
  margin-left: auto; flex: none; white-space: nowrap; text-decoration: none;
  font-size: 11px; color: rgba(255, 255, 255, 0.55); transition: color 0.15s;
}
.buy-link:hover { color: var(--accent); text-decoration: underline; }

/* ---- hint: floats above the dock, then removes itself ------------------- */
.hint {
  position: absolute; left: 50%; bottom: 100px;
  transform: translateX(-50%); pointer-events: none;
  background: var(--glass-bg);
  backdrop-filter: blur(24px) saturate(150%);
  -webkit-backdrop-filter: blur(24px) saturate(150%);
  border: 1px solid var(--glass-brd);
  box-shadow: var(--glass-shadow);
  border-radius: 999px; padding: 7px 15px;
  font-size: 11.5px; color: var(--ink-dim); letter-spacing: 0.05em;
  user-select: none; white-space: nowrap;
  transition: opacity 0.6s ease, transform 0.6s ease;
}
.hint.gone { opacity: 0; transform: translateX(-50%) translateY(4px); }

/* ---- browse mode switch -------------------------------------------------
   Brand-first and type-first are peers, not a primary route and a fallback,
   so the control that swaps them sits in the header at equal weight. */
.seg {
  display: inline-flex; gap: 2px; padding: 3px;
  background: rgba(255, 255, 255, 0.07);
  border: 1px solid rgba(255, 255, 255, 0.08);
  border-radius: 999px;
}
.seg-btn {
  appearance: none; border: 0; cursor: pointer;
  padding: 7px 15px; border-radius: 999px;
  font: 600 12px/1 -apple-system, BlinkMacSystemFont, "Inter", sans-serif;
  letter-spacing: 0.01em;
  color: var(--ink-dim); background: transparent;
  transition: color .18s ease, background .18s ease;
  white-space: nowrap;
}
.seg-btn:hover { color: var(--ink); }
.seg-btn.on {
  color: #14161a; background: #f2f3f5;
  box-shadow: 0 1px 3px rgba(0, 0, 0, 0.28);
}

/* the header can now carry a wide control, so let it wrap gracefully */
.pk-head { flex-wrap: wrap; row-gap: 10px; }
.pk-head .seg { margin-left: auto; }

/* ---- product thumbnails -------------------------------------------------
   A real photo of the bag is the fastest way to recognise it; the render
   alone is not enough when 589 products share a silhouette language. */
.card { position: relative; }
/* Maker photography is shot on pure white. Dropped straight into a dark panel
   that glares, so the tile is a soft off-white with an inset edge — it reads as
   a deliberate product surface rather than a hole punched in the UI. */
.card-thumb {
  width: 100%; aspect-ratio: 3 / 2; border-radius: 9px; overflow: hidden;
  margin-bottom: 10px; background: #fff;
  box-shadow: inset 0 0 0 1px rgba(0, 0, 0, 0.10);
  display: block;
}
/* tile matches the photos' own white ground, so the two don't read as nested
   rectangles; the shot already carries its own margin, so no extra padding */
.card-thumb img {
  width: 100%; height: 100%; object-fit: contain; display: block;
  transition: transform .5s cubic-bezier(.2, .7, .2, 1);
}
.card:hover .card-thumb img { transform: scale(1.035); }
.card-thumb.empty {
  display: grid; place-items: center;
  color: var(--ink-faint); font-size: 11px; letter-spacing: 0.06em;
}

/* ---- colourway dots ----------------------------------------------------- */
.ways { display: flex; gap: 5px; align-items: center; margin-top: 8px; flex-wrap: wrap; }
.way {
  width: 15px; height: 15px; border-radius: 50%; cursor: pointer;
  border: 1.5px solid rgba(255, 255, 255, 0.22);
  padding: 0; appearance: none;
  transition: transform .14s ease, border-color .14s ease;
}
.way:hover { transform: scale(1.16); border-color: rgba(255, 255, 255, 0.6); }
.way.on { border-color: #fff; box-shadow: 0 0 0 2px rgba(0, 0, 0, 0.45); }

/* ---- bidon colour ------------------------------------------------------- */

.bottle-group .lbl {
  font-size: 9px; letter-spacing: 0.16em; text-transform: uppercase;
  color: var(--ink-faint); font-weight: 700;
}
.bidons { display: flex; gap: 6px; }
.bidon {
  width: 19px; height: 19px; border-radius: 50%; cursor: pointer;
  border: 1.5px solid rgba(255, 255, 255, 0.2); appearance: none; padding: 0;
  transition: transform .14s ease;
}
.bidon:hover { transform: scale(1.14); }
.bidon.on { border-color: #fff; }

/* ---- bottom bar, rebuilt -------------------------------------------------
   Was three unrelated concerns in one strip, with the loudest visual weight
   (a filled amber pill) on the least consequential action. Now: one rhythm,
   build actions left, appearance right, nothing shouting. */

.dock { display: flex; align-items: center; gap: 14px; flex-wrap: wrap; }

.btn.quiet {
  background: rgba(255, 255, 255, 0.07);
  border: 1px solid rgba(255, 255, 255, 0.10);
  color: var(--ink);
  font-weight: 600; font-size: 12.5px;
  padding: 8px 14px; border-radius: 999px;
  transition: background .18s ease, border-color .18s ease;
}
.btn.quiet:hover { background: rgba(255, 255, 255, 0.13); border-color: rgba(255, 255, 255, 0.2); }
.btn.quiet .bolt { opacity: 0.75; margin-right: 5px; }

.group-label {
  font-size: 9px; letter-spacing: 0.16em; text-transform: uppercase;
  color: var(--ink-faint); font-weight: 700; margin-right: 9px;
}


/* environment as swatches, not labelled photo cards */
.envs.compact { display: flex; gap: 6px; }
.envs.compact .env-chip {
  width: 26px; height: 26px; border-radius: 8px; padding: 0;
  border: 1.5px solid rgba(255, 255, 255, 0.16);
  cursor: pointer; appearance: none;
  transition: transform .14s ease, border-color .14s ease;
}
.envs.compact .env-chip:hover { transform: translateY(-1px); border-color: rgba(255, 255, 255, 0.5); }
.envs.compact .env-chip.on { border-color: #fff; box-shadow: 0 0 0 2px rgba(0, 0, 0, 0.4); }

.divider { width: 1px; height: 24px; background: rgba(255, 255, 255, 0.1); }

/* colourway row sits under the card's text column, not across the swatch */
.bag-card { flex-wrap: wrap; }
.bag-card .ways { flex-basis: 100%; margin: 8px 0 0 46px; }

/* ---- a bag that will not fit this frame ---------------------------------
   Shown rather than silently dropped, but clearly not part of the build. */
.bag-card.unfit { opacity: 0.72; border-color: rgba(226, 138, 72, 0.30); }
.bag-card.unfit .bag-sw { filter: grayscale(0.55); }
.bag-card.unfit .bag-model { color: rgba(255, 255, 255, 0.72); }
.unfit-msg {
  margin-top: 4px; font-size: 11px; line-height: 1.35;
  color: #e8a256; font-weight: 500;
}

/* ---- selection mirrored from the 3D scene -------------------------------
   Hover is a whisper, selection is definite — same two-step the canvas uses. */
.bag-card.hov { background: rgba(255, 255, 255, 0.07); border-color: rgba(255, 255, 255, 0.12); }
.bag-card.sel {
  background: rgba(232, 168, 72, 0.13);
  border-color: rgba(232, 168, 72, 0.55);
}
.bag-card.sel .bag-model { color: #fff; }
.bag-card.sel .bag-act { opacity: 1; }

/* product photo in the kit list — maker shots are on white, so give the tile
   the same ground rather than a dark hole punched in the panel */
.bag-sw.photo { background: #fff; overflow: hidden; padding: 2px; }
.bag-sw.photo img { width: 100%; height: 100%; object-fit: contain; display: block; }

/* ==========================================================================
   MOBILE
   Everything below lives inside a media query, so the 901px+ desktop with a
   mouse renders byte-identically to the rules above.

   Three queries, matching src/MOBILE.md:
     (pointer: coarse)                       touch, at any width
     (max-width: 900px), (pointer: coarse)   "compact" — one-row bottom bar
     (max-width: 560px)                      phone — panels become sheets
   ========================================================================== */

/* The sheet controls exist in the markup at every size but only get a box on
   a phone — above 560px the panel is a panel, not a sheet. */
.sheet-chevron, .sheet-add, .peek-total, .sheet-min-l { display: none; }

/* Phone-only: the little chevron that tucks a bottom nav so you can see the
   bike. Same control on the homepage, the builder, the bag picker and the
   wind tunnel — icon, top-right of the slab, nothing else. */
.nav-min {
  appearance: none;
  display: none;
  align-items: center;
  justify-content: center;
  width: 44px;
  height: 44px;
  padding: 0;
  margin: 0;
  border: 0;
  border-radius: 4px;
  background: transparent;
  color: rgba(244, 241, 236, 0.72);
  cursor: pointer;
  flex: 0 0 auto;
  pointer-events: auto;
}
.nav-min:hover { color: #F4F1EC; background: rgba(244, 241, 236, 0.06); }
.nav-min:focus-visible { outline: 2px solid #FF6A2B; outline-offset: 2px; }
.nav-min .nav-min-up { display: none; }
.is-min .nav-min .nav-min-dn,
.collapsed .nav-min .nav-min-dn { display: none; }
.is-min .nav-min .nav-min-up,
.collapsed .nav-min .nav-min-up { display: block; }
@media (max-width: 560px) {
  .nav-min { display: inline-flex; }
}

/* ---- touch, at any width -------------------------------------------------
   A small laptop window is not a phone, but an iPad in landscape is not a
   desktop either. These rules key off the pointer, not the viewport: they
   grow every target to 44px and unhide anything that only appeared on hover.
   Where 44px of paint would wreck the layout (the 15px colourway dots inside
   a kit card) the visual size stays and an invisible ::after carries the
   hit area out to 44. */
@media (pointer: coarse) {
  .tool-btn { width: 44px; height: 44px; font-size: 21px; }

  .paint { width: 44px; height: 44px; }
  .bidon { width: 44px; height: 44px; }
  .envs.compact .env-chip { width: 44px; height: 44px; }

  /* hover is the only way these ever appeared — there is no hover here */
  .bag-act { opacity: 1; }
  .paint:hover, .bidon:hover, .way:hover, .envs.compact .env-chip:hover { transform: none; }

  .bag-act, .way { position: relative; }
  .bag-act::after, .way::after { content: ''; position: absolute; }
  /* 30 + 7 + 7 = 44, and a 14px gap keeps neighbouring hit boxes from
     overlapping — mis-tapping "remove" instead of "swap" is destructive */
  .bag-act { width: 30px; height: 30px; font-size: 13px; }
  .bag-act::after { inset: -7px; }
  .bag-acts { gap: 14px; }
  /* a colourway mis-tap only recolours, so these may overlap slightly */
  .way { width: 22px; height: 22px; }
  .way::after { inset: -11px; }
  .ways { gap: 10px; }

  .size-chip { min-height: 44px; padding: 6px 15px; }
  .bottom-bar .btn { min-height: 44px; display: inline-flex; align-items: center; justify-content: center; }
  .pk-back, .pk-close { width: 44px; height: 44px; font-size: 16px; }
  .crumb { padding: 6px 0; }
  .share-kit { height: 44px; }
  .add-bag { height: 48px; }

  /* the canvas owns the gestures; a panel may pan itself and stop there */
  .bag-list, .zones, .brand-list, .lines, .cards {
    touch-action: pan-y; overscroll-behavior: contain;
  }
}

/* ---- compact bar ---------------------------------------------------------
   Five labelled swatch groups do not fit one phone-width row, and shrinking
   them to fit is how the touch targets got to 26px in the first place. So the
   bar keeps only what people actually press — Surprise me, Clear bike — and
   the appearance settings (frame, bidons, scene) move behind one button that
   opens them as a sheet, where they get 44px targets and visible labels.
   Horizontal scrolling was the alternative and loses on discoverability: an
   off-screen control with no affordance is a control nobody finds, and a
   sideways swipe fights the sheet and the canvas for the same gesture. */



@media (max-width: 900px), (pointer: coarse) {

  .dock > .divider { display: none; }
  .appearance-btn { display: inline-flex; align-items: center; justify-content: center; gap: 6px; }
  .dock { flex-wrap: nowrap; }
}

/* tablet / small window: same two-column shape, narrower.
   The bar stops starting where the panel ends and spans the full width
   instead — beside a panel there is not room for three labelled pills at
   561px, and the panel now sits above the bar rather than next to it. */
@media (min-width: 561px) and (max-width: 900px) {
  :root { --gutter: 20px; --panel-w: clamp(250px, 38vw, 300px); }
  .panel { top: 84px; max-height: calc(100vh - 250px); }
  .wordmark { top: 20px; }
  .viewtools { top: 20px; }
  .bottom-bar {
    left: max(var(--gutter), env(safe-area-inset-left));
    right: max(var(--gutter), env(safe-area-inset-right));
  }
  .hint { bottom: calc(var(--bar-bottom) + var(--bar-h) + 14px); }
  /* Three touch-sized buttons plus a photo leave a 250px panel about 70px for
     the product name, which crushes it to "Drop… V.2". Give them their own
     row: the card grows ~40px, but the name — the only thing on the card
     anyone reads — gets the full width. */
  .bag-acts { flex-basis: 100%; justify-content: flex-end; margin-top: 8px; }
  .zone-grid { grid-template-columns: repeat(auto-fit, minmax(160px, 1fr)); }
  .cards { grid-template-columns: repeat(auto-fit, minmax(180px, 1fr)); }
}

/* ---- phone ---------------------------------------------------------------
   The bike is the product, so it keeps the top of the screen and the kit
   becomes a bottom sheet stacked directly on the bar: rounded top corners,
   square where the two meet, so they read as one surface. */
@media (max-width: 560px) {
  :root {
    --gutter: 12px;
    --bar-h: 64px;
    --bar-bottom: 12px;
    --peek-h: 52px;
  }
  html, body { overscroll-behavior: none; }

  /* header: the subtitle is what ran under the view tools, and it says
     nothing the wordmark and the bike below it don't already say */
  .top-scrim { width: 260px; height: 150px; }
  .wordmark {
    top: calc(14px + env(safe-area-inset-top));
    left: max(var(--gutter), env(safe-area-inset-left));
  }
  .wordmark h1 { font-size: 16px; letter-spacing: 0.26em; }
  .wordmark p { display: none; }
  .viewtools {
    top: calc(10px + env(safe-area-inset-top));
    right: max(var(--gutter), env(safe-area-inset-right));
  }

  /* the kit panel, as a sheet */
  .panel {
    top: auto;
    bottom: calc(max(var(--bar-bottom), env(safe-area-inset-bottom)) + var(--bar-h));
    left: max(var(--gutter), env(safe-area-inset-left));
    right: max(var(--gutter), env(safe-area-inset-right));
    width: auto;
    max-height: 55vh;
    max-height: 55dvh;
    border-radius: var(--radius) var(--radius) 0 0;
    border-bottom: none;
    transition: max-height 0.28s cubic-bezier(0.2, 0.7, 0.2, 1);
  }
  /* peek: header only. Most of the time you want to look at the bike. */
  .panel.collapsed { max-height: var(--peek-h); }

  .panel-head {
    min-height: var(--peek-h);
    align-items: center;
    gap: 8px;
    padding: 8px 10px 8px 4px;
    cursor: pointer;
    user-select: none;
  }
  .sheet-chevron {
    display: flex; align-items: center; justify-content: center;
    flex: none; width: 38px; height: 44px; padding: 0;
    border: none; background: transparent; cursor: pointer;
    color: var(--ink-dim); font-size: 13px; line-height: 1;
    transition: transform 0.28s cubic-bezier(0.2, 0.7, 0.2, 1);
  }
  .panel.collapsed .sheet-chevron { transform: rotate(180deg); }
  .sheet-min-l {
    display: block;
    font-size: 13px; font-weight: 550;
    color: rgba(255, 255, 255, 0.78);
    white-space: nowrap;
  }
  .panel.collapsed .sheet-min-l { margin-right: auto; }
  .panel-head .panel-title { flex: none; }
  /* the count carries the slack, so "+" stays pinned right in both states */
  .panel-head .panel-count { margin-right: auto; }
  .peek-total {
    display: block;
    font-size: 12px; font-weight: 700;
    color: rgba(255, 255, 255, 0.72); font-variant-numeric: tabular-nums;
    letter-spacing: 0.04em;
  }
  /* expanded, the foot already carries the total — don't say it twice */
  .panel:not(.collapsed) .peek-total { display: none; }
  .sheet-add {
    display: flex; align-items: center; justify-content: center; flex: none;
    width: 44px; height: 44px; margin-left: 4px; padding: 0;
    border: 1px solid rgba(255, 255, 255, 0.18); border-radius: 12px;
    background: rgba(255, 255, 255, 0.10); color: #fff;
    font-size: 20px; font-weight: 500; line-height: 1; cursor: pointer;
  }
  .bag-list { padding: 2px 8px 8px; }
  .bag-card { min-height: 60px; }
  .bag-card .ways { margin-left: 46px; }

  /* the bar: two primary actions plus the appearance sheet, one row, no wrap */
  .bottom-bar {
    left: max(var(--gutter), env(safe-area-inset-left));
    right: max(var(--gutter), env(safe-area-inset-right));
    bottom: max(var(--bar-bottom), env(safe-area-inset-bottom));
    height: var(--bar-h);
    padding: 10px;
    border-radius: 0 0 var(--radius) var(--radius);
    border-top: none;
  }
  .dock { width: 100%; gap: 8px; }
  .bottom-bar .btn {
    flex: 1 1 0; min-width: 0; min-height: 44px;
    padding: 10px 6px; font-size: 12px;
    display: inline-flex; align-items: center; justify-content: center;
  }
  .btn.quiet .bolt { margin-right: 4px; }

  /* The bottom-sheet treatment moved to the shell (`.sheet` in ui/sheet.css),
     which is the only surface left to apply it to. The picker inside it is a
     plain flow child at every width. */
  .pk-head { padding: 14px 14px 12px; gap: 8px; }
  .pk-title h2 { font-size: 15.5px; }
  /* the mode switch needs a whole row; close stays up on the title row rather
     than being pushed below it */
  .pk-head .pk-close { order: 1; }
  .pk-head .seg { order: 2; margin-left: 0; flex: 1 1 100%; }
  .seg-btn { flex: 1; min-height: 40px; }
  .zones { padding: 2px 14px 20px; }
  .brand-list, .lines { padding: 2px 14px 20px; }
  /* One card per row on a phone. Two 170px columns could not hold a brand, a
     model name and a capacity without every one of them wrapping. */
  .cards { padding: 4px 14px 20px; gap: 8px; grid-template-columns: repeat(auto-fit, minmax(200px, 1fr)); }
  .zone-grid { grid-template-columns: repeat(auto-fit, minmax(150px, 1fr)); }
  .brand-row { padding: 14px 12px; }
  .br-palette { flex: 1; overflow: hidden; }

  /* hint: touch wording, and parked under the header where nothing else is */
  /* centring with left:50% caps shrink-to-fit at half the screen, which wraps
     a message that fits fine — max-content sizes it to the text instead */
  .hint {
    top: calc(78px + env(safe-area-inset-top));
    bottom: auto;
    width: max-content; max-width: calc(100% - 24px);
    white-space: normal; text-align: center;
  }
}

/* ---- one sheet at a time -------------------------------------------------
   MOBILE.md rule 2. Wherever the wind tunnel HUD is itself a bottom sheet, it
   and the kit panel claim the same bottom-anchored space — and in landscape
   the full-width HUD runs straight through the kit panel's left column. A
   collapsed peek row is not enough: it still occupies layout and still takes
   a hit-test region a thumb can land on, it is only painted over because the
   HUD comes later in the DOM. So the panel leaves entirely.

   The query deliberately matches aero.css's own sheet breakpoint. Above it
   the two panels sit on opposite sides of the screen and both stay visible,
   exactly as on desktop; the class is set at every width, so this is the only
   place it may act. main.js sets it: body.classList.toggle('aero-open', …). */
@media (max-width: 560px), ((pointer: coarse) and (max-height: 480px)) {
  body.aero-open .panel { display: none; }
}

/* ---- short landscape -----------------------------------------------------
   A 393px-tall phone on its side. The panel stays beside the bike — a bottom
   sheet here would leave no bike — but at that height the footer alone is
   taller than the panel, and the list (the only thing that can shrink) gets
   squeezed to nothing. So the total goes on one line and the two buttons sit
   side by side, which buys the list back about a hundred pixels. */
@media (min-width: 561px) and (max-width: 900px) and (max-height: 500px) {
  .wordmark, .viewtools { top: 14px; }
  .top-scrim { height: 130px; }
  .panel { top: 64px; max-height: calc(100vh - 164px); }
  /* the bar is full width here, so a hint centred on the screen would land on
     the panel; centre it on the open space to the right of the panel instead */
  .hint { left: calc(var(--gutter) + var(--panel-w) + (100% - var(--gutter) * 2 - var(--panel-w)) / 2); }
  .panel-foot { padding: 0 10px 8px; }
  .panel-foot.has-kit { display: flex; flex-wrap: wrap; align-items: center; gap: 8px; }
  .kit-summary { flex: 1 1 100%; padding: 7px 2px 8px; }
  .panel-foot.has-kit .add-bag,
  .panel-foot.has-kit .share-kit {
    flex: 1 1 0; width: auto; min-width: 0; height: 44px; margin-top: 0;
  }
}

/* ---- the appearance sheet ------------------------------------------------
   One stacked, labelled row per setting. The environment swatches get their
   names back here: five gradients with no hover tooltip are five guesses. */
/* `.appearance-sheet` is gone: the swatches live in the left column now. */
.appearance-sheet .paints,
.appearance-sheet .bidons,
.appearance-sheet .envs.compact { flex-wrap: wrap; gap: 12px; }
.appearance-sheet .paint,
.appearance-sheet .bidon { width: 44px; height: 44px; }
.appearance-sheet .envs.compact .env-chip {
  width: auto; min-width: 44px; height: 44px; padding: 0 7px;
  display: flex; align-items: center; justify-content: center;
}
.appearance-sheet .envs.compact .env-label {
  display: block; padding: 3px 7px; border-radius: 7px;
  background: var(--glass-bg);
  font-size: 10px; font-weight: 600; letter-spacing: 0.08em;
  text-transform: uppercase; color: var(--ink);
}
/* the label only exists for this sheet; in the bar the chip is a bare swatch */
.envs.compact .env-label { display: none; }

@media (prefers-reduced-motion: reduce) {
  .panel, .sheet-chevron { transition: none; }
}
