
  :root{
    --canvas:#08080a; --app-bg:#0f0f12; --card:#1a1a1f; --card-locked:#141417;
    --hairline:rgba(255,255,255,.08); --hairline-strong:rgba(255,255,255,.14);
    /* Structural labels (section eyebrows, card captions). Warm-biased so it
       reads as chosen rather than a default grey - but deliberately NOT gold:
       gold is reserved for actions and money, and it cannot mean "important"
       if it also marks every heading on the screen. */
    --label:#8b8175;
    /* #67676e measured 3.09:1 on --card / 3.41:1 on --app-bg - both below the
       4.5:1 text minimum. #8d8d94 clears it on either surface (~5.3:1 /
       5.8:1) without changing which 35+ rules use it - they all read this
       one token. Accessibility audit, Sep 2026. */
    --text:#f4f4f6; --text-2:#9a9aa2; --text-3:#8d8d94;
    --gold:#e6b450; --gold-deep:#c99a3a; --gold-soft:rgba(230,180,80,.14);
    --red:#e11c36; --green:#34d65f; --blue:#5aa9e6; --blue-deep:#2569b8;
    /* --red stays the vivid fill colour for badges/buttons (white text sits
       on top, contrast is fine there). --red-text is a lighter red for RED
       TEXT on the app's dark surfaces only - #e11c36 as text measured
       3.65:1/4.03:1, below 4.5:1; #ef4a5f clears it (~4.8:1/5.3:1). Two
       tokens because fills and text have different contrast needs against
       the same dark background. */
    --red-text:#ef4a5f;
    --cta-bg:linear-gradient(135deg,#e9bd58,#d2a23c); --cta-text:#1a1407;
  }
  *{box-sizing:border-box;margin:0;padding:0;-webkit-tap-highlight-color:transparent}
  /* Keyboard focus ring (section 06, Sep 2026 accessibility pass): the app
     had no focus indicator anywhere. :focus-visible only matches a
     keyboard (or equivalent, e.g. Switch Control) focus, never a pointer
     tap or click, so a touch user sees nothing new here - this is purely
     for someone who just Tabbed to makeKeyboardOperable()'s role="button"
     controls, or to a real link/button/input. */
  :focus-visible{outline:2px solid var(--gold);outline-offset:2px}
  body{font-family:'Inter',-apple-system,sans-serif;background:var(--canvas);color:var(--text);min-height:100vh;color:var(--text);
    display:flex;flex-direction:column;align-items:center;padding:26px 14px 48px}
  .phone{width:100%;max-width:420px;background:#000;border-radius:46px;padding:11px;box-shadow:0 18px 50px rgba(0,0,0,.55)}
  .screen-frame{background:var(--app-bg);border-radius:36px;overflow:hidden;position:relative}
  /* overflow-x is NOT decorative here. Setting only overflow-y makes the
     other axis compute to `auto` rather than staying `visible` (CSS
     overflow spec), so any stray pixel of width turns this box into a
     horizontally swipeable pane. Owner, 30 Aug 2026: "there are some
     screens that scroll left to right when i swipe my fingers on the
     phone that should otherwise be static". This is the guard; the actual
     4px came from the header (see .logo-img). Nothing in this app is
     meant to pan sideways - the rails that do scroll horizontally are
     their own nested boxes with their own overflow. */
  .scroll{height:78vh;overflow-y:auto;overflow-x:hidden;padding-bottom:84px}
  .scroll::-webkit-scrollbar{width:0}
  /* On real phones the app fills the screen edge-to-edge; the phone-mockup
     frame is desktop-only presentation. Overlays and the bottom nav are
     absolutely positioned inside .screen-frame, so they follow it. */
  /* ---------------------------------------------------------------------
     STAFF PILOT: master catalog import gets a real desktop layout at
     laptop-and-wider widths. Scoped entirely to #o-masterimport by id -
     every other screen, staff or collector, is completely unaffected
     regardless of viewport width. Colors reuse the app's own tokens
     (--card, --hairline, --gold, --text-*) so this reads as the same app
     with more room, not a different visual language bolted on.
     --------------------------------------------------------------------- */
  @media (min-width:900px){
    #o-masterimport{position:fixed!important;inset:0;width:100vw;height:100vh;max-width:none;border-radius:0;z-index:500;background:var(--app-bg)}
    #o-masterimport>.topbar{display:none}
    #o-masterimport>.ov-pad{display:grid;grid-template-columns:220px 1fr;grid-template-rows:1fr;height:100vh;max-width:none;padding:0;box-sizing:border-box}
    #o-masterimport #mci-wide-rail{display:flex!important}
    #o-masterimport #adm-mci-body{padding:30px 36px 60px;overflow-y:auto;height:100vh;box-sizing:border-box;max-width:920px}
    #o-masterimport .mci-landing-grid{display:grid;grid-template-columns:1fr 1fr;gap:16px;align-items:start}
    #o-masterimport .mci-landing-grid>div{margin-bottom:0!important}

    /* The other three tools the master-import rail links to. Same escape,
       but these keep their own topbar (it carries their back button and
       their in-screen navigation, unlike the importer's, which the rail
       replaced). Content is width-capped so long lists stay readable
       rather than stretching to 1400px. */
    /* Extended to the data-dense staff queues. Only three screens had this, so
       on a 1280px desktop the Orders, Disputes and Users screens sat inside a
       398px phone frame: item names truncated to about ten characters
       ("1970 Trinid..."), and staff scrolled inside a ~390px window on an 800px
       screen to reach the action buttons. The mechanism already existed and
       works; this is adding names to the selector list. Collector screens are
       deliberately NOT included - the phone frame is right for them. */
    #o-adminvarieties, #o-adminvaluations, #o-adminvalues, #o-adminorders, #o-admindisputes, #o-adminusers, #o-adminlistings, #o-adminoffers, #o-admin-listing-review, #o-adminaudit, #o-adminorders-review, #o-adminquicksell, #o-adminreports, #o-admin-kyc, #o-adminphotos{
      position:fixed!important;inset:0;width:100vw;height:100vh;max-width:none;border-radius:0;z-index:500;background:var(--app-bg);overflow-y:auto
    }
    #o-adminvarieties>.topbar, #o-adminvaluations>.topbar, #o-adminvalues>.topbar, #o-adminorders>.topbar, #o-admindisputes>.topbar, #o-adminusers>.topbar, #o-adminlistings>.topbar, #o-adminoffers>.topbar, #o-admin-listing-review>.topbar, #o-adminaudit>.topbar, #o-adminorders-review>.topbar, #o-adminquicksell>.topbar, #o-adminreports>.topbar, #o-admin-kyc>.topbar, #o-adminphotos>.topbar{
      position:sticky;top:0;z-index:6;padding-left:36px;padding-right:36px
    }
    #o-adminvarieties>.ov-pad, #o-adminvaluations>.ov-pad, #o-adminvalues>.ov-pad, #o-adminorders>.ov-pad, #o-admindisputes>.ov-pad, #o-adminusers>.ov-pad, #o-adminlistings>.ov-pad, #o-adminoffers>.ov-pad, #o-admin-listing-review>.ov-pad, #o-adminaudit>.ov-pad, #o-adminorders-review>.ov-pad, #o-adminquicksell>.ov-pad, #o-adminreports>.ov-pad, #o-admin-kyc>.ov-pad, #o-adminphotos>.ov-pad{
      max-width:1040px;margin:0 auto;padding:22px 36px 70px;box-sizing:border-box
    }
  }
  @media (max-width:520px){
    body{padding:0;overflow:hidden}
    /* iPhone Safari gotcha: with inset:0 (top+bottom pinned), a fixed
       element is sized against the LARGEST viewport — the one with the
       toolbars collapsed. Since body{overflow:hidden} means the page never
       scrolls, the toolbars never collapse, and the bottom ~70-100px of the
       app (the nav) sits permanently behind Safari's bottom bar (seen on
       iPhone 12 mini). Fix: pin the TOP only and give an explicit height in
       dvh (dynamic viewport height, iOS 15.4+), which always equals the
       currently VISIBLE area and re-resolves as toolbars show/hide.
       100vh stays as the fallback for older browsers. */
    .phone{position:fixed;top:0;left:0;right:0;height:100vh;height:100dvh;max-width:none;border-radius:0;padding:0;box-shadow:none}
    .screen-frame{border-radius:0;height:100%;display:flex;flex-direction:column}
    .scroll{height:auto;flex:1;min-height:0}
    .navwrap{padding-bottom:calc(14px + env(safe-area-inset-bottom,0px))}
    .topbar{padding-top:calc(18px + env(safe-area-inset-top,0px))}
    header{padding-top:calc(18px + env(safe-area-inset-top,0px))}
  }

  header{display:flex;align-items:center;gap:11px;padding:18px 20px 14px;
    border-bottom:1px solid var(--hairline);position:sticky;top:0;z-index:5;background:var(--app-bg)}
  /* THE 4px. On the two drill-in pages (add, melt) the header carries FOUR
     children instead of three - the back chevron appears - and at 375px
     wide the row needs 399px: 40 padding + 32 back + 169 logo + 84 bell/
     mail + 32 avatar + 33 gaps + 9. The avatar was pushed to 379 and
     genuinely clipped, not merely scrolled off.

     The logo is the one flexible item (flex-shrink:1 already), but a
     replaced element's `min-width:auto` resolves to its intrinsic width,
     which silently cancels that - so it never gave up a pixel and the
     overflow went to the avatar instead. min-width:0 lets it shrink;
     object-fit keeps the wordmark in proportion while it does.

     It only shrinks where it must: 169px on the five tab roots, 145px on
     add and melt. Measured on all seven pages. */
  .logo-img{height:28px;width:auto;display:block;
    min-width:0;object-fit:contain;object-position:left center}
  /* was overflowing the 375px viewport by 4px wherever a back arrow shares the
     header row; flex:none stops it being pushed past the edge */
  .avatar{flex:none;width:32px;height:32px;border-radius:50%;background:var(--gold-soft);color:var(--gold);
    border:1px solid var(--hairline);display:grid;place-items:center;font-weight:700;font-size:13px;flex:none}
  .body{padding:18px 20px 0}
  .eyebrow{font-size:11px;font-weight:700;letter-spacing:1.3px;color:var(--text-3);text-transform:uppercase}
  .gain{color:var(--green);font-weight:600}
  .gain.up{color:var(--green)}
  /* --red-text, not --red: this is TEXT on the dark surface, not a fill. See
     the token comment above. */
  .gain.down{color:var(--red-text)}
  .gain.flat{color:var(--text-3);font-weight:600}
  .sec-head{display:flex;align-items:center;justify-content:space-between;margin:26px 0 12px}
  .seeall{color:var(--gold);font-weight:600;font-size:13px;text-decoration:none}

  /* page system */
  .page{display:none}.page.on{display:block}

  /* HOME */
  .total{background:var(--card);border:1px solid var(--hairline);border-radius:20px;padding:20px;cursor:pointer}
  .total .amount{font-size:38px;font-weight:800;letter-spacing:-1.3px;margin-top:8px;font-variant-numeric:tabular-nums}
  .total .row2{display:flex;align-items:center;gap:10px;margin-top:8px;font-size:13px;flex-wrap:wrap}
  /* Melt header: "Updated" label stacked over its timestamp, so a long
     date can never squeeze the movement indicator out of alignment. */
  .melt-updated{margin-top:9px;line-height:1.35}
  .melt-updated .mu-l{display:block;font-size:9.5px;font-weight:800;letter-spacing:.6px;text-transform:uppercase;color:var(--text-3)}
  .melt-updated .mu-v{display:block;font-size:11.5px;font-weight:600;color:var(--gold);margin-top:2px}
  .pill{font-size:11px;font-weight:600;color:var(--gold);background:var(--gold-soft);padding:3px 9px;border-radius:999px}
  /* Total collection value card - trend, stat row and its own button. */
  .tcv-chart{margin:13px 0 0;min-height:58px}
  .tcv-dates{display:flex;justify-content:space-between;font-size:10px;color:var(--text-3);margin-top:5px;font-variant-numeric:tabular-nums}
  .tcv-empty{font-size:11.5px;color:var(--text-3);line-height:1.5;padding:14px 0 4px}
  .tcv-stats{display:flex;margin-top:14px;padding-top:14px;border-top:1px solid var(--hairline)}
  .tcv-stat{flex:1;min-width:0}
  .tcv-stat + .tcv-stat{border-left:1px solid var(--hairline);padding-left:12px}
  .tcv-stat:not(:last-child){padding-right:12px}
  .tcv-stat:last-child{flex:0 0 auto}
  .tcv-sl{font-size:9.5px;font-weight:700;letter-spacing:.8px;text-transform:uppercase;color:var(--text-3);white-space:nowrap}
  .tcv-sv{font-size:14px;font-weight:700;margin-top:4px;white-space:nowrap;font-variant-numeric:tabular-nums}
  /* Was a .pill - a label style doing a button's job, 3px of vertical
     padding and no border, which is why it looked undefined on a phone and
     gave a tap target well under the ~44px minimum. */
  .tcv-btn{display:flex;align-items:center;justify-content:center;gap:8px;width:100%;min-height:46px;
    margin-top:14px;border-radius:13px;cursor:pointer;background:var(--gold-soft);
    border:1px solid rgba(230,180,80,.42);color:var(--gold);font-size:13.5px;font-weight:700;font-family:inherit}
  .tcv-btn:active{background:rgba(230,180,80,.22);border-color:rgba(230,180,80,.6)}
  /* Gold + Silver in one card: holding, live 1oz spot, and when it updated. */
  .metals-card{background:var(--card);border:1px solid var(--hairline);border-radius:16px;padding:14px}
  .metals-row{display:flex;align-items:center;gap:9px;padding:7px 0}
  .metals-row + .metals-row{border-top:1px solid var(--hairline)}
  .metals-name{font-size:13.5px;font-weight:700;flex:none}
  .metals-count{font-size:11.5px;color:var(--text-3);font-weight:600}
  .metals-spot{flex:1;min-width:0;text-align:right;font-size:12px;color:var(--text-2);font-variant-numeric:tabular-nums;white-space:nowrap}
  .metals-oz{color:var(--text-3);font-size:10.5px}
  .metals-val{flex:none;text-align:right;min-width:96px}
  .metals-val>span:first-child{display:block;font-size:15px;font-weight:800;color:var(--gold);font-variant-numeric:tabular-nums}
  .metals-val .gain{display:block;font-size:11px;margin-top:1px}
  .metals-foot{font-size:10.5px;color:var(--text-3);margin-top:9px;padding-top:9px;border-top:1px solid var(--hairline)}
  .cta{margin-top:16px;width:100%;border:none;cursor:pointer;background:var(--cta-bg);color:var(--cta-text);
    border-radius:18px;padding:16px;display:flex;align-items:center;gap:14px;text-align:left}
  .cta-ic{width:44px;height:44px;border-radius:12px;background:rgba(26,20,7,.16);display:grid;place-items:center;flex:none}
  .cta-ic svg{width:22px;height:22px}.cta-t{font-size:15px;font-weight:700}.cta-s{font-size:12px;opacity:.72;margin-top:2px}
  .grid2{display:grid;grid-template-columns:1fr 1fr;gap:12px}

  /* ===== Home dashboard cards ===== */
  .dcard{position:relative;background:var(--card);border:1px solid var(--hairline);border-radius:18px;padding:15px;cursor:pointer;display:flex;flex-direction:column;gap:0;min-height:104px}
  .dcard:active{border-color:var(--hairline-strong)}
  .dcard-ic{width:38px;height:38px;border-radius:11px;background:var(--gold-soft);color:var(--gold);display:grid;place-items:center;flex:none}
  .dcard-ic svg{width:20px;height:20px}
  .dcard-n{font-size:26px;font-weight:800;letter-spacing:-.6px;margin-top:11px;color:var(--text);font-variant-numeric:tabular-nums}
  .dcard-l{font-size:12px;color:var(--text-2);font-weight:600;margin-top:1px}
  .dcard-chev{position:absolute;right:12px;bottom:13px;color:var(--gold);opacity:.85}
  .dcard-chev svg{width:18px;height:18px;display:block}
  .dcard-badge{position:absolute;top:11px;right:11px;min-width:20px;height:20px;padding:0 6px;border-radius:999px;background:var(--red);color:#fff;font-size:11px;font-weight:700;display:none;align-items:center;justify-content:center}
  .dcard-badge.on{display:flex}
  .dcard-wide,.dcard-mgmt{flex-direction:row;align-items:center;gap:13px;min-height:0;padding:15px}
  .dcard-wide .dcard-n,.dcard-mgmt .dcard-n{margin-top:0;font-size:21px}
  .dcard-wide .dcard-l,.dcard-mgmt .dcard-l{color:var(--text-2);font-weight:500}
  .dcard-wide .dcard-chev,.dcard-mgmt .dcard-chev{position:static;margin-left:auto}
  .dcard-mgmt{border:1px solid var(--gold);background:linear-gradient(135deg,var(--gold-soft),rgba(230,180,80,0))}
  .mgmt-t{font-size:15px;font-weight:700;color:var(--text)}
  .menu-card{background:var(--card);border:1px solid var(--hairline);border-radius:18px;overflow:hidden}
  .menu-row{display:flex;align-items:center;gap:13px;padding:14px 15px;cursor:pointer}
  .menu-row:active{background:rgba(255,255,255,.03)}
  .menu-t{font-size:14.5px;font-weight:700;color:var(--text)}
  .menu-row .dcard-l{color:var(--text-2);font-weight:500}
  .menu-row .dcard-chev{position:static;margin-left:auto}
  .menu-div{height:1px;background:var(--hairline);margin:0 15px}
  .act-row{display:flex;align-items:center;gap:12px;background:var(--card);border:1px solid var(--hairline);border-radius:14px;padding:12px 13px;margin-bottom:9px;cursor:pointer}
  .act-row:active{border-color:var(--hairline-strong)}
  .act-ic{width:34px;height:34px;border-radius:10px;background:var(--gold-soft);color:var(--gold);display:grid;place-items:center;flex:none}
  .act-ic svg{width:18px;height:18px}
  .act-t{font-size:13.5px;font-weight:600;color:var(--text);overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
  .act-s{font-size:12px;color:var(--text-3);margin-top:2px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
  .metal{background:var(--card);border:1px solid var(--hairline);border-radius:16px;padding:15px}
  .metal-top{display:flex;align-items:center;gap:7px;font-weight:700;font-size:14px}
  .dot{width:14px;height:14px;border-radius:50%;flex:none}
  .dot.g{background:radial-gradient(circle at 35% 30%,#f6dd9a,#d9a83c 70%,#a9781f)}
  .dot.s{background:radial-gradient(circle at 35% 30%,#f4f6fa,#c2c8d2 70%,#8c93a0)}
  .metal .lbl{font-size:11px;color:var(--text-3);margin-top:12px}
  .metal .val{font-size:19px;font-weight:800;margin-top:2px}
  .cat{background:var(--card);border:1px solid var(--hairline);border-radius:16px;padding:15px;min-height:104px;
    display:flex;flex-direction:column;justify-content:space-between;cursor:pointer}
  .cat.locked{background:var(--card-locked);border-style:dashed;border-color:var(--hairline-strong)}
  .cat-ic{width:26px;height:26px;color:var(--gold)}.cat.locked .cat-ic{color:var(--text-3)}
  .cat-name{font-size:15px;font-weight:700}.cat-meta{font-size:12px;color:var(--text-2);margin-top:2px}
  .cat-unlock{font-size:12px;font-weight:600;color:var(--gold);margin-top:2px;display:flex;align-items:center;gap:5px}
  .cat-unlock svg{width:12px;height:12px}
  .shop-row{display:grid;grid-template-columns:1fr 1fr 1fr;gap:10px}
  .prod-img{aspect-ratio:1;border-radius:14px;border:1px solid var(--hairline);background:var(--card-locked);display:grid;place-items:center}
  .prod-img svg{width:52%;height:52%;opacity:.85}
  .prod-name{font-size:12px;font-weight:600;margin-top:7px;line-height:1.25}.prod-price{font-size:12px;color:var(--text-3)}
  .request{margin-top:16px;display:flex;align-items:center;gap:13px;background:var(--card);border:1px solid var(--hairline);border-radius:16px;padding:15px;cursor:pointer}
  .request-ic{width:38px;height:38px;border-radius:11px;background:var(--gold-soft);color:var(--gold);display:grid;place-items:center;flex:none}
  .request-ic svg{width:19px;height:19px}.request-t{font-size:14px;font-weight:700}.request-s{font-size:12px;color:var(--text-2);margin-top:1px}

  /* shared list row */
  .item{display:flex;gap:13px;align-items:center;background:var(--card);border:1px solid var(--hairline);border-radius:16px;padding:12px;margin-bottom:11px}
  .thumb{width:54px;height:54px;border-radius:11px;flex:none;display:grid;place-items:center;border:1px solid var(--hairline);background:var(--card-locked);overflow:hidden}
  /* Banknotes are landscape — rectangular thumbnail in Browse (shape only; row layout unchanged) */
  .thumb.thumb-note{width:80px;height:44px;border-radius:8px}
  /* A banknote centre-cropped to a square is unreadable - show the whole note. */
  .thumb.thumb-note .catalog-thumb-img{object-fit:contain;background:#0b0d12}
  .thumb.thumb-sm{width:44px;height:44px;border-radius:9px}
  .thumb.thumb-sm.thumb-note{width:64px;height:36px;border-radius:7px}
  .thumb svg{width:60%;height:60%}
  .thumb.greyed svg{opacity:.32;filter:grayscale(1)}
  .catalog-suggest-thumb svg{width:20px;height:20px;color:var(--gold)}
  .item-name{font-size:14px;font-weight:700}.item-sub{font-size:12px;color:var(--text-2);margin-top:2px}
  .vals{margin-left:auto;text-align:right;flex:none}
  /* Entire Collection carries the most text of any list. In the plain three
     column row (thumb | text | price) the text column was left with 114px,
     while its longest line - "1905 . Paid TT$ 30,000 . Very Fine" - needs
     163px even at 10px, so it always wrapped mid-phrase. Shrinking the font
     alone cannot close a 49px gap.
     Instead the price FLOATS inside the text body: the name sits beside it,
     and every line below the float's height gets the body's full ~227px, so
     the long lines fit on one line each. Nothing is hidden or truncated. */
  .item.ec-card{align-items:flex-start}
  .ec-card .ec-body{flex:1;min-width:0}
  .ec-card .ec-body::after{content:'';display:block;clear:both}
  .ec-card .vals{float:right;margin-left:10px;margin-bottom:2px}
  /* The name sits beside the floated price, so a very long one ("10 Dollar
     Silver coin Anniversary of Independence") stacks three deep and stretches
     the card. Two lines max: the full name is always on the item's own
     screen, so nothing is lost - unlike the facts below, which are the only
     place the paid price appears in this list. */
  .ec-card .item-name{display:-webkit-box;-webkit-line-clamp:2;-webkit-box-orient:vertical;overflow:hidden}
  .ec-card .item-sub{font-size:10px;margin-top:3px}
  /* Slightly smaller thumbnails here than in other lists. The 80px banknote
     box left the text body only 153px at 320px wide, against a longest line
     needing 172px - so the line wrapped on small screens no matter how the
     price was laid out. Trimming the thumb buys back the width the text
     needs, and the photo is still perfectly legible at this size. */
  /* Browse rows: same reasoning as the Entire Collection cards. At 320px the
     80px banknote thumb plus the price column left the text column 67px,
     against a first line needing 91px, so year/metal and country both
     wrapped. Trimming the thumb here gives the text the width it needs. */
  .br-card .thumb{width:46px;height:46px}
  .br-card .thumb.thumb-note{width:58px;height:34px}
  .br-card .item-sub{font-size:10px;margin-top:3px}
  .ec-card .thumb{width:46px;height:46px}
  .ec-card .thumb.thumb-note{width:58px;height:34px}
  .ec-card .v-dom{font-size:13.5px}
  .ec-card .v-int{font-size:10.5px}
  .ec-card .grade-chip{margin-top:5px}
  .v-dom{font-size:14px;font-weight:700;color:var(--gold);font-variant-numeric:tabular-nums}
  .v-int{font-size:12px;color:var(--text-2);margin-top:1px;font-variant-numeric:tabular-nums}
  .grade-chip{display:inline-block;font-size:10px;font-weight:700;color:var(--text-2);background:rgba(255,255,255,.05);border:1px solid var(--hairline);padding:2px 7px;border-radius:6px;margin-top:4px}
  .sell-btn{font-size:11px;font-weight:700;color:#fff;background:var(--red);border:none;border-radius:8px;padding:6px 12px;cursor:pointer;margin-top:6px}

  /* browse */
  .search{display:flex;align-items:center;gap:9px;background:var(--card);border:1px solid var(--hairline);border-radius:12px;padding:11px 13px;color:var(--text-3);font-size:13px;margin-bottom:14px}
  .search svg{width:17px;height:17px}
  .chips{display:flex;gap:8px;overflow-x:auto;padding-bottom:4px;margin-bottom:14px}
  .chip{flex:none;font-size:12px;font-weight:600;padding:7px 14px;border-radius:999px;border:1px solid var(--hairline-strong);color:var(--text-2);display:flex;align-items:center;gap:5px;cursor:pointer}
  .chip.on{background:var(--gold);color:#1a1407;border-color:var(--gold)}
  .chip svg{width:11px;height:11px}

  /* add form */
  .photo-slot{aspect-ratio:16/10;border-radius:16px;border:1px solid var(--hairline);background:var(--card-locked);display:flex;flex-direction:column;align-items:center;justify-content:center;gap:8px;margin-bottom:8px}
  .photo-slot svg{width:46px;height:46px;opacity:.3;filter:grayscale(1)}
  .photo-note{font-size:12px;color:var(--text-3)}
  .add-photo{font-size:13px;font-weight:600;color:var(--gold);text-align:center;margin-bottom:16px;cursor:pointer}
  .field{margin-bottom:13px}.field label{display:block;font-size:12px;color:var(--text-2);margin-bottom:6px;font-weight:600}
  .field .inp{background:var(--card);border:1px solid var(--hairline);border-radius:11px;padding:12px 13px;font-size:14px;color:var(--text)}
  .field .inp.ph{color:var(--text-3)}
  .grade-row{display:flex;gap:7px;flex-wrap:wrap}
  .g-opt{font-size:12px;font-weight:600;padding:7px 12px;border-radius:9px;border:1px solid var(--hairline-strong);color:var(--text-2)}
  .g-opt.on{background:var(--gold);color:#1a1407;border-color:var(--gold)}
  .val-preview{background:var(--card);border:1px solid var(--hairline);border-radius:13px;padding:13px;display:flex;justify-content:space-between;margin:4px 0 16px}
  .val-preview .lbl{font-size:11px;color:var(--text-3)}.val-preview .num{font-size:16px;font-weight:800;margin-top:2px}
  .btn-primary{width:100%;background:var(--cta-bg);color:var(--cta-text);border:none;border-radius:14px;padding:15px;font-size:15px;font-weight:700;cursor:pointer;margin-bottom:10px}
  .btn-ghost{width:100%;background:none;color:var(--gold);border:1px solid var(--gold-soft);border-radius:14px;padding:14px;font-size:14px;font-weight:600;cursor:pointer}

  /* ask / chat */
  .pro-banner{display:flex;align-items:center;gap:9px;background:var(--gold-soft);border:1px solid rgba(230,180,80,.3);border-radius:13px;padding:11px 14px;margin-bottom:16px}
  .pro-banner svg{width:18px;height:18px;color:var(--gold);flex:none}
  .pro-banner b{color:var(--gold)}.pro-banner span{font-size:12px;color:var(--text-2)}
  .bubble{max-width:82%;padding:12px 14px;border-radius:16px;font-size:13.5px;line-height:1.5;margin-bottom:12px}
  .bubble.user{margin-left:auto;background:var(--gold);color:#1a1407;border-bottom-right-radius:5px;font-weight:500}
  .bubble.bot{background:var(--card);border:1px solid var(--hairline);border-bottom-left-radius:5px}
  .bubble.bot b{color:var(--gold)}
  .ask-input{display:flex;align-items:center;gap:9px;background:var(--card);border:1px solid var(--hairline);border-radius:14px;padding:11px 14px;color:var(--text-3);font-size:13px;margin-top:6px}
  .ask-input .send{margin-left:auto;width:30px;height:30px;border-radius:9px;background:var(--gold);display:grid;place-items:center;flex:none}
  .ask-input .send svg{width:16px;height:16px;color:#1a1407}

  /* collection folders + stats */
  .statbar{display:flex;background:var(--card);border:1px solid var(--hairline);border-radius:16px;padding:14px 6px;margin-bottom:16px}
  .statbar .s{flex:1;text-align:center;border-right:1px solid var(--hairline)}
  .statbar .s:last-child{border-right:none}
  .statbar .s .n{font-size:17px;font-weight:800;font-variant-numeric:tabular-nums}.statbar .s .n.gold{color:var(--gold)}
  .statbar .s .k{font-size:10px;color:var(--text-3);margin-top:3px;text-transform:uppercase;letter-spacing:.6px}
  .folder{display:flex;align-items:center;gap:13px;background:var(--card);border:1px solid var(--hairline);border-radius:16px;padding:14px;margin-bottom:11px;cursor:pointer}
  .folder-ic{width:42px;height:42px;border-radius:12px;flex:none;display:grid;place-items:center;background:var(--gold-soft);color:var(--gold)}
  .folder-ic svg{width:22px;height:22px}
  .folder.locked .folder-ic{background:var(--card-locked);color:var(--text-3)}
  .folder-name{font-size:15px;font-weight:700}.folder-meta{font-size:12px;color:var(--text-2);margin-top:2px}
  .folder .fv{margin-left:auto;text-align:right;flex:none}
  .folder .fv .a{font-size:14px;font-weight:700}.folder .fv .c{font-size:11px;color:var(--text-3);margin-top:1px}
  .folder .chev{color:var(--text-3);margin-left:4px}.folder .chev svg{width:18px;height:18px}

  /* scan / add */
  .scan-card{border-radius:18px;padding:24px 18px;background:linear-gradient(160deg,#1f1f26,#141417);border:1px solid var(--hairline-strong);text-align:center;margin-bottom:14px}
  .scan-ring{width:74px;height:74px;border-radius:50%;margin:0 auto 13px;display:grid;place-items:center;background:var(--gold-soft);border:2px solid var(--gold)}
  .scan-ring svg{width:33px;height:33px;color:var(--gold)}
  .scan-card h3{font-size:17px;font-weight:800}
  .scan-card p{font-size:12.5px;color:var(--text-2);margin-top:7px;line-height:1.55}
  .match{display:flex;align-items:center;gap:12px;background:var(--card);border:1px solid rgba(52,214,95,.4);border-radius:14px;padding:12px;margin-bottom:16px}
  .match .ok{width:22px;height:22px;border-radius:50%;background:var(--green);display:grid;place-items:center;flex:none}
  .match .ok svg{width:14px;height:14px;color:#08130b}
  .match .mt{font-size:14px;font-weight:700}.match .ms{font-size:11.5px;color:var(--text-2);margin-top:1px}
  .ai-badge{display:inline-flex;align-items:center;gap:3px;font-size:9.5px;font-weight:700;color:var(--gold);background:var(--gold-soft);padding:2px 6px;border-radius:6px;letter-spacing:.3px;margin-left:6px;vertical-align:middle}
  .ai-badge svg{width:9px;height:9px}
  .divider{display:flex;align-items:center;gap:10px;color:var(--text-3);font-size:11px;margin:14px 0}
  .divider::before,.divider::after{content:"";flex:1;height:1px;background:var(--hairline)}

  /* overlays (auth + sub screens) */
  .overlay{position:absolute;inset:0;background:var(--app-bg);z-index:20;overflow-y:auto;display:none}
  .overlay.on{display:block}
  .overlay::-webkit-scrollbar{width:0}
  .ov-pad{padding:24px 24px 80px}
  .topbar{display:flex;align-items:center;gap:14px;padding:18px 20px 6px;position:sticky;top:0;background:var(--app-bg);z-index:2}
  .topbar .back{width:34px;height:34px;border-radius:50%;border:1px solid var(--hairline);display:grid;place-items:center;cursor:pointer;color:var(--text);flex:none}
  .topbar .back svg{width:18px;height:18px}
  .topbar .ttl{font-size:16px;font-weight:700;flex:1}
  .ov-close{margin-left:auto;flex:none;width:34px;height:34px;border-radius:50%;border:1px solid var(--hairline);display:grid;place-items:center;cursor:pointer;color:var(--text-2)}
  .ov-close svg{width:16px;height:16px}
  .modal{position:relative}
  .modal-x{position:absolute;top:14px;right:14px;width:30px;height:30px;border-radius:50%;border:1px solid var(--hairline);background:var(--card);display:grid;place-items:center;cursor:pointer;color:var(--text-2);z-index:1;padding:0}
  .modal-x svg{width:14px;height:14px}
  .nav-hidden{display:none!important}

  .auth-logo{display:block;height:30px;width:auto;margin:14px auto 24px}
  .auth-h{font-size:21px;font-weight:800;text-align:center}
  .auth-sub{font-size:13px;color:var(--text-2);text-align:center;margin:7px 0 24px;line-height:1.5}
  /* Persistent post-signup notice on the login screen (o-login.html
     #login-notice) - bordered card, gold left edge so it reads as
     information rather than an error, sits above the form until a
     sign-in or session reset clears it (js/screens/auth.js). */
  .auth-notice{background:var(--card);border:1px solid var(--hairline);
    border-left:3px solid var(--gold);border-radius:12px;padding:12px 13px;
    font-size:13px;line-height:1.5;color:var(--text-2);margin-bottom:18px}
  .auth-notice b{color:var(--text)}
  .auth-notice .link{display:inline-block;margin-top:8px}
  .in-field{margin-bottom:15px}
  .in-field label{display:block;font-size:12px;font-weight:600;color:var(--text-2);margin-bottom:7px}
  .in-field .box{display:flex;align-items:center;gap:10px;background:var(--card);border:1px solid var(--hairline);border-radius:12px;padding:13px 14px;font-size:14px}
  .in-field .box.ph{color:var(--text-3)}
  .in-field .box svg{width:17px;height:17px;color:var(--text-3);flex:none}
  .in-field .hint{font-size:11.5px;color:var(--text-3);margin-top:6px;line-height:1.45}
  .biometric{width:48px;height:48px;border-radius:13px;border:1px solid var(--hairline-strong);display:grid;place-items:center;margin:6px auto 16px;color:var(--gold)}
  .biometric svg{width:25px;height:25px}
  .row-between{display:flex;align-items:center;justify-content:space-between;margin-bottom:18px}
  .check{display:flex;align-items:center;gap:9px;font-size:13px;color:var(--text-2);cursor:pointer}
  .check .bx{width:18px;height:18px;border-radius:5px;border:1.5px solid var(--hairline-strong);flex:none;display:grid;place-items:center}
  .check .bx.on{background:var(--gold);border-color:var(--gold)}
  /* The tick used to be held back by an inline visibility:hidden in
     o-signup.html, which no stylesheet could ever win against - so even a
     correctly-ticked box showed an empty gold square. The default belongs
     here, where the .on rule below can actually lift it. */
  .check .bx svg{visibility:hidden}
  .check .bx.on svg{width:12px;height:12px;color:#1a1407;visibility:visible}
  .link{color:var(--gold);font-weight:600;font-size:13px;text-decoration:none;cursor:pointer}
  /* Owner, 29 Aug 2026: "Terms and Conditions and Privacy go into another
     line and it doesn't look aesthetic." The sentence wrapped as "...Terms &
     Conditions and" on one line with "Privacy Policy." stranded alone on the
     next - a two-word orphan under a much longer line reads as broken, not
     considered. text-wrap:balance is the CSS built for exactly this: it
     chooses break points that even out a short paragraph's lines instead of
     packing every word it can onto the first one. Falls back to the old
     unbalanced wrap on a browser too old to know the property - never worse
     than today, only sometimes not improved. */
  .consent{font-size:11.5px;color:var(--text-3);text-align:center;line-height:1.55;margin:14px 0;text-wrap:balance}
  .consent b{color:var(--text-2)}
  .center-row{text-align:center;margin-top:16px;font-size:13px;color:var(--text-2)}
  .choice{display:grid;grid-template-columns:1fr 1fr;gap:10px;margin:2px 0 6px}
  .choice .opt{border:1px solid var(--hairline-strong);border-radius:13px;padding:13px;cursor:pointer;text-align:center}
  .choice .opt.on{border-color:var(--gold);background:var(--gold-soft)}
  .choice .opt .ot{font-size:14px;font-weight:700}
  .choice .opt .os{font-size:10.5px;color:var(--text-3);margin-top:3px}
  .code-boxes{display:flex;gap:9px;justify-content:center;margin:10px 0 16px}
  .code-boxes .cb{width:42px;height:52px;border-radius:11px;border:1px solid var(--hairline-strong);background:var(--card);display:grid;place-items:center;font-size:22px;font-weight:700}
  .code-boxes .cb.fill{border-color:var(--gold)}

  .prof-head{text-align:center;padding:4px 0 20px}
  .prof-av{width:82px;height:82px;border-radius:50%;margin:0 auto 13px;background:var(--gold-soft);border:2px solid var(--gold);display:grid;place-items:center;color:var(--gold)}
  .prof-av svg{width:40px;height:40px}
  .prof-name{font-size:20px;font-weight:800}
  .prof-mail{font-size:13px;color:var(--text-2);margin-top:3px}
  .prof-alias{display:inline-block;font-size:11px;font-weight:600;color:var(--gold);background:var(--gold-soft);padding:3px 11px;border-radius:999px;margin-top:11px}
  .invite{display:flex;align-items:center;gap:13px;background:linear-gradient(135deg,#1f7a4d,#25a35f);border-radius:15px;padding:15px;margin-bottom:16px;cursor:pointer}
  .invite-ic{width:40px;height:40px;border-radius:50%;background:rgba(255,255,255,.18);display:grid;place-items:center;flex:none;color:#fff}
  .invite-ic svg{width:21px;height:21px}
  .invite .it{font-size:15px;font-weight:700;color:#fff}
  .invite .is{font-size:12px;color:rgba(255,255,255,.85);margin-top:1px}
  .invite .chev{margin-left:auto;color:rgba(255,255,255,.9)}.invite .chev svg{width:18px;height:18px}
  .mrow{display:flex;align-items:center;gap:13px;background:var(--card);border:1px solid var(--hairline);border-radius:14px;padding:14px;margin-bottom:11px;cursor:pointer}
  .mrow-ic{width:38px;height:38px;border-radius:10px;background:var(--gold-soft);color:var(--gold);display:grid;place-items:center;flex:none}
  .mrow-ic svg{width:19px;height:19px}
  .mrow .ml{font-size:14.5px;font-weight:600}
  .mrow .mv{margin-left:auto;font-size:12.5px;color:var(--text-3)}
  .mrow .chev{color:var(--text-3);margin-left:auto}.mrow.has-val .chev{margin-left:8px}.mrow .chev svg{width:17px;height:17px}
  .signout{width:100%;background:#16161a;border:1px solid var(--hairline-strong);color:var(--text);border-radius:999px;padding:15px;font-size:15px;font-weight:700;cursor:pointer;margin-top:14px;display:flex;align-items:center;justify-content:center;gap:9px;font-family:inherit}
  .signout svg{width:18px;height:18px}
  .privacy-note{font-size:11.5px;color:var(--text-3);line-height:1.5;background:var(--card-locked);border:1px solid var(--hairline);border-radius:11px;padding:11px 13px;margin:0 0 11px}

  .lb-chips{display:flex;gap:8px;overflow-x:auto;padding-bottom:4px;margin-bottom:14px}
  /* Leaderboard category picker: a segmented control rather than a scrolling
     chip row. Four fixed columns fit phone width without clipping, the icons
     carry the meaning when the label is tight, and the selected state reads
     clearly instead of relying on a subtle border. */
  .lb-seg{display:grid;grid-template-columns:repeat(4,1fr);gap:5px;background:var(--card-locked);border:1px solid var(--hairline);border-radius:14px;padding:5px;margin-bottom:16px}
  .lb-seg .seg{display:flex;flex-direction:column;align-items:center;justify-content:center;gap:5px;padding:9px 3px;border-radius:10px;cursor:pointer;color:var(--text-3);transition:background .15s,color .15s;-webkit-tap-highlight-color:transparent}
  .lb-seg .seg svg{width:19px;height:19px;display:block}
  .lb-seg .seg .sl{font-size:10.5px;font-weight:700;letter-spacing:.2px;white-space:nowrap}
  .lb-seg .seg.on{background:var(--gold-soft);color:var(--gold);box-shadow:inset 0 0 0 1px rgba(230,180,80,.38)}
  .lb-seg .seg:not(.on):hover{color:var(--text-2)}
  .you-card{display:flex;align-items:center;gap:12px;background:var(--gold-soft);border:1px solid var(--gold);border-radius:14px;padding:13px;margin-bottom:16px}
  .rank-num{font-size:16px;font-weight:800;width:28px;text-align:center;flex:none;color:var(--text-2)}
  .rank-num.gold{color:var(--gold)}
  .lb-av{width:38px;height:38px;border-radius:50%;background:var(--card-locked);border:1px solid var(--hairline);display:grid;place-items:center;flex:none;color:var(--gold)}
  .lb-av svg{width:19px;height:19px}
  .lb-alias{font-size:14px;font-weight:700}
  .lb-meta{font-size:11.5px;color:var(--text-2);margin-top:1px}
  .lb-row{display:flex;align-items:center;gap:12px;background:var(--card);border:1px solid var(--hairline);border-radius:13px;padding:11px 12px;margin-bottom:9px;cursor:pointer}
  .lb-row .cnt{margin-left:auto;text-align:right;flex:none}
  .lb-row .cnt .a{font-size:14px;font-weight:700}.lb-row .cnt .c{font-size:11px;color:var(--text-3)}
  .lb-note{font-size:11.5px;color:var(--text-3);text-align:center;line-height:1.5;margin-top:12px}

  /* header back arrow */
  .hdr-left{display:flex;align-items:center;gap:11px}
  .hdr-back{width:30px;height:30px;border-radius:50%;border:1px solid var(--hairline);display:none;place-items:center;cursor:pointer;color:var(--text);flex:none}
  .hdr-back.show{display:grid}
  .hdr-back svg{width:17px;height:17px}

  /* toggle switch (green on / red off) */
  .switch-row{display:flex;align-items:center;gap:13px;background:var(--card);border:1px solid var(--hairline);border-radius:14px;padding:15px;margin-bottom:12px}
  .switch-row .sl{font-size:14.5px;font-weight:600}
  .switch-row .ss{font-size:11.5px;color:var(--text-3);margin-top:2px}
  .switch{margin-left:auto;width:50px;height:29px;border-radius:999px;position:relative;flex:none;cursor:pointer;transition:background .2s}
  .switch.on{background:#2ea043}.switch.off{background:#c8362e}
  .switch .knob{position:absolute;top:3px;width:23px;height:23px;border-radius:50%;background:#fff;transition:left .2s}
  .switch.on .knob{left:24px}.switch.off .knob{left:3px}

  /* account edit rows */
  .acc-row{display:flex;align-items:center;gap:13px;background:var(--card);border:1px solid var(--hairline);border-radius:14px;padding:14px;margin-bottom:11px}
  .acc-row .al{font-size:11.5px;color:var(--text-3)}
  .acc-row .av{font-size:14.5px;font-weight:600;margin-top:2px}
  .acc-row .edit{margin-left:auto;font-size:12.5px;font-weight:600;color:var(--gold);flex:none;cursor:pointer}
  .avatar-pick{display:flex;gap:10px;flex-wrap:wrap;margin:6px 0 6px}
  .av-opt{width:48px;height:48px;border-radius:50%;border:2px solid var(--hairline);background:var(--card-locked);display:grid;place-items:center;color:var(--text-3);cursor:pointer}
  .av-opt.on{border-color:var(--gold);color:var(--gold);background:var(--gold-soft)}
  .av-opt svg{width:24px;height:24px}

  /* billing */
  .bill-card{background:var(--card);border:1px solid var(--hairline);border-radius:16px;padding:16px;margin-bottom:14px}
  .bill-plan{display:flex;align-items:center;justify-content:space-between}
  .bill-plan .p{font-size:16px;font-weight:800}
  .bill-plan .badge{font-size:10px;font-weight:700;color:var(--gold);background:var(--gold-soft);padding:3px 9px;border-radius:999px}
  .bill-meta{display:flex;justify-content:space-between;margin-top:13px;font-size:12px}
  .bill-meta .k{color:var(--text-3)}.bill-meta .v{font-weight:600}
  .stripe-note{font-size:11px;color:var(--text-3);display:flex;align-items:center;gap:6px;margin:2px 0 12px}
  .stripe-note svg{width:13px;height:13px;color:var(--gold)}

  /* language */
  .lang-opt{display:flex;align-items:center;gap:13px;background:var(--card);border:1px solid var(--hairline);border-radius:14px;padding:15px;margin-bottom:11px;cursor:pointer}
  .lang-opt.on{border-color:var(--gold)}
  .lang-flag{font-size:22px;flex:none}
  .lang-name{font-size:15px;font-weight:600}
  .lang-check{margin-left:auto;color:var(--gold)}.lang-check svg{width:20px;height:20px}

  /* timeline */
  .period{margin-bottom:22px}
  .period-h{display:flex;align-items:baseline;justify-content:space-between;margin-bottom:11px}
  .period-h .pn{font-size:14.5px;font-weight:700}
  .period-h .pd{font-size:11px;color:var(--text-3);font-weight:600}
  .period-h .va{font-size:12px;font-weight:600;color:var(--gold);cursor:pointer}
  .feat-row{display:grid;grid-template-columns:repeat(4,1fr);gap:8px}
  .feat{position:relative;aspect-ratio:1;border-radius:12px;border:1px solid var(--hairline);background:var(--card-locked);display:grid;place-items:center;position:relative}
  .feat svg{width:56%;height:56%}
  .feat .rare{position:absolute;top:4px;right:4px;font-size:8px;font-weight:700;color:#1a1407;background:var(--gold);padding:1px 5px;border-radius:5px}
  .period.empty .feat-row{opacity:.4}
  .period-empty{font-size:12.5px;color:var(--label);padding:12px 0;line-height:1.5}

  /* toggle switches (green on / red off) */
  .set-row{display:flex;align-items:center;gap:13px;background:var(--card);border:1px solid var(--hairline);border-radius:14px;padding:15px;margin-bottom:12px}
  .set-row .sl{font-size:15px;font-weight:600}
  .set-row .ss{font-size:11.5px;color:var(--text-3);margin-top:3px;line-height:1.45}
  .tgl{width:50px;height:29px;border-radius:999px;position:relative;cursor:pointer;flex:none;margin-left:auto;transition:background .2s}
  .tgl.on{background:#2ea043}.tgl.off{background:#d23b3b}
  .tgl .knob{position:absolute;top:3px;width:23px;height:23px;border-radius:50%;background:#fff;transition:left .2s}
  .tgl.on .knob{left:24px}.tgl.off .knob{left:3px}
  /* account edit rows + avatar picker */
  .av-picker{display:flex;gap:11px;flex-wrap:wrap;justify-content:center;margin:2px 0 18px}
  .edit-row{display:flex;align-items:center;background:var(--card);border:1px solid var(--hairline);border-radius:12px;padding:13px 14px;margin-bottom:11px}
  .edit-row .er-l{font-size:11px;color:var(--text-3)}
  .edit-row .er-v{font-size:14px;font-weight:600;margin-top:2px}
  .edit-row .er-edit{margin-left:auto;color:var(--gold);font-size:13px;font-weight:600;cursor:pointer}
  /* missing items + request status */
  .status{font-size:10px;font-weight:700;padding:3px 9px;border-radius:999px;white-space:nowrap}
  .status.matched{color:#2ea043;background:rgba(46,160,67,.16)}
  .status.pending{color:var(--text-2);background:var(--card-locked);border:1px solid var(--hairline)}
  /* All Items for Sale only (owner, 12 Sep 2026): a listing still waiting for
     staff approval is the one state the seller should spot at a glance, so
     that screen's badge gets a soft gold wash. A modifier on that badge
     alone - .status.pending itself also dresses Expired, Sourcing and the
     sold-duplicate badges elsewhere and stays as it is. */
  .status.pending.afs-pending{color:#e0a24a;background:rgba(230,180,80,.14);border-color:rgba(230,180,80,.5)}
  .req-row{display:flex;align-items:center;gap:11px;background:var(--card);border:1px solid var(--hairline);border-radius:13px;padding:12px;margin-bottom:10px}
  .req-row .rt{font-size:13.5px;font-weight:600}.req-row .rd{font-size:11px;color:var(--text-3);margin-top:2px}
  .req-row .status{margin-left:auto}
  /* header back arrow */
  .hdr-back{width:32px;height:32px;border-radius:50%;border:1px solid var(--hairline);display:none;place-items:center;cursor:pointer;color:var(--text);flex:none}
  .hdr-back svg{width:17px;height:17px}

  /* view-all: tabs, sort, table rows */
  .vtabs{display:flex;gap:8px;margin-bottom:13px}
  .vtab{flex:1;text-align:center;font-size:13px;font-weight:700;padding:9px;border-radius:11px;border:1px solid var(--hairline-strong);color:var(--text-2);cursor:pointer}
  .vtab.on{background:var(--gold);color:#1a1407;border-color:var(--gold)}
  .sortbar{display:flex;gap:7px;overflow-x:auto;padding-bottom:4px;margin-bottom:12px}
  .sortbar .lbl{font-size:11px;color:var(--text-3);align-self:center;flex:none;margin-right:2px}
  .schip{flex:none;font-size:12px;font-weight:600;padding:6px 12px;border-radius:999px;border:1px solid var(--hairline-strong);color:var(--text-2);cursor:pointer;white-space:nowrap}
  .schip.on{background:var(--gold);color:#1a1407;border-color:var(--gold)}
  .va-head{display:flex;align-items:center;padding:0 11px 8px;font-size:10px;color:var(--text-3);text-transform:uppercase;letter-spacing:.5px}
  .va-head .s1{flex:1}.va-head .sy{width:48px;text-align:center}.va-head .sr{width:40px;text-align:center}.va-head .sd{width:40px;text-align:center}
  .va-row{display:flex;align-items:center;gap:10px;background:var(--card);border:1px solid var(--hairline);border-radius:12px;padding:9px 11px;margin-bottom:8px}
  .va-row .vt{width:38px;height:38px;border-radius:9px;border:1px solid var(--hairline);background:var(--card-locked);display:grid;place-items:center;flex:none}
  .va-row .vt svg{width:60%;height:60%}
  .va-row .vn{flex:1;min-width:0}.va-row .vn .nm{font-size:13px;font-weight:600}.va-row .vn .sub{font-size:11px;color:var(--text-3);margin-top:1px}
  .va-row .cy{width:48px;text-align:center;font-size:12.5px;font-weight:600}
  .va-row .cr,.va-row .cd{width:40px;text-align:center}
  .idx{display:inline-block;font-size:11px;font-weight:700;padding:2px 6px;border-radius:6px}
  .idx.r{color:var(--gold);background:var(--gold-soft)}
  .idx.d{color:var(--blue);background:rgba(90,169,230,.16)}
  /* duplicates tab */
  .dup-row{display:flex;align-items:center;gap:11px;background:var(--card);border:1px solid rgba(225,28,54,.3);border-radius:13px;padding:11px;margin-bottom:11px}
  .dup-row .dt{width:46px;height:46px;border-radius:10px;border:1px solid var(--hairline);background:var(--card-locked);display:grid;place-items:center;flex:none}
  .dup-row .dt svg{width:60%;height:60%}
  .dup-info{flex:1;min-width:0}.dup-info .nm{font-size:13.5px;font-weight:700}.dup-info .sub{font-size:11.5px;color:var(--text-3);margin-top:2px}
  .dup-btns{display:flex;flex-direction:column;gap:6px;flex:none}
  .dup-btn{font-size:11px;font-weight:700;border:none;border-radius:8px;padding:7px 10px;cursor:pointer;white-space:nowrap}
  .dup-btn.req{background:var(--gold-soft);color:var(--gold);border:1px solid rgba(230,180,80,.4)}
  .dup-btn.quick{background:var(--gold);color:#1a1407}
  /* buyer/seller Counter back and Decline share one recipe each with the
     seller's own .offer-acts .btn-counter / .btn-decline, so both sides
     of a negotiation read the same colour for the same action. */
  .dup-btn.blue{background:var(--blue-deep);color:#fff;border:1px solid var(--blue-deep)}
  .dup-btn.decline{background:#3a1414;color:#ff8a8a;border:1px solid rgba(255,90,90,.4)}
  /* place offer (buyer) */
  .po-photo{aspect-ratio:16/11;border-radius:16px;border:1px solid var(--hairline);background:var(--card-locked);display:grid;place-items:center;margin-bottom:14px}
  .po-photo svg{width:34%;height:34%;color:var(--gold)}
  .po-name{font-size:19px;font-weight:800}
  .po-seller{font-size:13px;color:var(--text-2);margin-top:4px}
  .po-seller b{color:var(--gold)}
  .po-ref{display:flex;justify-content:space-between;background:var(--card);border:1px solid var(--hairline);border-radius:12px;padding:12px 14px;margin:14px 0}
  .po-ref .k{font-size:12px;color:var(--text-3)}.po-ref .v{font-size:14px;font-weight:700}
  /* offers received (seller / eBay-like) */
  .offer-card{background:var(--card);border:1px solid var(--hairline);border-radius:16px;padding:13px;margin-bottom:14px}
  .offer-head{display:flex;gap:11px;align-items:center}
  .offer-photo{width:52px;height:52px;border-radius:11px;border:1px solid var(--hairline);background:var(--card-locked);display:grid;place-items:center;flex:none}
  .offer-photo svg{width:60%;height:60%}
  .offer-item{font-size:14px;font-weight:700}.offer-sub{font-size:11.5px;color:var(--text-3);margin-top:2px}
  .offer-line{border-top:1px solid var(--hairline);padding-top:11px;margin-top:11px}
  .offer-from{display:flex;align-items:center;gap:9px;min-width:0}
  .offer-av{width:28px;height:28px;border-radius:50%;background:var(--card-locked);border:1px solid var(--hairline);display:grid;place-items:center;color:var(--gold);flex:none}
  .offer-av svg{width:16px;height:16px}
  .offer-alias{font-size:13px;font-weight:700}
  /* R115: alias, amount and status were three things fighting for one line
     next to a 28px avatar, and on a 300px card the status pill lost - it ran
     past the right edge ("You countered") or sat flush against it with no
     breathing room ("New offer"). The alias and amount now stack in their own
     shrinkable column, exactly like a marketplace row's title/price, which
     leaves the pill a column of its own that nothing can push out. */
  .offer-idcol{flex:1;min-width:0}
  .offer-idcol .offer-alias{display:block;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
  .offer-idcol .offer-amt{display:block;margin-left:0;margin-top:2px;font-size:15px;font-weight:800;
    white-space:nowrap;color:var(--gold)}
  .offer-amt{margin-left:auto;font-size:15px;font-weight:800;white-space:nowrap}
  .offer-from > .status{flex:none;align-self:center}
  .fee-note{font-size:11px;color:var(--text-3);margin-top:6px;display:flex;align-items:center;gap:5px;flex-wrap:wrap}
  .fee-note .net{color:var(--green);font-weight:600}
  .fee-i{width:15px;height:15px;border-radius:50%;border:1px solid var(--text-3);display:inline-grid;place-items:center;font-size:9px;font-weight:700;cursor:pointer;flex:none}
  .fee-panel{display:none;background:var(--card-locked);border:1px solid var(--hairline);border-radius:10px;padding:11px 12px;margin-top:8px;font-size:11.5px;color:var(--text-2);line-height:1.55}
  .fee-panel.on{display:block}
  .offer-acts{display:flex;gap:8px;margin-top:11px;flex-wrap:wrap}
  .offer-acts button{flex:1;min-width:84px;font-size:13px;font-weight:700;border-radius:10px;padding:9px;cursor:pointer;border:none;font-family:inherit;white-space:normal;text-align:center;line-height:1.25;display:flex;align-items:center;justify-content:center}
  /* R112: a status pill sharing a flex row with a button used to STRETCH to
     that button's full height whenever the button's own label wrapped to two
     lines - flex rows stretch every child to the tallest one by default. A
     10px line of text in a 50px-tall pill reads as badly off-center. align-
     self:center gives the pill its own natural, compact height regardless of
     its neighbour; inline-flex centers the text inside THAT height. */
  /* Deal-stage labels are sentences, not words - "Accepted: TTcollector is
     arranging authentication of the item" is 55 characters. The base .status
     rule sets white-space:nowrap, which is right for "Sold" or "Expired" but
     drove this pill off the card and off the screen. Only the deal pill, in
     .offer-acts, carries sentence-length text, so only it is allowed to wrap -
     every other .status keeps nowrap. */
  /* Deal-stage labels are sentences ("Accepted: TTcollector is arranging
     authentication of the item" is 60 characters), and .status sets nowrap,
     which is right for "Sold" but drives these off the card and off the screen.
     BOTH containers that render a stage label are listed here - .offer-acts is
     the seller's card, .mp-row-acts the buyer's row. The first fix covered only
     the seller's, because that was the instance that had been reported; the
     buyer's twin was found later by testing. If a third container ever renders
     ttcDealStageLabel into a .status pill, add it here too. */
  .offer-acts .status,
  .mp-row-acts .status{align-self:center;display:inline-flex;align-items:center;justify-content:center;text-align:center;line-height:1.3;white-space:normal;max-width:100%;min-width:0;overflow-wrap:anywhere}
  .btn-decline{background:#3a1414;color:#ff8a8a;border:1px solid rgba(255,90,90,.4)!important}
  .igot-btn{font-size:11px;font-weight:700;padding:5px 11px;border-radius:8px;background:var(--gold-soft);color:var(--gold);border:1px solid rgba(230,180,80,.35);cursor:pointer;font-family:inherit;white-space:nowrap}
  .sd-opt{display:flex;align-items:center;gap:11px;padding:12px;border:1px solid var(--hairline-strong);border-radius:12px;margin-bottom:9px;cursor:pointer}
  .sd-opt.on{border-color:var(--gold);background:var(--gold-soft)}
  .sd-radio{width:18px;height:18px;border-radius:50%;border:2px solid var(--text-3);flex:none}
  .sd-opt.on .sd-radio{border-color:var(--gold);background:radial-gradient(circle,var(--gold) 0 5px,transparent 6px)}
  .btn-accept{background:#2ea043;color:#fff}
  .btn-counter{background:var(--card-locked);color:var(--text);border:1px solid var(--hairline-strong)}
  .btn-counter.on{background:var(--gold);color:#1a1407;border-color:var(--gold)}
  .offer-acts .btn-counter{background:var(--blue-deep);color:#fff;border:1px solid var(--blue-deep)}

  /* toast */
  /* Above everything, including the staff queues' desktop layer (z-index:500,
     above) and the modal dim (50) - a toast is a notice, not a surface, and
     pointer-events:none keeps it from ever blocking a tap (owner, 12 Sep 2026:
     the "now LIVE" confirmation after Approve was hidden behind the queue). */
  .toast{position:absolute;left:16px;right:16px;bottom:20px;background:#143020;border:1px solid #2ea043;color:#d8ffe6;font-size:12.5px;font-weight:600;padding:13px 15px;border-radius:13px;text-align:center;z-index:1000;opacity:0;transform:translateY(12px);transition:.25s;pointer-events:none}
  /* Every message wore the success colours, so "Enter an item name first." and
     "Staff only" arrived in confident green. showToast now tags failures. */
  .toast.bad{background:#3a1414;border-color:rgba(255,90,90,.55);color:#ffd8d8}
  /* A disabled destructive button must not look like a live one. This sat at
     opacity .55 on full red with cursor:pointer, so on a dark background it
     read as an enabled primary and tapping it did nothing. */
  #safe-delete-confirm-btn[disabled]{background:var(--card-locked)!important;color:var(--text-3)!important;
    border:1px solid var(--hairline)!important;cursor:not-allowed!important;opacity:1!important}
  .toast.on{opacity:1;transform:translateY(0)}
  /* counter offer */
  .ct-orig{background:var(--card);border:1px solid var(--hairline);border-radius:14px;padding:14px}
  .ct-from{display:flex;align-items:center;gap:11px}
  .ct-alias{font-size:14px;font-weight:700}
  .ct-lbl{font-size:11px;color:var(--text-3);margin-top:1px}
  .ct-amt{margin-left:auto;font-size:16px;font-weight:800}
  /* iOS Safari zooms the whole page when a text control under 16px gains
     focus, and in this single-page app the zoom outlives the screen change
     since nothing reloads - so every focusable input/select/textarea below
     must be 16px. The viewport meta deliberately has no maximum-scale, so
     that people can still pinch-zoom item photos. */
  /* the floor for any control that carries no size of its own - the iOS reason is in the block above. */
  input,select,textarea{font-size:16px}
  .ct-input{width:100%;background:var(--card);border:1px solid var(--hairline-strong);border-radius:12px;padding:13px 14px;font-size:16px;color:var(--text);font-family:inherit;outline:none;box-sizing:border-box}
  .ct-input::placeholder{color:var(--text-3)}
  /* outline:none above strips the ring unconditionally - restore it for
     keyboard focus only (see the :focus-visible rule near the top). */
  .ct-input:focus-visible{outline:2px solid var(--gold);outline-offset:2px}

  /* what-i-paid + manual entry + popup + product view */
  .paid-line{font-size:12px;color:var(--text-3);margin-top:8px}
  .paid-line b{color:var(--gold);font-weight:700}
  .man-field{margin-bottom:14px;position:relative}
  .man-field label{display:block;font-size:12px;font-weight:600;color:var(--text-2);margin-bottom:7px}
  .man-input{width:100%;background:var(--card);border:1px solid var(--hairline-strong);border-radius:12px;padding:13px 14px;font-size:16px;color:var(--text);font-family:inherit;outline:none;box-sizing:border-box}
  .man-input::placeholder{color:var(--text-3)}
  /* outline:none above strips the ring unconditionally - restore it for
     keyboard focus only (see the :focus-visible rule near the top). */
  .man-input:focus-visible{outline:2px solid var(--gold);outline-offset:2px}
  .man-drop{position:absolute;left:0;right:0;top:100%;margin-top:5px;background:#16161b;border:1px solid var(--hairline-strong);border-radius:12px;overflow:hidden;z-index:6;display:none}
  .man-drop.on{display:block}
  .man-opt{padding:11px 13px;font-size:13.5px;cursor:pointer;border-bottom:1px solid var(--hairline)}
  .man-opt:last-child{border-bottom:none}
  .man-opt .mo-sub{font-size:11px;color:var(--text-3);margin-top:1px}
  .info-row{font-size:11.5px;color:var(--text-3);line-height:1.5;margin-top:7px;display:flex;gap:7px;align-items:flex-start}
  .info-row .fee-i{margin-top:1px}
  .info-panel{display:none;background:var(--card-locked);border:1px solid var(--hairline);border-radius:10px;padding:11px 12px;margin-top:8px;font-size:11.5px;color:var(--text-2);line-height:1.55}
  .info-panel.on{display:block}
  .modal-bg{position:absolute;inset:0;background:rgba(0,0,0,.62);z-index:50;display:none;align-items:center;justify-content:center;padding:22px}
  .modal-bg.on{display:flex}
  .modal{background:var(--app-bg);border:1px solid var(--hairline-strong);border-radius:18px;padding:20px;width:100%;max-width:330px}
  .modal h3{font-size:17px;font-weight:800;margin-bottom:6px}
  .modal .sub{font-size:12.5px;color:var(--text-2);line-height:1.5;margin-bottom:14px}
  .pv-status{display:flex;align-items:center;gap:8px;background:rgba(230,180,80,.12);border:1px solid rgba(230,180,80,.4);color:var(--gold);font-size:12px;font-weight:600;padding:11px 13px;border-radius:12px;margin-bottom:14px;line-height:1.4}
  .pv-status svg{width:17px;height:17px;flex:none}
  .pv-paid{display:flex;align-items:center;background:var(--card);border:1px solid var(--gold);border-radius:12px;padding:11px 14px;margin:12px 0}
  .pv-paid .pl{font-size:11px;color:var(--text-3)}
  .pv-locked{font-size:11px;color:var(--text-3);margin-top:8px;font-style:italic}

  /* Melt item-breakdown cards. Own classes rather than borrowing the Offers
     card's - the two show different things, and .offer-sub's --text-3 is the
     faintest text in the app, which is the wrong place for weights and money. */
  .mb-card{display:flex;gap:11px;align-items:flex-start;background:var(--card);border:1px solid var(--hairline);border-radius:14px;padding:11px 12px;margin-bottom:9px}
  .mb-thumb{width:44px;height:44px;border-radius:11px;border:1px solid var(--hairline);background:var(--card-locked);display:grid;place-items:center;flex:none;overflow:hidden}
  .mb-thumb img{width:100%;height:100%;object-fit:cover;display:block}
  .mb-thumb svg{width:24px;height:24px}
  .mb-main{flex:1;min-width:0}
  /* baseline keeps the value level with the FIRST line of a name that wraps */
  .mb-top{display:flex;align-items:baseline;gap:9px}
  .mb-name{font-size:13.5px;font-weight:700;line-height:1.35;flex:1;min-width:0}
  .mb-val{font-size:14px;font-weight:800;color:var(--gold);font-variant-numeric:tabular-nums;flex:none;white-space:nowrap}
  .mb-spec{display:flex;align-items:baseline;gap:9px;margin-top:3px}
  .mb-metal{font-size:11.5px;color:var(--text-2);flex:1;min-width:0;font-variant-numeric:tabular-nums}
  .mb-gain{font-size:11px;font-weight:700;flex:none;white-space:nowrap;font-variant-numeric:tabular-nums}
  .mb-gain.up{color:var(--green)}
  .mb-gain.down{color:var(--red)}
  .mb-gain.flat{color:var(--text-3);font-weight:600}
  .mb-foot{display:flex;flex-wrap:wrap;gap:6px 18px;margin-top:9px;padding-top:8px;border-top:1px solid var(--hairline)}
  .mb-fact{font-size:10.5px;color:var(--label);line-height:1.35}
  .mb-fact b{display:block;font-size:11.5px;font-weight:700;color:var(--text-2);font-variant-numeric:tabular-nums;margin-top:1px}
  .ec-search{display:flex;align-items:center;gap:9px;background:var(--card);border:1px solid var(--hairline-strong);border-radius:12px;padding:11px 13px;margin-bottom:12px}
  .ec-search svg{width:17px;height:17px;color:var(--text-3);flex:none}
  .ec-search input{flex:1;background:none;border:none;outline:none;color:var(--text);font-size:16px;font-family:inherit}
  .ec-search input::placeholder{color:var(--text-3)}
  /* outline:none above strips the ring unconditionally - restore it for
     keyboard focus only (see the :focus-visible rule near the top). Just
     the input itself, not the whole search pill, matching where focus
     actually lands. */
  .ec-search input:focus-visible{outline:2px solid var(--gold);outline-offset:2px}

  /* dropdown selector */
  .dd{position:relative;margin-bottom:14px}
  .dd-label{display:block;font-size:12px;font-weight:600;color:var(--text-2);margin-bottom:7px}
  .dd-sel{display:flex;align-items:center;gap:8px;background:var(--card);border:1px solid var(--hairline-strong);border-radius:12px;padding:13px 14px;cursor:pointer}
  .dd-sel .dd-val{font-size:14px;font-weight:600}
  .dd-sel>svg{width:16px;height:16px;margin-left:auto;color:var(--text-3);flex:none}
  .dd-menu{display:none;position:absolute;left:0;right:0;top:100%;margin-top:5px;background:#16161b;border:1px solid var(--hairline-strong);border-radius:12px;overflow:hidden;z-index:9;max-height:240px;overflow-y:auto}
  .dd-menu.on{display:block}
  .dd-opt{padding:11px 14px;font-size:13.5px;cursor:pointer;border-bottom:1px solid var(--hairline)}
  .dd-opt:last-child{border-bottom:none}
  .dd-opt:hover{background:var(--card)}
  /* browse filter/sort bar */
  .bf-bar{display:flex;gap:9px;margin-bottom:13px;align-items:stretch}
  .bf-btn{display:flex;align-items:center;gap:7px;background:var(--card);border:1px solid var(--hairline-strong);color:var(--text);border-radius:12px;padding:0 14px;font-size:13px;font-weight:600;cursor:pointer;font-family:inherit;flex:none}
  .bf-btn svg{width:16px;height:16px}
  /* P1 (7 Sep 2026): Filter/Sort bar follows the scroll on Browse only - the
     owner wants filters/sort reachable without scrolling back to the top.
     Scoped by #p-browse rather than editing the shared .bf-bar rule above:
     Marketplace reuses that same class for its own bar (extra Clear button +
     #mp-statcard) and was not asked for this - the same rule would work
     there mechanically with zero risk if it's ever wanted, one line away.
     top:71px is the header's own rendered height: 18px padding-top + 38px
     content row (the tallest header child is the 38x38 bell/mail icon boxes
     messaging.js injects, taller than the 28px logo and 32px avatar) + 14px
     padding-bottom + its 1px border-bottom (header rule above). Both bars
     share that same sticky top:0 header, so this is exactly how much of
     .scroll it occupies once scrolled.
     z-index:4 sits below the header (5) and below the search-suggestions
     dropdown (6) and the sort dd-menu it can itself open (9, scoped to this
     bar's own stacking context) - never above the header, always above the
     plain, unpositioned .item cards beneath it.
     box-shadow, not border-bottom, draws the hairline: a border would grow
     this element's own box height, and the bar's size must not change. */
  #p-browse .bf-bar{position:sticky;top:71px;z-index:4;background:var(--app-bg);box-shadow:0 1px 0 var(--hairline)}
  /* filter popup groups */
  .fl-group{margin-bottom:14px}
  .fl-lbl{font-size:12px;font-weight:600;color:var(--text-2);margin-bottom:7px}
  .fl-chips{display:flex;flex-wrap:wrap;gap:7px}
  .fl-chips .schip{font-size:12px}
  /* in-collection status pill */
  .pv-incol{display:inline-flex;align-items:center;gap:6px;font-size:12px;font-weight:600;padding:5px 11px;border-radius:999px;margin-bottom:14px}
  .pv-incol.yes{color:var(--green);background:rgba(52,214,95,.14);cursor:pointer}
  /* .pv-incol is inline-flex, so the "Manage" label rides the flex line,
     pushed to the far right */
  .pv-incol .pv-incol-go{margin-left:auto;font-weight:700;opacity:.8}
  .pv-incol.no{color:var(--text-2);background:var(--card-locked);border:1px solid var(--hairline)}
  .pv-incol svg{width:14px;height:14px}
  /* "1 in a proof set" - a second line under the pill (Break set §5), same
     gold/accent treatment as the app's other small derived call-outs */
  .pv-in-set{font-size:11.5px;font-weight:700;color:var(--gold);margin:-8px 0 14px;line-height:1.5}

  .modal{max-height:86%;overflow-y:auto;-webkit-overflow-scrolling:touch}
  .dd-inline .dd-menu{position:static;max-height:210px}
  .pv-desc{font-size:13px;color:var(--text-2);line-height:1.55;margin:2px 0 14px}
  .pv-sec{font-size:12px;font-weight:700;color:var(--label);text-transform:uppercase;letter-spacing:.6px;margin:18px 0 10px}
  .spec{display:flex;border-bottom:1px solid var(--hairline);padding:9px 0;font-size:13px}
  .spec .sk{width:118px;flex:none;color:var(--text-3)}
  .spec .sv{font-weight:600}
  .spec:last-child{border-bottom:none}
  .idxpill{display:inline-block;font-size:11px;font-weight:700;padding:2px 8px;border-radius:6px}
  .idxpill.r{color:var(--gold);background:var(--gold-soft)}
  .idxpill.d{color:var(--blue);background:rgba(90,169,230,.16)}
  .design-b{margin-bottom:13px}
  .design-b .dh{font-size:13px;font-weight:700;margin-bottom:4px}
  .design-b .dl{font-size:12.5px;color:var(--text-2);line-height:1.5}
  .design-b .dx{font-size:11.5px;color:var(--text-3);margin-top:4px;line-height:1.5}
  .dual-photo{display:grid;grid-template-columns:1fr 1fr;gap:10px;margin-bottom:14px}
  .dual-photo .dp{aspect-ratio:1;border-radius:14px;border:1px solid var(--hairline);background:var(--card-locked);display:grid;place-items:center;position:relative}
  .dual-photo .dp svg{width:55%;height:55%;color:var(--gold)}
  .dual-photo .dp span{position:absolute;bottom:7px;font-size:9px;color:var(--text-3);text-transform:uppercase;letter-spacing:.5px}
  .dual-photo.banknote-photos .dp{aspect-ratio:1.62;border-radius:12px}
  .dual-photo.banknote-photos .dp svg{width:48%;height:48%}
  .dual-photo.banknote-photos .dp span{bottom:8px;letter-spacing:.4px}
  /* Item view: ONE large photo, tap for the rest. Same shape and ratios as
     the marketplace carousel so the two detail screens match. */
  .pv-hero{position:relative;border:1px solid var(--hairline);border-radius:14px;background:var(--card-locked);overflow:hidden;margin-bottom:14px}
  .pv-hero .pv-hero-frame{aspect-ratio:1.12;display:grid;place-items:center;cursor:zoom-in;touch-action:pan-y}
  .pv-hero.pv-hero-note .pv-hero-frame{aspect-ratio:1.62}
  .pv-hero img{width:100%;height:100%;object-fit:contain;display:block}
  .pv-hero .pv-hero-tag{position:absolute;left:9px;bottom:9px;background:rgba(0,0,0,.62);color:#fff;border-radius:7px;padding:3px 8px;font-size:10.5px;font-weight:800}
  .pv-hero .pv-hero-more{position:absolute;right:9px;bottom:9px;background:rgba(0,0,0,.62);color:#fff;border-radius:7px;padding:3px 8px;font-size:10.5px;font-weight:800;display:flex;align-items:center;gap:5px}
  .pv-hero .pv-hero-empty{display:grid;place-items:center;color:var(--gold);opacity:.5}
  .catalog-img{width:100%;height:100%;object-fit:contain;display:block;background:#0b0d12;cursor:zoom-in}
  .catalog-thumb-img{width:100%;height:100%;object-fit:cover;border-radius:inherit;display:block}
  /* Exhibit tiles letterbox the WHOLE piece. object-fit:cover cropped a round
     coin's top and bottom away inside a 1.6 frame - the one thing an
     exhibition exists to show. Same lit mat as the item page. */
  .ex-frame{aspect-ratio:1.15;padding:10px;display:grid;place-items:center;overflow:hidden;
    background:radial-gradient(ellipse 62% 48% at 50% 40%, rgba(230,180,80,.13), transparent 70%),
               linear-gradient(160deg,#17171c,#0e0e11)}
  /* offer screens: small square thumbnail, eBay style */
  /* my offers on this piece, newest first */
  .mp-hist{background:var(--card);border:1px solid var(--hairline);border-radius:14px;
    padding:4px 13px 8px;margin-top:12px}
  .mp-hist-h{font-size:13px;font-weight:800;padding:11px 0 5px}
  .mp-hist-row{display:flex;align-items:center;gap:10px;padding:9px 0;
    border-bottom:1px solid var(--hairline)}
  .mp-hist-row.last{border-bottom:none}
  /* the expiry, its own line, bold */
  .mp-row-ends{font-size:12px;font-weight:800;color:#e0a24a;margin-top:5px}
  .mp-row-ends.ended{color:#ff8a8a}
  /* offer limit reached */
  .mp-limit{background:rgba(255,138,138,.07);border:1px solid rgba(255,138,138,.35);
    border-radius:13px;padding:14px;margin-top:14px;text-align:center}
  .mp-limit-t{font-size:14px;font-weight:800;color:#ff8a8a}
  .mp-limit-s{font-size:11.5px;color:var(--text-2);line-height:1.5;margin-top:5px}
  .mp-limit-b{width:100%;margin-top:11px;padding:11px}
  /* thumbnail strip under the large photograph */
  .mp-thumbs{display:flex;gap:8px;margin:0 0 14px;overflow-x:auto;padding-bottom:2px}
  .mp-thumbs::-webkit-scrollbar{height:0}
  .mp-thumb{width:62px;height:62px;flex:none;border-radius:10px;border:1px solid var(--hairline);
    overflow:hidden;cursor:pointer;background:var(--card-locked);display:grid;place-items:center}
  .mp-thumb.on{border:2px solid var(--gold)}
  .mp-thumb img{width:100%;height:100%;object-fit:cover;display:block}
  /* banknotes are wide, not square - the carousel above already letterboxes
     them (ratio 1.62 + object-fit:contain), so the strip matches it here */
  .mp-thumb.note img{object-fit:contain;background:#0b0d12}
  .mp-offer-open{display:flex;align-items:center;gap:10px;background:rgba(90,169,230,.10);
    border:1px solid rgba(90,169,230,.45);border-radius:12px;padding:12px 13px;margin-top:10px;cursor:pointer}
  /* the offer progress timeline */
  .tl-wrap{background:var(--card);border:1px solid var(--hairline);border-radius:14px;padding:13px 13px 11px}
  .tl-row{display:flex;align-items:flex-start}
  .tl-step{flex:1;position:relative;text-align:center}
  .tl-bar{position:absolute;top:5px;height:2px}
  .tl-bar-l{left:0;right:50%}
  .tl-bar-r{left:50%;right:0}
  .tl-dot{position:relative;display:inline-block;width:12px;height:12px;border-radius:50%;border:2px solid}
  .tl-lbl{font-size:9.5px;margin-top:6px;font-weight:600;color:var(--text-3);line-height:1.2}
  .tl-cap{margin-top:11px;padding-top:10px;border-top:1px solid var(--hairline)}
  .spec-last{border-bottom:none}
  /* item specifics: compact label/value rows, one card, hairline between */
  .spec-row{display:flex;align-items:baseline;gap:14px;padding:7px 0;
    border-bottom:1px solid var(--hairline)}
  .spec-row:last-child{border-bottom:none}
  .spec-k{font-size:12px;color:var(--text-3);flex:none}
  .spec-v{font-size:12.5px;font-weight:700;margin-left:auto;text-align:right;
    min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
  .spec-head{font-size:11px;font-weight:800;text-transform:uppercase;letter-spacing:.8px;
    color:var(--label);padding:10px 0 2px}
  /* the seller, as a row rather than a table cell */
  .mp-seller-row{display:flex;align-items:center;gap:11px;background:var(--card);
    border:1px solid var(--hairline);border-radius:14px;padding:12px 13px;margin:12px 0}
  .mp-seller-av{width:38px;height:38px;flex:none;border-radius:50%;border:1px solid var(--gold);
    background:var(--gold-soft);color:var(--gold);display:grid;place-items:center}
  .mp-seller-av svg{width:19px;height:19px}
  /* Browse rows: eBay's shape. A 72px tile, the money large, and the meta
     line carrying listed-time, watchers and the (quiet) expiry. */
  .mp-row-wrap{background:var(--card);border:1px solid var(--hairline);border-radius:14px;
    padding:11px;margin-bottom:9px}
  .mp-row{display:flex;gap:12px;align-items:center;cursor:pointer}
  .mp-row-thumb{width:72px;height:72px;flex:none;border-radius:11px;border:1px solid var(--hairline);
    background:radial-gradient(ellipse 70% 60% at 50% 40%, rgba(230,180,80,.10), transparent 70%),
               linear-gradient(160deg,#17171c,#0e0e11);display:grid;place-items:center;overflow:hidden}
  .mp-row-title{font-size:14px;font-weight:700;line-height:1.3;overflow:hidden;text-overflow:ellipsis;
    display:-webkit-box;-webkit-line-clamp:2;-webkit-box-orient:vertical}
  .mp-row-price{font-size:15.5px;font-weight:900;color:var(--gold);margin-top:3px}
  .mp-row-obo{font-size:11px;color:var(--text-3);margin-top:1px}
  .mp-row-meta{font-size:10.5px;color:var(--text-3);margin-top:4px;line-height:1.4}
  /* R115: this wrapped, so a long alias pushed the Verified pill onto a
     second line and the row grew by 20px. It only happens on listings that
     ALSO carry an offer-state pill on the right, which squeezes this column
     to ~127px - which is why most rows looked fine. The alias truncates now
     and the badge holds its place beside it, one line always. */
  .mp-row-seller{font-size:11px;color:var(--text-2);margin-top:7px;display:flex;align-items:center;gap:6px;
    min-width:0;flex-wrap:nowrap}
  /* R116: the ellipsis here is a backstop only - sellerName() has already
     shortened anything over 18 characters, so this fires just on a freak
     narrow viewport rather than on ordinary rows. */
  .mp-seller-name{min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
  .mp-row-seller .ttc-vpill{flex:none}
  /* R114: wraps now. On the marketplace this row holds three small icon
     buttons and never needed to; the offers screens reuse it for up to three
     TEXT buttons plus a link, which ran off the right edge. */
  .mp-row-acts{display:flex;gap:7px;align-items:center;margin-top:10px;padding-top:9px;
    border-top:1px solid var(--hairline);flex-wrap:wrap;row-gap:8px}
  .mp-offer-sent{flex:none;font-size:10px;font-weight:800;color:#5aa9e6;border:1px solid #5aa9e6;
    border-radius:999px;padding:3px 9px;white-space:nowrap}
  /* Status told by shape as well as by colour (section 04, Sep 2026
     accessibility pass) - offerStateLabelLocal/offerStateLabel (purchases/
     list-logic.js, marketplace/offer-helpers.js) stamp data-state on these
     same two pills; this draws a small glyph ahead of the word so a
     colour-blind reader isn't relying on hue alone. Generated content is not
     part of .textContent, so the pill's actual words - what the automated
     test matches - are unchanged. States that already share a colour in the
     JS above (grey/"nothing happened", red/"terminal", blue/"in progress",
     green/"good progress") share a glyph here too. */
  .os-status[data-state="outbid"]::before,.mp-offer-sent[data-state="outbid"]::before{content:"↓ "}
  /* data-state="sent" (the plain "Offer sent" pill) is deliberately NOT in
     this group - the approved mock shows it unmarked, and it isn't really
     "in progress" the way a countered offer or a mid-delivery deal is, so
     it doesn't need to share a glyph with them. The attribute still ships
     (offerStateLabelLocal/offerStateLabel both return it), it just draws
     nothing here. */
  .os-status[data-state="countered"]::before,.mp-offer-sent[data-state="countered"]::before,
  .os-status[data-state="authenticating"]::before,.mp-offer-sent[data-state="authenticating"]::before,
  .os-status[data-state="paid"]::before,.mp-offer-sent[data-state="paid"]::before,
  .os-status[data-state="in-transit"]::before,.mp-offer-sent[data-state="in-transit"]::before{content:"⇄ "}
  .os-status[data-state="accepted"]::before,.mp-offer-sent[data-state="accepted"]::before,
  .os-status[data-state="authenticated"]::before,.mp-offer-sent[data-state="authenticated"]::before,
  .os-status[data-state="delivered"]::before,.mp-offer-sent[data-state="delivered"]::before{content:"✓ "}
  .os-status[data-state="declined"]::before,.mp-offer-sent[data-state="declined"]::before,
  .os-status[data-state="no-sale"]::before,.mp-offer-sent[data-state="no-sale"]::before{content:"✕ "}
  .os-status[data-state="disputed"]::before,.mp-offer-sent[data-state="disputed"]::before,
  .os-status[data-state="review"]::before,.mp-offer-sent[data-state="review"]::before{content:"! "}
  .os-status[data-state="closed"]::before,.mp-offer-sent[data-state="closed"]::before,
  .os-status[data-state="sold-elsewhere"]::before,.mp-offer-sent[data-state="sold-elsewhere"]::before,
  .os-status[data-state="expired"]::before,.mp-offer-sent[data-state="expired"]::before,
  .os-status[data-state="withdrawn"]::before,.mp-offer-sent[data-state="withdrawn"]::before{content:"○ "}
  /* verified: one pill, icon + word sharing a filled background */
  /* Back to the tinted blue the badge always used (#38bdf8 on its own soft
     wash) rather than a solid slab of it, a size down, and both the shield
     and the word centred on one line: line-height:1 plus a block-level svg,
     so neither sits on a text baseline. */
  .ttc-vpill{display:inline-flex;align-items:center;justify-content:center;gap:3px;
    background:rgba(56,189,248,.18);color:#38bdf8;border:1px solid rgba(56,189,248,.4);
    border-radius:999px;padding:2px 7px;font-size:9.5px;font-weight:800;letter-spacing:.2px;
    line-height:1;vertical-align:middle;white-space:nowrap;flex:none}
  .ttc-vpill svg{width:9.5px;height:9.5px;flex:none;display:block}
  .ttc-vpill span{display:block;line-height:1}
  .od-thumb{width:76px;height:76px;flex:none;border-radius:11px;border:1px solid var(--hairline);
    background:radial-gradient(ellipse 70% 60% at 50% 40%, rgba(230,180,80,.12), transparent 70%),
               linear-gradient(160deg,#17171c,#0e0e11);display:grid;place-items:center;overflow:hidden}
  .od-thumb img{max-width:100%;max-height:100%;width:auto;height:auto;object-fit:contain;border-radius:6px}
  .ex-frame img,.ex-frame .catalog-thumb-img{max-width:100%;max-height:100%;width:auto;height:auto;
    object-fit:contain;border-radius:6px;box-shadow:0 6px 18px rgba(0,0,0,.5)}
  .dual-photo .dp.catalog-has-img{overflow:hidden;background:#101217}
  .dual-photo .dp.catalog-has-img{cursor:zoom-in}
  .dual-photo .dp.catalog-has-img span{background:rgba(0,0,0,.62);color:#fff;border-radius:999px;padding:3px 8px;bottom:8px}
  .catalog-preview-card{background:var(--card);border:1px solid var(--hairline);border-radius:14px;padding:12px;margin-bottom:11px}
  .catalog-preview-photos{display:grid;grid-template-columns:1fr 1fr;gap:8px;margin:9px 0}
  .catalog-preview-photos img{width:100%;aspect-ratio:1.45;object-fit:contain;border-radius:10px;border:1px solid var(--hairline);background:#0b0d12;cursor:zoom-in}
  .catalog-status-pill{font-size:10px;font-weight:800;letter-spacing:.35px;border-radius:999px;padding:3px 7px;background:rgba(56,189,248,.14);color:#38bdf8;border:1px solid rgba(56,189,248,.3)}
  .photo-lightbox{position:absolute;inset:0;z-index:999;background:rgba(5,6,10,.94);display:none;align-items:center;justify-content:center;padding:18px}
  .photo-lightbox.on{display:flex}
  .photo-lightbox img{max-width:100%;max-height:84%;object-fit:contain;border-radius:12px;border:1px solid rgba(255,255,255,.18);background:#05060a}
  .photo-lightbox .pl-close{position:absolute;top:14px;right:14px;width:38px;height:38px;border-radius:999px;border:1px solid rgba(255,255,255,.2);background:rgba(255,255,255,.08);color:#fff;font-size:22px;line-height:1;cursor:pointer}
  .photo-lightbox .pl-caption{position:absolute;left:18px;right:18px;bottom:17px;text-align:center;color:#f6f1df;font-size:12px;font-weight:700}
  /* the transform is driven by touch, not the browser, so stop it starting a drag or a text selection of its own */
  #ttc-lb-imgwrap img{will-change:transform;user-select:none;-webkit-user-drag:none}
  .ac-photo{display:flex;align-items:center;gap:10px;border:1px dashed var(--hairline-strong);border-radius:12px;padding:14px;cursor:pointer;color:var(--text-2);font-size:13px;font-weight:600}
  .ac-photo svg{width:20px;height:20px;color:var(--gold)}
  .ac-photo .ac-opt{margin-left:auto;font-size:11px;color:var(--text-3);font-weight:500}

  /* bottom nav */
  .navwrap{position:absolute;left:0;right:0;bottom:0;z-index:30;display:flex;justify-content:center;background:var(--app-bg);border-top:1px solid var(--hairline);padding:10px 14px 14px}
  .nav{display:inline-flex;align-items:center;gap:14px}
  .nav button{background:none;border:none;display:flex;flex-direction:column;align-items:center;gap:3px;padding:7px 11px;border-radius:15px;color:var(--text-3);font-size:10px;font-weight:600;cursor:pointer;font-family:inherit}
  .nav button svg{width:21px;height:21px}
  .nav button.on{color:var(--gold);background:var(--gold-soft)}
  #o-mpdetail.overlay{z-index:45}
  #o-mpdetail .ov-pad{padding-bottom:132px}
  body.ttc-mpdetail-open .screen-frame > .scroll,
  body.ttc-mpdetail-open .screen-frame > .navwrap{visibility:hidden}
  body.ttc-mpdetail-open #o-mpdetail,
  body.ttc-mpdetail-open .modal-bg.on,
  body.ttc-mpdetail-open .photo-lightbox.on{visibility:visible}
  .caption{max-width:420px;color:#6a6a72;font-size:12px;text-align:center;margin-top:16px;line-height:1.5}

  /* ---------------------------------------------------------------------
     DEAL HISTORY (owner redesign, 29 Aug 2026)

     "I don't like the aesthetics of the deal history screen. Multiple random
     chips are allover the top the screen... keep the minimalist theme...
     inspired by the top auction apps."

     Every class below is NEW and dh-prefixed. Nothing here restyles a shared
     class - .offer-card, .status, .chip and friends are used on a dozen other
     screens and are deliberately left alone; the sheet borrows the catalog
     filter's own .modal / .fl-group / .fl-lbl / .fl-chips / .schip so the two
     read as the same sheet rather than as two filter designs in one app.

     The pattern taken from auction/marketplace purchase-history screens:
       one control bar (search + a single Filter affordance carrying a count),
       rows grouped by time under quiet headers,
       image left, money right, ONE status word carrying the only colour.
     Colour means something here and nothing else: green a deal that closed,
     red one that did not, neutral one still moving, gold for actions. ------ */
  .dh-bar{display:flex;gap:9px;align-items:stretch;margin-bottom:9px}
  .dh-bar .ec-search{flex:1;min-width:0;margin-bottom:0;padding:10px 12px}
  .dh-bar .ec-search input{font-size:16px}
  .dh-fbtn{display:flex;align-items:center;gap:7px;flex:none;background:var(--card);
    border:1px solid var(--hairline-strong);color:var(--text);border-radius:12px;
    padding:0 13px;font-size:13px;font-weight:600;font-family:inherit;cursor:pointer}
  .dh-fbtn svg{width:16px;height:16px;color:var(--text-3);flex:none}
  /* lit only while something is actually narrowing the list, so the default
     screen carries no colour at the top at all */
  .dh-fbtn.on{border-color:var(--gold);color:var(--gold)}
  .dh-fbtn.on svg{color:var(--gold)}
  .dh-fbtn .dh-n{display:none;min-width:17px;height:17px;padding:0 4px;border-radius:999px;
    background:var(--gold);color:#1a1407;font-size:10.5px;font-weight:800;
    align-items:center;justify-content:center;font-variant-numeric:tabular-nums}
  .dh-fbtn.on .dh-n{display:inline-flex}
  .dh-meta{display:flex;align-items:baseline;justify-content:space-between;gap:12px;
    margin:0 2px 16px;font-size:11px;color:var(--text-3);letter-spacing:.2px}
  .dh-meta .dh-clear{display:none;color:var(--gold);font-weight:700;cursor:pointer;flex:none}
  .dh-meta.narrowed .dh-clear{display:inline}

  /* time groups: the header is a quiet label, the rows below it are one card
     with hairline separators - fewer borders than a card per deal, and the
     whitespace between groups is what gives the list its shape */
  .dh-gap{height:19px}
  .dh-ghead{margin:0 2px 7px;font-size:10.5px;font-weight:800;letter-spacing:1.1px;
    text-transform:uppercase;color:var(--label)}
  .dh-card{background:var(--card);border:1px solid var(--hairline);border-radius:16px;overflow:hidden}
  .dh-row{display:flex;gap:11px;align-items:center;padding:9px 12px;cursor:pointer;position:relative}
  .dh-row + .dh-row{border-top:1px solid var(--hairline)}
  .dh-row:active{background:var(--card-locked)}
  .dh-thumb{width:44px;height:44px;border-radius:11px;border:1px solid var(--hairline);
    background:var(--card-locked);color:var(--text-3);display:grid;place-items:center;flex:none;overflow:hidden}
  .dh-thumb img{width:100%;height:100%;object-fit:cover;display:block}
  .dh-thumb svg{width:20px;height:20px}
  .dh-main{flex:1;min-width:0}
  .dh-name{font-size:13.5px;font-weight:700;line-height:1.3;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
  .dh-sub{font-size:11px;color:var(--text-3);margin-top:3px;line-height:1.3;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
  .dh-right{flex:none;text-align:right;max-width:40%}
  .dh-amt{font-size:13px;font-weight:800;font-variant-numeric:tabular-nums;white-space:nowrap}
  /* a deal that closed without a sale moved no money: the figure is history,
     not a receipt, so it is stated quietly rather than in the money weight */
  .dh-amt.void{color:var(--text-3);font-weight:700}
  .dh-state{font-size:10.5px;font-weight:700;margin-top:3px;white-space:nowrap}
  .dh-state.ok{color:var(--green)}
  .dh-state.bad{color:var(--red)}
  .dh-state.mute{color:var(--text-3)}
  .dh-state.wip{color:var(--text-2)}
  /* arrived here from a message or from Purchases: mark the row, don't reorder
     the list out from under its own date headers */
  .dh-row.focus{background:var(--gold-soft)}
  .dh-row.focus::before{content:'';position:absolute;left:0;top:0;bottom:0;width:3px;background:var(--gold)}

  /* the filter sheet - shared modal chrome, deal-shaped contents */
  .dh-sheet{max-width:344px}
  .dh-sheet-head{display:flex;align-items:flex-start;gap:10px;margin-bottom:16px}
  .dh-sheet-head h3{flex:1;margin:0}
  .dh-sheet-x{flex:none;width:30px;height:30px;margin:-4px -4px 0 0;border-radius:50%;
    display:grid;place-items:center;cursor:pointer;color:var(--text-3)}
  .dh-sheet-x svg{width:17px;height:17px}
  .dh-sheet-foot{display:flex;gap:9px;margin-top:18px}
  .dh-sheet-foot button{flex:1;width:auto;margin:0;padding:12px;border-radius:11px;
    font-size:13.5px;font-weight:700;font-family:inherit;cursor:pointer}

  /* the detail screen's own head, so the row and the deal it opens speak with
     one voice instead of a pill on one screen and a word on the other */
  .dh-dhead{display:flex;gap:11px;align-items:center;margin-bottom:12px}
  .dh-dhead .dh-main .dh-name{white-space:normal;font-size:14.5px}
  /* the list ellipsises the stage line because it has one line to give it; the
     detail has the room to say the whole thing, which is the point of drilling in */
  .dh-dhead .dh-sub{white-space:normal;font-size:11.5px}
  .dh-dhead .dh-state{margin-top:0;font-size:11px}

  /* ---- social sign-in (auth screens) ---------------------------------
     Built by js/screens/auth.js: ONE rule set for every provider, so a
     second button lines up with the first by construction rather than by
     someone remembering to copy the numbers.

     Proportions are deliberately .btn-primary's - full width, 14px
     radius, 15px/700 type, 14px padding - so "Log in" and "Sign in with
     Google" read as two equal-weight choices in one stack instead of a
     primary action with a widget bolted underneath. The chrome is the
     app's own card + hairline, the same as .in-field .box two rows above
     it, which is what keeps it looking designed on this screen rather
     than imported from someone else's.

     The provider MARKS carry literal brand hexes (Google's four Gs,
     Apple's white glyph). That is required by both companies' brand
     guidelines and is the one place in this block where an app token
     would be wrong. Everything else here is a token. */
  .social-auth{margin:2px 0 4px}
  .social-btn{width:100%;display:flex;align-items:center;justify-content:center;gap:10px;
    background:var(--card);color:var(--text);border:1px solid var(--hairline-strong);
    border-radius:14px;padding:14px;font-size:15px;font-weight:700;font-family:inherit;
    line-height:1;cursor:pointer;transition:background .15s,border-color .15s}
  .social-btn + .social-btn{margin-top:9px}
  .social-btn:active{background:var(--card-locked);border-color:var(--hairline)}
  .social-btn svg{flex:none;width:19px;height:19px}
  .social-btn.apple svg{color:var(--text)}
  .social-btn[disabled]{opacity:.55;cursor:default}
  /* Its own class rather than .hint: .hint is only ever styled inside an
     .in-field, and this note lives outside one. */
  .social-note{font-size:11.5px;color:var(--text-3);line-height:1.45;text-align:center;margin-top:10px}

  /* ===================================================================
     LOGIN LANDING - a coin falls into the logo, and gold light crosses
     the screen behind it. Owner-directed, 29 Aug 2026: "understated and
     premium, logo as the hero", then chosen from four built side by side.
     The other three (a settle, a slow glow, a weighted drop) are deleted
     rather than parked - an unused variant is a thing that rots quietly and
     then confuses whoever reads this next.

     THE RULE THESE ALL FOLLOW: nothing here delays a person who just wants
     to log in. The form is interactive from the first frame; the motion
     plays over it. Only transform, opacity and filter are animated - the
     three things a phone's compositor handles without touching layout, so
     this stays smooth on a mid-range Android and costs no measurable
     battery. Nothing loops forever except variant B's breath, which is
     8 seconds and barely there.

     Triggered by auth.js adding .auth-motion on arrival at the screen.
     =================================================================== */
  #o-login .auth-logo-wrap{position:relative;display:block;width:max-content;margin:38px auto 22px;overflow:hidden}
  #o-login .auth-logo{height:46px;margin:0}          /* the hero, up from 30px */

  /* every variant: the form arrives just behind the logo, in order.

     FILL MODE `both`, AND NO STANDALONE opacity:0 - that pairing is what
     makes this safe. These keyframes START at opacity 0, so a hard
     opacity:0 on the rule would leave the whole login screen INVISIBLE
     anywhere the animation never runs: a hidden or backgrounded tab creates
     no animation at all (measured 29 Aug - document.getAnimations() returns
     nothing there). With `both` the 0% frame is applied only while a REAL
     animation is pending, and the natural, visible styles stand otherwise.
     Decoration must never be able to hide the product. */
  .auth-motion .auth-h,
  .auth-motion .auth-sub,
  .auth-motion .in-field,
  .auth-motion .row-between,
  .auth-motion .btn-primary,
  .auth-motion .social-auth,
  .auth-motion .auth-alt{animation:ttcRise .55s cubic-bezier(.22,.61,.36,1) both}
  .auth-motion .auth-h{animation-delay:.16s}
  .auth-motion .auth-sub{animation-delay:.22s}
  .auth-motion .in-field:nth-of-type(1){animation-delay:.28s}
  .auth-motion .in-field:nth-of-type(2){animation-delay:.34s}
  .auth-motion .row-between{animation-delay:.40s}
  .auth-motion .btn-primary{animation-delay:.46s}
  .auth-motion .social-auth{animation-delay:.52s}
  .auth-motion .auth-alt{animation-delay:.58s}
  @keyframes ttcRise{from{opacity:0;transform:translateY(9px)}to{opacity:1;transform:none}}

  @media (prefers-reduced-motion: reduce){
    .auth-motion .auth-h,.auth-motion .auth-sub,.auth-motion .in-field,.auth-motion .row-between,
    .auth-motion .btn-primary,.auth-motion .social-auth,.auth-motion .auth-alt,
    .auth-motion .auth-logo{animation:none!important;opacity:1!important;transform:none!important}
    .auth-motion .auth-logo-wrap::after,.auth-motion .auth-logo-wrap::before{animation:none!important;display:none}
  }

  /* --- D. DROP: a coin falls into the logo, and gold light crosses the
         screen behind it. Owner-directed, 29 Aug 2026.

         The hook is that the logo ALREADY contains a coin - the O of
         COLLECTORS. Measured off the artwork rather than guessed: its centre
         sits at 29.6% across and 52% down, and it is 152 of the image's 255
         pixels wide, which is 9.88% of the width. Expressed as percentages
         so it stays locked to the O at any logo size.

         Sequence: the coin falls, bounces once, and the WORDMARK fades in at
         the moment of impact - so the logo appears to be struck from the
         coin. The falling coin then fades out over the logo's own coin,
         which is underneath at exactly that spot, so the handover is
         invisible. */
  .auth-motion .auth-logo{animation:ttcLogoWake .5s ease-out .46s both}
  @keyframes ttcLogoWake{from{opacity:0;transform:scale(.99)}to{opacity:1;transform:none}}
  .auth-motion .auth-logo-wrap{overflow:visible}
  .auth-motion .auth-logo-wrap::before{
    content:'';position:absolute;left:29.6%;top:52%;width:9.88%;aspect-ratio:1;border-radius:50%;
    pointer-events:none;z-index:2;
    background:radial-gradient(circle at 34% 28%,rgba(255,255,255,.5),transparent 44%),
               conic-gradient(from 200deg,#e6b450 0deg,#c99a3a 90deg,#e6b450 180deg,#c99a3a 270deg,#e6b450 360deg);
    box-shadow:0 3px 10px rgba(0,0,0,.5);
    animation:ttcCoinDrop .82s cubic-bezier(.34,0,.4,1) forwards,
              ttcCoinHandover .34s ease-out .86s forwards}
  /* 64px, not 150: the logo sits near the top of the screen and the overlay
     scrolls, so anything higher than the header starts CLIPPED - the coin was
     falling for a third of a second where nobody could see it. Measured, not
     guessed: at -150px the computed transform was right and the coin was
     simply outside the box. */
  @keyframes ttcCoinDrop{
    0%  {opacity:0;transform:translate(-50%,-50%) translateY(-64px) scale(.86) rotate(-26deg)}
    18% {opacity:1}
    58% {transform:translate(-50%,-50%) translateY(0) scale(1) rotate(0deg)}
    72% {transform:translate(-50%,-50%) translateY(-8px) scale(1) rotate(0deg)}
    88% {transform:translate(-50%,-50%) translateY(0) scale(1) rotate(0deg)}
    100%{opacity:1;transform:translate(-50%,-50%) rotate(0deg)}}
  @keyframes ttcCoinHandover{to{opacity:0}}

  /* the gold light crossing the whole screen, once. fixed + blurred so it
     reads as light rather than a shape, and behind everything the user
     touches. */
  /* NO position:relative HERE. It was added out of habit, to give the ::before
     a containing block - which it does not need, because that pseudo-element
     is position:FIXED and resolves against the viewport. The habit cost a
     broken screen: `.auth-motion` is two classes and beats `.overlay`'s
     one, so it replaced position:absolute with position:relative, the overlay
     dropped out of its pinned position into normal flow, and the login screen
     rendered as a strip of logo at the BOTTOM of an empty page. Reported from
     a real phone, 29 Aug 2026. */
  .auth-motion::before{
    content:'';position:fixed;inset:-25%;pointer-events:none;z-index:0;
    background:linear-gradient(100deg,transparent 38%,rgba(230,180,80,.20) 50%,transparent 62%);
    filter:blur(30px);transform:translateX(-125%);
    animation:ttcGoldSwipe 1.8s cubic-bezier(.4,0,.25,1) .18s forwards}
  @keyframes ttcGoldSwipe{to{transform:translateX(125%)}}
  .auth-motion .ov-pad{position:relative;z-index:1}

  @media (prefers-reduced-motion: reduce){
    .auth-motion::before,.auth-motion .auth-logo-wrap::before{animation:none!important;display:none}
  }

  /* ===================================================================
     BUTTON SYSTEM - four tiers, owner-approved 29 Aug 2026 from "The
     Polish Pass" design review. A resting screen gets exactly one loud
     element, and it is never the destructive one:
       PRIMARY     .btn-primary (existing) - filled gold, one per screen
       SECONDARY   .btn-ghost   (existing) - outline gold
       QUIET       .btn-quiet   (new)      - no border, text-2 grey
       DESTRUCTIVE .btn-danger-quiet (new) - same shape as QUIET, red text,
                    never a filled block sitting on a resting screen
     Only two classes are actually new; primary/secondary already existed
     and needed no change - this formalizes the system, it does not
     replace it.
     =================================================================== */
  .btn-quiet{width:100%;background:none;border:none;color:var(--text-2);
    font-size:13.5px;font-weight:700;padding:12px;cursor:pointer;font-family:inherit}
  .btn-quiet:active{color:var(--text)}
  .btn-danger-quiet{width:100%;background:none;border:none;color:#ff8a8a;
    font-size:13.5px;font-weight:700;padding:10px;cursor:pointer;font-family:inherit}
  .btn-danger-quiet:active{color:#ffb3b3}
  /* the blue counterpart to the confirm popup's filled red Confirm - same
     shape and weight, so Cancel reads as an equally real choice rather than
     the quiet fallback beside a loud destructive action. Reuses --blue-deep,
     already the fill this app uses for a filled blue button elsewhere
     (.offer-acts .btn-counter), so this is not a new colour, just a new
     place it appears. */
  .btn-counter.btn-blue{background:var(--blue-deep)!important;color:#fff!important;
    border-color:var(--blue-deep)!important}

  /* The Make-an-offer sheet's Cancel (owner, 12 Sep 2026): red, and the same
     block as Submit beside it - .btn-primary's padding/radius/type, plus the
     flex:1 the pair shares, with the red recipe .dup-btn.decline already uses.
     A modifier on this one button; .btn-counter itself dresses Cancel on
     every other sheet and stays as it is. */
  .btn-counter.mp-offer-cancel{flex:1;padding:14px;border-radius:14px;font-size:15px;font-weight:700;font-family:inherit;cursor:pointer;
    background:#3a1414;color:#ff8a8a;border:1px solid rgba(255,90,90,.4)}

  /* ===================================================================
     HOME REDESIGN - owner-approved 30 Aug 2026, reviewed as a rendered
     preview before any of it was written.

     Three pieces, in the order they appear on the screen:
       1. the glance row  - four stats on ONE line instead of a 2x2 of
                            boxes, saving ~100px above the fold
       2. .attn           - "Needs your attention", a new section
       3. .more-bed/.glass- the four destination cards as frosted panels

     NOTHING here touches .dcard, .dcard-* or .grid2. Those look unused
     now that home has stopped using them, but o-admin.html alone has 37
     occurrences - the redesign introduces fresh classes and simply walks
     away from the old ones.
     =================================================================== */

  /* 1. GLANCE ROW. Dividers rather than four separate bordered cards, so
     the group reads as one object. The badge copies .dcard-badge's
     mechanism EXACTLY (hidden by default, shown by .on) because
     dashSetBadge in home.js toggles that class and sets no inline style -
     a badge that defaulted to visible would sit there as an empty red
     pill forever. */
  .glance-row{display:flex;background:var(--card);border:1px solid var(--hairline);
    border-radius:16px;padding:14px 6px}
  .glance-item{position:relative;flex:1;min-width:0;display:flex;flex-direction:column;
    align-items:center;gap:7px;text-align:center;padding:0 4px;cursor:pointer}
  .glance-item+.glance-item{border-left:1px solid var(--hairline)}
  .glance-item:active .glance-n{color:var(--gold)}
  .glance-ic{width:30px;height:30px;border-radius:9px;display:grid;place-items:center}
  .glance-ic svg{width:15px;height:15px}
  .glance-ic.c-blue{background:rgba(90,169,230,.15);color:var(--blue)}
  .glance-ic.c-green{background:rgba(52,214,95,.15);color:var(--green)}
  .glance-ic.c-gold{background:var(--gold-soft);color:var(--gold)}
  .glance-ic.c-blued{background:rgba(47,128,216,.16);color:#7fb4ea}
  .glance-n{font-size:17px;font-weight:800;line-height:1;color:var(--text);
    font-variant-numeric:tabular-nums}
  .glance-l{font-size:9px;font-weight:700;line-height:1.25;color:var(--text-3)}
  .glance-badge{position:absolute;top:-4px;right:10px;min-width:15px;height:15px;padding:0 3px;
    border-radius:999px;background:var(--red);color:#fff;font-size:9px;font-weight:800;
    display:none;align-items:center;justify-content:center}
  .glance-badge.on{display:flex}

  /* 2. NEEDS YOUR ATTENTION. Each row carries its urgency on a coloured
     left edge, set per row through --accent/--accent-soft so one rule
     serves every trigger. The wrapper (#dash-attn-wrap) holds the heading
     too and starts display:none, so a quiet account shows nothing at all
     rather than a heading over empty space. */
  .attn{display:flex;flex-direction:column;gap:9px}
  .attn-row{display:flex;align-items:center;gap:12px;background:var(--card);
    border:1px solid var(--hairline);border-left:3px solid var(--accent);
    border-radius:12px;padding:12px 13px;cursor:pointer}
  .attn-row:active{border-color:var(--hairline-strong);border-left-color:var(--accent)}
  .attn-ic{width:32px;height:32px;border-radius:9px;background:var(--accent-soft);
    color:var(--accent);display:grid;place-items:center;flex:none}
  .attn-ic svg{width:16px;height:16px}
  .attn-tx{flex:1;min-width:0}
  .attn-t{font-size:13px;font-weight:700;color:var(--text)}
  .attn-s{font-size:11px;color:var(--text-3);margin-top:2px;
    overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
  .attn-chev{color:var(--text-3);flex:none}
  .attn-chev svg{width:14px;height:14px;display:block}

  /* 3. THE "MORE" PANELS. First translucent surface in the app - there is
     no earlier precedent to match, so the recipe is written out here.
     The wash under the panels is what the blur has to work with; without
     it, frosted glass over a flat background just looks like a grey card.
     backdrop-filter needs the -webkit- prefix on iOS Safari, and the
     rgba background alone has to stand up on its own where the property
     is unsupported - which it does, at .07 over --card. */
  .more-bed{position:relative;border-radius:20px;padding:14px;overflow:hidden;
    background:linear-gradient(160deg,rgba(230,180,80,.14),rgba(90,169,230,.08) 55%,rgba(0,0,0,0))}
  .more-bed::before,.more-bed::after{content:'';position:absolute;border-radius:50%;
    filter:blur(30px);pointer-events:none}
  .more-bed::before{width:150px;height:150px;background:var(--gold);opacity:.35;top:-50px;right:-40px}
  .more-bed::after{width:130px;height:130px;background:var(--blue);opacity:.28;bottom:-40px;left:-30px}
  .glass-list{position:relative;display:flex;flex-direction:column;gap:9px}
  .glass{display:flex;align-items:center;gap:12px;padding:13px;border-radius:15px;cursor:pointer;
    background:rgba(255,255,255,.07);border:1px solid rgba(255,255,255,.14);
    backdrop-filter:blur(18px) saturate(160%);-webkit-backdrop-filter:blur(18px) saturate(160%);
    box-shadow:inset 0 1px 0 rgba(255,255,255,.16),0 8px 20px -10px rgba(0,0,0,.5)}
  .glass:active{background:rgba(255,255,255,.11)}
  .glass-ic{width:36px;height:36px;border-radius:10px;background:rgba(255,255,255,.12);
    color:var(--gold);display:grid;place-items:center;flex:none}
  .glass-ic svg{width:17px;height:17px}
  .glass-t{font-size:13px;font-weight:700;color:var(--text)}
  .glass-l{font-size:10.5px;color:var(--text-2);margin-top:2px}
  .glass-chev{margin-left:auto;color:var(--text-2);flex:none}
  .glass-chev svg{width:14px;height:14px;display:block}

  /* 4. THE CTA'S REFINED FINISH. Same size, same padding, same words -
     only the surface changed: a gold-tinted drop shadow so it lifts off
     the page, and a one-pixel inset highlight along the top edge so it
     reads as a raised physical control rather than a flat gold rectangle.
     Deliberately the LAST word on .cta so it wins without !important. */
  .cta{box-shadow:0 14px 30px -12px rgba(230,180,80,.5),
    inset 0 1px 0 rgba(255,255,255,.35)}

  /* ===================================================================
     THE ADD BUTTON - owner-approved 30 Aug 2026 ("option 3 Slim",
     centred, with the G2 "lacquered" finish), chosen from a rendered
     study rather than a description.

     A MODIFIER, not a rewrite of .cta. The base class is shared: the
     marketplace empty state builds one in purchases.js:351, and
     02-late.css:67 styles `.ttc-empty .cta`. Restyling .cta itself would
     have silently reshaped a button on another screen.

     WHY LACQUERED AND NOT TRANSLUCENT. Every see-through option forces
     light text, because dark ink stops working the moment the fill goes
     transparent - and dark-on-gold is the highest contrast on the home
     screen, which is what marks this as THE primary action. This applies
     glass OVER the gold rather than making the gold out of glass, so the
     finish is gained and the contrast is kept.
     =================================================================== */
  .cta-add{flex-direction:column;justify-content:center;align-items:center;
    text-align:center;gap:3px;padding:14px 16px;border-radius:14px;
    position:relative;overflow:hidden}
  .cta-add .cta-row{display:flex;align-items:center;justify-content:center;gap:8px;
    position:relative;z-index:1;max-width:100%}
  .cta-add .cta-glyph{width:17px;height:17px;flex:none}
  .cta-add .cta-glyph svg{width:17px;height:17px;display:block}
  .cta-add .cta-s{position:relative;z-index:1;margin-top:0}
  /* the lacquer: a diagonal sheen across the face, plus a soft light streak.
     Both are decorative and must never eat the tap - hence pointer-events. */
  .cta-add::before{content:'';position:absolute;inset:0;border-radius:inherit;pointer-events:none;
    background:linear-gradient(158deg,rgba(255,255,255,.42) 0%,rgba(255,255,255,.10) 38%,
      rgba(255,255,255,0) 52%,rgba(120,86,22,.14) 100%)}
  .cta-add::after{content:'';position:absolute;left:-30%;top:-140%;width:60%;height:280%;
    transform:rotate(18deg);pointer-events:none;
    background:linear-gradient(90deg,rgba(255,255,255,0),rgba(255,255,255,.30),rgba(255,255,255,0))}
  .cta-add:active::before{background:linear-gradient(158deg,rgba(255,255,255,.30) 0%,
    rgba(255,255,255,.06) 38%,rgba(255,255,255,0) 52%,rgba(120,86,22,.2) 100%)}

  /* The app already has ONE AI mark (.ai-badge, line ~316: gold on gold-soft,
     used on the scan screen's Item and Category fields). Reused here rather
     than inventing a second one - but its gold-on-gold-soft is invisible on a
     gold button, so on .cta-add it becomes the button's own dark ink on a
     dark-translucent chip. Same mark, legible ground. */
  .cta-add .ai-badge{color:var(--cta-text);background:rgba(26,20,7,.19);
    margin-left:0;font-size:9px;padding:1px 5px 1px 4px;border-radius:5px}

  /* ---------------------------------------------------------------------
     OFFERS SENT - the eBay row (owner-approved mock, 6 Sep 2026)

     All new, .os-prefixed. .mp-row/.mp-row-wrap and friends stay exactly as
     they were - they are still the marketplace browse list's own row and
     the "Purchased"/"Closed without a sale" cards below this one - only
     the offers-sent list draws itself with these instead, from here on.
     Each group is one .os-card (same shape as .offer-card elsewhere: a
     bordered box, not a bare hairline list), holding one .os-row (the
     100px-photo grid the mock specifies) and, below it, the SAME
     .offer-acts/.dup-btn row purchaseActions() has always produced -
     nothing there is restyled. */
  .os-card{background:var(--card);border:1px solid var(--hairline);border-radius:14px;
    padding:13px;margin-bottom:10px}
  .os-row{display:grid;grid-template-columns:100px 1fr;gap:12px;cursor:pointer}
  .os-thumb{width:100px;height:100px;flex:none;border-radius:12px;border:1px solid var(--hairline);
    background:radial-gradient(ellipse 70% 60% at 50% 40%, rgba(230,180,80,.10), transparent 70%),
               linear-gradient(160deg,#17171c,#0e0e11);display:grid;place-items:center;overflow:hidden}
  .os-thumb img{width:100%;height:100%;display:block}
  .os-status{display:inline-flex;font-size:10px;font-weight:800;padding:3px 9px;border-radius:999px;
    white-space:nowrap;border:1px solid;line-height:1.3;text-transform:uppercase}
  .os-title{font-size:15px;font-weight:600;line-height:1.25;margin-top:6px;overflow:hidden;
    display:-webkit-box;-webkit-line-clamp:2;-webkit-box-orient:vertical}
  .os-price{font-size:19px;font-weight:900;color:var(--gold);margin-top:5px}
  .os-line{font-size:12.5px;color:var(--text-2);margin-top:2px}
  .os-foot{display:flex;align-items:center;justify-content:space-between;margin-top:8px}
  .os-time{font-size:12px;color:var(--text-3);white-space:nowrap}
  .os-icons{display:flex;gap:8px}
  .os-icon{width:30px;height:30px;flex:none;border-radius:50%;border:1px solid var(--hairline);
    display:grid;place-items:center;font-size:14px;color:var(--text-2);cursor:pointer;background:transparent}
  /* the ··· sheet's rows - same .modal-bg/.modal.dh-sheet chrome as the
     Refine sheet, plain rows rather than another fl-chips grid since these
     are one-tap actions, not a multi-select axis */
  .os-sheet-row{padding:13px 4px;font-size:13.5px;font-weight:700;cursor:pointer;
    border-bottom:1px solid var(--hairline)}
  .os-sheet-row:last-child{border-bottom:none}

  /* ---------------------------------------------------------------------
     ITEM VIEW, OPENED FROM AN OFFER (owner-approved mock, 6 Sep 2026)

     All new, .mpd-prefixed (this screen's existing "mp-" prefix was
     already taken by the browse row/carousel/thumbs/seller-row classes,
     which stay exactly as they are - .mpd- marks the item view's own new
     blocks so the two families never collide). Reuses .btn-primary,
     .btn-ghost, .dup-btn and .offer-acts everywhere a real button already
     existed for the job. */
  .mpd-offer{background:var(--card);border:1px solid var(--hairline);border-radius:14px;
    padding:14px;margin-bottom:14px}
  .mpd-offer-h{font-size:17px;font-weight:800;line-height:1.2}
  .mpd-offer-s{font-size:12.5px;color:var(--text-2);margin-top:6px;line-height:1.5}
  .mpd-offer-amt{font-size:22px;font-weight:900;color:var(--gold);margin-top:10px;line-height:1}
  .mpd-offer-amt small{font-size:12px;font-weight:700;color:var(--text-2);margin-left:8px}
  .mpd-offer-exp{font-size:12px;color:var(--text-2);margin-top:5px}
  .mpd-offer #mpd-offer-collapse{margin-top:12px;padding-top:12px;border-top:1px solid var(--hairline)}
  /* "you were outbid" (20260906230000_outbid.sql) - the block's own red
     accent; everything else about .mpd-offer stays as it is. */
  .mpd-offer.outbid{border-color:rgba(225,28,54,.55);box-shadow:0 0 0 1px rgba(225,28,54,.18)}
  .mpd-offer.outbid .mpd-offer-h{color:#ff8a8a}
  /* the hero photo's watcher badge - bottom-right, mirrors the carousel's
     own bottom-left tag+count chip already drawn by photoCarousel() */
  .mpd-hero-wrap{position:relative}
  .mpd-hero-watchers{position:absolute;right:9px;bottom:9px;background:rgba(0,0,0,.62);
    color:#fff;border-radius:7px;padding:3px 8px;font-size:10.5px;font-weight:800}
  .mpd-social{background:var(--card);border:1px solid var(--hairline);border-radius:14px;
    padding:12px;margin:14px 0;display:grid;gap:10px;font-size:12.5px;color:var(--text-2)}
  .mpd-social div{display:flex;gap:10px;align-items:flex-start}
  .mpd-social b{color:var(--text)}
  .mpd-social-ic{flex:none;width:28px;height:28px;border-radius:50%;background:var(--card-locked);
    border:1px solid var(--hairline);display:grid;place-items:center;font-size:13px;color:var(--gold)}
  .mpd-kvt{background:var(--card);border:1px solid var(--hairline);border-radius:14px;
    padding:12px;margin-bottom:14px}
  .mpd-kvt-h{font-size:11px;letter-spacing:.6px;text-transform:uppercase;color:var(--text-3);
    font-weight:800;margin-bottom:7px}
  .mpd-kvt-g{display:grid;grid-template-columns:1fr 1fr;gap:4px 14px;font-size:12.5px}
  .mpd-kvt-g div{display:flex;justify-content:space-between;border-bottom:1px solid var(--hairline);padding:5px 0}
  .mpd-kvt-g span:first-child{color:var(--text-3)}
  .mpd-kvt-g span:last-child{font-weight:700;text-align:right}
  /* the two rails - Similar listings, then More from this seller */
  .mpd-sech{font-size:14px;font-weight:800;margin:18px 0 8px}
  .mpd-hs{display:flex;gap:10px;overflow-x:auto;padding-bottom:2px;margin-bottom:6px}
  .mpd-hs::-webkit-scrollbar{height:0}
  .mpd-mini{flex:none;width:128px;background:var(--card);border:1px solid var(--hairline);
    border-radius:12px;overflow:hidden;cursor:pointer}
  .mpd-mini-im{aspect-ratio:1.12;display:grid;place-items:center;background:var(--card-locked)}
  .mpd-mini-im img{width:100%;height:100%;object-fit:contain}
  .mpd-mini-b{padding:8px;font-size:11px}
  .mpd-mini-b b{display:block;font-size:12px;font-weight:700;line-height:1.25;color:var(--text);
    overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
  .mpd-mini-p{font-weight:900;margin-top:3px;font-size:12.5px;color:var(--gold)}
  .mpd-mini-s{color:var(--text-3);font-size:10.5px;margin-top:1px;overflow:hidden;
    text-overflow:ellipsis;white-space:nowrap}
