/* ux-pass.css: Error pages, breadcrumbs, explainer page shell, listing terms, account-menu wallet, header overflow and the phone wallet. */
/* ============================================================
   UX PASS: error pages, breadcrumbs, listing surfaces, header
   overflow containment. Tokens only; no new colours.
   ============================================================ */

/* ---- Branded error / recovery pages (404, 403, 500, listing-gone) ---- */
.error-page {
    max-width: 60ch;
    margin: clamp(24px, 8vh, 80px) auto;
    text-align: center;
}
.error-page__glyph { font-size: 3.4rem; line-height: 1; margin: 0 0 10px; }
.error-page__body { color: var(--text-secondary); margin: 12px 0 24px; }
.error-page__actions { display: flex; flex-wrap: wrap; gap: 10px; justify-content: center; }

/* ---- Breadcrumbs: contextual back-navigation on detail/help pages ---- */
.breadcrumbs {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 8px;
    margin: 0 0 12px;
    font-family: var(--font-heading);
    font-size: 0.82rem;
    color: var(--text-muted);
}
.breadcrumbs a { color: var(--text-secondary); }
@media (hover: hover) { .breadcrumbs a:hover { color: var(--text-primary); } }
.breadcrumbs [aria-current="page"] { color: var(--text-primary); font-weight: 600; }

/* ---- Shared shell for the "explainer" pages: breeding_help, world, trait_codex,
   mutation_codex, incursion_help. Each of these five templates used to carry its own
   copy-pasted <style> block (never migrated here, against the house "no inline styles"
   rule), so a fix to one never reached the other four: world.html had already dropped
   the `max-width: 70ch` below at some point, while the other four still had prose
   sitting far short of its own bordered .breeding-help-section box. One copy now. ---- */
.breeding-help-page { max-width: 900px; margin: 0 auto; padding: 24px 16px; }
.breeding-help-header { margin-bottom: 32px; }
.breeding-help-header h1 { font-size: 1.8rem; margin-bottom: 8px; }
.breeding-help-intro { color: var(--text-secondary); line-height: 1.6; }
.breeding-help-sections { display: flex; flex-direction: column; gap: 32px; }
.breeding-help-section {
    background: var(--bg-surface);
    border: 1px solid var(--border-color);
    border-radius: var(--radius-md);
    padding: 20px 24px;
    scroll-margin-top: 16px;
}
.breeding-help-section h2 {
    font-size: 1.1rem;
    font-weight: 600;
    color: var(--text-primary);
    margin-bottom: 16px;
    padding-bottom: 8px;
    border-bottom: 1px solid var(--border-subtle);
}
.breeding-help-subhead {
    font-size: 1rem;
    font-weight: 600;
    color: var(--text-primary);
    margin: 20px 0 8px;
}
.breeding-help-table { width: 100%; border-collapse: collapse; font-size: 0.9rem; }
.breeding-help-table th {
    text-align: left;
    padding: 6px 10px;
    color: var(--text-secondary);
    font-weight: 600;
    border-bottom: 1px solid var(--border-subtle);
}
.breeding-help-table td {
    padding: 8px 10px;
    border-bottom: 1px solid var(--border-subtle);
    vertical-align: top;
}
.breeding-help-table tr:last-child td { border-bottom: none; }
.breeding-help-desc {
    font-size: 0.92rem;
    color: var(--text-secondary);
    line-height: 1.6;
    margin-bottom: 12px;
}
.breeding-help-note { margin-top: 12px; font-size: 0.85rem; color: var(--text-secondary); line-height: 1.6; }
/* Page-closing note that sits outside .breeding-help-sections, so it does not inherit that
   container's 32px gap and needs its own separation. Replaces an inline style on world.html. */
.breeding-help-footer { margin-top: 24px; }
.breeding-help-steps,
.breeding-help-list {
    margin: 0;
    padding-left: 20px;
    color: var(--text-secondary);
    font-size: 0.92rem;
    display: flex;
    flex-direction: column;
    gap: 8px;
}
.breeding-help-steps strong,
.breeding-help-list strong,
.breeding-help-note strong,
.breeding-help-desc strong,
.breeding-help-table strong { color: var(--text-primary); }

.trait-codex-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(240px, 1fr)); gap: 12px; }
.trait-codex-grid .trait-card { margin-top: 0; }

/* Public persona directory: a compact reference card around the shared portrait
   partial, with the real game surface kept as the primary action. */
.known-characters-grid {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(min(100%, 250px), 1fr));
    gap: 16px;
}
.known-character-card {
    display: flex;
    flex-direction: column;
    gap: 14px;
    min-width: 0;
    padding: 16px;
    border: 1px solid var(--border-subtle);
    border-radius: var(--radius-md);
    background: var(--bg-subtle);
}
.known-character-card__persona { display: flex; flex-direction: column; gap: 12px; }
.known-character-card__portrait {
    display: flex;
    align-items: center;
    justify-content: center;
    aspect-ratio: 4 / 3;
    min-height: 150px;
    overflow: hidden;
    border: 1px solid var(--border-subtle);
    border-radius: var(--radius-md);
    background: color-mix(in srgb, var(--color-primary) 7%, var(--bg-surface));
}
.known-character-card__art { object-fit: contain !important; }
.known-character-card__placeholder { font-size: 3rem; }
.known-character-card__body { display: flex; flex-direction: column; gap: 3px; }
.known-character-card__name { margin: 0; color: var(--text-primary); font-family: var(--font-heading); font-weight: 700; }
.known-character-card__role { margin: 0; color: var(--text-secondary); font-size: 0.82rem; }
.known-character-card__line { margin: 8px 0 0; color: var(--text-muted); font-size: 0.82rem; line-height: 1.5; }
.known-character-card__species { margin: 0; color: var(--text-muted); font-size: 0.78rem; }
.known-character-card__actions { display: flex; flex-direction: column; gap: 8px; margin-top: auto; }
.known-character-card__actions .btn { width: 100%; min-height: 44px; }
.known-character-card__story { text-align: center; }

/* Roomier than the Workshop's 88px picker grid: this page is for looking at the art,
   not for hitting a target in a form beside a live preview. */
.mutation-codex-section .mw-gallery-grid { grid-template-columns: repeat(auto-fill, minmax(128px, 1fr)); }
.mutation-codex-section .mw-gallery-group + .mw-gallery-group { margin-top: 20px; }
.mutation-codex-card {
    margin: 0;
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: 6px;
    padding: 6px 6px 8px;
    border: 1px solid var(--border-subtle);
    border-radius: var(--radius-md);
    background: var(--bg-subtle);
    text-align: center;
}

/* ---- Listing terms: flat price vs accepting-offers, told apart by shape and
   label as well as colour (a bare number and a bare word looked interchangeable). ---- */
.listing-terms {
    display: inline-flex;
    align-items: center;
    flex-wrap: wrap;
    gap: 6px;
    padding: 4px 10px;
    border-radius: var(--radius-pill);
    border: 1px solid var(--border-color);
    background: var(--bg-subtle);
    font-size: 0.82rem;
}
.listing-terms__label {
    font-family: var(--font-mono);
    font-size: 0.64rem;
    font-weight: 700;
    text-transform: uppercase;
    letter-spacing: 0.06em;
    color: var(--text-muted);
}
.listing-terms--flat { border-color: color-mix(in srgb, var(--color-reward) 45%, var(--border-color)); }
.listing-terms--flat .listing-price { color: var(--color-reward); font-weight: 700; }
/* Dashed edge for offer mode: "no fixed price" reads as an unsettled boundary. */
.listing-terms--offer { border-style: dashed; }
.listing-terms--offer .listing-price { color: var(--text-secondary); }
/* Double edge for an auction: a price that is still moving, with its clock beside it. */
.listing-terms--auction { border-style: double; border-width: 3px; border-color: var(--color-info-border); }
.listing-terms--auction .listing-price { font-weight: 700; }
.listing-terms__ends { font-size: 0.75rem; color: var(--text-secondary); }
.auction-summary p { margin: 0 0 6px; }
.auction-summary__rule { font-size: 0.85rem; }
.listing-terms__or { color: var(--text-muted); font-size: 0.75rem; }

/* A sub-heading inside a panel, below the h2 .panel-title. */
.section-heading {
    font-family: var(--font-heading);
    font-weight: 700;
    font-size: 0.95rem;
    margin: 4px 0 10px;
}

.listing-card { display: flex; flex-direction: column; gap: 8px; }
.listing-card__seller { color: var(--text-muted); font-size: 0.8rem; margin: 0; }
.listing-card__cta { margin-top: auto; text-align: center; }
@media (hover: hover) { .listing-card__cta:hover { text-decoration: none; } }

.listing-row { display: flex; flex-wrap: wrap; align-items: center; gap: 10px; }

/* Result count above a filtered grid. role="status" in the markup, so this is only the
   visual treatment. */
.results-count {
    margin: 0 0 12px;
    color: var(--text-secondary);
    font-family: var(--font-mono);
    font-size: 0.78rem;
    letter-spacing: 0.02em;
}

.listing-detail { display: grid; grid-template-columns: 1fr; gap: 18px; align-items: start; }
@media (min-width: 768px) {
    .listing-detail { grid-template-columns: 1fr 1fr; }
}
.listing-detail__media { text-align: center; }
.listing-detail__media img { max-width: 100%; height: auto; }
.listing-detail__facts { list-style: none; padding: 0; margin: 14px 0 0; display: grid; gap: 10px; }
.listing-detail__facts li {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 8px;
    padding-bottom: 9px;
    border-bottom: 1px solid var(--border-subtle);
}
.listing-detail__facts li:last-child { border-bottom: 0; padding-bottom: 0; }
.listing-detail__facts .eyebrow { flex: 0 0 7.5rem; }
.listing-detail__note {
    margin: 14px 0 0;
    padding: 10px 14px;
    border-left: 3px solid var(--border-color);
    color: var(--text-secondary);
    font-style: italic;
}

/* ---- Account-menu wallet: the exact balances, in full. The header chips are
   compact and their exact value lives in a title, which a touch device cannot
   show; this block is where those users read the real numbers. ---- */
.account-menu-wallet { display: grid; gap: 5px; padding: 8px 12px 10px; }
.account-menu-wallet__row {
    display: flex;
    justify-content: space-between;
    gap: 14px;
    font-size: 0.84rem;
    color: var(--text-secondary);
}
.account-menu-wallet__row .mono { color: var(--text-primary); font-weight: 700; }

/* ---- Header overflow containment ----
   The header is a flex row whose items all carry white-space: nowrap. Flex items
   default to min-width: auto, so none of them would shrink below their content
   width and the row pushed the whole document wider than the viewport (measured
   375 client / 662 scroll at a 390px viewport). Three parts to the fix:
     1. min-width: 0 on the shrinkable ancestors, so shrinking is permitted at all;
     2. the currency cluster scrolls *within itself* rather than pushing the page,
        and is the only item allowed to shrink (brand, bell and avatar stay put);
     3. compact number formatting server-side, so it rarely needs to scroll.
   overflow-x on the cluster is a containment backstop, not the mechanism: nothing
   is hidden, and every balance is also listed in full in the account menu. */
.site-header { min-width: 0; }
.site-nav-right { min-width: 0; }
.currency-cluster {
    min-width: 0;
    flex: 0 1 auto;
    overflow-x: auto;
    scrollbar-width: none;
    -webkit-overflow-scrolling: touch;
    scroll-padding: 2px;
}
.currency-cluster::-webkit-scrollbar { display: none; }
/* Each chip must establish a containing block. `.sr-only` is `position: absolute` with
   no left/top, so it sits at its static position, and its containing block was
   `.site-header` (`position: sticky`, therefore positioned) rather than this cluster.
   A positioned descendant whose containing block sits outside a scroll container is NOT
   clipped by it, so the four 1px exact-value spans escaped to their static positions in
   the cluster's unclipped content: the last landed near x=441 on a 390px phone and
   extended the document's scrollable width to 442. Every authenticated page then laid
   out 52px wider than the screen and rendered shrunk. Nothing VISIBLE overflowed (the
   chips themselves are clipped and scrollable here), and `body { overflow-x: clip }`
   suppressed the sideways scroll, which is why a 1px hidden span survived several
   releases as a global mobile layout break. Do not remove: `position: relative` here is
   what keeps those spans inside the clip. */
.currency-cluster .nav-currency { position: relative; }
.site-brand,
.notif-bell,
.account-menu { flex: 0 0 auto; }
/* Backstop so no future regression elsewhere can widen the document itself. `clip`,
   not `hidden`: `overflow-x: hidden` makes the element a scroll container, which breaks
   `position: sticky` on .site-header. `clip` constrains without that side effect. This is
   a safety net behind the three real fixes above, not the fix. */
body { overflow-x: clip; }

/* Background scroll lock while a modal is open (alpine-components.js::modalShell adds
   and removes this class). Without it a swipe aimed at the hatch reveal scrolls the
   forge behind it, which on a phone is how the payoff moment of the breeding loop got
   dismissed by accident.

   `overflow: hidden` (both axes), NOT the `clip` the rule above uses, and the difference
   is worth stating because the two rules look contradictory. The rule above sets
   overflow-x ALONE, and a single hidden axis forces the other to compute to `auto`,
   which makes <body> a real scroll container and breaks position: sticky on .site-header;
   `clip` carries no such side effect, which is why it is used there. Here both axes are
   set at once, so nothing computes to auto: the value propagates to the viewport and
   <body>'s own used overflow stays `visible`, leaving sticky intact and the scroll
   offset preserved. `clip` would be the riskier pick here, since a non-scrollable
   viewport has nowhere to keep the offset the player scrolls back to on close.

   The padding replaces the scrollbar the lock removes, or the whole page shifts sideways
   under the modal on a mouse device; --scrollbar-gap is measured in JS and is 0 on
   mobile overlay scrollbars. */
body.modal-open {
    overflow: hidden;
    padding-right: var(--scrollbar-gap, 0px);
}

/* ---- Tighter header on small screens ---- */
/* Header condense: below the tablet line the nav-right set (weather + 3 currency
   pills + bell + name + avatar) crowds the bar, so drop the two least-critical
   items (weather, display name) and tighten the currency pills. Tablets ≥768 have
   room for the full set. */
@media (width < 768px) {
    .site-brand { font-size: 1rem; }
    .weather-chip { display: none; }
    .account-name { display: none; }
    .currency-cluster .nav-currency { padding: 5px 8px; font-size: 0.74rem; }
}
/* Smallest phones (--bp-xxs): the brand drops its " Pets" suffix and the header tightens.
   Measured budget at 320px: brand 145 + nav-right 139 + gaps needed 364px against a 320px
   screen. Dropping the suffix reclaims ~45px, which is what actually closes it; shrinking
   the currency pills alone could not (there are only two chips left down here). */
@media (max-width: 380px) {
    .currency-cluster { gap: 5px; }
    .currency-cluster .nav-currency { padding: 4px 6px; font-size: 0.7rem; }
    .site-header { gap: 8px; padding: 0 12px; }
    .site-brand__suffix { display: none; }
}

/* ---- Phone wallet: two chips + a ＋3 opener (--bp-xs) ----
   Separate concern from the phantom-width fix above. Even correctly clipped, five chips
   in a ~92px scrollable strip means a player swipes a 44px-tall box to read their own
   Glimmer, and most of the five balances are off-screen at rest. So below this line the
   cluster carries the two currencies a player actually spends day-to-day and the ＋3
   button opens the account menu's Resources block, which lists all five in full.

   The hidden chips stay in the DOM (display: none, not removed): htmx-init.js's
   currency-updated listener resolves every chip by `#nav-<CURRENCY_FIELDS name>` and must
   still find them, or a spend would leave a stale figure behind the ＋3.

   This opener is ONLY the 480-900px tier's own control now (below). At 480px and under, the
   header has nowhere to put it at all: see the "no opener down here" block below for why, and
   for the actual header-overlap fix. */
.nav-currency-more { display: none; }
.nav-currency-more__label { display: none; }
@media (min-width: 480px) and (max-width: 900px) {
    .currency-cluster .nav-currency--glimmer,
    .currency-cluster .nav-currency--bloodline_essence,
    .currency-cluster .nav-currency--star_steel { display: none; }
    .nav-currency-more__4 { display: none; }
    .nav-currency-more {
        flex: 0 0 auto;
        display: inline-flex;
        align-items: center;
        justify-content: center;
        min-width: 38px;
        min-height: 32px;
        padding: 4px 8px;
        border: 1px dashed var(--border-color);
        border-radius: var(--radius-pill);
        background: var(--bg-subtle);
        color: var(--text-secondary);
        font-family: var(--font-mono);
        font-size: .72rem;
        font-weight: 700;
        cursor: pointer;
    }
    @media (hover: hover) { .nav-currency-more:hover { color: var(--text-primary); border-style: solid; } }
}
@media (max-width: 480px) {
    .currency-cluster .nav-currency--stardust,
    .currency-cluster .nav-currency--bloodline_essence,
    .currency-cluster .nav-currency--star_steel { display: none; }
    /* The brand loses its " Pets" suffix at the phone line, not just below 380px: every
       byte matters once the wallet is fighting the logo for room down here. */
    .site-brand__suffix { display: none; }
    /* The cluster must stop shrinking down here, or the flex row compresses it to ~35px and
       clips the second chip: the whole point of this tier is two VISIBLE balances, and with
       `flex: 0 1 auto` the player saw one. Never solve that with `overflow-x: visible`
       instead: the cluster would then spill its chips across the bell and the account
       avatar, which swallows every tap on them. Tighter gap/padding than the 768px tier
       above claws back a little more room without touching either chip's legibility. */
    .currency-cluster { flex: 0 0 auto; }
    .currency-cluster { gap: 4px; }
    .currency-cluster .nav-currency { padding: 5px 6px; }
    .site-nav-right { gap: 6px; }
    /* No opener down here, at any width in this tier: NOT a smaller/repositioned copy of
       the 480-900px one above, gone outright. Real measurement at 390px (an iPhone
       12/13/14-width phone, squarely inside the intended phone range) found the two-chip
       wallet's own fixed width, plus a 44px opener, plus the bell and the avatar, adding up
       to more than site-nav-right actually has: with justify-content: flex-end the overflow
       spills off the START of the row instead of clipping, so the wallet rendered ON TOP OF
       the site-brand logo. The opener's only job is to open the account menu's Resources
       block (see the comment above) — the avatar button one tap to its right opens that
       exact same view, so dropping the opener loses no capability, only a redundant second
       way to reach it, and reclaims the ~60px the fix actually needed. Confirmed via
       real getBoundingClientRect() measurement, not arithmetic (the comment this replaced
       had done the arithmetic and gotten a materially different, wrong, answer): >=13px of
       slack left over at 390px, and the same fix independently closes a second, deeper
       overflow this tier had at 320px (single-chip wallet still didn't fit before this). */
    .nav-currency-more { display: none; }
}
/* Narrowest phones: ONE balance instead of two. Glimmer stays because it is the everyday
   spending currency. The ＋N opener is already gone site-wide at this width (see above), so
   there is no label to swap here anymore: this block is purely which CHIP survives. */
@media (max-width: 380px) {
    .currency-cluster .nav-currency--prisms { display: none; }
}

