/* Pullseye app shell -- ADDITIVE layer over design/components.css.
   Anything components.css already owns (.tile .btn .candidate .pocket
   .binder-grid .bento .tab .receipt .pill) is NOT redefined here; this file
   only supplies what a live app needs and a static mockup didn't:
   a phone-shaped column, view switching, and the Lab diagnostics panel. */

body { padding: 0; background: var(--canvas);
  line-height: var(--lh-body, 1.45); }
.h2 { line-height: var(--lh-title, 1.2); }
.provenance { line-height: var(--lh-micro, 1.3); }

.phone {
  max-width: 430px; margin: 0 auto; min-height: 100vh;
  display: flex; flex-direction: column; position: relative;
}
body.lab-open .phone { max-width: 1080px; }

/* ---- top bar ---- */
.topbar { display: flex; align-items: center; gap: 10px;
  flex-wrap: wrap;  /* at 130% Dynamic Type the toggles wrap, never clip */
  /* installed as a home-screen app the page runs under the iOS status
     bar (black-translucent + viewport-fit=cover) -- pad past it */
  padding: calc(14px + env(safe-area-inset-top, 0px)) var(--sp-4) 8px; }
/* the mark replaced the old rounded-square glyph tile */
.topbar h1 { font-size: var(--fs-body); }
.topbar .right { margin-left: auto; display: flex; align-items: center; gap: 8px; }
.topbar .pill { white-space: nowrap; }
.lab-toggle { background: none; border: 1px solid var(--line);
  color: var(--text-2); border-radius: var(--r-pill); font: inherit;  /* text-3 fails AA at 11px on paper (axe) */
  font-size: var(--fs-micro); font-weight: 600; padding: 4px 11px;
  cursor: pointer; }
.lab-toggle.on { border-color: var(--accent); color: var(--accent); }
#sound-toggle.muted { text-decoration: line-through; opacity: .7; }

/* ---- view switching ---- */
.body { flex: 1; padding: 0 var(--sp-4) 104px; }
.view { display: none; }
/* page turn: content slides subtly, never a crossfade (DESIGN.md v2.2) */
.view.on { display: block;
  animation: page-in var(--mo-deliberate) var(--ease-screen); }
/* margin, not transform: a transformed ancestor becomes the containing
   block for position:fixed and the bottom-half scan CTA jumped to the top */
@keyframes page-in {
  from { margin-left: 10px; margin-right: -10px; opacity: 0; }
  to { margin-left: 0; margin-right: 0; opacity: 1; }
}
.hidden { display: none !important; }

.h2 { font-family: var(--font-display); font-size: var(--fs-title);
  font-weight: 600; letter-spacing: -.01em; margin: var(--sp-2) 0 var(--sp-3); }
.sub { font-size: var(--fs-caption); color: var(--text-3); }
.guide { font-size: var(--fs-sub); color: var(--text-2); margin: var(--sp-2) 0;
  max-width: 46ch; }
.empty-note { color: var(--text-3); font-size: var(--fs-sub);
  padding: var(--sp-5) 0; text-align: center; }
.linkish { background: none; border: none; color: var(--accent); font: inherit;
  font-size: var(--fs-sub); font-weight: 500; cursor: pointer; padding: 10px 0; }
/* Section + field labels: sentence case, secondary ink, weight does the
   separating (audit 2026-08-17). All-caps micro was the app's dominant
   texture -- ten instances -- and it is what cheap dashboards are made
   of. The ONE place caps survives is the receipt header, where it is
   skeuomorphic and earned. */
.field-label { font-size: var(--fs-label); line-height: var(--lh-label);
  font-weight: var(--fw-label); color: var(--text-2);
  margin-top: var(--sp-3); }
.rowbtns { display: flex; gap: 8px; margin-top: var(--sp-3); }
.rowbtns .btn { flex: 1; }

/* ---- scan ---- */
.drop { border: 2px dashed var(--line); border-radius: var(--r-card);
  padding: var(--sp-7) var(--sp-4); text-align: center; color: var(--text-2);
  cursor: pointer; transition: border-color var(--dur-fast) var(--ease),
                               background var(--dur-fast) var(--ease); }
.drop.hot { border-color: var(--accent); background: var(--accent-soft); }
.drop b { color: var(--text); font-family: var(--font-display); display: block; }
.scan-opts { display: flex; gap: 14px; align-items: center; margin-top: var(--sp-3);
  font-size: var(--fs-sub); color: var(--text-2); }
.scan-opts label { display: flex; gap: 7px; align-items: center; cursor: pointer; }

.hero { text-align: center; margin: var(--sp-4) 0 var(--sp-3); }
.hero img { width: 200px; border-radius: var(--r-art); display: block;
  margin: 0 auto; box-shadow: inset 0 0 0 1px var(--line); }

/* ---- the Lock (DESIGN.md v2.1): card seats into a sleeve ---- */
.sleeve { position: relative; display: inline-block; padding: 4px;
  border-radius: calc(var(--r-art) + 4px); background: var(--surface); }
.sleeve::after { content: ""; position: absolute; inset: 0;
  border-radius: inherit; box-shadow: inset 0 0 0 2px var(--accent);
  opacity: 0; transition: opacity 120ms var(--ease-screen);
  pointer-events: none; }
.sleeve.seated::after { opacity: 1; }
/* $100+ lock: the holo event layers on -- the SLEEVE gets rare, the
   choreography never changes (one of the three earned holo events) */
.sleeve.holo-edge::after { box-shadow: none; opacity: 1;
  border: 2px solid transparent;
  background: var(--holo) border-box; background-size: 220% 220%;
  animation: holo-sheen 6s linear infinite;
  -webkit-mask: linear-gradient(#fff 0 0) padding-box,
                linear-gradient(#fff 0 0);
  -webkit-mask-composite: xor; mask-composite: exclude; }
.hero img.seat { animation: seat-drop var(--mo-standard) var(--ease-card) both; }
@keyframes seat-drop {
  0% { transform: translateY(-26px); opacity: 0; }
  55% { opacity: 1; }
  100% { transform: translateY(0); opacity: 1; }
}
.hero.locking .hero-meta-wrap { opacity: 0; }
.hero.locking .hero-meta-wrap.in { opacity: 1;
  transition: opacity 60ms var(--ease-screen); }

/* UNSURE: the held hand -- candidates fan in, ease-out, silent */
.candidate.fan-in { animation: fan-in var(--mo-standard) var(--ease-screen) both; }
@keyframes fan-in {
  0% { transform: translateY(10px); opacity: 0; }
  100% { transform: translateY(0); opacity: 1; }
}
/* price tick: a changed figure rolls up half a digit-height, no flash */
.stat-value.tick { animation: num-roll var(--mo-standard) var(--ease-screen); }
@keyframes num-roll {
  from { transform: translateY(.55em); opacity: 0; }
  to { transform: translateY(0); opacity: 1; }
}

@media (prefers-reduced-motion: reduce) {
  .hero img.seat, .candidate.fan-in, .view.on,
  .sweep-map .cell.done, .stat-value.tick { animation: none; }
  .sleeve.holo-edge::after { animation: none; background-position: 35% 50%; }
}
.hero .hname { font-family: var(--font-display); font-size: var(--fs-title);
  font-weight: 600; letter-spacing: -.01em; margin-top: var(--sp-3); }
.hero .hmeta { font-size: var(--fs-sub); color: var(--text-2); margin-top: 2px; }
.hero .hpill { margin-top: 10px; }

.cands { display: flex; flex-direction: column; gap: var(--sp-2);
  margin-top: var(--sp-2); }
.candidate .thumb { width: 42px; flex: 0 0 auto; border-radius: var(--r-art-mini);
  display: block; box-shadow: inset 0 0 0 1px var(--line); }
.candidate .c-name { font-weight: 600; font-size: var(--fs-sub); }
.candidate .c-meta { font-size: var(--fs-caption); color: var(--text-3); }
.candidate .c-price { margin-left: auto; text-align: right; }
.candidate .c-price .price { font-weight: 600; font-size: var(--fs-sub); }
.candidate .c-price .cap { font-size: var(--fs-micro); color: var(--text-3); }

.chips { display: flex; gap: 6px; flex-wrap: wrap; margin: 6px 0 0; }
.chip { font-size: var(--fs-caption); font-weight: 600; padding: 9px 14px;
  border-radius: var(--r-pill); background: var(--raised); color: var(--text-2);
  cursor: pointer; border: 1px solid transparent; min-height: 40px; }
.chip.on { background: var(--accent-soft); color: var(--accent-soft-ink);
  border-color: var(--accent); }
.addbar { display: none; margin-top: var(--sp-4); }
.addbar.show { display: block; }

input[type=text] { width: 100%; min-height: 44px; border-radius: var(--r-control);
  border: 1px solid var(--line); background: var(--raised); color: var(--text);
  padding: 0 14px; font: inherit; }
.searchrow { display: flex; gap: 8px; margin-top: 6px; }
.searchrow .btn { width: auto; padding: 0 18px; min-height: 44px; }

/* ---- collection: set rows ---- */
.bento { margin-bottom: var(--sp-4); }
.bento .tile { padding: var(--sp-3); }
.bento .b-hero .stat-value { font-size: 1.7rem; }
.bento .stat-value { font-size: 1.15rem; }
.bento .b-hero { display: flex; flex-direction: column; }
.bento .b-hero .sub { margin-top: auto; padding-top: 6px; }
#stat-spark svg { display: block; width: 100%; height: 40px; margin-top: 8px;
  overflow: visible; }

.set-row { width: 100%; text-align: left; background: var(--surface);
  border: 1px solid var(--line-soft); border-radius: var(--r-card);
  padding: var(--sp-3) var(--sp-4); margin-bottom: var(--sp-2);
  cursor: pointer; color: inherit; font: inherit; }
.set-row:hover { border-color: var(--line); }
.set-row .top { display: flex; align-items: baseline; gap: 10px; }
.set-row .nm { font-family: var(--font-display); font-weight: 600;
  font-size: var(--fs-sub); }
.set-row .ct { margin-left: auto; font-family: var(--font-data);
  font-size: var(--fs-caption); color: var(--text-2);
  font-variant-numeric: tabular-nums; }
/* track uses --line, not --raised: in the light theme --raised is #FFFFFF,
   which is the row's own background -- the track vanished entirely. */
.bar { height: 6px; border-radius: 3px; background: var(--line);
  overflow: hidden; margin-top: 8px; }
.bar > i { display: block; height: 100%; background: var(--accent);
  border-radius: 3px; transition: width var(--dur-base) var(--ease); }

/* ---- the binder page: pockets (components.css owns the base) ---- */
.binder-grid { margin-top: var(--sp-3); }
.pocket { overflow: hidden; border: none; background: none; padding: 0;
  cursor: pointer; }
.pocket img { width: 100%; height: 100%; object-fit: cover; display: block; }
.pocket.owned::after { content: ""; position: absolute; inset: 0;
  box-shadow: inset 0 0 0 1.5px var(--accent); border-radius: var(--r-art); }
.pocket.empty { border: 1.5px dashed var(--line); cursor: default; }
.pocket.empty .ghost { text-align: center; padding: 0 4px; line-height: 1.2; }
/* owned but no reference image: the pocket is filled, we just can't show it */
.pocket.owned.noart { background: var(--raised); display: flex;
  align-items: center; justify-content: center; flex-direction: column; gap: 2px; }
.pocket.owned.noart .num { font-family: var(--font-data);
  font-size: var(--fs-sub); color: var(--text-2); }
.page-nav { display: flex; align-items: center; justify-content: center;
  gap: 14px; margin-top: var(--sp-4); }
.page-nav .btn { width: auto; padding: 0 18px; min-height: 40px; }
.page-nav .pg { font-family: var(--font-data); font-size: var(--fs-caption);
  color: var(--text-2); font-variant-numeric: tabular-nums; }

/* ---- live page sweep ---- */
.sweep-modal { position: fixed; inset: 0; z-index: 40; background: #000;
  display: flex; flex-direction: column;
  padding-top: env(safe-area-inset-top, 0px); }
.sweep-modal video { flex: 1; width: 100%; object-fit: cover; min-height: 0; }
.sweep-hud { padding: 14px 18px calc(16px + env(safe-area-inset-bottom));
  background: var(--surface); display: flex; flex-direction: column; gap: 10px; }
.sweep-hud .guide { margin: 0; max-width: none; }
.sweep-hud .btn { min-height: 44px; }
.sweep-rowname { font-family: var(--font-display); font-weight: 600;
  font-size: var(--fs-body); }
/* pocket-mode progress map: a tiny 3x3 of the page, current pocket pulsing */
.sweep-map { display: grid; grid-template-columns: repeat(3, 34px);
  gap: 5px; justify-content: center; }
.sweep-map .cell { height: 44px; border: 1.5px dashed var(--line);
  border-radius: 6px; }
.sweep-map .cell.current { border: 2px solid var(--accent);
  animation: sweep-pulse 1.2s ease-in-out infinite; }
.sweep-map .cell.done { border: 1.5px solid var(--ok);
  background: var(--ok-soft);
  /* the check STAMPS -- capture is a physical event (card physics) */
  animation: stamp var(--mo-standard) var(--ease-card); }
@keyframes stamp { from { transform: scale(1.18); } to { transform: scale(1); } }
.sweep-map .cell.skipped { border-style: dotted; opacity: .45; }
@keyframes sweep-pulse { 50% { box-shadow: 0 0 0 4px var(--accent-soft); } }
@media (prefers-reduced-motion: reduce) {
  .sweep-map .cell.current { animation: none; } }

/* ---- Page Builder: wishlist pockets carry their price ----
   The tag is a corner chip, not a banner: a full-width bar was covering the
   bottom ~15% of the card art, and art is sacred (DESIGN.md §4). */
.pocket.wish::after { content: ""; position: absolute; inset: 0;
  border: 1.5px dashed var(--accent); border-radius: var(--r-art); }
.pocket .tag { position: absolute; right: 4px; bottom: 4px;
  font-family: var(--font-data); font-size: 10px; font-weight: 600;
  font-variant-numeric: tabular-nums; text-align: center;
  padding: 2px 7px; border-radius: 6px; pointer-events: none; }
.pocket .tag.have { background: rgba(10, 16, 28, .72); color: var(--text-2); }
.pocket .tag.want { background: var(--accent); color: var(--accent-ink); }
.pocket .tag.grade { background: var(--accent-soft); color: var(--accent-soft-ink);
  right: auto; left: 4px; }
/* binder-review captions: full-width ON the tester's own crop (not sacred
   official art) -- the pick has to be readable on a phone, where the old
   title-attribute tooltips simply don't exist */
.pocket .tag.pick { left: 4px; right: 4px; overflow: hidden;
  white-space: nowrap; text-overflow: ellipsis; }
.pocket .tag.none { background: var(--warn-soft); color: var(--warn-ink); }
#cell-review .hero img { width: 160px; }
[data-theme="light"] .pocket .tag.have,
.theme-light .pocket .tag.have { background: rgba(255, 255, 255, .82);
  color: var(--text-2); }

/* ---- holdings + movers rows (shared skeleton) ----
   Thumb -> art first; prices are neutral text (green/red mean movement,
   never decoration); "×2" and a grade ride the name line as quiet badges. */
.h-row { display: flex; align-items: center; gap: 10px; width: 100%;
  padding: 9px 2px; border: none; border-bottom: 1px dashed var(--line-soft);
  background: none; color: inherit; font: inherit; font-size: var(--fs-sub);
  text-align: left; cursor: pointer; }
.h-row:hover { background: var(--raised); }
.h-thumb { width: 38px; flex: 0 0 auto; border-radius: var(--r-art-mini);
  box-shadow: inset 0 0 0 1px var(--line); }
.h-mid { flex: 1; min-width: 0; }
.h-row .h-name { font-weight: 600; display: flex; align-items: baseline;
  gap: 7px; white-space: nowrap; overflow: hidden; }
.h-row .h-meta { color: var(--text-3); font-size: var(--fs-caption);
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.h-row .h-val { margin-left: auto; font-weight: 600; flex: 0 0 auto; }
.h-qty { font-family: var(--font-data); font-size: var(--fs-micro);
  font-weight: 600; color: var(--text-2); background: var(--raised);
  border-radius: var(--r-pill); padding: 1px 7px; flex: 0 0 auto; }
.h-row .h-grade { color: var(--accent-soft-ink); background: var(--accent-soft);
  border-radius: var(--r-pill); font-size: var(--fs-micro); padding: 1px 8px;
  flex: 0 0 auto; }
.m-val { margin-left: auto; text-align: right; flex: 0 0 auto; }
.m-cap { font-family: var(--font-data); font-size: var(--fs-micro);
  color: var(--text-3); font-variant-numeric: tabular-nums; }
.h-val.up { color: var(--gain); }
.h-val.down { color: var(--loss); }
.view-flip { margin-left: auto; display: flex; gap: 6px; }
#holdings.binder-grid { margin-top: var(--sp-3); }
.fact-grid { display: grid; grid-template-columns: 1fr 1fr; gap: 8px 16px;
  margin: var(--sp-3) 0; font-size: var(--fs-sub); }
.fact-grid .k { color: var(--text-3); font-size: var(--fs-caption); }
.applied-line { margin-top: var(--sp-3); padding: 10px 14px;
  background: var(--accent-soft); color: var(--accent-soft-ink);
  border-radius: var(--r-control); font-size: var(--fs-sub); }
.noprice-line { margin-top: var(--sp-3); padding: 10px 14px;
  background: var(--warn-soft); color: var(--warn-ink);
  border-radius: var(--r-control); font-size: var(--fs-caption); }
/* A withheld price is not a warning about the app -- it is the app
   working. Warm amber says "something is wrong here"; this is the
   calm, neutral surface that says "we checked, and we won't guess."
   Line-height opens up because it is a sentence, not a status. */
.noprice-line.withheld {
  background: var(--raised); color: var(--text-2);
  border: 1px solid var(--line);
  line-height: 1.5; padding: 12px 14px;
}
.provnote { margin-top: var(--sp-3); }

/* ---- qty stepper + remove ---- */
.qty-row { display: flex; align-items: center; gap: 10px; }
.qty-btn { width: 34px; height: 34px; border-radius: 50%; border: 1px solid
  var(--line); background: var(--raised); color: var(--text); font: inherit;
  font-size: 1.05rem; cursor: pointer; line-height: 1; }
.qty-btn:disabled { opacity: .35; cursor: default; }
.qty-n { font-family: var(--font-data); font-variant-numeric: tabular-nums;
  font-weight: 600; min-width: 2.2ch; text-align: center; }
#d-remove { color: var(--danger); background: transparent;
  box-shadow: inset 0 0 0 1px var(--line); }
#d-remove.danger-armed { background: var(--danger); color: #fff;
  box-shadow: none; }

/* ---- slab: a graded copy is a different object from a raw card ----
   Restrained on purpose. The point is that you can tell at a glance which of
   your copies is encased, not to draw a photorealistic case around sacred art. */
.slab { display: inline-block; position: relative; }
.slab.on { padding: 10px 10px 12px; border-radius: 14px;
  background: linear-gradient(180deg, var(--raised), var(--surface));
  box-shadow: inset 0 0 0 1px var(--line), 0 1px 0 var(--line-soft); }
.slab.on img { border-radius: var(--r-art-mini); }
.slab-label { display: flex; align-items: center; justify-content: center;
  gap: 8px; margin-bottom: 8px; }
.slab-label .grade { font-family: var(--font-display); font-weight: 600;
  font-size: var(--fs-caption); letter-spacing: .04em;
  background: var(--accent-soft); color: var(--accent-soft-ink);
  border-radius: var(--r-pill); padding: 3px 11px; }
.slab-label .cert { font-family: var(--font-data); font-size: var(--fs-micro);
  color: var(--text-3); font-variant-numeric: tabular-nums; }

/* the receipt row that actually values this copy -- the rest is context */
.receipt .rc-row.applies .src, .receipt .rc-row.applies .price {
  color: var(--accent-soft-ink); font-weight: 600; }
.receipt .rc-row.applies { background: var(--accent-soft);
  border-radius: var(--r-control); margin: 0 -8px; padding-left: 8px;
  padding-right: 8px; }

/* ---- price trend: averages and direction, never individual sale receipts ---- */
.trend { margin-top: var(--sp-3); background: var(--surface);
  border: 1px solid var(--line-soft); border-radius: var(--r-card);
  padding: var(--sp-3) var(--sp-4); }
.trend .t-top { display: flex; align-items: baseline; gap: 8px; }
.trend .t-label { font-size: var(--fs-label); font-weight: var(--fw-label);
  color: var(--text-2); }
.trend .t-delta { margin-left: auto; font-family: var(--font-data);
  font-weight: 600; font-size: var(--fs-sub);
  font-variant-numeric: tabular-nums; }
.trend .t-delta.up { color: var(--gain); }
.trend .t-delta.down { color: var(--loss); }
.trend .t-delta.flat { color: var(--text-3); }
.trend svg { display: block; width: 100%; height: 54px; margin-top: 10px;
  overflow: visible; }
.trend .t-note { font-size: var(--fs-caption); color: var(--text-3);
  margin-top: 8px; }

/* ---- Lab: diagnostics, deliberately plain. Hidden unless toggled. ---- */
.lab { display: none; margin-top: var(--sp-4); border-top: 1px solid var(--line);
  padding-top: var(--sp-3); }
body.lab-open .lab { display: block; }
.lab .lab-head { font-size: var(--fs-micro); font-weight: 600;
  text-transform: uppercase; letter-spacing: .07em; color: var(--accent);
  margin-bottom: var(--sp-2); }
.lab-grid { display: grid; grid-template-columns: 110px 1fr; gap: var(--sp-3);
  align-items: start; }
.lab-grid img { width: 100%; border-radius: var(--r-art-mini); display: block;
  box-shadow: inset 0 0 0 1px var(--line); }
.lab-grid .cap { font-size: var(--fs-micro); color: var(--text-3);
  margin-top: 4px; text-align: center; }
.lab table { width: 100%; border-collapse: collapse; font-family: var(--font-data);
  font-size: 11px; color: var(--text-2); }
.lab td { padding: 3px 6px 3px 0; vertical-align: top;
  border-bottom: 1px dashed var(--line-soft); }
.lab td.k { color: var(--text-3); white-space: nowrap; width: 1%; }
.lab td.n { font-variant-numeric: tabular-nums; text-align: right; width: 1%; }

/* ---- tab bar: solid, pinned; components.css owns the look ---- */
.tab-bar { position: fixed; bottom: 0; left: 0; right: 0;
  max-width: 430px; margin: 0 auto; z-index: 10;
  padding: 6px 0 max(10px, env(safe-area-inset-bottom)); }
body.lab-open .tab-bar { max-width: 1080px; }
.tab { background: none; border: none; cursor: pointer;
  color: var(--text-2);  /* text-3 fails AA on paper for inactive labels (axe) */ }

/* ---- the holo moment: three events only (DESIGN.md §2) ---- */
.moment { position: fixed; inset: 0; z-index: 30; display: none;
  align-items: center; justify-content: center; padding: var(--sp-5);
  background: rgba(4, 8, 16, .72); }
.moment.show { display: flex; }
.moment-card { width: 100%; max-width: 330px; border-radius: var(--r-card);
  padding: 2px; text-align: center; }
.moment-inner { background: var(--surface); border-radius: calc(var(--r-card) - 2px);
  padding: var(--sp-5) var(--sp-4); }
.moment .kicker { font-size: var(--fs-label); font-weight: var(--fw-label);
  color: var(--text-2); }
.moment .line { font-family: var(--font-display); font-size: var(--fs-display);
  font-weight: 600; letter-spacing: -.02em; margin-top: 6px; }
.moment .note { font-size: var(--fs-sub); color: var(--text-2); margin-top: 6px; }
.moment .what { font-size: var(--fs-caption); color: var(--text-3);
  margin-top: var(--sp-3); }
.moment .btn { margin-top: var(--sp-4); }
/* a completed set announces itself at the top of its own page */
.set-complete { border-radius: var(--r-control); padding: 2px;
  margin-top: var(--sp-3); }
.set-complete b { display: block; background: var(--surface);
  border-radius: calc(var(--r-control) - 2px); padding: 10px 14px;
  font-family: var(--font-display); font-size: var(--fs-sub); text-align: center; }
/* A PSA 10's grade chip is itself a holo moment. This must restate
   background-image AND background-size: `.slab-label .grade` sets the
   `background` SHORTHAND, which resets both to their initial values and
   outranks the plain `.holo` class -- the chip rendered flat green. */
.slab-label .grade.holo {
  background-color: transparent;
  background-image: var(--holo);
  background-size: 220% 220%;
  color: #0B1120;
}

/* ---- beta: the full app on the tester's OWN binder (task #60).
   Only the Lab (engine internals) stays private. ---- */
body.beta .lab-toggle { display: none; }

.toast { position: fixed; left: 50%; bottom: 92px; transform: translateX(-50%);
  background: var(--surface); border: 1px solid var(--line);
  border-radius: var(--r-pill); padding: 10px 20px; font-size: var(--fs-sub);
  display: none; z-index: 20; }
.toast.show { display: block; }

/* offline pill goes ACCENT (a feature, not an apology) + skeleton shapes */
.pill.offline-live { background: var(--accent-soft);
  color: var(--accent-soft-ink); cursor: pointer; }
.set-pocket-sk { aspect-ratio: 63 / 88; border-radius: var(--r-art); }

/* ---- the shelf + the ladder (DESIGN.md v2.8) ---- */
.set-shelf { display: grid; grid-template-columns: repeat(3, 1fr);
  gap: 10px; margin-top: var(--sp-3); }
.set-pocket { position: relative; aspect-ratio: 63 / 88; overflow: hidden;
  border: 1.5px dashed var(--line); border-radius: var(--r-art);
  background: var(--surface); cursor: pointer; padding: 0; color: inherit;
  font: inherit; text-align: center; }
.set-pocket .fill { position: absolute; left: 0; right: 0; bottom: 0;
  display: block; background: var(--accent-soft);
  border-top: 2px solid var(--accent);
  transition: height var(--mo-deliberate) var(--ease-screen); }
.set-pocket.nearly { border-color: var(--warn); border-style: solid; }
.set-pocket.complete { border-color: transparent; }
.set-pocket .fill.holo { border-top: none; }
.set-pocket .sp-in { position: relative; display: flex; flex-direction: column;
  align-items: center; justify-content: center; height: 100%; gap: 3px;
  padding: 6px; }
.sp-name { font-family: var(--font-display); font-weight: 600;
  font-size: var(--fs-micro); line-height: 1.2; max-height: 2.4em;
  overflow: hidden; }
.sp-count { font-weight: 600; font-size: var(--fs-caption);
  font-variant-numeric: tabular-nums; }
.sp-togo { font-family: var(--font-data); font-size: var(--fs-micro);
  color: var(--warn-ink); background: var(--warn-soft);
  border-radius: var(--r-pill); padding: 1px 7px; }
.set-pocket.complete .sp-togo { color: var(--accent-soft-ink);
  background: var(--accent-soft); }
.ladder-strip { display: grid; grid-template-columns: repeat(5, 1fr);
  gap: 8px; margin-top: var(--sp-2); }
.ladder-strip .pocket { aspect-ratio: 63 / 88; }
.ladder-sum { margin-top: var(--sp-2); font-family: var(--font-data);
  font-size: var(--fs-caption); color: var(--text-2);
  font-variant-numeric: tabular-nums; }

/* ---- first run: one screen, then the first lock is the tutorial ---- */
.firstrun { min-height: 74vh; display: flex; flex-direction: column;
  align-items: center; justify-content: center; text-align: center;
  gap: var(--sp-3); padding: var(--sp-5); }
.fr-mark { width: 84px; height: 84px; color: var(--accent); }
.fr-word { font-family: var(--font-display); font-weight: 800;
  font-size: var(--fs-display); letter-spacing: -.02em; }
.fr-line { color: var(--text-2); font-size: var(--fs-body); }
.firstrun .btn { min-height: var(--tap-primary); width: 100%;
  max-width: 320px; margin-top: var(--sp-4); font-size: var(--fs-body); }
.fr-note { color: var(--text-3); font-size: var(--fs-caption); }
#family-sheet input { margin-top: 4px; }
#family-sheet .rowbtns { margin-top: var(--sp-4); }

/* ---- primary scan action: bottom half, kid-thumb reach (DESIGN.md §7) ---- */
.scan-cta { position: fixed; bottom: 96px; left: 50%;
  transform: translateX(-50%); z-index: 9;
  width: min(430px, 100vw); padding: 0 var(--sp-4); box-sizing: border-box; }
.scan-cta .btn { width: 100%; min-height: 56px; font-size: var(--fs-body); }

/* ---- focus rings for app-shell controls (components.css covers its own) ---- */
.h-row:focus-visible, .set-row:focus-visible, .chip:focus-visible,
.linkish:focus-visible, .qty-btn:focus-visible, .lab-toggle:focus-visible,
.drop:focus-visible, .sweep-slots .slot:focus-visible {
  outline: 2px solid var(--accent); outline-offset: 2px; }

/* ---- scanning overlay (2026-08-15): obvious progress, never a spinner.
   A card-shaped frame with a light sweep -- the same shape the answer
   arrives in -- plus staged status text. Backdrop dims the app so it is
   unmistakable that work is happening and no input is needed. ---- */
#scan-overlay { position: fixed; inset: 0; z-index: 60; display: flex;
  flex-direction: column; align-items: center; justify-content: center;
  gap: 18px; background: color-mix(in srgb, var(--canvas) 88%, transparent);
  backdrop-filter: blur(5px); -webkit-backdrop-filter: blur(5px); }
#scan-overlay.hidden { display: none; }
/* THE LIVING LOGO. The arcs turn while the engine looks; the card at the
   centre never moves, because the card is the subject and the reticle is
   the search. On a lock the arcs snap home on the card spring
   (--ease-card) and stop — the same physics the identified card uses
   when it seats into its sleeve. */
.ps-live { width: 132px; height: 132px; color: var(--accent); display: block; }
.ps-live .ps-arcs { transform-origin: 48px 48px;
  animation: ps-hunt 2.6s linear infinite; }
@keyframes ps-hunt { to { transform: rotate(360deg); } }
.ps-live.snap .ps-arcs {
  animation: ps-lock var(--mo-standard) var(--ease-card) both; }
@keyframes ps-lock {
  from { transform: rotate(-24deg) scale(1.10); }
  to   { transform: rotate(0deg) scale(1); }
}
.scan-stage { font-family: var(--font-display); font-weight: 600;
  color: var(--text); font-size: var(--fs-body); text-align: center;
  padding: 0 24px; }
@media (prefers-reduced-motion: reduce) {
  /* static arcs; the lock's haptic and sound still fire, so certainty
     survives when the motion does not (DESIGN.md v2.2) */
  .ps-live .ps-arcs, .ps-live.snap .ps-arcs { animation: none; }
}
.scan-cta { display: flex; flex-direction: column; align-items: center;
  gap: 6px; }
.scan-cta .linkish { font-size: var(--fs-micro); color: var(--text-2);
  background: none; border: none; cursor: pointer;
  text-decoration: underline; text-underline-offset: 3px; }

/* ---- the Living Binder home (task #70) ---- */
#home-tiles { margin-top: 18px; }
.home-sec { display: flex; align-items: baseline;
  font-family: var(--font-display); font-weight: 650;
  font-size: var(--fs-micro); color: var(--text-2);
  margin: 16px 2px 8px; }
.home-more { margin-left: auto; background: none; border: none;
  color: var(--accent); font: inherit; font-weight: 600; cursor: pointer;
  min-height: var(--tap-min); display: inline-flex; align-items: center;
  padding: 0 4px; margin-right: -4px; }
.home-pulls { display: grid; grid-template-columns: repeat(3, 1fr);
  gap: 8px; }
.home-pull { position: relative; cursor: pointer; }
.home-pull img { width: 100%; aspect-ratio: 63/88; object-fit: cover;
  border-radius: 8px; background: var(--surface); display: block; }
/* The price sits BELOW the art, never on it (audit 2026-08-17). A chip
   laid over the bottom of the card is a crop by another name -- and on
   paper the translucent chip over bright art was the app's worst
   contrast. Card art is sacred: 63:88, uncropped, nothing on top. */
.home-pull .pp { display: block; margin-top: 6px; text-align: center;
  font-family: var(--font-data); font-variant-numeric: tabular-nums;
  font-size: var(--fs-caption); color: var(--text-2);
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.home-mrows { padding: 6px 12px; }
.home-mrows .mrow { display: flex; align-items: center; gap: 10px;
  padding: 7px 2px; }
.home-mrows .mrow img { width: 34px; aspect-ratio: 63/88;
  object-fit: cover; border-radius: 4px; background: var(--surface); }
.home-mrows .nm, #home-ladder .nm { font-size: 13px; font-weight: 600; }
.home-mrows .st, #home-ladder .st { font-size: 11px; color: var(--text-3);
  margin-top: 2px; }
.home-mrows .d { margin-left: auto; font-family: var(--font-data);
  font-variant-numeric: tabular-nums; font-size: 13px; font-weight: 700; }
.home-mrows .d.up { color: var(--gain); }
.home-mrows .d.dn { color: var(--loss); }
#home-ladder { padding: 12px 14px; }
.ladder-head { display: flex; justify-content: space-between;
  align-items: baseline; }
.ladder-bar { display: flex; gap: 3px; margin-top: 8px; }
.ladder-bar i { flex: 1; height: 7px; border-radius: 2px;
  background: var(--surface); border: 1px solid var(--line-soft); }
.ladder-bar i.f { background: var(--accent); border-color: var(--accent); }

/* promise state: an empty binder shows what a scan gives, our sleeve
   motif -- no borrowed art, no fake urgency */
#home-promise { position: relative; height: 210px; margin-top: 14px; }
.promise-backs { position: absolute; inset: 0; display: flex;
  align-items: center; justify-content: center; }
.promise-backs .cardback { width: 96px; aspect-ratio: 63/88;
  border-radius: 10px; position: absolute;
  background: linear-gradient(150deg,
    color-mix(in srgb, var(--surface) 80%, var(--accent)) ,
    var(--surface) 55%);
  border: 1.5px solid var(--line);
  display: flex; align-items: center; justify-content: center; }
.promise-backs .cardback b { font-size: 26px; color: var(--accent);
  font-family: var(--font-display); }
.promise-backs .c1 { transform: rotate(-14deg) translateX(-72px) translateY(6px); }
.promise-backs .c2 { transform: rotate(-2deg) translateY(-8px); width: 104px; }
.promise-backs .c3 { transform: rotate(13deg) translateX(74px) translateY(8px); }
.promise-cap { position: absolute; bottom: 0; left: 0; right: 0;
  text-align: center; font-size: 12.5px; color: var(--text-2);
  line-height: 1.5; }

/* phones can't drag-and-drop: the drop zone is desktop furniture. On
   narrow screens the camera button + photos link cover both paths and
   the reclaimed space lifts the user's own cards to the top. */
@media (max-width: 700px) {
  .drop { display: none; }
}
/* the fixed CTA floats over the page; let the last tile scroll clear */
#home-tiles { padding-bottom: 170px; }

/* ---- final design pass 2026-08-17 ---- */

/* top bar: one control, drawn not typed */
.icon-btn { width: var(--tap-min); height: var(--tap-min);
  display: flex; align-items: center;
  justify-content: center; background: none; border: 1px solid var(--line);
  border-radius: var(--r-pill); color: var(--text-2); cursor: pointer;
  padding: 0; }
.icon-btn svg { width: 20px; height: 20px; }
.icon-btn:hover { color: var(--text); border-color: var(--text-3); }

/* labelled settings rows, replacing four glyph buttons in the top bar */
.menu-rows { display: flex; flex-direction: column; margin-top: var(--sp-2);
  border-radius: var(--r-control); overflow: hidden;
  border: 1px solid var(--line-soft); }
.menu-row { display: flex; align-items: center; justify-content: space-between;
  gap: 12px; width: 100%; min-height: var(--tap-min); padding: 0 var(--sp-4);
  background: var(--raised); border: none; border-bottom: 1px solid var(--line-inset);
  color: var(--text); font: inherit; font-size: var(--fs-sub);
  cursor: pointer; text-align: left; }
.menu-row:last-child { border-bottom: none; }
.menu-row .mr-val { color: var(--text-2); font-family: var(--font-data);
  font-size: var(--fs-caption); }

/* primary action: our own icon, never an OS emoji */
.btn-ico { width: 20px; height: 20px; flex: 0 0 auto; }
.scan-alts { display: flex; align-items: center; justify-content: center;
  gap: 10px; margin-top: 2px; }
.scan-alts .alt-dot { color: var(--text-3); }
/* 44pt floor is a craft-floor rule, not a suggestion: the hit area grows
   with padding while the type stays quiet */
.linkish.quiet { color: var(--text-2); font-size: var(--fs-caption);
  text-decoration: none; min-height: var(--tap-min); padding: 0 8px;
  display: inline-flex; align-items: center; }
.linkish.quiet:hover { color: var(--accent); }

/* the price and its source, together, under the identified card */
.hero-price { display: flex; align-items: baseline; justify-content: center;
  gap: 8px; margin-top: var(--sp-3); }
.hero-price .price { font-family: var(--font-data); font-weight: 700;
  font-size: var(--fs-title); font-variant-numeric: tabular-nums;
  letter-spacing: -.01em; }
.hero-price .hp-cap { font-size: var(--fs-caption); color: var(--text-2); }
.hero .provenance { display: block; margin-top: 3px; text-align: center;
  font-family: var(--font-data); font-size: var(--fs-mono-prov);
  color: var(--text-3); }

/* a certain answer: one action, bottom half, no radio-of-one */
.lock-act { display: flex; flex-direction: column; align-items: center;
  gap: 2px; margin-top: var(--sp-5); }
.lock-act .btn.primary { width: 100%; }

/* UNSURE: shrink the evidence so the decision fits on one screen */
.compact-hero .hero img { width: 116px; }
.compact-hero .hero { margin: var(--sp-2) 0 var(--sp-2); }
.compact-hero .hero .hname { font-size: var(--fs-body); margin-top: var(--sp-2); }
.compact-hero .hero .hmeta { font-size: var(--fs-caption); }

/* dead end: the subject still on screen, the way out in the thumb zone */
.dead-end { display: flex; flex-direction: column; align-items: center;
  gap: var(--sp-6); padding: var(--sp-6) 0 0; }
.de-mark { width: 92px; color: var(--line); }
.de-acts { width: 100%; }
/* headline, then the one line, then the subject, then the way out --
   the source order put the label last, which is the same inverted
   hierarchy the old screen was criticised for */
.is-dead { display: flex; flex-direction: column;
  min-height: calc(100vh - 320px); }
.is-dead .hero, .is-dead .cands, .is-dead .lock-act { display: none; }
/* "Try again" is the exit here; a second one at the top is noise */
.is-dead #scan-again { display: none; }
.is-dead #cand-label { order: 1; display: block; text-align: center;
  font-family: var(--font-display); font-size: var(--fs-title);
  font-weight: 600; color: var(--text); margin-top: var(--sp-6); }
.is-dead #guide { order: 2; text-align: center; max-width: 28ch;
  margin: var(--sp-2) auto 0; color: var(--text-2); }
.is-dead .dead-end { order: 3; margin-top: auto; }
.de-acts .btn { flex: 1; }
/* the ladder's next-three: the app's most action-shaped content, and what
   now occupies the third of the home screen that was empty */
.ladder-next { display: grid; grid-template-columns: repeat(3, 1fr);
  gap: var(--sp-3); margin-top: var(--sp-4); }
.ln-card { cursor: pointer; text-align: center; }
.ln-card img { width: 100%; aspect-ratio: 63/88; object-fit: cover;
  border-radius: var(--r-art-mini); display: block;
  /* not owned yet: present but dimmed, the empty pocket made literal */
  opacity: .38; filter: saturate(.5);
  transition: opacity var(--mo-instant) var(--ease-screen),
              filter var(--mo-instant) var(--ease-screen); }
.ln-card:hover img { opacity: .85; filter: saturate(1); }
.ln-num { font-family: var(--font-data); font-size: var(--fs-mono-prov);
  color: var(--text-3); margin-top: 5px; }
.ln-price { font-family: var(--font-data); font-size: var(--fs-caption);
  color: var(--text-2); font-variant-numeric: tabular-nums; }
#home-ladder .st { margin-top: var(--sp-3); }

/* ---- the viewfinder (task #75): a camera, not a form ---- */
.vf { position: fixed; inset: 0; z-index: 80; background: #000;
  display: flex; align-items: center; justify-content: center; }
.vf.hidden { display: none; }
.vf video { position: absolute; inset: 0; width: 100%; height: 100%;
  object-fit: cover; }
/* the scrim darkens everything OUTSIDE the card window, so the eye is
   told where the card goes without a word */
/* the window is CARD-SHAPED, not an ellipse: the hole in the scrim is the
   63:88 rectangle the whole system is built from, cut with a spread
   shadow so the geometry is exact and cheap */
.vf-scrim { display: none; }
/* card-shaped reticle in the system's own geometry, 63:88 */
/* Sized by BOTH axes. It used to be width-only -- min(76vw, 310px) --
   which on a short viewport left no room between the reticle and the
   HUD, and the status pill landed on top of the card being framed.
   The reticle is centred at 44%, so its bottom sits at 0.44H + h/2;
   clearing the HUD (which is anchored 104px up, ~67px tall, plus a 16px
   gap) needs h <= 1.12H - 374px. svh, not vh: with Safari's toolbars
   showing, vh overstates the height actually available. */
.vf-reticle { position: absolute; top: 44%; left: 50%;
  transform: translate(-50%, -50%);
  width: max(160px, min(76vw, 310px, calc((112vh - 374px) * 63 / 88)));
  width: max(160px, min(76vw, 310px, calc((112svh - 374px) * 63 / 88)));
  aspect-ratio: 63/88; pointer-events: none;
  border-radius: 14px;
  box-shadow: 0 0 0 100vmax rgba(4, 8, 16, .58); }
.vf-reticle .c { position: absolute; width: 30px; height: 30px;
  border: 3px solid rgba(255, 255, 255, .82); border-radius: 4px;
  transition: border-color var(--mo-instant) var(--ease-screen); }
.vf-reticle .tl { top: 0; left: 0; border-right: 0; border-bottom: 0;
  border-top-left-radius: 12px; }
.vf-reticle .tr { top: 0; right: 0; border-left: 0; border-bottom: 0;
  border-top-right-radius: 12px; }
.vf-reticle .bl { bottom: 0; left: 0; border-right: 0; border-top: 0;
  border-bottom-left-radius: 12px; }
.vf-reticle .br { bottom: 0; right: 0; border-left: 0; border-top: 0;
  border-bottom-right-radius: 12px; }
/* armed = the engine can see it. The corners close in ONE spring: this
   happens to a card, so it springs (DESIGN.md v2.2) */
.vf-reticle.armed .c { border-color: var(--accent); }
.vf-reticle.armed { animation: vf-arm var(--mo-standard) var(--ease-card); }
@keyframes vf-arm {
  0% { transform: translate(-50%, -50%) scale(1.04); }
  100% { transform: translate(-50%, -50%) scale(1); }
}
/* Anchored ABOVE the control bar, not at a fraction of the viewport.
   top: calc(44% + 26vh) positioned the HUD independently of the bar, so
   on a short viewport the two collided and the hint rendered underneath
   the shutter (reported on an iPhone, 2026-08-25). Safari's toolbars
   make the real viewport shorter than any desktop window this was
   checked in, which is why it looked fine here and broke there.
   The bar sits 20px up and is 68px tall, so 104px clears it with room. */
.vf-hud { position: absolute; left: 0; right: 0;
  bottom: calc(104px + env(safe-area-inset-bottom, 0px));
  text-align: center; padding: 0 var(--sp-6); pointer-events: none; }
.vf-line { display: inline-block; font-family: var(--font-display);
  font-size: var(--fs-body); font-weight: 600; color: #fff;
  background: rgba(4, 8, 16, .55); border-radius: var(--r-pill);
  padding: 8px 16px; backdrop-filter: blur(6px); }
.vf-bar { position: absolute; left: 0; right: 0;
  bottom: calc(20px + env(safe-area-inset-bottom, 0px));
  display: flex; align-items: center; justify-content: space-between;
  padding: 0 var(--sp-6); }
/* The shutter. Auto-capture still works and most people will never
   press this -- it exists so that anyone who wants to decide for
   themselves can, without hunting for a setting. Deliberately the
   familiar ring-and-disc so it needs no label. */
.vf-shutter {
  width: 68px; height: 68px; border-radius: var(--r-pill);
  background: transparent; border: 3px solid rgba(255,255,255,.9);
  padding: 0; cursor: pointer; flex: 0 0 auto;
  display: grid; place-items: center;
  -webkit-tap-highlight-color: transparent;
}
.vf-shutter span {
  width: 54px; height: 54px; border-radius: var(--r-pill);
  background: #fff; display: block;
  transition: transform var(--mo-instant) var(--ease-screen);
}
.vf-shutter:active span { transform: scale(.86); }
.vf-shutter:focus-visible { outline: none; box-shadow: var(--focus-ring); }
@media (prefers-reduced-motion: reduce) {
  .vf-shutter span { transition: none; }
}

/* The hint sits with the guidance line now, not in the bar -- the bar
   belongs to the shutter. */
.vf-hud .vf-hint { margin-top: var(--sp-2); text-align: center; }

.vf-ghost { width: var(--tap-primary); height: var(--tap-primary);
  border-radius: 50%; border: 1px solid rgba(255, 255, 255, .28);
  background: rgba(4, 8, 16, .45); color: #fff; cursor: pointer;
  display: flex; align-items: center; justify-content: center; padding: 0; }
.vf-ghost svg { width: 24px; height: 24px; }
/* This sits over whatever the camera is pointed at -- wood grain, a
   patterned mat, anything -- so it carries its own contrast rather than
   relying on the page behind it. */
.vf-hint { font-size: var(--fs-caption); color: rgba(255, 255, 255, .88);
  text-shadow: 0 1px 3px rgba(0, 0, 0, .9), 0 0 10px rgba(0, 0, 0, .55); }
@media (prefers-reduced-motion: reduce) { .vf-reticle.armed { animation: none; } }

/* names give way, money never does -- but they give way with an ellipsis,
   not a stray glyph (audit: "Mega Charizard >" truncated mid-word) */
.mrow .nm, .home-mrows .nm, .candidate .c-name, .hrow .nm {
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
  min-width: 0; }
.mrow, .home-mrows .mrow, .hrow { min-width: 0; }
.mrow > div, .home-mrows .mrow > div { min-width: 0; }
/* a set you have ANY of never renders as an empty pocket */
.pocket .fill { min-height: 3px; }

/* ---- Pages as objects, not rows (founder 2026-08-17) ----
   A page is a picture of nine cards; the list used to be its name and a
   fraction. Now each page wears the art of its first pocket. */
.page-shelf { display: grid; grid-template-columns: 1fr 1fr;
  gap: var(--sp-4); }
.page-card { display: block; width: 100%; padding: 0; text-align: left;
  background: var(--surface); border: 1px solid var(--line-soft);
  border-radius: var(--r-card); overflow: hidden; cursor: pointer;
  transition: border-color var(--mo-instant) var(--ease-screen); }
.page-card:hover { border-color: var(--accent); }
.pc-art { aspect-ratio: 63/88; background: var(--raised);
  display: flex; align-items: center; justify-content: center; }
.pc-art img { width: 100%; height: 100%; object-fit: cover; display: block; }
.pc-art.empty { border-bottom: 1px dashed var(--line); }
.pc-empty { font-size: var(--fs-micro); color: var(--text-3); }
.pc-meta { padding: var(--sp-3); }
.pc-name { font-family: var(--font-display); font-weight: 600;
  font-size: var(--fs-sub); color: var(--text);
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.pc-sub { font-size: var(--fs-caption); color: var(--text-2); margin-top: 2px;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.pc-count { font-family: var(--font-data); font-size: var(--fs-mono-prov);
  font-variant-numeric: tabular-nums; color: var(--text-3); margin-top: 4px; }

/* the set picker */
.set-choices { display: flex; flex-direction: column; margin: var(--sp-3) 0;
  max-height: 46vh; overflow-y: auto; border-radius: var(--r-control);
  border: 1px solid var(--line-soft); }
.set-choice { display: flex; align-items: center; justify-content: space-between;
  gap: 12px; width: 100%; min-height: var(--tap-min); padding: 0 var(--sp-4);
  background: var(--raised); border: none;
  border-bottom: 1px solid var(--line-inset);
  color: var(--text); font: inherit; font-size: var(--fs-sub);
  cursor: pointer; text-align: left; }
.set-choice:last-child { border-bottom: none; }
.set-choice:hover { background: var(--raised-2); }
.sc-name { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.sc-ct { font-family: var(--font-data); font-size: var(--fs-caption);
  color: var(--text-2); flex: 0 0 auto; }

/* The commit button never scrolls away (2026-08-17). On a lock the card,
   its price with provenance, and the printing/attribution chips are all
   worth showing -- which pushed Add past the fold. The chips scroll; the
   action stays in the thumb zone, which is the craft-floor rule. */
.addbar.show #add-btn { position: fixed; left: 50%; transform: translateX(-50%);
  bottom: calc(78px + env(safe-area-inset-bottom, 0px));
  width: min(398px, calc(100vw - 32px)); z-index: 40;
  box-shadow: 0 6px 24px rgba(4, 8, 16, .45); }
.addbar.show { padding-bottom: 84px; }
/* an identified card the catalog can't describe still shows the card:
   the headline says we found it, so hiding it would contradict itself */
.is-dead.kept-hero .hero { display: block; }
.is-dead.kept-hero .hero img { width: 132px; }
.is-dead.kept-hero .de-mark { display: none; }

/* ---- the Pullseye mark and wordmark (brand handoff 2026-08-20) ----
   The mark lives in ONE <symbol>; every appearance is a <use>. It draws
   in currentColor so it takes the colour of its context — Prism in the
   header, whatever the sheet uses elsewhere — which is what makes it
   safe on any background per MARK.md. */
.ps-mark { width: 28px; height: 28px; flex: 0 0 auto; color: var(--accent);
  display: block; }
/* clear space: one card-width all round (MARK.md) */
.topbar .ps-mark { margin-right: 2px; }

/* Wordmark: Bricolage ExtraBold, Title case, Prism period. The period IS
   the bullseye — it echoes the mark's centre dot — and is NEVER any
   colour but Prism (TYPE.md). */
.ps-word { font-family: var(--font-display); font-weight: 800;
  letter-spacing: -.015em; color: var(--text); }
.ps-dot { color: var(--accent); }

.fr-mark { width: 96px; height: 96px; color: var(--accent); }

/* ---- printing hint (vintage runs) ----
   Shown only when a card has two or more print runs to choose between.
   It is reference material, not a warning: quiet surface, and the RUN
   NAME is the anchor because that is what the chips above say. */
.printing-hint {
  /* Positive top margin: the chips above WRAP to two rows on a phone,
     and a negative margin slid this box up underneath the wrapped row. */
  margin: var(--sp-2) 0 var(--sp-3);
  padding: 12px 14px; background: var(--raised);
  border: 1px solid var(--line); border-radius: var(--r-control);
}
.printing-hint .ph-lead {
  font-size: var(--fs-caption); line-height: var(--lh-caption);
  color: var(--text-2); margin-bottom: 10px;
}
/* Name over tell, not beside it. Two columns at 375px left the tell a
   ~150px gutter and broke "drop-shadow" across lines mid-word. */
.printing-hint .ph-row { margin-top: 9px; }
.printing-hint .ph-row:first-of-type { margin-top: 0; }
.printing-hint .ph-name {
  display: block;
  font-size: var(--fs-caption); font-weight: 600; color: var(--text);
}
.printing-hint .ph-tell {
  display: block; margin-top: 1px;
  font-size: var(--fs-caption); line-height: 1.4; color: var(--text-2);
}
