*, *::before, *::after {
    box-sizing: border-box;
    margin: 0;
    padding: 0;
}

/* ONE uniform product/card thumbnail size for every normal browse
   card/list/grid/carousel context — Binder (list, page/pocket, grid, Move
   Spot) and Trade (Community, Your Trade Listings, Recommended/Recently
   Added carousel, ISO Match alert) all render thumbnails at this exact
   width x aspect-ratio, at EVERY viewport width (no separate mobile tier —
   the surrounding layout reflows around the fixed image, the image itself
   never resizes). Replaces the earlier --card-img-compact(108)/
   -standard(72)/-standard-mobile(100) three-tier scale, which is why
   Binder still visibly disagreed with Trade after the Phase-1 pass: three
   different components each picked a different tier of that scale, and
   Binder's own classes (.binder-card__image-frame, .binder-pocket-*,
   .move-preview-thumb/.move-done-thumb) didn't consume it at all — see the
   selector list on .thumb-frame-card below, which is now the single
   source every one of those classes maps onto.

   object-fit: fill (not contain) on the image itself is deliberate: a
   sealed product or off-ratio user photo would otherwise letterbox inside
   an unchanged-size frame, which still LOOKS like a different (smaller)
   thumbnail even though the outer box is identical. Uniform DISPLAY size
   takes priority over preserving every source image's intrinsic
   proportions (explicit product decision) — stretch, never crop, never
   letterbox.

   --card-img-large is the ONE deliberate exception: the large hero image
   on an individual listing-detail page, untouched by this convergence. */
:root {
    --product-thumb-size:   108px;      /* the one normal-thumbnail width, every context, every width */
    --product-thumb-aspect: 2.5 / 3.5;  /* portrait card box; sealed/user photos stretch to fit via object-fit: fill */
    --card-img-large:       280px;      /* listing-detail hero image only — not part of this convergence */
}

/* ── Breakpoint system (Part J) ──────────────────────────────────────────
   Three tiers, formalized here rather than left implicit: phone <=480px,
   tablet 481-900px, desktop >900px. This reuses the breakpoints that
   already existed and were already load-bearing across many earlier
   passes (480px: touch-target/density tweaks throughout; 700px: the
   item-detail-layout image/info stack point, comfortably inside the
   tablet band) rather than renumbering everything to a fresh 480/900 pair
   — an existing, already-tested 700px stack point that behaves correctly
   for both phone and tablet widths is not a bug to "fix" by renaming it.
   900px is the new explicit tablet/desktop line (Part J), used going
   forward wherever a genuine three-way distinction is needed instead of
   the older two-tier (mobile vs everything-else) shorthand. See
   /dev/mobile-test's Mobile UI Debug HUD (Part K, mobile_ui_debug.js) for
   a live reading of which tier the current viewport falls into. */

body {
    font-family: system-ui, sans-serif;
    background: #1a1a2e;
    color: #e0e0e0;
    min-height: 100vh;
    display: flex;
    flex-direction: column;
}

/* ── Header ── */
.site-header {
    background: #16213e;
    border-bottom: 2px solid #e63946;
    padding: 0.75rem 2rem;
}

.header-inner {
    display: flex;
    align-items: center;
    justify-content: space-between;
    max-width: 960px;
    margin: 0 auto;
    width: 100%;
    gap: 1rem;
}

.site-title {
    font-size: 1.25rem;
    font-weight: 700;
    color: #e63946;
    letter-spacing: 0.03em;
    text-decoration: none;
    white-space: nowrap;
}

.site-title:hover { color: #ff6b6b; }

/* ── Nav ── */
.site-nav {
    display: flex;
    align-items: center;
    gap: 0.85rem;
    flex-wrap: wrap;
}

.nav-link {
    color: #a8dadc;
    text-decoration: none;
    font-size: 0.9rem;
    font-weight: 500;
    padding: 0.2rem 0;
    border-bottom: 2px solid transparent;
    transition: color 0.15s, border-color 0.15s;
}

.nav-link:hover {
    color: #fff;
    border-bottom-color: #e63946;
}

.nav-username {
    color: #7a8fa6;
    font-size: 0.85rem;
}

.nav-link-current {
    color: #fff;
    border-bottom-color: #e63946;
}

/* Hamburger control — hidden on desktop (.site-nav already fits there);
   shown only at narrow widths where the nav would otherwise wrap into
   several awkward short lines. Real button, not a hover-only affordance —
   keyboard/AT accessible via aria-expanded (see nav_toggle.js). */
.nav-toggle {
    display: none;
    flex-direction: column;
    justify-content: center;
    gap: 4px;
    width: 40px;
    height: 40px;
    padding: 0;
    background: transparent;
    border: 1px solid #2a3a5e;
    border-radius: 6px;
    cursor: pointer;
    flex-shrink: 0;
}
.nav-toggle-bar {
    display: block;
    width: 20px;
    height: 2px;
    margin: 0 auto;
    background: #e0e0e0;
}
.nav-toggle:focus-visible {
    outline: 2px solid #7289da;
    outline-offset: 2px;
}

@media (max-width: 700px) {
    .nav-toggle { display: flex; }
    .site-nav {
        display: none;
        position: absolute;
        top: 100%;
        left: 0;
        right: 0;
        flex-direction: column;
        align-items: stretch;
        gap: 0;
        background: #16213e;
        border-bottom: 2px solid #e63946;
        padding: 0.5rem 1.25rem 1rem;
        /* Above the Trade sub-nav (z-index 25) and any other sticky
           content-area bar — the open global-nav dropdown must never render
           underneath page content. */
        z-index: 30;
    }
    .site-nav.site-nav-open { display: flex; }
    .header-inner { position: relative; }
    .nav-link {
        padding: 0.6rem 0;
        border-bottom: 1px solid #2a3a5e;
        font-size: 1rem;
    }
    .nav-username { padding: 0.5rem 0; }
    .site-nav .btn { margin-top: 0.5rem; align-self: flex-start; }
}

/* ── Buttons ── */
.btn {
    display: inline-block;
    padding: 0.45rem 1rem;
    border-radius: 5px;
    font-size: 0.9rem;
    font-weight: 600;
    text-decoration: none;
    cursor: pointer;
    border: none;
    transition: opacity 0.15s;
}

.btn:hover { opacity: 0.85; }

@media (max-width: 480px) {
    /* Shared mobile touch-target baseline (Part E1) — one rule for every
       primary/secondary .btn use site-wide (Select Match, Confirm Match,
       Add to Binder, Make Available, Save/Submit, Edit, photo/camera,
       Trade actions, ISO match-selection) rather than per-page overrides.
       .btn-sm keeps its own smaller footprint for genuinely secondary
       inline actions (status toggles, "Refresh Market Price", etc.) —
       bumping every .btn-sm to 44px would make already-dense action rows
       (Reactivate / Pause / Mark Completed) worse, not better. */
    .btn:not(.btn-sm) {
        min-height: 44px;
        padding: 0.6rem 1.1rem;
        display: inline-flex;
        align-items: center;
        justify-content: center;
    }
}

.btn-sm {
    padding: 0.3rem 0.75rem;
    font-size: 0.8rem;
    background: #333;
    color: #ccc;
}

.btn-discord { background: #5865f2; color: #fff; }
.btn-action  { background: #e63946; color: #fff; }
.btn-secondary { background: #2a2a4a; color: #a8dadc; }

.btn-lg {
    padding: 0.65rem 1.5rem;
    font-size: 1rem;
}

/* ── Main ── */
.site-main {
    flex: 1;
    padding: 2rem;
    max-width: 860px;
    margin: 0 auto;
    width: 100%;
}

/* ── Landing / Hero ── */
.hero { margin-top: 1.5rem; }

.hero h2 {
    font-size: 1.8rem;
    margin-bottom: 1rem;
    color: #fff;
}

.gate-notice {
    background: #0f3460;
    border-left: 4px solid #e63946;
    padding: 0.85rem 1rem;
    border-radius: 4px;
    margin-bottom: 1.5rem;
    line-height: 1.5;
}

.feature-list { margin-bottom: 2rem; }

.feature-list h3 {
    margin-bottom: 0.5rem;
    color: #a8dadc;
}

.feature-list ul,
.denied-steps ol { padding-left: 1.25rem; }

.feature-list li,
.denied-steps li { padding: 0.3rem 0; line-height: 1.6; }

.login-area { margin-top: 2rem; }

/* ── Dashboard (logged-in home) ── */
.dashboard { margin-top: 1.5rem; }

.dashboard h2 {
    font-size: 1.8rem;
    color: #fff;
    margin-bottom: 0.5rem;
}

.member-notice {
    color: #a8dadc;
    margin-bottom: 1.5rem;
}

.dash-cards {
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(220px, 1fr));
    gap: 1rem;
    margin-bottom: 1.5rem;
}

.dash-card {
    background: #16213e;
    border: 1px solid #2a2a4a;
    border-radius: 8px;
    padding: 1.25rem 1.5rem;
    text-decoration: none;
    color: inherit;
    display: block;
    transition: border-color 0.15s, background 0.15s;
}

.dash-card:hover {
    border-color: #e63946;
    background: #1d2a50;
}

.dash-card h3 { color: #a8dadc; font-size: 1.1rem; margin-bottom: 0.4rem; }
.dash-card p  { font-size: 0.9rem; color: #999; line-height: 1.4; }

.coming-soon { color: #666; font-size: 0.85rem; font-style: italic; }

/* ── Trade dashboard ── */
.trade-dashboard { margin-top: 1.5rem; }

.trade-dashboard-header { margin-bottom: 1.25rem; }

.trade-dashboard-header h2 { font-size: 1.8rem; color: #fff; margin-bottom: 0.3rem; }

.create-actions {
    display: flex;
    gap: 0.75rem;
    flex-wrap: wrap;
    margin-bottom: 1.5rem;
}

.trade-notes {
    background: #16213e;
    border: 1px solid #2a2a4a;
    border-radius: 6px;
    padding: 1rem 1.25rem;
    display: flex;
    flex-direction: column;
    gap: 0.4rem;
    font-size: 0.9rem;
    color: #aaa;
    margin-top: 1.5rem;
    margin-bottom: 1.5rem;
}

.coming-soon-grid { margin-top: 0.5rem; }

/* ── Unified Trade Board ── */
.trade-board-section { margin-bottom: 2rem; }
.section-heading { font-size: 1.3rem; color: #fff; margin-bottom: 0.75rem; }

.trade-card {
    background: #16213e;
    border: 1px solid #2a2a4a;
    border-radius: 7px;
    transition: border-color 0.15s;
}
.trade-card:hover { border-color: #5865f2; }

/* Responsive "Tetris" composition: flexbox, never CSS Grid auto-placement —
   grid auto-placement silently let sibling items (badges vs. title) land in
   the SAME implicit cell and visually overlap once the viewport got narrow
   enough that other assumptions broke. Flex siblings can never occupy the
   same box, so each logical field group gets its own row (.trade-card-row)
   that wraps its OWN content independently, growing the card's height
   naturally instead of ever squeezing text into an unreadable column or
   letting a badge escape the card edge. Thumb keeps its fixed size
   (flex-shrink: 0 on .thumb-frame) at every width. */
.trade-card-link {
    padding: 0.85rem 1rem;
    text-decoration: none;
    color: inherit;
    display: flex;
    align-items: flex-start;
    gap: 0.75rem;
}
.trade-card-body {
    display: flex;
    flex-direction: column;
    gap: 0.3rem;
    flex: 1 1 200px;
    min-width: 0;
}
.trade-card-row {
    display: flex;
    align-items: center;
    flex-wrap: wrap;
    gap: 0.3rem 0.5rem;
}
.trade-card-row-owner,
.trade-card-row-date { justify-content: flex-end; }

.trade-card-actions {
    display: flex;
    gap: 0.5rem;
    padding: 0 1rem 0.75rem 1rem;
}

/* Recommended for You: compact image-first horizontal carousel. Native
   scroll-snap so trackpad/wheel, touch swipe, and keyboard/arrow-key
   scrolling all work with zero JS as a baseline; trade_carousel.js adds
   arrow buttons and desktop pointer-drag on top without replacing any of
   that native behavior. No item cap; every qualifying recommendation is
   reachable by scrolling. */
.trade-carousel-wrap {
    display: flex;
    align-items: center;
    gap: 0.4rem;
}
.trade-carousel {
    display: flex;
    flex-direction: row;
    overflow-x: auto;
    gap: 0.6rem;
    padding: 0.15rem 0.15rem 0.75rem;
    scroll-snap-type: x proximity;
    -webkit-overflow-scrolling: touch;
    flex: 1 1 auto;
    min-width: 0;
    cursor: grab;
    /* Hide the native scrollbar visually — arrows + native gestures are the
       intended interface — while overflow-x: auto keeps every scroll
       method (wheel, touch, keyboard, JS scrollBy) fully functional. */
    scrollbar-width: none;
    -ms-overflow-style: none;
}
.trade-carousel::-webkit-scrollbar { display: none; height: 0; }
.trade-carousel.trade-carousel-dragging { cursor: grabbing; scroll-snap-type: none; }
.trade-carousel-item {
    flex: 0 0 auto;
    scroll-snap-align: start;
}

/* Compact Recommended card: large image, small info strip — deliberately
   smaller/simpler than .trade-card (Community/Your Listings keep those
   unchanged).

   Part I ground-up rebuild: the card's own width is intentionally NOT set
   to a fixed 108px anymore. It used to be (`width: var(--product-thumb-
   size)`), but with the global box-sizing:border-box reset, that 108px
   included this card's own 2px border on each side — shrinking the
   CONTENT box to 104px while the 108px-wide thumbnail inside it stayed
   108px, so this card's own `overflow: hidden` silently clipped ~2px off
   each side of every carousel image. A DOM measurement of the thumbnail
   element alone (width: 108px, matching --product-thumb-size) would report
   "correct" while the ancestor was still clipping it — exactly the kind of
   false-positive the real iPhone testing kept catching. Letting the card
   size itself around its fixed-size media child (see .compact-product-
   media below) removes the fixed/border arithmetic entirely: whatever the
   card's total rendered width ends up being (108px image + 2px border each
   side = 112px), it is never less than what its child actually needs. */
.trade-rec-card {
    display: flex;
    flex-direction: column;
    background: #0d1018;
    border: 2px solid #2a3a5a;
    border-radius: 6px;
    overflow: hidden;
    text-decoration: none;
    color: #e0e0e0;
    transition: border-color 0.15s;
    -webkit-user-drag: none;
}
.trade-rec-card:hover { border-color: #5865f2; }

/* Part I — ground-up rebuild of the compact carousel media box, deliberately
   NOT reusing .thumb-frame/.thumb-frame-card/.thumb-img (the shared "one
   box everywhere" system every other normal thumbnail uses). Repeated real
   iPhone testing (414x696 Safari) kept showing the carousel image wrong
   there even when desktop DOM measurements of the shared classes looked
   correct — so this uses its OWN uniquely-named classes with fixed PIXEL
   geometry (not aspect-ratio, not percentage-of-flex-parent), so there is
   nothing generic left that could resize or clamp it: no other selector in
   this file targets .compact-product-media or .compact-product-media-img,
   so no shared/legacy rule (.thumb-img, a bare `img`, the shared
   thumb-frame selector list, etc.) can ever apply to this element by
   accident. */
.compact-product-media {
    width: 108px;
    height: 151px;
    /* flex: 0 0 108px (real-device-failures pass fix) — .trade-rec-card is
       a COLUMN flex container, so flex-basis controls the MAIN axis
       (height), not width: `0 0 108px` silently collapsed this box's
       computed height to 108px (a square), overriding the explicit
       height:151px rule above it, while width stayed correct at 108px
       (untouched by flex-basis in column mode) — a real, confirmed-live
       bug (getComputedStyle showed height:108px despite this rule), not a
       framing-data or background-image bug: the SAME background-position/
       size values render a visibly different, more "zoomed out"/wrong crop
       in a squished 108x108 box than the intended 108x151 one. flex-basis:
       auto lets the explicit width/height above win normally, matching
       every other sibling in the (unrelated) .compact-product-media-img
       absolute-positioned child, which inherits this box's now-correct
       151px height via inset:0. */
    flex: 0 0 auto;
    overflow: hidden;
    position: relative;
    background: #0d1018;
}
.compact-product-media-img {
    position: absolute;
    inset: 0;
    width: 100%;
    height: 100%;
    max-width: none;
    max-height: none;
    /* object-fit: fill (previous value) stretched every stock image to
       exactly 108x151 regardless of its real aspect ratio — visibly
       squished artwork on real-device testing. contain preserves the
       source image's intrinsic aspect ratio and letterboxes it centered
       within the fixed 108x151 frame instead; no cropping, no stretch.
       object-fit has no effect on the sibling user-photo-framed <div>
       (background-image sizing is controlled entirely by its own inline
       style from build_photo_pan_style), so this is a no-op for Part A2. */
    object-fit: contain;
    object-position: center;
    display: block;
}
/* Framed user photo — background-image/background-size/background-position
   set inline (build_photo_pan_style), never stretched (Part A8 rule); the
   div itself still fills the wrapper exactly via the same inset:0 rule. */
.compact-product-media-img.user-photo-framed { background-repeat: no-repeat; }
.compact-product-media-placeholder {
    position: absolute;
    inset: 0;
    display: flex;
    align-items: center;
    justify-content: center;
    color: #3a5a7a;
    font-size: 0.6rem;
    text-align: center;
}
/* Dev-only visual-debug outline (Part I) — draws distinct-colored borders on
   the card/wrapper/image so screenshot diagnosis can see all three
   boundaries at once. Toggled by adding .carousel-debug to .trade-carousel
   (see app/config.py MOBILE_DEV_MODE / /dev/mobile-test) — produces ZERO
   visual change when the class is absent, and is never applied outside
   that dev-only page. */
.trade-carousel.carousel-debug .trade-rec-card { outline: 2px solid #ff3b6b; outline-offset: -2px; }
.trade-carousel.carousel-debug .compact-product-media { outline: 2px dashed #3bafff; outline-offset: -2px; }
.trade-carousel.carousel-debug .compact-product-media-img { outline: 1px solid #3bff6b; outline-offset: -1px; }

/* Part S (mobile-workflow-polish pass) — resolved-framing text readout,
   same dev-only gate as the outline debug above. */
.carousel-debug-info {
    display: flex;
    flex-direction: column;
    gap: 0.1rem;
    padding: 0.15rem;
    font-size: 0.55rem;
    line-height: 1.2;
    color: #3bff6b;
    background: #0d1018;
    word-break: break-all;
}

.trade-rec-strip {
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: 1px;
    padding: 4px 4px 5px;
    background: rgba(0, 0, 0, 0.72);
    text-align: center;
    line-height: 1.25;
}
.trade-rec-type {
    font-size: 0.68rem;
    font-weight: 700;
    letter-spacing: 0.03em;
    color: #a8dadc;
}
.trade-rec-value {
    font-size: 0.72rem;
    font-weight: 600;
    color: #e8d060;
}

.carousel-arrow {
    flex: 0 0 auto;
    width: 30px;
    height: 30px;
    border-radius: 50%;
    background: #16213e;
    border: 1px solid #2a2a4a;
    color: #e0e0e0;
    font-size: 0.9rem;
    line-height: 1;
    cursor: pointer;
    display: flex;
    align-items: center;
    justify-content: center;
    transition: border-color 0.15s, background 0.15s;
}
.carousel-arrow:hover:not(:disabled) { border-color: #5865f2; background: #1d2a50; }
.carousel-arrow:disabled { opacity: 0.35; cursor: default; }
.carousel-arrow[hidden] { display: none; }

/* Trade sub-navigation — switches between the two Trade destinations
   (Trade Board / Your Trade Listings). Deliberately its own component:
   visually distinct from the global hamburger nav (.site-nav) above it and
   from the Community filter/sort controls below it, per the established
   "each control has one job" convention. Sticky so it stays reachable
   without scrolling back up, on both desktop and mobile — the page's own
   header isn't sticky (see .lifecycle-filter-rail), so this only needs a
   plain top:0 anchor. */
.trade-subnav {
    display: flex;
    gap: 0.4rem;
    padding: 0.5rem 0;
    margin: -0.5rem 0 0.75rem;
    border-bottom: 1px solid #2a3a5e;
    position: sticky;
    top: 0;
    z-index: 25;
    background: #1a1a2e;
}
.trade-subnav-link {
    display: flex;
    align-items: center;
    gap: 0.4rem;
    padding: 0.55rem 1rem;
    border-radius: 6px 6px 0 0;
    color: #aab8d0;
    text-decoration: none;
    font-weight: 600;
    font-size: 0.92rem;
}
.trade-subnav-link:hover { background: #1e2b4d; }
.trade-subnav-link.trade-subnav-current {
    color: #fff;
    background: #16213e;
    border-bottom: 2px solid #5865f2;
}
.trade-subnav-badge {
    background: #e63946;
    color: #fff;
    font-size: 0.7rem;
    font-weight: 700;
    line-height: 1;
    padding: 0.2rem 0.4rem;
    border-radius: 20px;
    min-width: 1rem;
    text-align: center;
}
@media (max-width: 480px) {
    .trade-subnav-link { flex: 1 1 0; justify-content: center; padding: 0.7rem 0.5rem; font-size: 0.85rem; }
}

/* Your Trade Listings: mailbox-style sidebar + content */
.own-listings-layout {
    display: flex;
    gap: 1.5rem;
    align-items: flex-start;
}
.lifecycle-filter-rail {
    display: flex;
    flex-direction: column;
    gap: 0.3rem;
    flex: 0 0 180px;
    /* Desktop: stays reachable without scrolling back to the top of a long
       listings page, rather than scrolling away with the rest of the
       section. The page's own header isn't sticky, so this just needs a
       fixed viewport offset, not a header-height calc. */
    position: sticky;
    top: 1rem;
    align-self: flex-start;
}
.filter-link {
    padding: 0.4rem 0.75rem;
    border-radius: 6px;
    color: #ccc;
    text-decoration: none;
    font-size: 0.9rem;
}
.filter-link:hover { background: #1e2b4d; }
.filter-link.filter-active { background: #5865f2; color: #fff; font-weight: 600; }
.own-listings-content { flex: 1 1 auto; min-width: 0; }

/* Mobile "Filters / Sort" toggle — hidden on desktop, where the sticky
   rail above is already always reachable. */
.filter-toggle {
    display: none;
    align-items: center;
    gap: 0.5rem;
    width: 100%;
    padding: 0.6rem 0.85rem;
    background: #16213e;
    border: 1px solid #2a3a5e;
    border-radius: 6px;
    color: #e0e0e0;
    font-size: 0.9rem;
    font-weight: 600;
    cursor: pointer;
    position: sticky;
    top: 0.5rem;
    z-index: 15;
}
.filter-toggle:focus-visible { outline: 2px solid #7289da; outline-offset: 2px; }
.filter-toggle-active {
    background: #5865f2;
    color: #fff;
    font-size: 0.75rem;
    font-weight: 700;
    padding: 0.1rem 0.5rem;
    border-radius: 20px;
}

@media (max-width: 700px) {
    .own-listings-layout { flex-direction: column; }
    /* Previously squeezed the full sidebar horizontally (flex-direction:
       row + wrap) — every filter label wrapping/crowding at phone widths.
       Now hidden behind the persistent .filter-toggle button instead,
       opening as a dropdown sheet (below) rather than being squeezed. */
    .filter-toggle { display: flex; }
    .lifecycle-filter-rail {
        display: none;
        position: static;
        flex-direction: column;
        width: 100%;
        margin-top: 0.4rem;
        padding: 0.5rem;
        background: #16213e;
        border: 1px solid #2a3a5e;
        border-radius: 6px;
    }
    .lifecycle-filter-rail.filter-rail-open { display: flex; }
    .filter-link { padding: 0.65rem 0.75rem; font-size: 0.95rem; }
    /* NOTE: this breakpoint used to force an oversized fixed flex-basis
       onto .trade-carousel-item here — more than double the actual 108px
       compact card it wraps, wasting over half of each slot's width and
       directly causing the "~1.5 cards visible" narrow-viewport bug.
       .trade-carousel-item's own flex: 0 0 auto already sizes it to its
       child card correctly at every width; no override needed. */
}

/* At phone widths, the round arrow buttons + their flex gaps were eating a
   disproportionate share of the already-narrow carousel row (measured ~25%
   of a 288px content width at a 320px viewport). Shrinking them (never
   hiding them — touch swipe is a supplement, not a replacement for visible
   navigation) reclaims real card-viewing width without touching the
   108px compact-card size that was already visually approved. */
@media (max-width: 480px) {
    .carousel-arrow { width: 24px; height: 24px; font-size: 0.75rem; }
    .trade-carousel-wrap { gap: 0.25rem; }
    .trade-carousel { gap: 0.4rem; padding: 0.15rem 0.05rem 0.75rem; }
    /* Deliberately NO thumbnail-size override here anymore — the uniform
       product thumbnail (--product-thumb-size) never resizes by viewport;
       only the surrounding layout reflows. */
}

/* ── Item list (browse & binder) ── */
.item-list {
    display: flex;
    flex-direction: column;
    gap: 0.6rem;
    margin-bottom: 1.5rem;
}
/* Binder List view specifically (id-scoped so Trade/ISO/confirm-queue pages
   sharing .item-list are untouched — Part F4): a responsive multi-column
   grid rather than one ~860px-wide card per row on tablet/desktop. minmax's
   340px floor means auto-fit collapses to exactly 1 column on its own once
   the viewport can't fit two, so this needs no separate mobile override —
   320/375px still get one full-width card per row, same DOM order either
   way (grid auto-placement follows source order, same as the flex-column
   it replaces). */
#binder-item-list {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(340px, 1fr));
}

.item-card {
    background: #16213e;
    border: 1px solid #2a2a4a;
    border-radius: 7px;
    padding: 0.85rem 1rem;
    text-decoration: none;
    color: inherit;
    display: grid;
    grid-template-columns: auto 1fr auto;
    grid-template-rows: auto auto;
    align-items: center;
    gap: 0.3rem 0.75rem;
    transition: border-color 0.15s;
}

.item-card:hover { border-color: #5865f2; }

.item-type-badge {
    font-size: 0.72rem;
    font-weight: 700;
    letter-spacing: 0.05em;
    padding: 0.2rem 0.55rem;
    border-radius: 20px;
    text-transform: uppercase;
    white-space: nowrap;
    flex-shrink: 0;
    grid-row: 1;
}

.type-iso { background: #1d4e89; color: #a8dadc; }
.type-wtb { background: #2d4a22; color: #a8f0a8; }
.type-wts { background: #4a2d22; color: #f0c8a8; }
.type-wtt { background: #4a3d22; color: #f0e8a8; }

.item-title {
    font-weight: 600;
    color: #e0e0e0;
    font-size: 0.95rem;
    grid-row: 1;
    grid-column: 2;
}

.item-desc {
    font-size: 0.83rem;
    color: #888;
    grid-row: 2;
    grid-column: 2;
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}

.item-owner {
    font-size: 0.8rem;
    color: #7a8fa6;
    grid-row: 2;
    grid-column: 3;
    text-align: right;
}

.item-date {
    font-size: 0.78rem;
    color: #555;
    grid-row: 1;
    grid-column: 3;
    text-align: right;
    white-space: nowrap;
}

/* ── Position Photo editor ── */
/* Large on-screen editing box (bigger than the final --product-thumb-size
   footprint for usability — see the module comment on binder_photo_frame.html)
   but the SAME 2.5:3.5 aspect/composition as the final display, so what the
   user frames here is exactly what renders everywhere else. */
.photo-frame-page { margin-top: 1.5rem; max-width: 420px; }
/* Identical mechanism to .user-photo-framed — background-image +
   background-size + background-position, set inline per the live drag/zoom
   state by photo_frame_editor.js (see build_photo_pan_style in
   app/photo_framing.py for why, not object-fit + transform:scale()). This
   guarantees the editor preview and every final rendered thumbnail produce
   the SAME crop for the same saved focal_x/focal_y/zoom/fit_axis,
   regardless of the source photo's own aspect ratio. */
.photo-frame-box {
    position: relative;
    width: 100%;
    max-width: 300px;
    aspect-ratio: var(--product-thumb-aspect);
    margin: 1rem auto;
    background-color: #0d1018;
    background-repeat: no-repeat;
    border: 2px solid #2a3a5a;
    border-radius: 6px;
    overflow: hidden;
    cursor: grab;
    touch-action: none;
    user-select: none;
}
.photo-frame-box:active { cursor: grabbing; }
.photo-frame-box:focus-visible { outline: 2px solid #7289da; outline-offset: 2px; }

/* Alignment guide overlays (Part A, split into Basic/Advanced per Part C) —
   reference marks only, drawn ON TOP of the live pan/zoom preview so the
   user can line up the card's own edges against the box's edges (= the
   exact final 2.5:3.5 crop, since the box itself is already that aspect
   ratio). pointer-events:none on both layers is what keeps drag/touch
   working — every event still reaches .photo-frame-box underneath
   untouched. Faint by design (Part A3/C1) so they guide without hiding the
   card art at 320-375px. No numeric percentages in either layer (Part C2 —
   explicitly out of scope this iteration).

   Basic — default visible, minimal: corner brackets + a subtle center dot.
   Advanced Centering (toggled via the "Advanced Centering" checkbox,
   photo_frame_editor.js) adds on top of Basic: rule-of-thirds gridlines +
   a full center crosshair — more precise alignment detail for a user who
   wants it, never forced on everyone by default. */
.photo-frame-guide {
    position: absolute;
    inset: 0;
    pointer-events: none;
}
.photo-frame-guide-corner {
    position: absolute;
    width: 22px;
    height: 22px;
    border: 2px solid rgba(255, 255, 255, 0.65);
}
.photo-frame-guide-corner-tl { top: 6px; left: 6px; border-right: none; border-bottom: none; }
.photo-frame-guide-corner-tr { top: 6px; right: 6px; border-left: none; border-bottom: none; }
.photo-frame-guide-corner-bl { bottom: 6px; left: 6px; border-right: none; border-top: none; }
.photo-frame-guide-corner-br { bottom: 6px; right: 6px; border-left: none; border-top: none; }
.photo-frame-guide-center {
    position: absolute;
    top: 50%;
    left: 50%;
    width: 8px;
    height: 8px;
    margin: -4px 0 0 -4px;
    border-radius: 50%;
    border: 2px solid rgba(255, 255, 255, 0.6);
}
@media (max-width: 480px) {
    /* Even fainter at phone widths so the guide never competes with the
       actual card art on a small screen (Part A3). */
    .photo-frame-guide-corner { width: 16px; height: 16px; }
}
.photo-frame-guide-advanced {
    position: absolute;
    inset: 0;
    pointer-events: none;
}
/* Real card-centering-tool look (Part F), not a photography thirds grid:
   an explicit outer crop boundary, several evenly-spaced inset "gauge"
   rectangles a user can compare the card's printed border against, tick
   marks along each edge, and a center crosshair. Two visually distinct
   weights — the outer boundary is the strong "this is the final crop edge"
   line; the inset rectangles/ticks are softer secondary gauge marks. */
.photo-frame-guide-outer {
    position: absolute;
    inset: 0;
    border: 1.5px solid rgba(255, 255, 255, 0.55);
}
.photo-frame-guide-inset {
    position: absolute;
    border: 1px solid rgba(255, 255, 255, 0.22);
    pointer-events: none;
}
.photo-frame-guide-inset-1 { inset: 5%; }
.photo-frame-guide-inset-2 { inset: 10%; }
.photo-frame-guide-inset-3 { inset: 15%; border-style: dashed; }
.photo-frame-guide-tick {
    position: absolute;
    background-repeat: repeat;
    opacity: 0.55;
}
.photo-frame-guide-tick-top,
.photo-frame-guide-tick-bottom {
    left: 0;
    right: 0;
    height: 6px;
    background-image: repeating-linear-gradient(to right, rgba(255, 255, 255, 0.8) 0, rgba(255, 255, 255, 0.8) 1px, transparent 1px, transparent calc(100% / 14));
}
.photo-frame-guide-tick-top { top: 0; }
.photo-frame-guide-tick-bottom { bottom: 0; }
.photo-frame-guide-tick-left,
.photo-frame-guide-tick-right {
    top: 0;
    bottom: 0;
    width: 6px;
    background-image: repeating-linear-gradient(to bottom, rgba(255, 255, 255, 0.8) 0, rgba(255, 255, 255, 0.8) 1px, transparent 1px, transparent calc(100% / 18));
}
.photo-frame-guide-tick-left { left: 0; }
.photo-frame-guide-tick-right { right: 0; }
.photo-frame-guide-crosshair-v,
.photo-frame-guide-crosshair-h {
    position: absolute;
    background: rgba(114, 137, 218, 0.65);
}
.photo-frame-guide-crosshair-v { top: 0; bottom: 0; left: 50%; width: 1px; margin-left: -0.5px; }
.photo-frame-guide-crosshair-h { left: 0; right: 0; top: 50%; height: 1px; margin-top: -0.5px; }
.photo-frame-guide-toggle {
    display: flex;
    align-items: center;
    gap: 0.4rem;
    font-size: 0.85rem;
    color: #ccc;
    margin: 0.5rem 0;
    cursor: pointer;
}
/* Take New Photo — full-screen camera layer (Part H). Fixed at a high
   z-index so it's unmistakably camera mode the instant it opens, no
   scrolling needed. The live <video> fills the entire screen; the capture
   frame is a centered viewfinder mask at the SAME 2.5:3.5 aspect ratio as
   .photo-frame-box, reusing the SAME .photo-frame-guide overlay component
   (Part F) — not a second guide design. */
.camera-capture-panel {
    position: fixed;
    inset: 0;
    z-index: 3000;
    background: #000;
    display: flex;
    flex-direction: column;
}
.camera-capture-panel[hidden] { display: none; }
.camera-capture-stage {
    position: relative;
    flex: 1 1 auto;
    min-height: 0;
    overflow: hidden;
}
.camera-capture-video-fill {
    position: absolute;
    inset: 0;
    width: 100%;
    height: 100%;
    object-fit: cover;
    background: #000;
}
/* The capture frame itself keeps .photo-frame-box's aspect-ratio/overflow,
   but is centered as an overlay ON TOP of the full-screen video rather than
   being the video's own container — box-shadow with a huge spread dims
   everything outside it, like a real camera viewfinder mask. */
.camera-capture-frame {
    position: absolute;
    top: 50%;
    left: 50%;
    margin: 0;
    transform: translate(-50%, -50%);
    width: min(78vw, 340px);
    max-width: 340px;
    background: transparent;
    box-shadow: 0 0 0 999px rgba(0, 0, 0, 0.55);
}
.camera-capture-toolbar {
    flex: 0 0 auto;
    background: rgba(8, 10, 16, 0.94);
    padding: 0.75rem 1rem calc(0.75rem + env(safe-area-inset-bottom, 0px));
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: 0.5rem;
}
.camera-capture-guide-toggle { color: #ccc; margin: 0; }
.camera-capture-hint-text { text-align: center; }
.camera-capture-controls {
    display: flex;
    gap: 0.75rem;
    width: 100%;
    max-width: 420px;
}
.camera-capture-controls .btn { flex: 1 1 0; padding: 0.85rem 0.5rem; font-size: 0.95rem; text-align: center; }
/* html.camera-fullscreen-open (set by camera_capture.js while the panel is
   open) locks background scroll so the page behind the fixed overlay can't
   bounce-scroll on touch devices. */
html.camera-fullscreen-open,
html.camera-fullscreen-open body { overflow: hidden; height: 100%; }
.photo-frame-controls { max-width: 300px; margin: 0 auto 1rem; }
.photo-frame-zoom-label {
    display: flex;
    flex-direction: column;
    gap: 0.35rem;
    font-size: 0.9rem;
    color: #ccc;
    margin-bottom: 0.5rem;
}
.photo-frame-zoom-label input[type="range"] {
    width: 100%;
    /* Native range inputs default to a tiny hit target — bump height for
       touch usability at phone widths (Part A12). */
    height: 2rem;
}
.photo-frame-actions {
    display: flex;
    gap: 0.75rem;
    max-width: 300px;
    margin: 0 auto;
}
.photo-frame-actions .btn { flex: 1 1 0; text-align: center; padding: 0.75rem; }
@media (max-width: 480px) {
    .photo-frame-page { max-width: 100%; padding: 0 0.25rem; }
    .photo-frame-box { max-width: 100%; }
    .photo-frame-controls { max-width: 100%; }
    .photo-frame-actions { max-width: 100%; }
}

/* ── Item detail ── */
.item-detail-page { margin-top: 1.5rem; }

/* Image-first responsive detail layout — LEFT: large image, RIGHT: name/
   badges/price/seller/details/actions on desktop; stacks image-first
   (image, then everything else in existing order) on mobile. The image was
   previously buried deep inside .item-detail-meta under "Product
   Reference" — the actual root cause of the reported mobile "bunched"
   feel, not font size. */
.item-detail-layout {
    display: flex;
    gap: 1.5rem;
    align-items: flex-start;
}
.item-detail-media {
    flex: 0 0 var(--card-img-large);
    max-width: var(--card-img-large);
}
.item-detail-hero-placeholder {
    width: 100%;
    aspect-ratio: 2.5 / 3.5;
    background: #0d1018;
    border: 1px solid #2a3a5a;
    border-radius: 6px;
    display: flex;
    align-items: center;
    justify-content: center;
    color: #444;
    font-size: 2rem;
}
.item-detail-info { flex: 1 1 auto; min-width: 0; }

/* Binder Edit Item (Part B, mobile-workflow-polish pass; width fix in the
   real-device-failures pass) — the same .item-detail-media/.detail-
   product-img/.item-detail-hero-placeholder image treatment as Binder Item
   Detail, but standalone (not inside .item-detail-layout's row-direction
   flex, where .item-detail-media's `flex: 0 0 var(--card-img-large)` sizes
   WIDTH). .trade-form is a COLUMN flex container, so that same flex-basis
   instead sizes HEIGHT (matching --card-img-large's 280px value exactly —
   this is why the photo rendered as a fixed 280px-tall box regardless of
   the image's real 2.5:3.5 aspect ratio) while WIDTH is left to stretch —
   and a plain `margin: 0 auto` on a column flex item's cross axis doesn't
   center a stretched box: auto margins on the cross axis take priority
   over stretch per the flexbox spec, so they absorb 100% of the available
   width as margin, collapsing the box itself to 0×0 (proven via
   getComputedStyle in the live app: removing the auto margins alone
   restored a non-zero width; only removing flex-basis's height override
   AND giving the box its own explicit width restores both dimensions
   correctly). Both are fixed at once by giving the box its own explicit,
   context-independent width instead of relying on flex sizing at all —
   height then follows correctly from the inner element's own
   aspect-ratio:2.5/3.5, and margin:auto correctly centers a box that has a
   real width. */
.binder-edit-photo {
    width: min(100%, var(--card-img-large));
    margin: 0 auto 1rem;
}

.binder-identity-chips {
    margin-bottom: 1rem;
}
@media (max-width: 700px) {
    .item-detail-layout { flex-direction: column; }
    .item-detail-media { flex: 0 0 auto; max-width: 100%; width: 100%; }
}

.item-detail-header {
    display: flex;
    align-items: center;
    gap: 0.75rem;
    margin-bottom: 1.25rem;
}

.item-detail-header h2 { font-size: 1.6rem; color: #fff; }

.item-detail-meta {
    background: #16213e;
    border: 1px solid #2a2a4a;
    border-radius: 8px;
    padding: 1.25rem 1.5rem;
    display: grid;
    grid-template-columns: max-content 1fr;
    gap: 0.6rem 1.5rem;
    margin-bottom: 1.5rem;
}

.item-detail-meta dt {
    color: #7a8fa6;
    font-size: 0.83rem;
    font-weight: 600;
    text-transform: uppercase;
    letter-spacing: 0.04em;
    align-self: start;
    padding-top: 0.1rem;
}

.item-detail-meta dd { color: #e0e0e0; font-size: 0.95rem; line-height: 1.5; }
.item-detail-meta a  { color: #a8dadc; }

.item-detail-actions { display: flex; gap: 0.75rem; }

/* ── Browse page ── */
.browse-page { margin-top: 1.5rem; }

.browse-header {
    display: flex;
    flex-wrap: wrap;
    align-items: baseline;
    gap: 0.75rem;
    margin-bottom: 1.25rem;
}

.browse-header h2 { font-size: 1.8rem; color: #fff; flex: 1 0 100%; }

/* ── Form page ── */
.form-page { margin-top: 1.5rem; max-width: 640px; }

.form-page h2 { font-size: 1.8rem; color: #fff; margin-bottom: 0.4rem; }

.form-subtitle { color: #7a8fa6; font-size: 0.9rem; margin-bottom: 1.25rem; }

.form-error-banner {
    background: #3a1a1a;
    border-left: 4px solid #e63946;
    padding: 0.75rem 1rem;
    border-radius: 4px;
    margin-bottom: 1.25rem;
    font-size: 0.9rem;
    color: #ffaaaa;
}

.trade-form { display: flex; flex-direction: column; gap: 1rem; }

.form-group { display: flex; flex-direction: column; gap: 0.3rem; }

.form-row {
    display: grid;
    grid-template-columns: 1fr 1fr;
    gap: 1rem;
}

@media (max-width: 500px) { .form-row { grid-template-columns: 1fr; } }

.form-label {
    font-size: 0.88rem;
    font-weight: 600;
    color: #a8dadc;
}

.req { color: #e63946; }

.form-input,
.form-select,
.form-textarea {
    background: #16213e;
    border: 1px solid #2a2a4a;
    border-radius: 5px;
    color: #e0e0e0;
    padding: 0.55rem 0.75rem;
    font-size: 0.9rem;
    font-family: inherit;
    width: 100%;
    transition: border-color 0.15s;
}

.form-input:focus,
.form-select:focus,
.form-textarea:focus {
    outline: none;
    border-color: #5865f2;
}

.form-textarea { resize: vertical; min-height: 90px; }

@media (max-width: 480px) {
    /* iOS Safari auto-zooms the whole page on focus of any text input
       under 16px — the exact "had to pinch/zoom to use the page" report
       (Part E4). 0.9rem (14.4px) triggered it on every form field. */
    .form-input,
    .form-select,
    .form-textarea {
        font-size: 16px;
    }
}

.form-hint { font-size: 0.78rem; color: #666; }

.form-actions { display: flex; gap: 0.75rem; align-items: center; margin-top: 0.5rem; flex-wrap: wrap; }

/* ── Mobile persistent primary action bar (Part F, superseded by FINAL
   STICKY-ACTION ARCHITECTURE) — ONE shared component, opted into per-page
   by adding `mobile-sticky-actions` on the primary .form-actions row (in
   the page template) and `has-mobile-workflow-actions` on <body> (added
   automatically by base.html whenever a page defines the mobile_actions
   block — no per-page change needed). Only the specific long mobile
   workflows opt in. Desktop (>480px) is completely untouched.

   FINAL STICKY-ACTION ARCHITECTURE pass: every earlier attempt (fixed,
   transform-corrected fixed, document-space absolute) tried to keep this
   bar visually pinned WHILE THE WHOLE DOCUMENT SCROLLED underneath it —
   all three showed real iOS Safari paint instability (buttons/panel
   shifting or fading mid-scroll) despite mathematically-correct post-
   event geometry on every diagnostic reading. This stops repositioning
   anything: the bar is `position: static`, an ordinary normal-flow flex
   footer that never moves — PAGE CONTENT becomes the thing that scrolls
   instead, inside `.mobile-workflow-scroll` (see base.html), a SIBLING of
   this bar, not an ancestor. No JS touches this element's position/size
   at all anymore (mobile_sticky_actions.js is now a documented no-op). */
@media (max-width: 480px) {
    body.has-mobile-workflow-actions {
        /* body is already `display:flex; flex-direction:column` globally
           (see the base body rule above) — this just bounds it to the
           real visible viewport height and stops IT from being the
           scrolling element, so its flex children (.mobile-workflow-scroll
           and the action bar) divide that fixed height between them
           instead. 100dvh is the real "currently visible" viewport height
           on modern mobile Safari — it naturally shrinks/grows as the
           toolbar collapses/expands or the keyboard opens, no JS needed
           (Part 5/17). 100vh fallback for browsers without dvh support. */
        height: 100vh;
        height: 100dvh;
        min-height: 100vh;
        min-height: 100dvh;
        overflow: hidden;
    }
    body.has-mobile-workflow-actions .mobile-workflow-scroll {
        /* THE scrolling region — everything that should move when the
           user scrolls (header, page content, footer) lives inside it.
           flex:1 1 auto + min-height:0 is what lets a flex child actually
           shrink below its content height and become independently
           scrollable instead of pushing the flex container taller. */
        flex: 1 1 auto;
        min-height: 0;
        overflow-y: auto;
        overflow-x: hidden;
        -webkit-overflow-scrolling: touch;
    }
    .mobile-sticky-actions {
        /* Normal-flow flex footer — a sibling of .mobile-workflow-scroll,
           not inside it, so it never needs to "follow" scrolling at all
           (Part 1/3). flex:0 0 auto keeps it sized to its own content,
           never stretched or shrunk by the flex layout. */
        position: static;
        flex: 0 0 auto;
        z-index: auto;
        background: #1a1a2e;
        border-top: 1px solid #2a3a5e;
        box-shadow: 0 -2px 10px rgba(0, 0, 0, 0.35);
        margin: 0;
        /* Safe area stays exactly here, on the real footer's own padding
           (Part 7) — no JS calculation, no top/bottom offset correction. */
        padding: 0.6rem 1rem calc(0.6rem + env(safe-area-inset-bottom, 0px));
    }
    /* Primary action (Confirm Match / Add to Binder / Save Listing /
       Submit Listing) takes the remaining space and stays visually
       dominant; Cancel/secondary stays a fixed, smaller companion so it
       never outweighs the primary submit (Part F3, unchanged). */
    .mobile-sticky-actions button[type="submit"] {
        flex: 1 1 auto;
        min-width: 0;
        order: -1;
    }
    .mobile-sticky-actions a.btn-secondary,
    .mobile-sticky-actions a.btn:not([class*="btn-action"]):not([class*="btn-discord"]) {
        flex: 0 0 auto;
    }
    /* Part 8: the old spacer compensated for the bar OVERLAYING page
       content — now that the bar is a normal-flow footer occupying its
       own real layout space, that compensation is not just unnecessary
       but wrong (it would leave a second, now-empty gap below the real
       footer). Overridden to 0 here rather than removed from the
       .form-page templates that still carry the class, keeping this a
       single shared CSS-only change rather than N per-page edits. */
    .has-mobile-sticky-actions {
        padding-bottom: 0;
    }
}

/* ── Misc ── */
.empty-state {
    background: #16213e;
    border: 1px dashed #2a2a4a;
    border-radius: 8px;
    padding: 2rem 1.5rem;
    text-align: center;
    color: #666;
    margin-bottom: 1.5rem;
}

.empty-state a { color: #a8dadc; }

.listing-type-grid {
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(170px, 1fr));
    gap: 1rem;
    margin: 1rem 0;
}

.listing-type-card {
    background: #16213e;
    border: 1px solid #2a2a4a;
    border-radius: 8px;
    padding: 1.25rem 1rem;
    text-align: center;
}

.listing-type-card h3 { font-size: 1.5rem; color: #a8dadc; margin-bottom: 0.3rem; }
.listing-type-card p  { font-size: 0.85rem; color: #888; margin-bottom: 0.75rem; }

/* ── Listing-type radio selector (setup form) ──────────────────────────── */
.listing-type-selector {
    display: flex;
    flex-wrap: wrap;
    gap: 0.5rem;
    margin-top: 0.25rem;
}

.listing-type-option {
    display: flex;
    align-items: center;
    gap: 0.4rem;
    padding: 0.5rem 0.85rem;
    border: 1px solid #2a2a4a;
    border-radius: 8px;
    cursor: pointer;
    font-size: 0.9rem;
    color: #ccc;
    background: #16213e;
    transition: border-color 0.15s, background 0.15s;
}

.listing-type-option input[type=radio] { display: none; }

.listing-type-option:hover { border-color: #7289da; background: #1a2550; }

.listing-type-option.selected {
    border-color: #7289da;
    background: #1e2f5e;
    color: #e0e0e0;
}

.type-badge-inline {
    display: inline-block;
    font-size: 0.72rem;
    font-weight: 700;
    border-radius: 4px;
    padding: 0.15rem 0.4rem;
    letter-spacing: 0.04em;
}

.listing-type-fields { margin-top: 0; }

.setup-market-ref {
    background: #1a2535;
    border-left: 3px solid #3a5068;
    padding: 0.4rem 0.75rem;
    border-radius: 4px;
    margin-bottom: 0.75rem;
    font-size: 0.85rem;
    color: #aaa;
}

.photo-req-note {
    font-size: 0.82rem;
    color: #c8a860;
    margin-top: 0.25rem;
}

/* ── Field-level warnings ──────────────────────────────────────────────────── */
.field-warning {
    font-size: 0.82rem;
    color: #e8b84b;
    background: #2a220a;
    border-left: 3px solid #c8a020;
    padding: 0.3rem 0.6rem;
    border-radius: 3px;
    margin: 0.25rem 0 0.5rem;
}

.field-input-warn {
    border-color: #c8a020 !important;
}

.field-warnings-summary {
    background: #2a220a;
    border: 1px solid #c8a020;
    border-radius: 6px;
    padding: 0.6rem 1rem;
    margin: 0.75rem 0;
    font-size: 0.86rem;
    color: #e8d090;
}

.field-warnings-summary ul {
    margin: 0.3rem 0 0 1rem;
    padding: 0;
}

.field-warning-item {
    margin-bottom: 0.2rem;
}

/* ── Notifications ─────────────────────────────────────────────────────────── */
.notifications-section {
    background: #16213e;
    border: 1px solid #2a3a5e;
    border-radius: 8px;
    padding: 1rem 1.25rem;
    margin-bottom: 1.5rem;
}

.notifications-header {
    display: flex;
    align-items: center;
    gap: 0.75rem;
    margin-bottom: 0.75rem;
}

.notifications-header h3 {
    margin: 0;
    font-size: 1rem;
    color: #a8dadc;
}

.notif-unread-badge {
    display: inline-block;
    background: #7289da;
    color: #fff;
    font-size: 0.72rem;
    font-weight: 700;
    border-radius: 20px;
    padding: 0.15rem 0.5rem;
}

.notifications-list {
    display: flex;
    flex-direction: column;
    gap: 0.5rem;
}

.notification-item {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 0.5rem;
    padding: 0.5rem 0.75rem;
    border-radius: 6px;
    background: #1a2840;
    border-left: 3px solid #3a5068;
    font-size: 0.88rem;
}

.notification-item.notif-unread {
    border-left-color: #7289da;
    background: #1a243a;
}

.notification-item.notif-read {
    opacity: 0.7;
}

.notif-type-badge {
    display: inline-block;
    font-size: 0.7rem;
    font-weight: 700;
    border-radius: 4px;
    padding: 0.15rem 0.4rem;
    background: #2a3a5e;
    color: #a8dadc;
    white-space: nowrap;
}

.notif-message {
    flex: 1;
    color: #ccc;
}

.notif-date {
    font-size: 0.75rem;
    color: #777;
    white-space: nowrap;
}

.btn-muted {
    background: #1a1a2e;
    color: #666;
    border-color: #333;
}

/* ISO Match alert — Phase-1 image-first visual exemplar (other notification
   types keep the plain-text .notification-item presentation above). The
   image + badge communicate the alert at a glance; explanatory prose is
   deliberately minimal. Actions live in their OWN row, sibling to (never
   nested inside) the listing link, so Dismiss can never accidentally
   trigger navigation. */
.iso-match-alert {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    justify-content: space-between;
    gap: 0.6rem;
    padding: 0.6rem 0.75rem;
    border-radius: 6px;
    background: #1a2840;
    border-left: 3px solid #7289da;
}
.iso-match-alert.notif-read { opacity: 0.7; }
.iso-match-alert-link {
    display: flex;
    align-items: center;
    gap: 0.6rem;
    text-decoration: none;
    color: inherit;
    min-width: 0;
    flex: 1 1 220px;
}
.iso-match-alert-thumb { flex-shrink: 0; }
.iso-match-alert-body {
    display: flex;
    flex-direction: column;
    gap: 0.15rem;
    min-width: 0;
}
.iso-match-badge {
    display: inline-block;
    align-self: flex-start;
    font-size: 0.68rem;
    font-weight: 700;
    letter-spacing: 0.04em;
    text-transform: uppercase;
    padding: 0.15rem 0.5rem;
    border-radius: 20px;
    background: #7289da;
    color: #fff;
}
.iso-match-name {
    font-weight: 600;
    color: #e0e0e0;
    font-size: 0.92rem;
    overflow-wrap: break-word;
}
.iso-match-price {
    color: #e9c46a;
    font-weight: 600;
    font-size: 0.85rem;
}
.iso-match-alert-actions {
    display: flex;
    align-items: center;
    gap: 0.4rem;
    flex-shrink: 0;
}

.coming-soon-badge {
    display: inline-block;
    font-size: 0.72rem;
    background: #2a2a4a;
    color: #777;
    border-radius: 20px;
    padding: 0.2rem 0.65rem;
    font-weight: 600;
    letter-spacing: 0.03em;
}

/* ── Profile page ── */
.profile-page { margin-top: 1.5rem; }

.profile-page h2 { font-size: 1.8rem; color: #fff; margin-bottom: 1.25rem; }

.avatar {
    width: 96px;
    height: 96px;
    border-radius: 50%;
    border: 3px solid #2a2a4a;
    margin-bottom: 1.25rem;
    display: block;
}

.avatar-placeholder {
    background: #16213e;
    color: #555;
    font-size: 2rem;
    font-weight: 700;
    display: flex;
    align-items: center;
    justify-content: center;
}

.profile-fields {
    background: #16213e;
    border: 1px solid #2a2a4a;
    border-radius: 8px;
    padding: 1.25rem 1.5rem;
    display: grid;
    grid-template-columns: max-content 1fr;
    gap: 0.5rem 1.5rem;
    max-width: 540px;
}

.profile-fields dt {
    color: #7a8fa6;
    font-size: 0.85rem;
    font-weight: 600;
    text-transform: uppercase;
    letter-spacing: 0.04em;
    align-self: center;
}

.profile-fields dd { color: #e0e0e0; font-size: 0.95rem; word-break: break-all; }

/* ── Denied page ── */
.denied { margin-top: 1.5rem; }

.denied h2 { font-size: 1.8rem; color: #e63946; margin-bottom: 1rem; }

.error-notice {
    background: #3a1a1a;
    border-left: 4px solid #e63946;
    padding: 0.75rem 1rem;
    border-radius: 4px;
    margin-bottom: 1rem;
    font-size: 0.9rem;
}

.denied-message { margin-bottom: 1.5rem; line-height: 1.6; }
.denied-steps   { margin-bottom: 1.5rem; }
.denied-steps h3 { color: #a8dadc; margin-bottom: 0.5rem; }
.denied-actions  { margin-top: 1.5rem; }

/* ── Status badges ── */
.status-badge {
    font-size: 0.72rem;
    font-weight: 700;
    padding: 0.2rem 0.55rem;
    border-radius: 20px;
    text-transform: uppercase;
    letter-spacing: 0.05em;
    white-space: nowrap;
}

.status-active    { background: #1a4a1a; color: #90ee90; }
.status-paused    { background: #4a4a1a; color: #f0e060; }
.status-completed { background: #1a1a4a; color: #a0a0ff; }
.status-removed   { background: #3a1a1a; color: #ff9090; }
/* classify_trade_listing_state's non-terminal display states (Binder card /
   item detail). No .status-pending_review — that state was retired
   (feature/trade-board-owner-controls): a submitted-but-not-yet-available
   row is classified as in_progress, same as any other draft, since there
   is no third-party reviewer for these listings. */
.status-in_progress   { background: #2a2a4a; color: #8888cc; }
/* classify_trade_listing_state returns state="none"/label=None when no
   listing exists at all — the Binder card explicitly labels that "Not
   Listed" rather than rendering nothing. */
.status-none { background: #2a2f3a; color: #8a94a8; }

/* ── Owner controls (detail page) ── */
.owner-controls {
    display: flex;
    align-items: center;
    gap: 0.75rem;
    flex-wrap: wrap;
    margin-bottom: 1.25rem;
}
@media (max-width: 480px) {
    /* Part C2: a wrapped row of many small buttons is hard to tap
       accurately on a phone — stack them full-width instead. */
    .owner-controls { flex-direction: column; align-items: stretch; }
    .owner-controls > * { width: 100%; }
    .owner-controls .btn { width: 100%; text-align: center; }
    .owner-controls form.inline-form,
    .owner-controls .make-available-inline { width: 100%; }
    .owner-controls .make-available-inline .form-select-inline { width: 100%; margin-bottom: 0.4rem; }
}

.status-form {
    display: flex;
    gap: 0.5rem;
    flex-wrap: wrap;
    align-items: center;
}

/* ── Item row (dashboard binder) ── */
.item-row {
    background: #16213e;
    border: 1px solid #2a2a4a;
    border-radius: 7px;
    padding: 0.75rem 1rem;
    display: flex;
    align-items: center;
    gap: 0.75rem;
    flex-wrap: wrap;
}

.item-row-link {
    flex: 1;
    min-width: 100px;
    font-weight: 600;
    color: #e0e0e0;
    text-decoration: none;
    font-size: 0.95rem;
}

.item-row-link:hover { color: #a8dadc; }

/* ── Danger button ── */
.btn-danger {
    background: #8b0000;
    color: #ffaaaa;
}

.btn-danger:hover { opacity: 0.85; }

/* ── Form section divider ── */
.form-section-divider {
    display: flex;
    align-items: baseline;
    gap: 0.75rem;
    border-top: 1px solid #2a2a4a;
    padding-top: 0.75rem;
    margin-top: 0.25rem;
    font-size: 0.88rem;
    font-weight: 600;
    color: #a8dadc;
}

/* ── More Details / Additional fields collapsed expander — shared by the
   listing create/edit form (optional fields, Part C) and listing detail
   pages (empty/secondary optional fields, Part B2). Native <details>/
   <summary> for built-in keyboard operation and accessibility (Part H) —
   no custom JS toggle needed. Deliberately understated (no border/bg by
   default) so it never visually competes with populated primary content;
   the summary itself is the only always-visible affordance. */
.more-details-expander {
    margin: 0.5rem 0 1rem;
    border: 1px solid #2a2a4a;
    border-radius: 6px;
    overflow: hidden;
}
/* Real-mobile-closure pass (Part A) — native <details>/<summary> kept
   (no custom JS accordion), but styled to actually look tappable: a
   full-width row with a subtle background and an explicit chevron that
   rotates on open, rather than relying on Safari's own tiny/inconsistent
   default disclosure triangle (list-style:revert, the previous approach —
   easy to miss entirely on a real phone, per physical-device feedback).
   list-style:none removes that native marker so the ::before chevron below
   is the only one rendered. */
.more-details-expander summary {
    cursor: pointer;
    font-size: 0.88rem;
    font-weight: 600;
    color: #a8dadc;
    padding: 0.6rem 0.8rem;
    /* Comfortable touch target (Part A2) — the ENTIRE row is tappable, not
       just the marker, since summary's own click target already spans its
       full box (block-level, full-width). */
    min-height: 44px;
    display: flex;
    align-items: center;
    gap: 0.5rem;
    list-style: none;
    background: #171730;
    transition: background-color 0.1s ease;
    -webkit-tap-highlight-color: transparent;
    user-select: none;
}
.more-details-expander summary::-webkit-details-marker { display: none; }
.more-details-expander summary::before {
    content: "\25B6"; /* ▶ */
    display: inline-block;
    font-size: 0.7em;
    color: #7289da;
    transition: transform 0.15s ease;
    flex: 0 0 auto;
}
.more-details-expander[open] > summary::before {
    transform: rotate(90deg);
}
.more-details-expander summary:hover {
    background: #1d1d3d;
}
.more-details-expander summary:focus-visible {
    outline: 2px solid #7289da;
    outline-offset: -2px;
}
.more-details-expander[open] > summary {
    border-bottom: 1px solid #2a2a4a;
}
.more-details-count {
    font-weight: 400;
    color: #8aa8c8;
    margin-left: 0.35rem;
}
.more-details-body {
    display: flex;
    flex-direction: column;
    gap: 0.75rem;
    padding: 0.75rem 0.8rem;
}
.additional-fields-list {
    margin: 0;
    padding-left: 1.2rem;
    font-size: 0.85rem;
    color: #aab8d0;
}
.additional-fields-list li { margin-bottom: 0.2rem; }

/* ── Price flag banner (detail page) ── */
.price-flag-banner {
    border-radius: 5px;
    padding: 0.6rem 1rem;
    font-size: 0.88rem;
    margin-bottom: 1rem;
    border-left: 4px solid;
}

.price-flag-needs_review {
    background: #3a2a10;
    border-color: #c8820a;
    color: #f0c060;
}

.price-flag-approved_exception {
    background: #102a10;
    border-color: #3a8a3a;
    color: #80d080;
}

/* ── Price flag badge (dashboard row) ── */
.price-flag-badge {
    font-size: 0.7rem;
    font-weight: 700;
    padding: 0.15rem 0.5rem;
    border-radius: 20px;
    white-space: nowrap;
    cursor: default;
}

.flag-needs_review {
    background: #3a2a10;
    color: #f0c060;
}

.flag-approved_exception {
    background: #102a10;
    color: #80d080;
}

/* ── Browse compact price ── */
.item-price-compact {
    display: flex;
    align-items: center;
    flex-wrap: wrap;
    gap: 0.2rem 0.4rem;
    grid-row: 2;
    grid-column: 1 / 3;
    font-size: 0.78rem;
}

.item-price-value {
    color: #a8dadc;
    font-weight: 600;
}

.item-print-status {
    color: #666;
    font-size: 0.72rem;
}

/* ── Footer ── */
.site-footer {
    background: #16213e;
    border-top: 1px solid #333;
    text-align: center;
    padding: 1rem;
    color: #666;
    font-size: 0.85rem;
}

/* ── Prefill widget ── */
.prefill-section {
    margin-bottom: 1rem;
}

.prefill-result-card {
    background: #16213e;
    border: 1px solid #333;
    border-radius: 6px;
    padding: 0.75rem 1rem;
    margin-top: 0.5rem;
}

.prefill-result-inner {
    display: flex;
    gap: 0.75rem;
    align-items: flex-start;
}

.prefill-img {
    width: 60px;
    height: auto;
    border-radius: 4px;
    object-fit: contain;
    flex-shrink: 0;
}

.prefill-result-text {
    display: flex;
    flex-direction: column;
    gap: 0.3rem;
}

.prefill-result-name {
    font-weight: 600;
    color: #a8dadc;
}

.prefill-tags {
    display: flex;
    flex-wrap: wrap;
    gap: 0.25rem;
}

.prefill-tag {
    background: #0f3460;
    color: #a8dadc;
    font-size: 0.72rem;
    padding: 0.1rem 0.4rem;
    border-radius: 3px;
}

/* Set-symbol icon inside the set tag (Part E, FINAL MOBILE UX CLOSURE
   pass) — small/compact, preserves the symbol's own aspect ratio (no
   fixed width), never stretches the tag row. */
.prefill-tag-set {
    display: inline-flex;
    align-items: center;
    gap: 0.3rem;
}
.set-symbol-icon {
    height: 0.85rem;
    width: auto;
    max-width: 1.4rem;
    vertical-align: middle;
    flex-shrink: 0;
}

/* Card Information — read-only static canonical data inside More Details,
   visually distinct from the editable "My Item Details" fields below it
   (Part G/H, FINAL MOBILE UX CLOSURE pass). Definition-list layout, not
   form inputs, so static data never looks editable. */
.card-information {
    background: #16213e;
    border: 1px solid #2a2a4a;
    border-radius: 6px;
    padding: 0.75rem 1rem;
    margin-bottom: 1rem;
}
.card-information h4 {
    margin: 0 0 0.5rem;
    font-size: 0.85rem;
    color: #a8dadc;
    text-transform: uppercase;
    letter-spacing: 0.03em;
}
.card-information dl {
    display: grid;
    grid-template-columns: auto 1fr;
    gap: 0.25rem 0.75rem;
    margin: 0;
}
.card-information dt {
    color: #8892a6;
    font-size: 0.8rem;
}
.card-information dd {
    margin: 0;
    font-size: 0.85rem;
    color: #e6e6e6;
}
.my-item-details-heading {
    margin: 0 0 0.5rem;
    font-size: 0.85rem;
    color: #a8dadc;
    text-transform: uppercase;
    letter-spacing: 0.03em;
}

.prefill-price {
    color: #e9c46a;
    font-size: 0.85rem;
    font-weight: 600;
}

.iso-results-group-label {
    color: #a8dadc;
    font-size: 0.85rem;
    margin: 0.5rem 0 0.25rem;
    padding-bottom: 0.25rem;
    border-bottom: 1px solid #333;
}

.iso-no-set-match {
    color: #e9c46a;
    font-size: 0.9rem;
    margin-bottom: 0.5rem;
}

.prefill-img-placeholder {
    width: 60px;
    height: 80px;
    border-radius: 4px;
    background: #2a2a2a;
    border: 1px solid #444;
    display: flex;
    align-items: center;
    justify-content: center;
    flex-shrink: 0;
}
.prefill-img-placeholder span {
    font-size: 0.65rem;
    color: #777;
    text-align: center;
}

.iso-ambiguous-notice {
    background: #1e1e10;
    border: 1px solid #666;
    border-radius: 4px;
    color: #e9c46a;
    font-size: 0.875rem;
    padding: 0.4rem 0.75rem;
    margin-bottom: 0.75rem;
}

.iso-offer-warning {
    color: #e9c46a;
    font-size: 0.8rem;
    margin-top: 0.25rem;
    padding: 0.2rem 0.5rem;
    background: #1e1e10;
    border-left: 2px solid #e9c46a;
    border-radius: 2px;
}

.iso-image-warn {
    font-size: 0.7rem;
    color: #e9c46a;
    display: block;
    margin-top: 0.2rem;
}

.iso-selected-card {
    display: flex;
    align-items: center;
    gap: 0.75rem;
    padding: 0.5rem 0.75rem;
    background: #1a2a1a;
    border: 1px solid #2d6a2d;
    border-radius: 6px;
    margin-bottom: 0.75rem;
    font-size: 0.9rem;
}
.iso-back-btn {
    margin-left: auto;
    font-size: 0.75rem;
    padding: 0.2rem 0.6rem;
}

.iso-similar-alias-hint {
    color: #aaa;
    font-size: 0.85rem;
    margin-top: 0.5rem;
}

.match-conf-badge {
    display: inline-block;
    font-size: 0.75rem;
    font-weight: 600;
    color: #a8dadc;
    margin-left: 0.25rem;
}

.ranking-reasons {
    display: flex;
    flex-wrap: wrap;
    gap: 0.25rem;
    margin-top: 0.3rem;
}

.iso-show-more-btn {
    background: #1e3a4a;
    color: #a8dadc;
    border: 1px solid #3a5a6a;
    padding: 0.4rem 1.2rem;
    font-size: 0.85rem;
}

.ranking-reason-tag {
    display: inline-block;
    font-size: 0.68rem;
    font-weight: 500;
    padding: 0.1rem 0.4rem;
    border-radius: 3px;
    background: #2a3a4a;
    color: #a8dadc;
    border: 1px solid #3a5a6a;
}

.stale-badge {
    display: inline-block;
    font-size: 0.7rem;
    font-weight: 700;
    padding: 0.1em 0.4em;
    border-radius: 3px;
    background: #5a3e00;
    color: #e9c46a;
    text-transform: uppercase;
    letter-spacing: 0.05em;
    vertical-align: middle;
}

.price-stale {
    color: #e9c46a;
}

.price-update-btn {
    margin-left: 0.5rem;
    font-size: 0.78rem;
    padding: 0.15em 0.6em;
}

.price-update-msg {
    font-size: 0.82rem;
    margin-left: 0.4rem;
}

.prefill-error {
    color: #e63946;
    font-size: 0.85rem;
}

.prefill-warn {
    color: #f4a261;
    font-size: 0.85rem;
    margin: 0.25rem 0 0;
}

.market-unavailable {
    color: #888;
    font-size: 0.85rem;
    font-style: italic;
}

.prefill-price-unavail {
    color: #888;
    font-size: 0.85rem;
    font-style: italic;
}

.prefill-price-date {
    font-size: 0.78rem;
    color: #aaa;
    font-style: italic;
}

.prefill-basis {
    font-size: 0.78rem;
    color: #a0c4ff;
    font-style: italic;
}

.verified-tag {
    background: #2d6a4f;
    color: #b7e4c7;
}

.limited-badge {
    display: inline-block;
    font-size: 0.7rem;
    font-weight: 700;
    padding: 0.15em 0.5em;
    border-radius: 3px;
    background: #7b2d8b;
    color: #f3c6ff;
    text-transform: uppercase;
    letter-spacing: 0.05em;
    vertical-align: middle;
}

.item-market-price {
    font-size: 0.78rem;
    color: #a0c4ff;
    margin-left: 0.4rem;
}

.item-price-meta {
    font-size: 0.78rem;
    color: #a0c4ff;
}

/* ── Detail page product image ── */
/* Large tier — a genuinely prominent detail-page presentation (was 80px,
   the same size as several unrelated small-list thumbnails). object-fit:
   contain rather than cover so a non-2.5:3.5 image (e.g. a real photo of a
   physical card, not perfectly cropped) letterboxes instead of being
   cropped/distorted — same principle .thumb-img already uses. width:100%
   + max-width makes it shrink on narrow viewports rather than overflowing. */
.detail-product-img {
    width: 100%;
    max-width: var(--card-img-large);
    aspect-ratio: 2.5 / 3.5;
    object-fit: contain;
    background: #0d1018;
    border-radius: 6px;
    display: block;
    margin-bottom: 0.4rem;
}

.detail-product-name {
    font-weight: 600;
    color: #a8dadc;
    display: block;
    margin-bottom: 0.25rem;
}

/* ── Compact product meta in rows/cards ── */
.item-product-meta {
    display: inline-flex;
    gap: 0.2rem;
    flex-wrap: wrap;
}

.prefill-hint {
    color: #888;
    font-size: 0.78rem;
    margin-top: 0.2rem;
}

/* ── Binder page ── */
.binder-page { margin-top: 1.5rem; }

.binder-header {
    display: flex;
    flex-wrap: wrap;
    align-items: baseline;
    gap: 0.75rem;
    margin-bottom: 1.25rem;
}

.binder-header h2 { font-size: 1.8rem; color: #fff; flex: 1 0 100%; }

/* ── Listing workflow status badges ── */
.status-draft      { background: #2a2a4a; color: #8888cc; }
.status-previewed  { background: #1a3a2a; color: #80d090; }
.status-submitted  { background: #1a3a50; color: #80c8e0; }

/* ── Rule guidance box ── */
.rule-guidance-box {
    border-radius: 5px;
    padding: 0.75rem 1rem;
    margin-bottom: 1rem;
    border-left: 4px solid;
    font-size: 0.9rem;
}

.rule-ok           { background: #102a10; border-color: #3a8a3a; color: #a0d0a0; }
.rule-warning      { background: #2a2a10; border-color: #888;    color: #cccc80; }
.rule-needs-review { background: #3a2a10; border-color: #c8820a; color: #f0c060; }

.rule-reason { margin-top: 0.3rem; font-size: 0.85rem; }

/* ── Listing status box (binder detail) ── */
.listing-status-box {
    background: #16213e;
    border: 1px solid #2a2a4a;
    border-radius: 6px;
    padding: 0.75rem 1rem;
    font-size: 0.9rem;
    margin-top: 1rem;
    display: flex;
    align-items: center;
    gap: 0.75rem;
    flex-wrap: wrap;
}

/* ── Import flow ── */
.binder-header-actions {
    display: flex;
    gap: 0.5rem;
    flex-wrap: wrap;
    align-items: center;
}

.import-info-box {
    background: #16213e;
    border: 1px solid #2a4a6a;
    border-radius: 6px;
    padding: 0.75rem 1rem;
    margin-bottom: 1.25rem;
    font-size: 0.9rem;
    color: #b0c8e0;
}

.import-info-box ul {
    margin-top: 0.4rem;
    padding-left: 1.4rem;
}

.import-controls {
    display: flex;
    gap: 0.5rem;
    margin-bottom: 0.75rem;
}

.btn-sm {
    padding: 0.25rem 0.7rem;
    font-size: 0.82rem;
}

.import-table-wrap {
    overflow-x: auto;
    margin-bottom: 1.25rem;
}

.import-preview-table {
    width: 100%;
    border-collapse: collapse;
    font-size: 0.88rem;
}

.import-preview-table th,
.import-preview-table td {
    padding: 0.45rem 0.6rem;
    border: 1px solid #2a2a4a;
    text-align: left;
    vertical-align: top;
}

.import-preview-table th {
    background: #16213e;
    color: #a0b8d0;
    font-weight: 600;
}

.import-preview-table tr:nth-child(even) td {
    background: #12192e;
}

.import-row-warn td {
    background: #2a2010 !important;
}

.import-warn-text {
    display: block;
    color: #c8820a;
    font-size: 0.8rem;
}

.row-num {
    color: #666;
    font-size: 0.8rem;
    width: 2rem;
    text-align: right;
}

.import-col-guide {
    margin-top: 2rem;
    font-size: 0.88rem;
}

.import-col-guide h3 {
    margin-bottom: 0.5rem;
    color: #a0b8d0;
}

.col-guide-table {
    border-collapse: collapse;
    width: 100%;
    max-width: 640px;
}

.col-guide-table th,
.col-guide-table td {
    padding: 0.35rem 0.6rem;
    border: 1px solid #2a2a4a;
    text-align: left;
}

.col-guide-table th {
    background: #16213e;
    color: #a0b8d0;
}

.file-input {
    color: #ccc;
}

/* ── Confirmation queue ── */
.confirm-queue-notice {
    display: flex;
    align-items: center;
    gap: 1rem;
    flex-wrap: wrap;
    background: #1e2a18;
    border: 1px solid #4a7a2a;
    border-radius: 6px;
    padding: 0.65rem 1rem;
    margin-bottom: 1.25rem;
    font-size: 0.9rem;
    color: #b0d890;
}

.confirm-badge {
    background: #3a5a18;
    color: #c0e880;
    border-radius: 4px;
    padding: 0.1rem 0.4rem;
    font-size: 0.75rem;
    font-weight: 600;
}

.confirm-queue-card {
    border-left: 3px solid #4a7a2a;
}

/* ── Confirmation item page ── */
.confirm-imported-data {
    background: #16213e;
    border: 1px solid #2a4a6a;
    border-radius: 6px;
    padding: 0.75rem 1rem;
    margin-bottom: 1.5rem;
}

.confirm-imported-data h3 {
    font-size: 0.9rem;
    color: #80a8c8;
    margin-bottom: 0.5rem;
    text-transform: uppercase;
    letter-spacing: 0.05em;
}

.confirm-nudge {
    font-size: 0.85rem;
    color: #a0a0c0;
    display: block;
    margin-bottom: 0.4rem;
}

/* ── Source format / TCGplayer import badges ── */
.source-format-badge {
    display: inline-block;
    font-size: 0.75rem;
    padding: 0.15em 0.5em;
    border-radius: 3px;
    background: #2a2a40;
    color: #9090b0;
    border: 1px solid #444460;
    vertical-align: middle;
    margin-left: 0.4rem;
}

.source-format-badge.source-tcgplayer {
    background: #1a3020;
    color: #60c080;
    border-color: #30602a;
}

.source-confirmed-badge {
    display: inline-block;
    font-size: 0.75rem;
    padding: 0.15em 0.5em;
    border-radius: 3px;
    background: #1a3020;
    color: #60c080;
    border: 1px solid #30602a;
}

.source-confirmed-banner {
    background: #1a3020;
    border: 1px solid #30602a;
    border-radius: 6px;
    color: #60c080;
    font-size: 0.85rem;
    padding: 0.5rem 0.9rem;
    margin-bottom: 1rem;
}

/* ── Import detection header ── */
.import-detection-box {
    background: #12192e;
    border: 1px solid #2a2a4a;
    border-radius: 6px;
    padding: 0.55rem 0.9rem;
    margin: 0.5rem 0 0.75rem;
    font-size: 0.88rem;
    color: #a0b8d0;
}

.detected-format-label {
    color: #6080a0;
    margin-right: 0.25rem;
}

.import-pending-warning {
    margin-top: 0.4rem;
    color: #c09020;
    font-size: 0.82rem;
}

/* ── Import row status badges ── */
.import-status-badge {
    display: inline-block;
    font-size: 0.72rem;
    padding: 0.12em 0.45em;
    border-radius: 3px;
    border: 1px solid #2a2a4a;
    background: #1a1a2e;
    color: #8090a8;
}

.import-status-exact_match {
    background: #1a3020;
    color: #60c080;
    border-color: #30602a;
}

.import-status-unique_fuzzy_match {
    background: #1a2a10;
    color: #90c040;
    border-color: #3a5010;
}

.import-status-ambiguous {
    background: #2a2000;
    color: #d0a020;
    border-color: #5a4000;
}

.import-status-unmatched {
    background: #1a1a2e;
    color: #8090a8;
    border-color: #2a2a4a;
}

.import-status-invalid {
    background: #2a1010;
    color: #c06060;
    border-color: #5a1010;
}

.import-status-reason {
    display: block;
    font-size: 0.72rem;
    margin-top: 0.15rem;
}

/* ── Candidate selection UI ── */
.candidates-header {
    margin: 0.75rem 0 0.5rem;
    font-size: 0.9rem;
    color: #ccc;
}

.candidates-list {
    display: flex;
    flex-direction: column;
    gap: 0.6rem;
    margin-bottom: 1rem;
}

.candidate-card {
    display: flex;
    align-items: flex-start;
    /* Real-device regression (Part G/Select Match): the card's row never
       wrapped, .candidate-img/.candidate-select-btn both have flex-
       shrink:0, and .candidate-info's flex-basis:0% let it shrink but
       never all the way to zero — on narrow phones the row's total
       min-content width exceeded the viewport, and since the button
       cannot shrink, it was the button that got pushed past the right
       edge instead of anything visually clipping in place. Wrapping lets
       the button drop to its own full-width row (see .candidate-select-
       btn's mobile rule below) instead of being shoved off-screen. */
    flex-wrap: wrap;
    gap: 0.8rem;
    background: #16213e;
    border: 1px solid #333;
    border-radius: 6px;
    padding: 0.6rem 0.8rem;
    /* Part 12: the whole card is a tap/click target now (see
       _onCandidateCardClick in binder_item_form.html), not just the
       Select button inside it — cursor + hover/focus feedback signal
       that to a pointer/keyboard user. */
    cursor: pointer;
    transition: border-color 0.15s, background 0.15s;
}
.candidate-card:hover { border-color: #5865f2; }
.candidate-card:focus-visible {
    outline: 2px solid #5865f2;
    outline-offset: 2px;
}

.candidate-card.candidate-auto {
    border-color: #2a6030;
    background: #152218;
}

.candidate-img {
    width: 56px;
    height: auto;
    border-radius: 3px;
    flex-shrink: 0;
}

.candidate-info {
    flex: 1;
    display: flex;
    flex-direction: column;
    gap: 0.25rem;
}

.candidate-name {
    font-size: 0.9rem;
    font-weight: 600;
    color: #e8e8e8;
}

.candidate-recommended {
    display: block;
    color: #60c080;
    font-weight: 700;
    font-size: 0.8rem;
}

.candidate-confidence {
    display: block;
    font-size: 0.8rem;
    color: #aaa;
}

.candidate-reasons {
    display: flex;
    flex-direction: column;
    gap: 0.1rem;
    margin-top: 0.1rem;
}

.candidate-reason {
    font-size: 0.72rem;
    color: #7ab87a;
}

.candidate-warnings {
    display: flex;
    flex-direction: column;
    gap: 0.15rem;
    margin-top: 0.2rem;
}

.candidate-select-btn {
    flex-shrink: 0;
    align-self: center;
    font-size: 0.8rem;
    padding: 0.3em 0.7em;
}
@media (max-width: 480px) {
    /* Full width on its own wrapped row (Part E/G) — guarantees "Select
       This Card (suggested)" and "Select Match" render completely, never
       clipped or pushed off-screen, and gives the primary action a
       comfortable full-width touch target instead of a narrow squeezed
       button sharing a row with the image/text. */
    .candidate-select-btn {
        flex: 1 1 100%;
        width: 100%;
        margin-top: 0.4rem;
        min-height: 44px;
        font-size: 0.85rem;
        text-align: center;
    }
    .alternative-card { flex-wrap: wrap; }
    .alternative-card .candidate-select-btn { order: 3; }
}

.prefill-ok {
    color: #60c080;
    font-size: 0.85rem;
}

.prefill-selected-banner {
    display: flex;
    align-items: center;
    flex-wrap: wrap;
    gap: 0.5rem;
    padding: 0.4rem 0.6rem;
    margin: 0.5rem 0;
    background: #1a2a1a;
    border: 1px solid #3a5a3a;
    border-radius: 6px;
    font-size: 0.85rem;
}

/* ── Selected match visual card ── */
.selected-match-card {
    border: 1px solid #3a5a3a;
    border-radius: 8px;
    background: #1a2a1a;
    margin: 0.5rem 0;
    overflow: hidden;
}
.selected-match-header {
    display: flex;
    align-items: center;
    justify-content: space-between;
    padding: 0.4rem 0.75rem;
    background: #22332a;
    border-bottom: 1px solid #3a5a3a;
    gap: 0.5rem;
    flex-wrap: wrap;
}
.selected-match-label {
    font-weight: 600;
    color: #7fe87f;
    font-size: 0.85rem;
}
.selected-match-actions {
    display: flex;
    gap: 0.4rem;
    flex-wrap: wrap;
}
.selected-match-body {
    display: flex;
    gap: 0.75rem;
    padding: 0.6rem 0.75rem;
    align-items: flex-start;
}
.selected-match-img {
    width: 80px;
    flex-shrink: 0;
    border-radius: 4px;
    object-fit: contain;
}
.selected-match-img-placeholder {
    width: 80px;
    height: 112px;
    flex-shrink: 0;
    background: #2a3a2a;
    border-radius: 4px;
    border: 1px dashed #4a6a4a;
}
.selected-match-meta {
    display: flex;
    flex-direction: column;
    gap: 0.2rem;
    font-size: 0.85rem;
    flex: 1;
    min-width: 0;
}
.selected-match-name {
    font-weight: 600;
    font-size: 0.95rem;
    word-break: break-word;
}
.selected-match-set { color: #aaa; }
.selected-match-number { color: #9b9; }
.selected-match-rarity { color: #9b9; }
.selected-match-price { color: #fff; margin-top: 0.2rem; }
.selected-match-date { font-size: 0.78rem; margin-top: 0.1rem; }
.selected-match-conf { font-size: 0.78rem; }

/* ── Alternatives section ── */
.alternatives-section {
    margin: 0.5rem 0;
    border: 1px solid #2a3a3a;
    border-radius: 8px;
    overflow: hidden;
}
.alternatives-header {
    padding: 0.4rem 0.75rem;
    background: #1a2a2a;
    border-bottom: 1px solid #2a3a3a;
    font-size: 0.85rem;
}
.alternatives-list {
    display: flex;
    flex-direction: column;
    gap: 0;
}
.alternative-card {
    display: flex;
    align-items: center;
    gap: 0.6rem;
    padding: 0.4rem 0.75rem;
    border-bottom: 1px solid #1a2a2a;
    font-size: 0.82rem;
}
.alternative-card:last-child { border-bottom: none; }
.alternative-img {
    width: 46px;
    flex-shrink: 0;
    border-radius: 3px;
    object-fit: contain;
}
.alternative-img-placeholder {
    width: 46px;
    height: 64px;
    flex-shrink: 0;
    background: #1a2828;
    border-radius: 3px;
    border: 1px dashed #3a4a4a;
}
.alternative-info {
    flex: 1;
    display: flex;
    flex-wrap: wrap;
    gap: 0.2rem 0.5rem;
    min-width: 0;
    align-items: baseline;
}
.alternative-name { font-weight: 600; word-break: break-word; }
.alternative-set { color: #aaa; }
.alternative-number { color: #9b9; }
.alternative-price { color: #cfc; }
.alternative-date { font-size: 0.75rem; }
.alternative-conf { font-size: 0.75rem; }

/* ── Confirm Price / Source ── */
.confirm-price-source {
    border: 1px solid #3a4a5a;
    border-radius: 8px;
    background: #16222e;
    margin: 0.5rem 0;
    padding: 0.75rem;
}
.confirm-price-header { margin-bottom: 0.5rem; }
.confirm-price-identity {
    display: flex;
    align-items: center;
    gap: 0.75rem;
    margin-bottom: 0.75rem;
}
.confirm-price-img {
    width: 56px;
    height: auto;
    border-radius: 3px;
    flex-shrink: 0;
}
.confirm-price-name { font-weight: 600; }
.price-source-list {
    display: flex;
    flex-wrap: wrap;
    gap: 0.6rem;
    margin-bottom: 0.75rem;
}
.price-source-card {
    flex: 1 1 160px;
    min-width: 150px;
    border: 1px solid #333;
    border-radius: 6px;
    background: #16213e;
    padding: 0.5rem 0.65rem;
}
.price-source-card.price-source-baseline {
    border-color: #2a4a6a;
    background: #14202e;
}
.price-source-label { font-weight: 600; margin-bottom: 0.15rem; }
.price-source-value { font-size: 1.05rem; color: #cfc; margin-bottom: 0.15rem; }
.confirm-price-actions {
    display: flex;
    align-items: center;
    gap: 0.5rem;
    flex-wrap: wrap;
}

.spelling-suggestions { margin-top: 0.5rem; display: flex; flex-direction: column; gap: 0.25rem; }
.btn-link.spelling-suggestion {
    background: none;
    border: none;
    padding: 0;
    color: #6cb4ff;
    text-decoration: underline;
    cursor: pointer;
    font-size: 0.85rem;
    text-align: left;
}
.btn-link.spelling-suggestion:hover { color: #9ccfff; }

.confirm-pending-highlight {
    outline: 2px solid #e6a23c;
    outline-offset: 2px;
    transition: outline-color 0.3s;
}

/* ── Binder Add-button confirmation tooltip ──
   Component-unique class names (binder-confirm-tooltip*) — deliberately not
   a generic .tooltip, to rule out any collision with another component's
   rules anywhere else in this file. Base state hides via visibility+opacity
   (not display:none/[hidden]) so the element is never in normal flow and
   the show/hide transition can be a smooth fade — a real browser load
   showed this exact text rendering permanently as in-flow page content
   beside the button because the browser had a STALE CACHED style.css from
   before any tooltip rule existed at all (no cache-busting query param was
   ever added to the stylesheet <link>, so nothing forced a refetch across
   this session's rounds of edits — see app/config.py STATIC_VERSION and
   base.html's ?v={{ static_version }}). With no matching CSS rule at all,
   a bare <span> is inline, in-flow, and permanently visible regardless of
   any class the JS ever adds or removes to it — exactly the reported
   symptom. pointer-events: none keeps it purely informational — it can
   never intercept a click intended for the button underneath it or
   anything else on the page. */
.binder-confirm-tooltip-anchor { position: relative; display: inline-block; }
.binder-confirm-tooltip {
    position: absolute;
    bottom: calc(100% + 8px);
    left: 50%;
    transform: translateX(-50%);
    z-index: 1000;
    visibility: hidden;
    opacity: 0;
    pointer-events: none;
    transition: opacity 0.12s ease;
    width: max-content;
    max-width: min(280px, calc(100vw - 2rem));
    background: #1c2b3a;
    border: 1px solid #e6a23c;
    border-radius: 6px;
    padding: 0.5rem 0.65rem;
    font-size: 0.8rem;
    line-height: 1.35;
    color: #f0d9b5;
    box-shadow: 0 4px 12px rgba(0, 0, 0, 0.4);
}
.binder-confirm-tooltip.is-visible {
    visibility: visible;
    opacity: 1;
}

/* ── Search diagnostics (prefill form) ── */
.search-diag-details {
    margin: 0.5rem 0 0;
    border-top: 1px solid #2a3a4a;
}
.search-diag-summary {
    display: flex;
    align-items: center;
    gap: 0.5rem;
    padding: 0.3rem 0.25rem;
    font-size: 0.78rem;
    color: #8aa8c8;
    cursor: pointer;
    list-style: none;
    user-select: none;
}
.search-diag-summary::-webkit-details-marker { display: none; }
.search-diag-summary::before {
    content: '▶';
    font-size: 0.6rem;
    transition: transform 0.15s;
}
.search-diag-details[open] .search-diag-summary::before { transform: rotate(90deg); }
.search-diag-body {
    padding: 0.4rem 0.25rem 0.6rem;
    font-size: 0.78rem;
}
.search-diag-grid {
    display: grid;
    grid-template-columns: max-content 1fr;
    gap: 0.1rem 0.75rem;
    margin: 0 0 0.5rem;
    font-size: 0.78rem;
}
.search-diag-grid dt { color: #6a88a8; font-weight: normal; white-space: nowrap; }
.search-diag-grid dd { margin: 0; color: #c8d8e8; }
.search-diag-table {
    width: 100%;
    border-collapse: collapse;
    font-size: 0.75rem;
    margin-top: 0.5rem;
}
.search-diag-table th { color: #6a88a8; padding: 0.15rem 0.4rem; border-bottom: 1px solid #2a3a4a; text-align: left; }
.search-diag-table td { padding: 0.15rem 0.4rem; border-bottom: 1px solid #1a2a3a; color: #c8d8e8; }
.search-diag-id { font-family: monospace; font-size: 0.7rem; }
.search-diag-copy-btn { margin-left: auto; padding: 0.1rem 0.5rem; font-size: 0.72rem; }

/* ── Archive / danger ── */
.archived-badge {
    display: inline-block;
    font-size: 0.8rem;
    padding: 0.2em 0.6em;
    border-radius: 3px;
    background: #301010;
    color: #c06060;
    border: 1px solid #602020;
    margin-right: 0.5rem;
}

.btn-danger {
    background: #5a1515;
    color: #f0b0b0;
    border: 1px solid #8a2020;
}

.btn-danger:hover {
    background: #7a1515;
    color: #ffd0d0;
}

.inline-form {
    display: inline;
    margin: 0;
    padding: 0;
}

/* ── Binder item summary card (make-available mode) ── */
.binder-item-summary {
    display: flex;
    gap: 1rem;
    background: #1a1a2e;
    border: 1px solid #2a2a40;
    border-radius: 6px;
    padding: 1rem;
    margin-bottom: 1.25rem;
}

.summary-img {
    width: 80px;
    height: 80px;
    object-fit: contain;
    border-radius: 4px;
    flex-shrink: 0;
}

.summary-details {
    flex: 1;
    min-width: 0;
}

.summary-tags {
    display: flex;
    flex-wrap: wrap;
    gap: 0.4rem;
    margin-bottom: 0.6rem;
}

.summary-dl {
    display: grid;
    grid-template-columns: auto 1fr;
    gap: 0.15rem 0.75rem;
    font-size: 0.88rem;
    margin: 0;
}

.summary-dl dt {
    color: #8080a0;
    font-weight: 500;
    white-space: nowrap;
}

.summary-dl dd {
    margin: 0;
    color: #c0c0d8;
}

.summary-photos {
    display: flex;
    gap: 0.5rem;
    margin-top: 0.75rem;
}

.summary-user-photo {
    width: 64px;
    height: 64px;
    object-fit: cover;
    border-radius: 4px;
    border: 1px solid #2a2a40;
}

/* ── Provider diagnostics page ── */
.diag-page .diag-notice {
    background: #1a1a2e;
    border: 1px solid #2a2a50;
    border-radius: 6px;
    color: #aaa;
    font-size: 0.85rem;
    padding: 0.5rem 0.9rem;
    margin-bottom: 1.2rem;
}

.diag-section {
    margin-bottom: 1.5rem;
}

.diag-section h3 {
    font-size: 0.95rem;
    color: #c8c8e8;
    border-bottom: 1px solid #2a2a40;
    padding-bottom: 0.3rem;
    margin-bottom: 0.6rem;
}

.diag-actions {
    display: flex;
    gap: 0.6rem;
    flex-wrap: wrap;
    margin-bottom: 1.5rem;
}

.diag-meta dt { width: 10rem; }

.diag-table {
    width: 100%;
    border-collapse: collapse;
    font-size: 0.8rem;
    color: #ccc;
}

.diag-table th {
    background: #16213e;
    color: #aab;
    padding: 0.3rem 0.5rem;
    text-align: left;
    border-bottom: 1px solid #2a2a50;
    white-space: nowrap;
}

.diag-table td {
    padding: 0.3rem 0.5rem;
    border-bottom: 1px solid #1e1e30;
    vertical-align: top;
}

.diag-table tr:hover td { background: #0f1520; }

.diag-top-match td { background: #102018; }

.diag-ok  { color: #60c080; font-weight: 700; }
.diag-warn { color: #e0a840; }
.diag-na  { color: #888; }
.diag-error { color: #e06060; font-size: 0.75rem; }
.diag-score { font-weight: 700; color: #88aaff; text-align: right; }
.diag-name { font-weight: 600; max-width: 14rem; word-break: break-word; }
.diag-price { white-space: nowrap; }
.diag-pid { font-family: monospace; font-size: 0.75rem; }
.diag-query code { font-size: 0.8rem; color: #aad; }
.diag-label { white-space: nowrap; color: #aab; }
.diag-hint { color: #888; font-size: 0.82rem; margin: 0.3rem 0; }
.diag-hint-inline { color: #888; font-size: 0.8rem; font-style: italic; }
.diag-ok-text { color: #60c080; font-size: 0.8rem; }

.diag-warn-list {
    margin: 0;
    padding-left: 1rem;
    list-style: disc;
    color: #e0a840;
    font-size: 0.78rem;
}

.diag-candidates-scroll {
    overflow-x: auto;
}

.diag-confidence { white-space: nowrap; }
.diag-rec { text-align: center; }
.diag-reasons { font-size: 0.7rem; }
.diag-reason-list {
    margin: 0;
    padding-left: 1rem;
    list-style: disc;
    color: #7ab87a;
    font-size: 0.75rem;
}

/* ── Task 23: Make-available confirmation + trade setup ──────────────────── */
.make-available-summary { margin: 1rem 0 1.5rem; }
.source-label-badge {
    display: inline-block;
    background: #1a2a1a;
    border: 1px solid #3a6a3a;
    color: #7ada7a;
    padding: 0.2rem 0.7rem;
    border-radius: 4px;
    font-size: 0.82rem;
    margin-bottom: 1rem;
}
.info-banner {
    background: #1a2030;
    border: 1px solid #3a5080;
    color: #8ab8e8;
    padding: 0.5rem 0.9rem;
    border-radius: 4px;
    margin: 0.75rem 0;
    font-size: 0.88rem;
}
.make-available-form .form-actions { margin-top: 1.5rem; }

/* Trade setup page */
.setup-match-section { margin-bottom: 2rem; }
.setup-match-actions {
    display: flex;
    gap: 0.5rem;
    flex-wrap: wrap;
    margin: 0.75rem 0;
}
.setup-candidates-area {
    background: #111620;
    border: 1px solid #2a3a5a;
    border-radius: 6px;
    padding: 0.75rem 1rem;
    margin: 0.75rem 0;
}
.setup-confirm-current {
    background: #1a1a2a;
    border: 1px solid #3a3a6a;
    border-radius: 4px;
    padding: 0.6rem 0.9rem;
    margin: 0.75rem 0;
}

/* Product match status badges */
.match-status-badge {
    display: inline-block;
    padding: 0.15rem 0.6rem;
    border-radius: 4px;
    font-size: 0.8rem;
    font-weight: 600;
}
.match-status-source_confirmed  { background: #1a3a1a; color: #7ada7a; border: 1px solid #3a8a3a; }
.match-status-api_matched       { background: #1a2a3a; color: #7aadda; border: 1px solid #3a6a9a; }
.match-status-current_info_confirmed { background: #2a2a1a; color: #c0c07a; border: 1px solid #6a6a3a; }
.match-status-no_match_current_info  { background: #2a1a1a; color: #c08080; border: 1px solid #6a3a3a; }

/* ── Parts A-K: listing owner, viewer match, price history, binder grid ───── */

.listing-owner-section {
    margin: 0.4rem 0 0.8rem;
    color: #9ab8d8;
    font-size: 0.88rem;
}

.user-photos-row {
    display: flex;
    gap: 0.75rem;
    flex-wrap: wrap;
}
.user-photos-row .detail-product-img {
    max-height: 160px;
    border-radius: 4px;
}

/* Viewer match section */
.viewer-match-section {
    margin: 1.5rem 0 1rem;
    background: #111820;
    border: 1px solid #2a4a3a;
    border-radius: 6px;
    padding: 0.9rem 1rem;
}
.viewer-match-section .section-heading {
    font-size: 0.95rem;
    color: #7ada7a;
    margin: 0 0 0.7rem;
}
.viewer-match-section .item-list {
    display: flex;
    flex-wrap: wrap;
    gap: 0.5rem;
}
.viewer-match-section .item-card {
    display: flex;
    align-items: center;
    gap: 0.4rem;
    background: #181e28;
    border: 1px solid #2a3a4a;
    border-radius: 4px;
    padding: 0.3rem 0.6rem;
    text-decoration: none;
    font-size: 0.82rem;
    max-width: 220px;
}
.viewer-match-section .item-card .prefill-img {
    max-height: 36px;
    border-radius: 3px;
}
.viewer-match-section .item-title {
    color: #c8d8e8;
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
    max-width: 120px;
}
.match-reason-tag {
    background: #1a2a3a;
    color: #7aadda;
    border-color: #3a6a8a;
    font-size: 0.7rem;
}

/* Shared mobile primitive (Part E): a scroll wrapper for genuinely tabular
   content, so a table's columns never force the whole document wider than
   the viewport — the table scrolls horizontally inside its own box instead.
   Reused anywhere a real <table> exists outside the diagnostics pages that
   already had their own *-table-wrap classes. */
.table-scroll-wrap { overflow-x: auto; }

/* Price history section */
.price-history-section {
    margin: 1.5rem 0 1rem;
}
.price-history-table {
    width: 100%;
    border-collapse: collapse;
    font-size: 0.82rem;
    margin-bottom: 0.75rem;
}
.price-history-table th {
    text-align: left;
    color: #8aa8c8;
    border-bottom: 1px solid #2a3a5a;
    padding: 0.3rem 0.5rem;
    font-weight: 600;
}
.price-history-table td {
    padding: 0.25rem 0.5rem;
    color: #c0d0e0;
    border-bottom: 1px solid #1a2a3a;
}
.price-history-chart {
    display: block;
    width: 100%;
    max-width: 400px;
    height: 80px;
    background: #0d1018;
    border: 1px solid #1e2a3a;
    border-radius: 4px;
}

/* Binder view toggle */
.binder-view-toggle {
    display: flex;
    gap: 0.4rem;
    margin: 0.75rem 0;
}
.btn-active {
    background: #3a5a8a !important;
    color: #e8f0ff !important;
    border-color: #5a8acf !important;
}

/* Binder 3×3 page grid */
/* Page navigation — sticky so Prev/Next stays reachable without scrolling
   back up on a tall page, and sized as a real touch target on mobile
   (previously small text links). Swipe/drag (binder_swipe.js) is a
   progressive-enhancement supplement over these same Prev/Next hrefs, never
   a replacement for them. */
.binder-page-nav {
    display: flex;
    align-items: center;
    justify-content: center;
    gap: 0.75rem;
    margin: 0.75rem 0;
    padding: 0.5rem;
    position: sticky;
    top: 0.5rem;
    z-index: 5;
    background: #16213e;
    border: 1px solid #2a3a5e;
    border-radius: 6px;
}
.binder-page-nav .btn { min-width: 4.5rem; text-align: center; }
.binder-page-info {
    font-size: 0.85rem;
    color: #8aa8c8;
    font-weight: 600;
    white-space: nowrap;
}
@media (max-width: 480px) {
    .binder-page-nav .btn { min-width: 0; flex: 1 1 0; padding: 0.65rem 0.5rem; font-size: 0.9rem; }
}
.binder-page-grid {
    display: grid;
    grid-template-columns: repeat(3, 1fr);
    gap: 10px;
    max-width: 540px;
    margin: 0.75rem 0;
    padding: 14px;
    touch-action: pan-y;
    background: #111828;
    border: 2px solid #1e2a3a;
    border-radius: 8px;
}
/* Each binder pocket: fixed aspect-ratio image area + separate caption area
   below it. The image area's size is never affected by caption text length —
   collector-number/price tags live in the caption area only, so neighboring
   pockets' images stay pixel-aligned regardless of how many tags a card has. */
.binder-pocket {
    position: relative;
    background: #0d1018;
    border: 2px solid #2a3a5a;
    border-radius: 5px;
    overflow: hidden;
    display: flex;
    flex-direction: column;
    text-decoration: none;
    color: #e0e0e0;
    transition: border-color 0.15s;
}
.binder-pocket:hover { border-color: #5a8acf; }
.binder-pocket-empty {
    border-style: dashed;
    border-color: #1e2a3a;
    cursor: default;
    aspect-ratio: 2.5 / 3.5;
    align-items: center;
    justify-content: center;
}
/* This slot is whatever width the 3-column grid gives it (bigger than the
   standard thumbnail on typical viewports) — deliberately NOT sized as
   "100% of slot, aspect-ratio 2.5:3.5" anymore (that made the pocket's
   effective image size a function of the grid's column width, not of the
   shared token, which is the actual reason Binder's page/grid view still
   visibly disagreed with Trade/list-view thumbnails after the earlier
   pass). Instead this is just a flex-centering wrapper; the actual fixed
   size lives on .binder-pocket-img (below), same box every other "normal
   thumbnail" context uses. Height follows the centered image's own
   intrinsic height, not this slot's width. */
.binder-pocket-image-area {
    position: relative;
    width: 100%;
    flex-shrink: 0;
    display: flex;
    align-items: center;
    justify-content: center;
    overflow: hidden;
    padding: 6px 0;
}
.binder-pocket-num {
    position: absolute;
    top: 3px;
    left: 5px;
    font-size: 0.55rem;
    color: #3a5a7a;
    z-index: 2;
    line-height: 1;
}
.binder-pocket-qty {
    position: absolute;
    top: 3px;
    right: 4px;
    background: rgba(0,0,0,0.72);
    color: #e8d060;
    font-size: 0.58rem;
    padding: 1px 3px;
    border-radius: 2px;
    z-index: 2;
    line-height: 1.2;
}
/* Size (width/aspect-ratio) comes from the shared selector list further
   down this file — this is chrome/fit only, so it can't drift out of sync
   via cascade order the way .binder-pocket-image-area briefly did. */
.binder-pocket-img {
    object-fit: fill;
    display: block;
}
/* Framed user photo exception — see .thumb-img.user-photo-framed comment.
   Rendered via background-image (app/photo_framing.py), not <img> content —
   .binder-pocket-img's width/aspect-ratio (set below) still apply since
   they aren't img-specific. */
.binder-pocket-img.user-photo-framed { background-repeat: no-repeat; }
.binder-pocket-placeholder {
    font-size: 1rem;
    color: #4a6a8a;
    font-weight: 700;
    letter-spacing: 0.05em;
}
.binder-pocket-empty-icon {
    font-size: 1.4rem;
    color: #1e2a3a;
}
/* Caption area — normal flow, below the fixed image area. Its content
   (title + optional number/price tags) can never shift or resize the image. */
.binder-pocket-caption-area {
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: 1px;
    padding: 2px 3px;
    background: rgba(0,0,0,0.72);
    font-size: 0.55rem;
    color: #a8c0d8;
    text-align: center;
    line-height: 1.3;
}
.binder-pocket-caption {
    display: block;
    width: 100%;
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}
.binder-pocket-num-tag {
    color: #a8c0d8;
    font-size: 0.55rem;
    line-height: 1.2;
}
/* Sort controls for list view */
.binder-sort-controls {
    display: flex;
    align-items: center;
    gap: 0.4rem;
    flex-wrap: wrap;
    margin: 0.5rem 0 0.25rem;
}
.sort-label {
    font-size: 0.82rem;
    color: #8aa8c8;
    margin-right: 0.2rem;
}
.binder-apply-sort-bar {
    margin: 0.4rem 0 0.75rem;
}
/* Binder dashboard List-view Sort by / Listing filter controls — compact on
   one row on desktop, wraps to stacked full-width controls on mobile with
   no horizontal overflow. */
.binder-list-controls { margin: 0.5rem 0 0.25rem; }
.binder-list-controls-row {
    display: flex;
    align-items: center;
    gap: 0.75rem;
    flex-wrap: wrap;
}
.binder-control-group {
    display: flex;
    align-items: center;
    gap: 0.4rem;
    flex: 0 1 auto;
    min-width: 0;
}
.binder-control-group select {
    max-width: 100%;
}
.binder-reset-link { margin-left: auto; }
.binder-result-summary {
    font-size: 0.82rem;
    color: #8aa8c8;
    margin: 0.35rem 0 0.6rem;
}
@media (max-width: 560px) {
    .binder-list-controls-row { flex-direction: column; align-items: stretch; }
    .binder-control-group { flex-direction: column; align-items: stretch; gap: 0.25rem; }
    .binder-control-group select { width: 100%; }
    .binder-reset-link { margin-left: 0; align-self: flex-start; }
}

/* Community Trade Board type/sort controls — reuses the Binder List-view
   controls above for the inner form (identical select/label/summary
   markup and CSS, one convention across the site) plus its own sticky
   wrapper so it stays reachable while scrolling a long Community feed, on
   desktop AND mobile alike, without a second hamburger-style drawer. */
.community-filter-controls {
    position: sticky;
    top: 0.5rem;
    z-index: 5;
    background: #16213e;
    border: 1px solid #2a3a5e;
    border-radius: 6px;
    padding: 0.5rem 0.75rem;
    margin: 0 0 0.5rem;
}

/* ── Binder dashboard List-view card (redesigned) ──────────────────────────
   Deliberately new, Binder-specific classes (binder-card__*) rather than
   reworking the shared .item-card/.item-card-wrap/.thumb-frame rules —
   those are also used by Trade/ISO list pages (trade_item_list.html,
   trade_item_detail.html) and must not change. The .item-card-wrap /
   .item-card-select / .bulk-select-checkbox / .item-card-actions class
   names ARE still present on this new markup (alongside the new
   binder-card__* classes) purely so the existing shared bulk_select.js
   and its .mass-remove-mode CSS keep working unmodified — that contract
   is selector-name-based, not markup-shape-based. A stable two-column
   grid (content column + fixed-width media rail) replaces the previous
   single-<a>-wraps-everything layout, which was the root cause of the
   reported crowding/misalignment and the condition badge disappearing at
   narrow widths (it was sharing a CSS grid row with the title/image in
   the old .item-card rules with no width floor of its own). */
.binder-card {
    display: grid;
    /* Fixed px for the media column, not "auto" — an "auto" track sizes to
       the max-content width of whatever's inside it, and flex/grid items
       default to min-width:auto (their own content's natural width), so
       the old "auto" column let the Move Spot button's un-wrapped text
       silently widen the whole column (and therefore the whole card)
       past its 72px intent, past the card's own border, and past the
       viewport at narrow widths — the actual cause of the reported 320px
       horizontal overflow/uneven margins. A fixed px column caps it
       regardless of content, and min-width:0 below lets children actually
       shrink to fit it instead of resisting. */
    grid-template-columns: minmax(0, 1fr) var(--product-thumb-size);
    gap: 0.85rem;
    align-items: start;
    position: relative;
    width: 100%;
    max-width: 100%;
    min-width: 0;
    box-sizing: border-box;
    padding: 0.85rem;
}
/* The checkbox is display:none until Mass Remove mode reveals it (see
   .item-card-select's own base rule) — reserving space for it via
   permanent asymmetric card padding made the card look uneven/off-center
   even when the checkbox was invisible. Left padding only grows once
   Mass Remove mode is actually active. */
.item-list.mass-remove-mode .binder-card { padding-left: 2.4rem; }
/* Positioning only — deliberately a distinct class from .item-card-select
   (added alongside it on the same <label>, never replacing it) so this
   rule's own text can never be mistaken for a redefinition of
   .item-card-select's base display:none/mass-remove-mode reveal rules
   (app/static/js/bulk_select.js's contract) by any selector-text-based
   check. */
.binder-card__select {
    position: absolute;
    left: 0.5rem;
    top: 0.85rem;
    padding: 0;
}
.binder-card__main {
    display: flex;
    flex-direction: column;
    gap: 0.5rem;
    min-width: 0;
}
.binder-card__identity { min-width: 0; }
.binder-card__title-link { text-decoration: none; }
.binder-card__title {
    margin: 0 0 0.3rem;
    font-size: 1rem;
    font-weight: 600;
    color: #e0e0e0;
    line-height: 1.3;
    overflow-wrap: anywhere;
}
.binder-card__tags {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 0.35rem;
}
/* Vendor-handoff value summary — shared component (_item_value_summary.html,
   Part A) rendered identically by Binder list, Binder detail, and Trade
   detail. Two size modes: compact (Binder-card chip row) and detail (larger
   item/listing detail panel). See resolve_binder_value_display (app/routers/
   pricing.py) for the Cash/Trade/Market precedence rule. */
.binder-card__value { display: contents; }
.item-value-summary {
    display: flex;
    flex-wrap: wrap;
    align-items: baseline;
    gap: 0.15rem 0.6rem;
}
.item-value-summary-compact { font-size: 0.9rem; font-weight: 700; }
.item-value-summary-detail {
    font-size: 1.3rem;
    font-weight: 800;
    gap: 0.4rem 1.2rem;
    margin: 0.5rem 0;
}
.item-value-chip { white-space: nowrap; }
.item-value-cash { color: #7ee787; }
.item-value-trade { color: #79c0ff; }
.item-value-market { color: #cfc; font-weight: 600; }
.item-value-market.item-value-none { color: #7a8fa6; font-weight: 400; }
/* Stale is flagged by the stopwatch glyph (&#9201;) + title tooltip text,
   never color alone (Part D7) — this dimmed color is a secondary
   reinforcement only. */
.item-value-market.item-value-stale { color: #d8b25c; }
.binder-card__dates {
    display: flex;
    flex-wrap: wrap;
    gap: 0.15rem 0.9rem;
    font-size: 0.78rem;
    color: #8aa8c8;
}
.binder-card__state {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 0.4rem;
}
.binder-card__actions {
    display: flex;
    flex-direction: column;
    gap: 0.4rem;
    margin-top: 0.15rem;
}
/* Trade-listing action and Binder-metadata/removal actions are visually
   grouped as two separate rows within the actions footer — see the
   binder_dashboard.html comment on why "Edit"/"Remove" alone were
   ambiguous about which thing (Binder item vs Trade listing) they acted on. */
.binder-card__listing-action,
.binder-card__binder-actions {
    display: flex;
    flex-wrap: wrap;
    gap: 0.5rem;
}
.binder-card__media {
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: 0.35rem;
    width: 100%;
    min-width: 0;
    flex-shrink: 0;
}
.binder-card__condition {
    font-size: 0.68rem;
    font-weight: 700;
    letter-spacing: 0.05em;
    text-transform: uppercase;
    padding: 0.15rem 0.5rem;
    border-radius: 20px;
    background: #2a3a4a;
    color: #cfe0f0;
    white-space: nowrap;
}
.binder-card__condition.cond-sealed { background: #3a2a4a; color: #e0c8f0; }
.binder-card__condition.cond-graded { background: #2a4a3a; color: #c8f0d8; }
/* Size (width/aspect-ratio) comes from the shared .thumb-frame-card /
   .binder-card__image-frame selector list further down this file — this
   block is chrome only (background/border/centering), so the size can
   never drift out of sync via cascade order again. */
.binder-card__image-frame {
    border-radius: 5px;
    overflow: hidden;
    background: #0f1626;
    display: flex;
    align-items: center;
    justify-content: center;
    flex-shrink: 0;
}
.binder-card__image-frame img { width: 100%; height: 100%; object-fit: fill; }
/* Framed user photo exception — see .thumb-img.user-photo-framed comment.
   Rendered via background-image (app/photo_framing.py) on a div, not an
   <img>, so this can't be an "img.user-photo-framed" type selector. */
.binder-card__image-frame .user-photo-framed { width: 100%; height: 100%; background-repeat: no-repeat; }
.binder-card__reposition-link { font-size: 0.72rem; padding: 0.25rem 0.4rem; margin-top: 0.2rem; }
.binder-card__image-placeholder { color: #444; font-size: 1.4rem; }
/* Move Spot: allowed to wrap ("Move" / "Spot" on two lines) rather than
   forcing one line that can only fit by overflowing its button — a fixed
   72px column plus normal white-space makes that wrap safe instead of a
   last resort. min-width:0 lets the button itself shrink to the column
   rather than pushing the column wider (see the .binder-card comment). */
.binder-card__move-spot {
    width: 100%;
    max-width: 100%;
    min-width: 0;
    box-sizing: border-box;
    text-align: center;
    white-space: normal;
    overflow-wrap: break-word;
    line-height: 1.15;
    padding: 0.35rem 0.25rem;
}

@media (max-width: 480px) {
    .site-main { padding: 0.6rem; }
    /* Vendor-handoff card (Part C): tight two-region layout — image on the
       LEFT, name/value/badges/metadata stacked on the RIGHT, so a vendor
       scanning a phone screen sees "what it is + what it's worth" as one
       glance instead of scrolling past a wide media rail first. Achieved
       with `order` on the existing grid items (DOM order — and therefore
       tab order, and every text-index-based test above — stays untouched);
       desktop/tablet keep the image on the right, unaffected. */
    .binder-card {
        grid-template-columns: var(--product-thumb-size) minmax(0, 1fr);
        padding: 0.6rem;
        gap: 0.6rem;
    }
    .binder-card__media { order: -1; }
    .item-list.mass-remove-mode .binder-card { padding-left: 2.1rem; }
    /* No image-size override here — the uniform product thumbnail
       (--product-thumb-size) is identical at every width, so the media
       column (below, on .binder-card) doesn't need a mobile variant either. */
    .binder-card__move-spot { font-size: 0.72rem; padding: 0.3rem 0.2rem; }
    .binder-card__actions { flex-direction: column; align-items: stretch; }
    .binder-card__actions .btn { width: 100%; text-align: center; }
}

/* Apply-sort section in binder view */
.binder-sort-section {
    margin: 1rem 0 0.5rem;
    padding: 0.75rem;
    background: #111828;
    border: 1px solid #1e2a3a;
    border-radius: 5px;
    max-width: 540px;
}
.binder-apply-sort-form {
    display: flex;
    align-items: center;
    gap: 0.5rem;
    flex-wrap: wrap;
    margin-top: 0.4rem;
}
/* Move controls in list view */
.binder-move-controls {
    padding: 0 0.5rem 0.5rem;
}
.binder-move-details summary { cursor: pointer; }
.binder-move-details[open] summary { margin-bottom: 0.4rem; }
.binder-move-form {
    display: flex;
    align-items: center;
    gap: 0.4rem;
    flex-wrap: wrap;
    padding: 0.4rem 0;
}
.binder-move-form label {
    display: flex;
    align-items: center;
    gap: 0.25rem;
    font-size: 0.8rem;
    color: #8aa8c8;
}
/* Binder position badge on list items */
.item-binder-pos {
    font-size: 0.7rem;
    color: #5a7a9a;
    margin-left: auto;
}
/* Compact form inputs */
.form-input-sm {
    padding: 0.2rem 0.4rem;
    font-size: 0.82rem;
    width: auto;
}
.form-input-xs {
    padding: 0.15rem 0.3rem;
    font-size: 0.78rem;
    width: 4em;
}
.btn-disabled {
    opacity: 0.4;
    pointer-events: none;
    cursor: default;
}
/* Binder pagination (kept for compatibility) */
.binder-pagination {
    display: flex;
    align-items: center;
    gap: 0.75rem;
    margin: 0.75rem 0;
}

.match-status-row { margin: 0.5rem 0 1rem; }

/* ── Thumbnail frame system ── */
/* Shared image inside any frame. object-fit: fill (not contain) is
   deliberate — see the --product-thumb-size comment at the top of this
   file: a non-matching source ratio (sealed product, off-ratio user photo)
   stretches to exactly fill the frame instead of letterboxing, so it never
   LOOKS like a smaller thumbnail than a card that fills its frame
   natively. */
.thumb-img {
    width: 100%;
    height: 100%;
    object-fit: fill;
    border-radius: 2px;
    display: block;
}
/* A user photo the owner has deliberately framed (Position Photo editor) is
   the ONE exception to the fill-stretch rule above: since the user already
   chose their own crop/zoom, preserving proportions (never stretch) is
   correct here — see Part A8. Rendered via background-image/background-size
   /background-position (app/photo_framing.py's build_photo_pan_style), not
   <img> content + object-fit — see that module's docstring for why. Set
   inline per-item from the saved focal point + zoom + fit_axis. */
.thumb-img.user-photo-framed { background-repeat: no-repeat; }

/* Card frame chrome (background/border/centering) — the ACTUAL size comes
   from the single selector list below, which is the one shared source of
   truth every "normal thumbnail" context in Binder and Trade consumes:
   Trade full cards, Recommended/Recently Added carousel, ISO Match alert,
   detail-page candidate suggestions (.thumb-frame-card/-product), the
   Binder list card (.binder-card__image-frame), Binder 9x9-grid/3x3-page
   pocket image (.binder-pocket-img — NOT .binder-pocket-image-area, which
   is deliberately left at width:100% of its slot so it can center this
   fixed-size image inside a larger slot, per the "center the standard
   image inside a bigger container" rule), and Move Spot preview/done thumbs
   (.move-preview-thumb/.move-done-thumb). Every one of these renders at
   the exact same --product-thumb-size x --product-thumb-aspect box, at
   every viewport width — no separate mobile/desktop/carousel/Binder tier.
   Sealed products get NO separate wider frame anymore (was
   .thumb-frame-product's 4:3 box) — they fit the same portrait box via
   object-fit: fill on the image itself, per explicit product decision
   (uniform display size over intrinsic proportions). */
.thumb-frame {
    flex-shrink: 0;
    background: #0d1018;
    border: 1px solid #2a3a5a;
    border-radius: 3px;
    overflow: hidden;
    display: flex;
    align-items: center;
    justify-content: center;
}
.thumb-frame-card,
.thumb-frame-product,
.thumb-frame-card-grid,
.thumb-frame-product-grid,
.binder-card__image-frame,
.binder-pocket-img,
.move-preview-thumb,
.move-done-thumb,
.iso-match-alert-thumb {
    width: var(--product-thumb-size);
    aspect-ratio: var(--product-thumb-aspect);
}

/* Placeholder text inside any frame */
.thumb-placeholder-text {
    color: #3a5a7a;
    font-size: 0.6rem;
    text-align: center;
    padding: 2px;
}

/* Grid-row span for item-card grid layout (list view cards) */
.thumb-frame.thumb-card-col { grid-row: 1 / span 2; }

/* Legacy aliases — keep templates that still use old classes working */
.item-thumb          { flex-shrink: 0; }
.item-thumb-placeholder {
    width: 44px;
    height: 60px;
    background: #0d1018;
    border: 1px solid #2a3a5a;
    border-radius: 3px;
    display: flex;
    align-items: center;
    justify-content: center;
    flex-shrink: 0;
    color: #3a5a7a;
    font-size: 0.7rem;
    text-align: center;
}
.item-card-thumb-placeholder {
    width: 40px;
    height: 55px;
    background: #0d1018;
    border: 1px solid #2a3a5a;
    border-radius: 3px;
    grid-row: 1 / span 2;
}

/* ── Item card wrap (binder list view) ── */
.item-card-wrap {
    background: #16213e;
    border: 1px solid #2a2a4a;
    border-radius: 7px;
    overflow: hidden;
    transition: border-color 0.15s;
}
.item-card-wrap:hover { border-color: #5865f2; }
/* Cancel border/bg on the <a> inside wrap — wrapper owns them */
.item-card-wrap > .item-card {
    background: transparent;
    border: none;
    border-radius: 0;
    transition: none;
}
.item-card-wrap > .item-card:hover { border-color: transparent; }
.item-card-footer {
    padding: 0.3rem 0.8rem 0.4rem;
    border-top: 1px solid #1a2030;
    display: flex;
    align-items: center;
    gap: 0.5rem;
    flex-wrap: wrap;
}
.move-spot-btn {
    cursor: pointer;
    font-size: 0.72rem;
    color: #5865f2;
    padding: 0.1rem 0.45rem;
    border: 1px solid #2a3a5a;
    border-radius: 4px;
    background: transparent;
    user-select: none;
    list-style: none;
}
.move-spot-btn:hover { background: #1a2a3a; }

/* ── Binder pocket wrap (pocket view) ── */
.binder-pocket-wrap {
    display: flex;
    flex-direction: column;
    gap: 3px;
}
.binder-pocket-move-link {
    font-size: 0.65rem;
    color: #5a7aaa;
    text-align: center;
    text-decoration: none;
    padding: 1px 0;
    opacity: 0.75;
    white-space: nowrap;
}
.binder-pocket-move-link:hover { opacity: 1; color: #5865f2; }

/* ── Move preview pockets ── */
.binder-pocket-moving {
    border-color: #5865f2 !important;
    box-shadow: 0 0 0 2px #5865f250;
}
.binder-pocket-target {
    border-color: #5865f2 !important;
    border-style: solid !important;
}
.binder-pocket-badge {
    position: absolute;
    bottom: 4px;
    left: 50%;
    transform: translateX(-50%);
    background: #5865f2;
    color: #fff;
    font-size: 0.58rem;
    padding: 1px 5px;
    border-radius: 3px;
    white-space: nowrap;
    z-index: 2;
}
.move-preview-grid { margin: 0.75rem 0 1rem; }

/* ── Move preview page ── */
.move-preview-card-info {
    display: flex;
    align-items: center;
    gap: 1rem;
    margin: 1rem 0;
    padding: 0.75rem 1rem;
    background: #16213e;
    border: 1px solid #2a3a5a;
    border-radius: 7px;
}
/* No fixed width/height here — the size comes from the shared selector
   list on .thumb-frame-card (this element also carries .thumb-frame-card,
   see binder_move_preview.html) — the SAME box every other normal
   thumbnail uses, including Move Spot's own grid pockets below. */
.move-preview-thumb { flex-shrink: 0; }
.move-preview-card-text { min-width: 0; }
.move-preview-title { font-weight: 600; margin: 0 0 0.2rem; font-size: 0.95rem; }
.move-preview-dest { font-size: 0.85rem; color: #8aa8c8; margin: 0 0 0.2rem; }
.move-preview-swap-notice { font-size: 0.82rem; color: #f0a020; margin: 0; }
.move-preview-actions {
    display: flex;
    gap: 0.75rem;
    margin: 1rem 0;
    align-items: center;
    flex-wrap: wrap;
}
.move-preview-change { margin-top: 0.5rem; }

/* ── Move done page ── */
.move-done-notice { margin: 1rem 0; }
.move-done-card {
    display: flex;
    align-items: center;
    gap: 1rem;
    padding: 0.75rem 1rem;
    background: #16213e;
    border: 1px solid #2a3a5a;
    border-radius: 7px;
    margin: 0.5rem 0;
}
/* Same shared-token fix as .move-preview-thumb above. */
.move-done-thumb { flex-shrink: 0; }
.move-done-card-text { min-width: 0; }
.move-done-item-name { font-weight: 600; margin: 0 0 0.2rem; font-size: 0.95rem; }
.move-done-location { font-size: 0.85rem; color: #8aa8c8; margin: 0 0 0.4rem; }
.move-done-swap-divider {
    text-align: center;
    font-size: 0.85rem;
    color: #5a7a9a;
    padding: 0.3rem;
}
.move-done-actions {
    display: flex;
    gap: 0.75rem;
    margin: 1.25rem 0;
    align-items: center;
    flex-wrap: wrap;
}

/* ── Import preview match-card layout (Parts A–E) ── */
.import-rows-list {
    display: flex;
    flex-direction: column;
    gap: 1rem;
    margin: 1rem 0;
}
.import-match-card {
    border: 1px solid #2a3a5a;
    border-radius: 7px;
    background: #0d1421;
    overflow: hidden;
}
.import-match-card-header {
    display: flex;
    align-items: center;
    gap: 0.5rem;
    padding: 0.4rem 0.75rem;
    background: #111828;
    border-bottom: 1px solid #1e2a3a;
    font-size: 0.82rem;
}
.import-row-select-label {
    display: flex;
    align-items: center;
    gap: 0.3rem;
    cursor: pointer;
}
.import-row-num {
    font-size: 0.75rem;
    color: #5a7a9a;
    font-weight: bold;
}
.import-confidence-pct {
    font-size: 0.78rem;
    color: #8aa8c8;
    font-weight: 600;
}
.import-row-invalid-reason {
    font-size: 0.75rem;
    color: #c06060;
    margin-left: auto;
}

/* Status badge colours */
.import-status-badge {
    font-size: 0.72rem;
    padding: 0.15rem 0.45rem;
    border-radius: 4px;
    font-weight: 600;
    letter-spacing: 0.01em;
}
.import-status-proposed_exact_match  { background:#0a3a1a; color:#4caf80; border:1px solid #2a6a3a; }
.import-status-proposed_strong_match { background:#0a2a3a; color:#5ab4d0; border:1px solid #1a4a6a; }
.import-status-proposed_weak_match   { background:#2a2a0a; color:#c8b84a; border:1px solid #4a4a1a; }
.import-status-ambiguous_match       { background:#2a1a0a; color:#d08040; border:1px solid #4a2a0a; }
.import-status-manual_match_selected { background:#1a0a3a; color:#a080e0; border:1px solid #3a1a6a; }
.import-status-confirmed_correct     { background:#0a2a0a; color:#60d060; border:1px solid #1a5a1a; }
.import-status-import_as_manual_entry{ background:#1a1a2a; color:#8a9ab0; border:1px solid #2a3a4a; }
.import-status-no_match              { background:#1a1a1a; color:#6a7a8a; border:1px solid #2a2a2a; }
.import-status-invalid               { background:#2a0a0a; color:#c06060; border:1px solid #4a1a1a; }

/* Card body: two-column layout */
.import-match-card-body {
    display: flex;
    align-items: stretch;
    gap: 0;
    min-height: 90px;
}
.import-src-panel {
    flex: 1;
    display: flex;
    align-items: flex-start;
    gap: 0.6rem;
    padding: 0.6rem 0.75rem;
    border-right: 1px solid #1e2a3a;
}
.import-src-thumb {
    flex-shrink: 0;
    width: 44px;
}
.import-src-info {
    flex: 1;
    min-width: 0;
}
.import-src-title {
    font-size: 0.87rem;
    font-weight: 600;
    color: #c8d8e8;
    margin-bottom: 0.2rem;
    word-break: break-word;
}
.import-src-meta {
    display: flex;
    flex-wrap: wrap;
    gap: 0.25rem;
    margin-bottom: 0.2rem;
}
.import-src-id { font-size: 0.7rem; color: #4a6a8a; }
.import-src-row-status { margin-top: 0.3rem; }

.import-row-parse-badge {
    font-size: 0.68rem;
    padding: 0.1rem 0.35rem;
    border-radius: 3px;
    font-weight: 600;
}
.import-parse-valid   { background:#0a2a0a; color:#4caf80; }
.import-parse-invalid { background:#2a0a0a; color:#c06060; }
.import-parse-manual  { background:#1a1a2a; color:#8a9ab0; }

.import-match-arrow {
    flex-shrink: 0;
    width: 28px;
    display: flex;
    align-items: center;
    justify-content: center;
    color: #3a5a7a;
    font-size: 1.2rem;
    background: #0a1018;
}
.import-match-panel {
    flex: 1;
    padding: 0.6rem 0.75rem;
    display: flex;
    align-items: flex-start;
    gap: 0.6rem;
}
.import-match-detail {
    display: flex;
    align-items: flex-start;
    gap: 0.6rem;
}
.import-match-thumb { flex-shrink: 0; width: 44px; }
.import-match-info  { flex: 1; min-width: 0; }
.import-match-name  { font-size: 0.85rem; display: block; word-break: break-word; }
.import-match-meta  { display: flex; flex-wrap: wrap; gap: 0.2rem; margin-top: 0.2rem; }
.import-no-match-box {
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: 0.3rem;
    padding: 0.5rem;
    color: #4a5a6a;
    text-align: center;
}
.import-match-actions {
    display: flex;
    align-items: center;
    gap: 0.5rem;
    flex-wrap: wrap;
    padding: 0.4rem 0.75rem;
    background: #080e18;
    border-top: 1px solid #1a2a3a;
}
.import-warn-text { color: #c08040; }

/* Status-specific card border */
.import-ms-proposed_exact_match  { border-color: #2a5a3a; }
.import-ms-confirmed_correct     { border-color: #1a5a1a; }
.import-ms-manual_match_selected { border-color: #3a1a6a; }
.import-ms-invalid               { border-color: #4a1a1a; opacity:0.7; }
.import-ms-no_match              { border-color: #2a2a2a; }

/* Detection box */
.import-detection-box {
    font-size: 0.82rem;
    background: #0a1828;
    border: 1px solid #1a3050;
    border-radius: 5px;
    padding: 0.4rem 0.7rem;
    margin: 0.4rem 0;
}
.import-info-box {
    font-size: 0.83rem;
    color: #8aa8c8;
    background: #090f1a;
    border-left: 3px solid #2a5a8a;
    padding: 0.5rem 0.8rem;
    margin: 0.6rem 0;
    border-radius: 0 4px 4px 0;
}
.import-controls {
    display: flex;
    gap: 0.5rem;
    margin-bottom: 0.5rem;
}
.import-pending-warning {
    color: #c08040;
    font-size: 0.8rem;
    margin-top: 0.25rem;
}

/* ── Binder cover screen — sized to match binder-page-grid ── */
.binder-cover-screen {
    position: relative;
    max-width: 540px;
    margin: 0.75rem 0;
    background: #111828;
    border: 2px solid #1e2a3a;
    border-radius: 8px;
    overflow: hidden;
    aspect-ratio: 1 / 1;
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
}
.binder-cover-img {
    position: absolute;
    inset: 0;
    width: 100%;
    height: 100%;
    object-fit: cover;
    border-radius: 6px;
}
.binder-cover-default {
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: 0.5rem;
    color: #3a5a7a;
    z-index: 1;
}
.binder-cover-icon {
    font-size: 5rem;
    line-height: 1;
    opacity: 0.5;
}
.binder-cover-default-label {
    font-size: 1.1rem;
    font-weight: 600;
    letter-spacing: 0.05em;
    opacity: 0.6;
}
.binder-cover-info {
    position: absolute;
    bottom: 0;
    left: 0;
    right: 0;
    background: linear-gradient(transparent, rgba(0,0,0,0.85));
    padding: 1.5rem 1rem 0.75rem;
    z-index: 2;
    text-align: center;
}
.binder-cover-info h3 {
    margin: 0 0 0.2rem;
    font-size: 1rem;
    font-weight: 700;
    color: #e0e8f0;
}
.binder-cover-info .form-hint { color: #8aa8c8; font-size: 0.8rem; }
.binder-cover-actions {
    position: absolute;
    top: 50%;
    left: 50%;
    transform: translate(-50%, -50%);
    z-index: 3;
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: 0.5rem;
}
/* When there's a cover image, shift actions to not overlap info */
.binder-cover-screen:has(.binder-cover-img) .binder-cover-actions {
    top: 40%;
}

/* ── Set Want Binder cover-tile grid (Main ISO Binder) ── */
.iso-set-binder-grid {
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(160px, 1fr));
    gap: 1rem;
    margin: 0.75rem 0 1.5rem;
}
.iso-set-binder-card {
    background: #111828;
    border: 1px solid #1e2a3a;
    border-radius: 8px;
    overflow: hidden;
    display: flex;
    flex-direction: column;
}
.iso-set-binder-cover-link {
    display: block;
    aspect-ratio: 1 / 1;
    background: #16213e;
}
.iso-set-binder-cover-img {
    width: 100%;
    height: 100%;
    object-fit: cover;
    display: block;
}
.iso-set-binder-cover-default {
    width: 100%;
    height: 100%;
    justify-content: center;
}
.iso-set-binder-cover-default .binder-cover-icon { font-size: 3rem; }
.iso-set-binder-cover-default .binder-cover-default-label { font-size: 0.85rem; text-align: center; padding: 0 0.5rem; }
.iso-set-binder-info {
    padding: 0.5rem 0.6rem 0.25rem;
    display: flex;
    flex-direction: column;
    gap: 0.15rem;
}
.iso-set-binder-actions {
    padding: 0.5rem 0.6rem 0.6rem;
    display: flex;
    gap: 0.4rem;
}

/* Set catalog logo/symbol art and card-image cover fallbacks must never
   stretch/crop like a photo cover — center, preserve aspect ratio, pad
   inside the existing frame. Only applied when the resolved cover is a
   logo/symbol or a fallback card image, never a user-uploaded cover photo
   (which keeps the existing object-fit: cover behavior). */
.cover-fit-contain {
    object-fit: contain;
    padding: 14%;
    box-sizing: border-box;
    background: #0b1220;
}

/* ── ISO checklist AJAX status ── */
.checklist-status-ok { color: #4caf80; }


/* ── Import diagnostics panel ── */
.import-diag-details {
    border-top: 1px solid #2a3a4a;
    margin: 0.25rem 0.5rem 0;
}
.import-diag-summary {
    display: flex;
    align-items: center;
    gap: 0.5rem;
    padding: 0.3rem 0.25rem;
    font-size: 0.78rem;
    color: #8aa8c8;
    cursor: pointer;
    list-style: none;
    user-select: none;
}
.import-diag-summary::-webkit-details-marker { display: none; }
.import-diag-summary::before {
    content: '▶';
    font-size: 0.6rem;
    transition: transform 0.15s;
}
.import-diag-details[open] .import-diag-summary::before { transform: rotate(90deg); }
.import-diag-copy-btn {
    margin-left: auto;
    padding: 0.1rem 0.5rem;
    font-size: 0.72rem;
}
.import-diag-body {
    padding: 0.4rem 0.5rem 0.6rem;
    font-size: 0.78rem;
    color: #8aa8c8;
}
.import-diag-note { margin: 0.2rem 0; }
.import-diag-table {
    width: 100%;
    border-collapse: collapse;
    margin: 0.3rem 0;
    font-size: 0.75rem;
}
.import-diag-table th {
    text-align: left;
    padding: 0.15rem 0.4rem;
    border-bottom: 1px solid #2a3a4a;
    color: #6080a0;
}
.import-diag-table td {
    padding: 0.15rem 0.4rem;
    border-bottom: 1px solid #1a2a3a;
    vertical-align: top;
}
.import-diag-table tr.import-diag-zero td { color: #c04050; }
.import-diag-query { max-width: 260px; word-break: break-word; }
.import-diag-err { color: #c06060; max-width: 160px; word-break: break-word; }

/* ── Import matching progress page ─────────────────────────────────────────── */
.import-match-progress-box {
    margin: 2rem auto;
    max-width: 520px;
    background: #111c28;
    border: 1px solid #2a3a4a;
    border-radius: 8px;
    padding: 1.5rem 2rem;
}
.import-match-progress-label {
    font-size: 1rem;
    font-weight: 600;
    color: #c8d8e8;
    margin-bottom: 0.75rem;
}
.import-match-progress-bar-track {
    background: #1a2a3a;
    border-radius: 4px;
    height: 10px;
    overflow: hidden;
}
.import-match-progress-bar {
    height: 100%;
    background: #3a8fd0;
    border-radius: 4px;
    transition: width 0.4s ease;
}
.import-match-progress-sub {
    font-size: 0.78rem;
    color: #6080a0;
    margin-top: 0.5rem;
    min-height: 1.1em;
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}

/* ── Batch-level diagnostics panel ─────────────────────────────────────────── */
.import-batch-diag-details {
    margin: 0.75rem 0 1rem;
    border: 1px solid #1a2a3a;
    border-radius: 6px;
    background: #0d1825;
}
.import-batch-diag-summary {
    padding: 0.5rem 0.75rem;
    cursor: pointer;
    user-select: none;
    font-size: 0.88rem;
    color: #90aabf;
    display: flex;
    align-items: center;
    gap: 0.5rem;
}
.import-batch-diag-body {
    padding: 0.75rem 1rem;
    border-top: 1px solid #1a2a3a;
}
.batch-diag-grid {
    display: grid;
    grid-template-columns: 1fr 1fr;
    gap: 0.5rem 1.5rem;
}
.batch-diag-group {
    display: flex;
    flex-direction: column;
    gap: 0.15rem;
}
.batch-diag-label {
    font-size: 0.75rem;
    color: #6080a0;
    font-weight: 600;
    text-transform: uppercase;
    letter-spacing: 0.04em;
}
.batch-diag-value {
    font-size: 0.83rem;
    color: #c0d4e8;
}
.import-diag-warn {
    color: #e09060;
}

/* ── Quota-paused / interrupted banners ── */
.import-quota-paused-banner {
    background: #2a1e0a;
    border-left: 4px solid #c08020;
    padding: 0.75rem 1rem;
    border-radius: 4px;
    margin-bottom: 1rem;
    font-size: 0.9rem;
    color: #e0c080;
}
.import-quota-paused-banner strong {
    display: block;
    margin-bottom: 0.25rem;
    color: #f0d090;
}

.import-interrupted-banner {
    background: #1a1a2e;
    border-left: 4px solid #6060c0;
    padding: 0.75rem 1rem;
    border-radius: 4px;
    margin-bottom: 1rem;
    font-size: 0.9rem;
    color: #a0a0e0;
}
.import-interrupted-banner strong {
    display: block;
    margin-bottom: 0.25rem;
    color: #c0c0f0;
}

/* ── Catalog admin page ── */
.catalog-admin-page { margin-top: 1.5rem; }
.catalog-stats { display: flex; gap: 1rem; flex-wrap: wrap; margin: 1rem 0; }
.stat-card {
    background: var(--card-bg, #1e293b);
    border: 1px solid var(--border-color, #334155);
    border-radius: 8px;
    padding: 0.75rem 1.25rem;
    text-align: center;
    min-width: 120px;
}
.stat-value { display: block; font-size: 1.8rem; font-weight: 700; color: #60a5fa; }
.stat-label { display: block; font-size: 0.75rem; color: #94a3b8; margin-top: 0.2rem; }
.catalog-status-breakdown { display: flex; gap: 0.5rem; flex-wrap: wrap; margin: 0.5rem 0 1.5rem; }
.catalog-actions { display: flex; gap: 1rem; flex-wrap: wrap; margin-bottom: 1.5rem; }
.action-card {
    flex: 1 1 280px;
    background: var(--card-bg, #1e293b);
    border: 1px solid var(--border-color, #334155);
    border-radius: 8px;
    padding: 1rem 1.25rem;
}
.action-card h3 { margin: 0 0 0.5rem; font-size: 1rem; }
.action-card p { font-size: 0.85rem; color: #94a3b8; margin-bottom: 0.75rem; }
.inline-label { display: inline-flex; align-items: center; gap: 0.3rem; font-size: 0.85rem; margin-left: 0.5rem; }
.catalog-progress-area {
    background: #0f172a;
    border: 1px solid #334155;
    border-radius: 8px;
    padding: 1rem;
    margin-bottom: 1.5rem;
}
.progress-header { display: flex; align-items: baseline; gap: 0.5rem; margin-bottom: 0.5rem; font-size: 0.85rem; color: #94a3b8; }
.progress-bar-wrap { background: #1e293b; border-radius: 4px; height: 8px; overflow: hidden; margin-bottom: 0.5rem; }
.progress-bar { height: 100%; background: #3b82f6; transition: width 0.4s ease; border-radius: 4px; }
.catalog-table-wrap { overflow-x: auto; }
.catalog-table { width: 100%; border-collapse: collapse; font-size: 0.85rem; }
.catalog-table th, .catalog-table td { padding: 0.4rem 0.6rem; border-bottom: 1px solid #334155; text-align: left; }
.catalog-table th { color: #94a3b8; font-weight: 600; }
.catalog-row.status-complete { opacity: 0.75; }
.status-pill {
    display: inline-block;
    padding: 0.1rem 0.4rem;
    border-radius: 4px;
    font-size: 0.72rem;
    font-weight: 600;
    text-transform: uppercase;
}
.status-complete   { background: #064e3b; color: #6ee7b7; }
.status-partial    { background: #78350f; color: #fcd34d; }
.status-not_synced { background: #1e3a5f; color: #93c5fd; }
.status-unknown    { background: #374151; color: #d1d5db; }
.status-failed     { background: #7f1d1d; color: #fca5a5; }
.status-syncing    { background: #1d3f6f; color: #67e8f9; }

/* ── ISO Master Set Binder — 9-card framed viewport ── */
/* Wraps the checklist grid so exactly 3 rows (9 cards) are visible;
   controls stay above the scroll area. */
.checklist-viewport {
    max-height: 440px;
    overflow-y: auto;
    border: 1px solid #334155;
    border-radius: 6px;
    background: #0f172a;
    padding: 0.5rem;
    margin: 0.5rem 0 0.75rem;
    scrollbar-width: thin;
    scrollbar-color: #475569 #0f172a;
}
.checklist-viewport .tile-image-wrap {
    max-height: 80px;
}

/* ── ISO Master Set Binder — 3-card checklist grid ── */
.checklist-card-grid {
    display: grid;
    grid-template-columns: repeat(3, 1fr);
    gap: 0.6rem;
    margin-top: 0.75rem;
}
@media (max-width: 768px) {
    .checklist-card-grid { grid-template-columns: repeat(2, 1fr); }
}
@media (max-width: 480px) {
    .checklist-card-grid { grid-template-columns: 1fr; }
}

.checklist-tile {
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: 0.25rem;
    border: 2px solid #334155;
    border-radius: 8px;
    padding: 0.5rem 0.35rem;
    cursor: pointer;
    user-select: none;
    background: #1e293b;
    transition: border-color 0.12s, background 0.12s;
    position: relative;
    text-align: center;
}
.checklist-tile:hover { border-color: #60a5fa; background: #1e2d40; }
.checklist-tile.is-selected { border-color: #3b82f6; background: #172554; }
.checklist-tile input[type="checkbox"] {
    position: absolute;
    top: 0.35rem;
    left: 0.35rem;
    width: 1rem;
    height: 1rem;
    cursor: pointer;
    z-index: 1;
    accent-color: #3b82f6;
}
.tile-image-wrap {
    width: 100%;
    aspect-ratio: 63/88;
    display: flex;
    align-items: center;
    justify-content: center;
    background: #0f172a;
    border-radius: 4px;
    overflow: hidden;
    margin-top: 0.5rem;
}
.tile-image-wrap img {
    width: 100%;
    height: 100%;
    object-fit: contain;
    display: block;
}
.tile-num {
    font-size: 0.68rem;
    color: #94a3b8;
    font-weight: 600;
    margin-top: 0.2rem;
}
.tile-name {
    font-size: 0.75rem;
    font-weight: 600;
    color: #e2e8f0;
    line-height: 1.25;
    word-break: break-word;
}
.tile-rarity {
    font-size: 0.62rem;
    color: #64748b;
}

/* ── ISO Master Set Binder — match preview paired layout ── */
.iso-match-pair {
    border: 1px solid #334155;
    border-radius: 8px;
    background: #1e293b;
    padding: 0.75rem;
}
.pair-matched { border-color: #15803d; background: #052e16; }
.pair-unmatched { border-color: #991b1b; background: #1c0a09; }
.pair-header {
    display: flex;
    align-items: center;
    gap: 0.5rem;
    margin-bottom: 0.6rem;
}
.pair-columns {
    display: flex;
    gap: 0.75rem;
    align-items: flex-start;
}
.pair-side {
    flex: 1;
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: 0.3rem;
}
.pair-side-label {
    font-size: 0.7rem;
    color: #64748b;
    text-transform: uppercase;
    letter-spacing: 0.04em;
    font-weight: 600;
}
.pair-thumb {
    width: 64px;
    height: 89px;
    background: #0f172a;
    border-radius: 4px;
    overflow: hidden;
    display: flex;
    align-items: center;
    justify-content: center;
    flex-shrink: 0;
}
.pair-thumb img {
    width: 100%;
    height: 100%;
    object-fit: contain;
}
.pair-card-info {
    width: 100%;
    font-size: 0.8rem;
    display: flex;
    flex-direction: column;
    gap: 0.15rem;
}
.pair-card-name { font-weight: 600; color: #e2e8f0; }
.pair-card-set  { color: #94a3b8; font-size: 0.75rem; }
.pair-card-meta { color: #64748b; font-size: 0.72rem; }
.pair-price {
    margin-top: 0.25rem;
    font-size: 0.8rem;
    color: #6ee7b7;
}
.pair-reasons { margin-top: 0.2rem; display: flex; flex-wrap: wrap; gap: 0.15rem; }
.pair-arrow {
    flex-shrink: 0;
    font-size: 1.2rem;
    color: #475569;
    padding-top: 3rem;
}

/* ── Bulk select / bulk remove (Binder, Main ISO, Set Want Binder lists) ── */
.bulk-select-bar {
    display: flex;
    align-items: center;
    gap: 0.75rem;
    flex-wrap: wrap;
    background: #16213e;
    border: 1px solid #2a2a4a;
    border-radius: 7px;
    padding: 0.6rem 1rem;
    margin-bottom: 0.9rem;
}
.bulk-select-count {
    font-weight: 600;
    color: #cbd5e1;
    margin-right: 0.25rem;
}
.item-card-select {
    display: none;
    align-items: center;
    justify-content: center;
    padding: 0.4rem 0.8rem 0;
    cursor: pointer;
}
.bulk-select-checkbox {
    display: none;
    width: 16px;
    height: 16px;
    cursor: pointer;
}
.item-row .bulk-select-checkbox {
    margin-right: 0.5rem;
}
/* Mass Remove is an explicit toggled mode — checkboxes stay hidden and normal
   per-card actions stay visible until the list container gets this class. */
.item-list.mass-remove-mode .item-card-select {
    display: flex;
}
/* Normal Binder list wraps its checkbox in a .item-card-select label rather
   than a .item-row (Main ISO / Set Want Binder use .item-row directly) — the
   checkbox itself still needs its own reveal rule for that shape, or it stays
   display:none even once its label wrapper becomes visible. */
.item-list.mass-remove-mode .item-card-select .bulk-select-checkbox,
.item-list.mass-remove-mode .item-row .bulk-select-checkbox {
    display: inline-block;
}
.item-list.mass-remove-mode .item-card-actions,
.item-list.mass-remove-mode .item-row-actions {
    display: none;
}
/* Mass Remove: whole card/row becomes the selection target — clicking it
   toggles the checkbox instead of navigating (see bulk_select.js). */
.item-list.mass-remove-mode .item-card-wrap,
.item-list.mass-remove-mode .item-row {
    cursor: pointer;
}
.item-list.mass-remove-mode .item-card-wrap.is-selected,
.item-list.mass-remove-mode .item-row.is-selected {
    border-color: #3b82f6;
    background: #172554;
}
.list-toolbar {
    margin-bottom: 0.75rem;
}
.mass-remove-trigger[hidden] {
    display: none;
}

/* ── Modal / dialog (Manual Match, and any future .modal-overlay use) ──────
   The markup (role="dialog" aria-modal="true", .modal-overlay wrapper,
   .modal-close-btn) was always written as a modal, but this stylesheet
   never had a single rule for these classes — with zero CSS, toggling
   style.display = 'flex' just placed the wrapper as a normal in-flow block
   wherever it sits in the DOM (the very end of the page body), so it
   rendered as an oddly-sized panel at the bottom of the page instead of
   an overlay. This is the fix, not a new component. */
.modal-overlay {
    position: fixed;
    inset: 0;
    z-index: 2000;
    background: rgba(10, 12, 20, 0.72);
    align-items: center;
    justify-content: center;
    padding: 1.5rem;
}
.modal-box {
    background: #16213e;
    border: 1px solid #334155;
    border-radius: 10px;
    box-shadow: 0 12px 40px rgba(0, 0, 0, 0.5);
    width: min(640px, 100%);
    max-height: min(80vh, 800px);
    display: flex;
    flex-direction: column;
    overflow: hidden;
}
.modal-header {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 1rem;
    padding: 1rem 1.25rem;
    border-bottom: 1px solid #334155;
    flex-shrink: 0;
}
.modal-header h3 {
    margin: 0;
    font-size: 1.05rem;
}
.modal-close-btn {
    background: none;
    border: none;
    color: #e0e0e0;
    font-size: 1.4rem;
    line-height: 1;
    cursor: pointer;
    padding: 0.15rem 0.5rem;
    border-radius: 6px;
}
.modal-close-btn:hover {
    background: #1c2b3a;
}
.modal-body {
    padding: 1rem 1.25rem;
    overflow-y: auto;
    flex: 1 1 auto;
}

/* Manual Match candidate list — image + name + set + collector number +
   price + match reasons per row, so distinct printings (e.g. the four
   Obsidian Flames Charizard ex cards) stay visually distinguishable
   instead of collapsing into indistinct rows of text. */
.manual-match-candidate-list {
    display: flex;
    flex-direction: column;
    gap: 0.6rem;
    margin-top: 0.75rem;
}
.manual-match-candidate {
    display: flex;
    align-items: center;
    gap: 0.75rem;
    padding: 0.6rem;
    border: 1px solid #334155;
    border-radius: 8px;
    background: #1c2b3a;
}
.manual-match-thumb {
    flex-shrink: 0;
    width: 56px;
    height: 56px;
}
.manual-match-candidate-info {
    flex: 1 1 auto;
    min-width: 0;
}
.manual-match-candidate-info strong {
    display: block;
    margin-bottom: 0.2rem;
}
.manual-match-select-btn {
    flex-shrink: 0;
}

/* Development-only mobile diagnostics page (Part G). */
.mobile-diag-output {
    background: #0d1018;
    border: 1px solid #2a3a5a;
    border-radius: 6px;
    padding: 0.85rem;
    font-size: 0.82rem;
    line-height: 1.5;
    white-space: pre-wrap;
    word-break: break-word;
    color: #cfd8e6;
    max-width: 100%;
    overflow-x: auto;
}

/* Development-only "Mobile UI Debug" HUD badge (Part K) — injected by
   mobile_ui_debug.js only when base.html's mobile_dev_mode global is true.
   Fixed corner pill, above everything else, but small and out of the way
   of any real control; pointer-events limited to the badge itself so it
   never blocks page interaction underneath it. */
.mobile-ui-debug-badge {
    position: fixed;
    bottom: 0.5rem;
    right: 0.5rem;
    z-index: 9999;
    background: rgba(10, 12, 20, 0.88);
    color: #7ee787;
    border: 1px solid #2a3a5a;
    border-radius: 999px;
    padding: 0.3rem 0.7rem;
    font-size: 0.72rem;
    font-family: monospace;
    cursor: pointer;
    user-select: none;
    pointer-events: auto;
    white-space: nowrap;
}
.mobile-ui-debug-badge.mobile-ui-debug-overflow { color: #ff6b6b; border-color: #6a2a2a; }
.mobile-ui-debug-badge.mobile-ui-debug-collapsed { padding: 0.3rem 0.5rem; font-size: 0.9rem; }

/* ── My Trade Chats (trade-chat-history workstream) ────────────────────── */

.trade-chats-intro { color: #a8a8c8; font-size: 0.9rem; margin: 0.25rem 0 1rem 0; max-width: 60ch; }

.form-success-banner {
    background: #16321f;
    border-left: 4px solid #3fbf6f;
    color: #9be8b4;
}

.trade-chats-filters {
    display: flex;
    gap: 0.4rem;
    margin-bottom: 1rem;
}

.trade-empty-state { color: #a8a8c8; padding: 1.5rem 0; }

.trade-chats-list {
    display: flex;
    flex-direction: column;
    gap: 0.75rem;
}

.trade-chat-card-body { padding: 0.85rem 1rem 0.4rem 1rem; }

.trade-chat-status-badge {
    font-size: 0.75rem;
    font-weight: 700;
    letter-spacing: 0.03em;
    padding: 0.15rem 0.5rem;
    border-radius: 4px;
}
.trade-chat-status-open   { background: #16321f; color: #6fe89b; }
.trade-chat-status-closed { background: #2a2a3a; color: #a8a8c8; }

.trade-chat-meta { color: #a8a8c8; font-size: 0.85rem; }
.trade-chat-chatid { font-family: monospace; font-size: 0.8rem; color: #6b6b8a; }

.trade-chat-actions { flex-wrap: wrap; }

.trade-chat-report-details,
.trade-chat-close-details { position: relative; }
.trade-chat-report-details > summary,
.trade-chat-close-details > summary { list-style: none; cursor: pointer; }
.trade-chat-report-details > summary::-webkit-details-marker,
.trade-chat-close-details > summary::-webkit-details-marker { display: none; }
.trade-chat-report-form,
.trade-chat-close-form {
    display: flex;
    flex-direction: column;
    gap: 0.4rem;
    margin-top: 0.5rem;
    padding: 0.75rem;
    background: #16213e;
    border: 1px solid #2a2a4a;
    border-radius: 6px;
    min-width: 240px;
}
.trade-chat-report-form label { font-size: 0.8rem; color: #a8a8c8; }
.trade-chat-report-form select,
.trade-chat-report-form textarea {
    background: #0d1326;
    border: 1px solid #2a2a4a;
    border-radius: 4px;
    color: #fff;
    padding: 0.4rem;
    font-size: 0.85rem;
}

.trade-chat-close-warning {
    font-size: 0.8rem;
    color: #a8a8c8;
    margin: 0;
    max-width: 260px;
}
.trade-chat-close-confirm-row {
    display: flex;
    gap: 0.5rem;
}

.trade-chat-start-new-form { margin: 0; }

.trade-chats-pagination {
    display: flex;
    justify-content: space-between;
    margin-top: 1rem;
}

@media (max-width: 600px) {
    .trade-chat-report-form,
    .trade-chat-close-form { min-width: 0; }
}

/* View Chat History (trade-chat-history workstream, View Chat History
   correction pass) */
.trade-chat-transcript-header { margin-bottom: 1rem; }
.trade-chat-transcript-closed-note {
    font-size: 0.85rem;
    color: #a8a8c8;
    margin: 0.5rem 0 0 0;
}

.trade-chat-transcript-messages {
    display: flex;
    flex-direction: column;
    gap: 0.6rem;
}
.trade-chat-transcript-message {
    padding: 0.6rem 0.9rem;
    background: #16213e;
    border: 1px solid #2a2a4a;
    border-radius: 6px;
}
.trade-chat-transcript-message-meta {
    display: flex;
    align-items: center;
    gap: 0.5rem;
    margin-bottom: 0.25rem;
    font-size: 0.85rem;
}
.trade-chat-transcript-tag {
    font-size: 0.7rem;
    color: #e6b85c;
    border: 1px solid #e6b85c;
    border-radius: 4px;
    padding: 0.05rem 0.35rem;
}
.trade-chat-transcript-message-body {
    color: #e6e6f0;
    white-space: pre-wrap;
    word-break: break-word;
}
.trade-chat-transcript-attachments {
    margin-top: 0.35rem;
    display: flex;
    flex-direction: column;
    gap: 0.15rem;
}
.trade-chat-transcript-attachment { font-size: 0.85rem; }
