/* LFG Discord Activity — brand-kit theme (v3 "sticker" direction).
   Dark shell, restrained sticker treatment on key elements only:
   the hub card, primary CTAs, QR codes, and NFT art. Fonts are
   self-hosted in fonts/ (the Activity CSP blocks third-party origins). */

:root {
  /* Primary accents (sampled from logo assets) */
  --orange: #D89030;
  --red: #D84830;
  --blue: #4890C0;
  --yellow: #F0D848;
  --green: #3DA35D;
  --purple: #601878;

  /* Structure + surfaces */
  --ink: #0A0A0A;
  --paper: #FFFFFF;
  --bg: #0A0A0A;
  --surface: #181818;
  --surface-2: #222;
  --line: #2C2C2C;
  --text: #F5F4F1;
  --muted: #9C9A94;

  /* Signature: the sticker frame */
  --sticker: 0 0 0 3px var(--paper), 5px 5px 0 var(--ink);
  --sticker-sm: 0 0 0 2px var(--paper), 3px 3px 0 var(--ink);
  --sticker-pressed: 0 0 0 2px var(--paper), 1px 1px 0 var(--ink);
  --radius: 18px;
  --speed: 180ms;
}

* { box-sizing: border-box; }

/* Author display rules below (e.g. .qr { display:block }) would otherwise
   beat the UA stylesheet's [hidden] rule. */
[hidden] { display: none !important; }

body {
  margin: 0;
  min-height: 100vh;
  background: radial-gradient(120% 80% at 50% -10%, #14110d 0%, var(--bg) 55%);
  color: var(--text);
  font-family: 'Inter', system-ui, sans-serif;
  line-height: 1.5;
  display: flex;
  justify-content: center;
  -webkit-font-smoothing: antialiased;
}

#app {
  width: 100%;
  max-width: 440px;
  /* Clear notches and Discord's overlaid activity controls (issue #23) */
  padding: calc(28px + env(safe-area-inset-top, 0px)) 16px 64px;
  text-align: center;
}

h1, h2, h3 { font-family: 'Fredoka', sans-serif; line-height: 1.1; }
code, .mono { font-family: 'JetBrains Mono', monospace; }

/* --- Header / lockup --- */

.app-header { margin-bottom: 10px; }

.lockup {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 12px;
  margin-bottom: 6px;
}
/* Brand wordmark image; the .wordmark text below is the fallback when
   assets/logo.png is missing or fails to load. */
.wordmark-img {
  height: 56px;
  width: auto;
  max-width: 220px;
  object-fit: contain;
}

.wordmark {
  margin: 0;
  font-weight: 700;
  font-size: 2.5rem;
  letter-spacing: .01em;
}
.wordmark span {
  -webkit-text-stroke: 2.5px var(--ink);
  paint-order: stroke fill;
}
.wordmark .l { color: var(--orange); }
.wordmark .f { color: var(--red); }
.wordmark .g { color: var(--blue); }

.status {
  color: var(--muted);
  min-height: 1.4em;
  font-size: .92rem;
  margin: 0 0 4px;
}

/* --- Typography --- */

h2 {
  margin: 0 0 8px;
  font-size: 1.25rem;
  font-weight: 600;
}
.card-sub {
  color: #cfcdc7;
  font-size: .95rem;
  line-height: 1.55;
  margin: 0 auto 14px;
  max-width: 36ch;
}
.wallet-onboarding-kicker {
  margin: 0 0 8px;
  color: var(--blue);
  font-family: 'JetBrains Mono', monospace;
  font-size: .7rem;
  font-weight: 700;
  letter-spacing: .12em;
  text-transform: uppercase;
}
.wallet-onboarding-intro {
  margin: 0 auto 18px;
  max-width: 38ch;
  color: #cfcdc7;
  line-height: 1.5;
}
.wallet-onboarding-summary {
  margin: 0 0 18px;
  padding: 14px 16px;
  border: 1px solid var(--line);
  border-radius: 12px;
  background: #111;
  text-align: left;
}
.wallet-onboarding-summary h3,
.wallet-verification-note h3 {
  margin: 0 0 6px;
  font-size: .95rem;
}
.wallet-onboarding-summary p,
.wallet-onboarding-summary ul,
.wallet-verification-note p {
  margin: 0;
  color: #cfcdc7;
  font-size: .85rem;
  line-height: 1.5;
}
.wallet-onboarding-summary ul {
  margin-top: 8px;
  padding-left: 20px;
}
.wallet-verification-help {
  margin-top: 18px;
  text-align: left;
}
.wallet-verification-help summary {
  color: var(--blue);
  cursor: pointer;
  font-size: .85rem;
  font-weight: 600;
}
.wallet-verification-note {
  margin-top: 12px;
  padding-left: 12px;
  border-left: 2px solid var(--line);
}
.wallet-mainnet-note {
  margin: 16px 0 0;
  color: var(--muted);
  font-size: .78rem;
  line-height: 1.5;
  text-align: left;
}
code {
  color: var(--green);
  word-break: break-all;
  font-size: .8rem;
}
.wallet-line {
  display: flex;
  align-items: center;
  gap: 8px;
  justify-content: space-between;
  background: #111;
  border: 1px solid var(--line);
  border-radius: 12px;
  padding: 10px 14px;
  margin: 0 0 18px;
  font-size: .85rem;
  text-align: left;
}
.wallet-lbl { color: var(--muted); flex-shrink: 0; }
.wallet-line code { flex: 1; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }

/* --- Cards --- */

.card {
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: var(--radius);
  padding: 22px;
  margin-top: 16px;
  animation: rise .35s ease;
}
.card.sticker {
  border: 3px solid var(--ink);
  box-shadow: var(--sticker);
}

@keyframes rise {
  from { opacity: 0; transform: translateY(8px); }
  to   { opacity: 1; transform: none; }
}

/* --- Inputs --- */

input[type="text"] {
  width: 100%;
  padding: 12px 14px;
  margin: 8px 0 14px;
  border-radius: 12px;
  border: 1px solid var(--line);
  background: #111;
  color: var(--text);
  font-family: 'JetBrains Mono', monospace;
  font-size: .9rem;
  transition: border-color var(--speed), box-shadow var(--speed);
}
/* A text input on its own in a card (the price box on Place a bid / List
   for sale) sizes with the form instead of the card, which reaches ~790px
   once the app widens at >=760px (Discord desktop), and centers its text
   like the rest of the card. Inputs that share a row (filters, the listing
   bid form) keep their flex sizing. */
.card > input[type="text"] { max-width: 280px; text-align: center; }
input[type="text"]:focus {
  outline: none;
  border-color: var(--blue);
  box-shadow: 0 0 0 3px rgba(72, 144, 192, .25);
}
input::placeholder { color: var(--muted); }

/* --- Selects (dropdowns) ---
   Every <select> gets the text inputs' calm treatment: dark fill, hairline
   border, blue focus. color-scheme keeps the OS-drawn option list dark in
   browsers that skip the customizable select below. The chevron is a
   same-origin file, not a data: URI, so the Activity CSP can't block it. */

select {
  -webkit-appearance: none;
  appearance: none;
  color-scheme: dark;
  max-width: 100%;
  padding: 12px 38px 12px 14px;
  border: 1px solid var(--line);
  border-radius: 12px;
  background: #111 url(assets/select-chevron.svg) no-repeat right 14px center / 12px 8px;
  color: var(--text);
  /* 16px: iOS zooms the page into any focused control with smaller text. */
  font: 500 1rem/1.25 'Inter', system-ui, sans-serif;
  text-overflow: ellipsis;
  cursor: pointer;
  transition: border-color var(--speed), box-shadow var(--speed);
}
select:hover { border-color: var(--blue); }
select:focus-visible {
  outline: none;
  border-color: var(--blue);
  box-shadow: 0 0 0 3px rgba(72, 144, 192, .25);
}
select:disabled { opacity: .45; cursor: not-allowed; border-color: var(--line); }
select option { background: var(--surface); color: var(--text); }

/* Customizable select (Chromium 135+: Chrome/Edge, Android webviews, and
   Electron apps such as Discord desktop once built on 135+) lets us draw the
   open list too. Safari/iOS, Firefox and older Chromium skip this block and
   keep the dark native list above. */
@supports (appearance: base-select) {
  select, ::picker(select) { appearance: base-select; }
  /* One line, like a native select: a long label runs under the chevron's
     fade and is clipped at the border. The UA forces overflow: visible on a
     base-select, so contain: paint does the clipping — the focus ring and
     the open list (top layer) sit outside it. */
  select {
    display: inline-grid;
    grid-template-columns: minmax(0, 1fr) auto;
    align-items: center;
    gap: 0; /* the icon's fade is the gap */
    white-space: nowrap;
    contain: paint;
    padding-right: 14px;
    background-image: none;
  }
  select::picker-icon {
    content: '';
    width: 26px; /* 14px fade + the 12px chevron */
    height: 20px;
    margin-right: -14px; /* reach over the right padding so no text shows past it */
    padding-right: 14px;
    background:
      url(assets/select-chevron.svg) no-repeat right 14px center / 12px 8px,
      linear-gradient(90deg, rgba(17, 17, 17, 0), #111 14px);
    transition: scale var(--speed);
  }
  select:open::picker-icon { scale: 1 -1; }
  select:open {
    border-color: var(--blue);
    box-shadow: 0 0 0 3px rgba(72, 144, 192, .25);
  }

  ::picker(select) {
    min-inline-size: anchor-size(width);
    margin: 6px 0;
    padding: 6px;
    border: 1px solid var(--line);
    border-radius: 14px;
    background: var(--surface);
    color: var(--text);
    box-shadow: 0 16px 40px rgba(0, 0, 0, .6);
    scrollbar-width: thin;
    scrollbar-color: #3a3a3a transparent;
  }
  select option {
    padding: 10px 12px;
    border-radius: 8px;
    background: transparent;
    color: var(--text);
    cursor: pointer;
    transition: background-color var(--speed);
  }
  select option:hover { background: var(--surface-2); }
  select option:focus-visible {
    background: var(--surface-2);
    outline: 2px solid var(--blue);
    outline-offset: -2px;
  }
  select option:checked { font-weight: 600; }
  select option::checkmark { order: 1; margin-left: auto; color: var(--yellow); }
  select option:disabled { color: var(--muted); background: transparent; cursor: not-allowed; }
}

/* --- Buttons ---
   One primary per view; it gets the loud yellow sticker. Secondaries get the
   same sticker frame in a calm dark fill, so a row of actions reads as one
   family. Both keep a flatter radius than the base pill — at the widths these
   buttons take in a row, a 99px radius rounds a short label into a blob. */

button {
  font-family: 'Inter', sans-serif;
  font-weight: 600;
  font-size: 1rem;
  color: var(--text);
  background: transparent;
  border: 2px solid #3a3a3a;
  border-radius: 99px;
  padding: 12px 20px;
  cursor: pointer;
  margin: 4px;
  transition: transform .08s, box-shadow .08s, border-color var(--speed), color var(--speed);
}
button:hover { border-color: var(--blue); color: var(--blue); }
button:disabled {
  opacity: .45;
  cursor: not-allowed;
  transform: none;
}
button:focus-visible {
  outline: 3px solid var(--blue);
  outline-offset: 2px;
}

button.primary, button.secondary {
  border-radius: 14px;
  border: 3px solid var(--ink);
  box-shadow: var(--sticker-sm);
}
button.primary:active, button.secondary:active {
  transform: translate(2px, 2px);
  box-shadow: var(--sticker-pressed);
}

button.primary {
  font-family: 'Fredoka', sans-serif;
  font-size: 1.05rem;
  background: var(--yellow);
  color: var(--ink);
}
button.primary:hover { color: var(--ink); border-color: var(--ink); }
button.primary:disabled {
  filter: grayscale(.6) brightness(.7);
  box-shadow: var(--sticker-sm);
}
button.big { padding: 14px 20px; }

button.secondary {
  background: var(--surface-2);
  color: var(--text);
}
/* The ink border vanishes against the dark fill; the white ring of
   --sticker-sm is what draws the outline, so hover recolors the label
   rather than the (invisible) border. */
button.secondary:hover { color: var(--blue); border-color: var(--ink); }
button.secondary:disabled { box-shadow: var(--sticker-sm); }

button.link, a.link {
  background: none;
  border: none;
  color: var(--blue);
  padding: 2px 4px;
  font-weight: 600;
  font-size: .9rem;
  text-decoration: none;
  cursor: pointer;
}
button.link:hover, a.link:hover { text-decoration: underline; color: var(--blue); }

/* "Share on X" control (#41 T9): the anchor + copy button sit inline, with
   the copy-fallback input only appearing once revealed by app.js. */
.share-row { display: flex; align-items: center; justify-content: center; gap: 10px; flex-wrap: wrap; }
.copy-input {
  width: 100%;
  margin-top: 6px;
  font-size: .8rem;
  font-family: 'JetBrains Mono', monospace;
  padding: 6px 8px;
  border-radius: 8px;
  border: 2px solid var(--ink);
  background: var(--paper);
  color: var(--text);
}

button.back {
  background: none;
  border: none;
  color: var(--muted);
  padding: 2px 4px;
  font-weight: 500;
  font-size: .9rem;
}
button.back:hover { color: var(--text); border: none; }

.actions { display: flex; flex-direction: column; gap: 10px; align-items: stretch; }
.actions button { margin: 0; width: 100%; }
.row-links { margin: 14px 0 0; }
.row-links .dot { color: var(--muted); margin: 0 6px; }

/* --- Mint flow: step indicator --- */

.steps {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 0;
  list-style: none;
  margin: 0 0 20px;
  padding: 0;
  counter-reset: step;
}
.steps li {
  counter-increment: step;
  font-size: .8rem;
  color: var(--muted);
  display: flex;
  align-items: center;
  gap: 7px;
  flex-shrink: 0;
}
.steps li::before {
  content: counter(step);
  width: 26px;
  height: 26px;
  border-radius: 99px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  background: #111;
  border: 2px solid var(--line);
  color: var(--muted);
  font-family: 'JetBrains Mono', monospace;
  font-weight: 700;
  font-size: .78rem;
  transition: background var(--speed), border-color var(--speed), color var(--speed);
}
.steps li:not(:last-child)::after {
  content: "";
  width: 16px;
  height: 2px;
  background: var(--line);
  margin: 0 8px;
}
.steps li.active { color: var(--text); font-weight: 600; }
.steps li.active::before {
  background: var(--blue);
  border-color: var(--blue);
  color: #fff;
}
.steps li.done { color: var(--green); }
.steps li.done::before {
  content: "✓";
  background: var(--green);
  border-color: var(--green);
  color: #fff;
}
.steps li.done:not(:last-child)::after { background: var(--green); }

/* --- Payment-method pill ("Paying with LFGO" / "Paying with XRP") --- */

.method { margin: -2px 0 12px; }
.pill {
  display: inline-block;
  font-family: 'JetBrains Mono', monospace;
  font-size: .68rem;
  letter-spacing: .06em;
  text-transform: uppercase;
  font-weight: 700;
  border-radius: 99px;
  padding: 5px 13px;
  border: 2px solid var(--ink);
}
.pill.lfgo, .pill.brix { background: var(--yellow); color: var(--ink); }
.pill.xrp { background: var(--blue); color: #fff; }

/* Swap cost line above the final CTA */
.cost {
  text-align: center;
  color: var(--muted);
  font-size: .82rem;
  line-height: 1.5;
  margin: 8px auto 14px;
  max-width: 36ch;
}

/* --- Mint flow: QR, spinner, result image --- */

.qr, #flow-qr {
  width: 210px;
  height: 210px;
  border-radius: 16px;
  background: var(--paper);
  border: 3px solid var(--ink);
  box-shadow: var(--sticker);
  padding: 12px;
  margin: 18px auto 6px;
  display: block;
}

#nft-image {
  width: 240px;
  border-radius: 16px;
  border: 3px solid var(--ink);
  box-shadow: var(--sticker);
  margin-top: 16px;
}

/* With the minted NFT on screen the art is the hero and the accept QR
   shrinks to a companion size (issue #22). */
#flow-panel.with-image #flow-qr {
  width: 150px;
  height: 150px;
  padding: 8px;
  box-shadow: var(--sticker-sm);
}
#nft-image.celebrate { animation: pop-in 500ms cubic-bezier(.2, 1.4, .4, 1); }

@keyframes pop-in {
  0%   { opacity: 0; transform: scale(.6) rotate(-3deg); }
  70%  { transform: scale(1.05) rotate(1deg); }
  100% { opacity: 1; transform: none; }
}

.spinner {
  width: 46px;
  height: 46px;
  margin: 26px auto 10px;
  border-radius: 99px;
  border: 4px solid #2a2a2a;
  border-top-color: var(--blue);
  animation: spin 1s linear infinite;
}

@keyframes spin { to { transform: rotate(360deg); } }

/* --- Trait Swapper: the dress-up grid --- */

.nft-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(120px, 1fr));
  gap: 14px;
  /* Fill the viewport instead of a fixed strip: leave ~380px for the
     header/tabs/back chrome, never dropping below the old 360px strip.
     dvh (where supported) tracks mobile browser-chrome collapse. */
  max-height: max(360px, calc(100vh - 380px));
  max-height: max(360px, calc(100dvh - 380px));
  overflow-y: auto;
  padding: 4px;
  scrollbar-width: thin;
  scrollbar-color: var(--line) transparent;
}

.nft-card {
  position: relative;
  background: var(--surface-2);
  border: 3px solid var(--ink);
  border-radius: 14px;
  overflow: hidden;
  padding: 0;
  margin: 0;
  display: flex;
  flex-direction: column;
  font-size: .78rem;
  color: var(--muted);
  transition: transform .12s, opacity .15s, box-shadow .12s;
}
.nft-card:hover {
  transform: translate(-2px, -2px);
  border-color: var(--ink);
  color: var(--muted);
}
.nft-card img {
  width: 100%;
  aspect-ratio: 1;
  object-fit: cover;
  display: block;
}
.nft-card .cap {
  padding: 7px 6px;
  font-family: 'JetBrains Mono', monospace;
}
.nft-card .cap .body {
  display: block;
  color: #7c7a74;
  font-size: .64rem;
  letter-spacing: .05em;
  text-transform: uppercase;
  margin-top: 3px;
}
.nft-card.dim {
  opacity: .32;
  filter: grayscale(.5);
  pointer-events: none;
}
.nft-card.sel-1 { box-shadow: 0 0 0 4px var(--blue); }
.nft-card.sel-2 { box-shadow: 0 0 0 4px var(--green); }
.nft-card .pick {
  position: absolute;
  top: 7px;
  left: 7px;
  width: 24px;
  height: 24px;
  border-radius: 99px;
  display: none;
  place-items: center;
  font-family: 'Fredoka', sans-serif;
  font-weight: 700;
  font-size: .85rem;
  color: #fff;
  border: 2px solid var(--ink);
}
.nft-card.sel-1 .pick { display: grid; background: var(--blue); }
.nft-card.sel-2 .pick { display: grid; background: var(--green); }
/* Animated-NFT marker (#250/#298): grid tiles stay stills, the badge flags
   cards/tiles whose art plays as video in the detail/focused views. */
.nft-card .anim-badge,
.go-tile .anim-badge {
  position: absolute;
  top: 7px;
  right: 7px;
  width: 22px;
  height: 22px;
  border-radius: 99px;
  display: grid;
  place-items: center;
  font-size: .6rem;
  color: #fff;
  background: rgba(0, 0, 0, .55);
  border: 2px solid var(--ink);
  pointer-events: none;
}

/* Skeleton loaders while the wallet's NFTs are fetched */
.nft-card.skeleton {
  pointer-events: none;
  border-color: var(--line);
  padding: 6px;
  gap: 6px;
  align-items: center;
}
.nft-card.skeleton .ph-img,
.nft-card.skeleton .ph-line {
  background: linear-gradient(90deg, var(--surface) 25%, var(--surface-2) 50%, var(--surface) 75%);
  background-size: 200% 100%;
  animation: shimmer 1.2s ease-in-out infinite;
  border-radius: 8px;
}
.nft-card.skeleton .ph-img { width: 100%; aspect-ratio: 1; }
.nft-card.skeleton .ph-line { width: 70%; height: 10px; }

@keyframes shimmer { to { background-position: -200% 0; } }

/* --- Trait Swapper: chooser --- */

.swap-pair {
  display: flex;
  gap: 14px;
  justify-content: center;
  align-items: center;
  margin: 16px 0 6px;
}
.swap-pair figure {
  margin: 0;
  font-family: 'JetBrains Mono', monospace;
  font-size: .7rem;
  color: var(--muted);
}
.swap-pair img,
.swap-pair video {
  width: 96px;
  border-radius: 12px;
  border: 3px solid var(--ink);
  display: block;
  margin-bottom: 6px;
}
.swap-arrows { font-size: 1.5rem; color: var(--blue); }

.trait-list { text-align: left; margin: 6px 0 18px; }
.trait-row {
  display: flex;
  gap: 12px;
  align-items: center;
  padding: 12px 4px;
  border-bottom: 1px solid var(--line);
  font-size: .95rem;
  cursor: pointer;
  transition: background var(--speed);
}
.trait-row:last-child { border-bottom: none; }
.trait-row:hover { background: #111; }
.trait-row input[type="checkbox"] {
  width: 22px;
  height: 22px;
  accent-color: var(--green);
  flex-shrink: 0;
}
.trait-row::after {
  /* category color dot, set per-row via --cat in JS */
  content: "";
  order: 1;
  width: 9px;
  height: 9px;
  border-radius: 99px;
  background: var(--cat, var(--muted));
  flex-shrink: 0;
}
.trait-row strong { order: 2; font-weight: 600; }
.trait-row span {
  order: 3;
  color: var(--muted);
  margin-left: auto;
  text-align: right;
  font-family: 'JetBrains Mono', monospace;
  font-size: .8rem;
}

.swap-result { margin-top: 20px; }
.swap-result h3 { font-size: 1.05rem; font-weight: 600; margin: 0 0 10px; }
.swap-result .result-img {
  width: 160px;
  border-radius: 14px;
  border: 3px solid var(--ink);
  box-shadow: var(--sticker-sm);
}
.swap-result .modified-note { display: block; margin-top: 10px; color: var(--green); font-size: .9rem; }

#swap-results .result-qr,
.swap-result .result-qr {
  width: 160px;
  border-radius: 14px;
  background: var(--paper);
  border: 3px solid var(--ink);
  box-shadow: var(--sticker-sm);
  padding: 8px;
  display: block;
  margin: 14px auto 4px;
}

/* --- Toasts --- */

.toasts {
  position: fixed;
  bottom: 16px;
  left: 50%;
  transform: translateX(-50%);
  display: flex;
  flex-direction: column;
  gap: 8px;
  z-index: 100;
  width: min(420px, calc(100vw - 32px));
}
.toast {
  background: var(--surface-2);
  color: var(--text);
  border: 3px solid var(--ink);
  border-left: 6px solid var(--red);
  border-radius: 12px;
  box-shadow: var(--sticker-sm);
  padding: 10px 14px;
  font-size: .9rem;
  font-weight: 500;
  text-align: left;
  animation: rise var(--speed) ease-out;
}
.toast.out { opacity: 0; transition: opacity 300ms; }

/* --- In-app confirm dialog ---
   Discord serves the Activity in a sandboxed iframe where native window.confirm
   is a silent no-op, so destructive actions confirm through this overlay. */
.confirm-overlay {
  position: fixed; inset: 0; z-index: 200;
  display: flex; align-items: center; justify-content: center;
  padding: 16px; background: rgba(0, 0, 0, .55);
}
.confirm-overlay[hidden] { display: none; }
.confirm-box {
  width: min(380px, 100%); text-align: center;
  animation: rise var(--speed) ease-out;
}
.confirm-actions {
  display: flex; gap: 10px; justify-content: center; margin-top: 18px;
}
.confirm-actions button { flex: 1; max-width: 160px; }

/* --- Motion preferences --- */

@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after {
    animation-duration: .01ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: .01ms !important;
  }
  .spinner { animation-duration: 1s !important; animation-iteration-count: infinite !important; }
}

/* --- Small screens --- */

@media (max-width: 380px) {
  .wordmark { font-size: 2.1rem; }
  .wordmark-img { height: 46px; max-width: 180px; }
  .swap-pair img,
  .swap-pair video { width: 80px; }
  .qr, #flow-qr { width: 180px; height: 180px; }
}

/* --- Discord mobile overlay spacer (issue #23) ---
   On touch devices in portrait, Discord draws its activity controls over
   the top edge of the iframe; push the header below them. */

@media (pointer: coarse) and (orientation: portrait) {
  .app-header { padding-top: 52px; }
}

/* --- Responsive layout (issue #20) ---
   Portrait/mobile keeps the default single stacked column; landscape on
   desktop/tablet widens the shell and puts the mint flow's copy and visual
   (QR / NFT art) side by side. */

@media (min-width: 760px) {
  #app { max-width: 880px; }
  .card { padding: 26px 30px; }
  /* Labels here are dynamic and can get long ("🧱 Claim 1,234 BRIX",
     "🎁 Offers (3)"). In a row, a label that wraps makes its button taller
     and align-items: stretch matches every sibling to it — one long label
     inflates the whole row. So keep labels on one line and let the ROW wrap
     instead. (The stacked mobile layout above is full-width and may wrap.) */
  .actions { flex-direction: row; flex-wrap: wrap; justify-content: center; gap: 8px; }
  /* Natural width, not flex: 1 — an even split squeezes every button to
     ~110px, narrower than most of these labels. Buttons that don't fit move
     to a second row at full legibility, so the padding here is tighter than
     the stacked layout's to keep the whole set on one row. */
  .actions button {
    width: auto; flex: 0 1 auto; max-width: 280px;
    white-space: nowrap; padding: 12px 14px;
  }
  .actions button.big { padding: 14px 16px; }
}

/* Side-by-side flow copy/visual needs the horizontal room, so it stays
   landscape-only; the width bump above applies in any orientation. */
@media (min-width: 760px) and (orientation: landscape) {
  .flow-body {
    display: flex;
    align-items: center;
    justify-content: center;
    gap: 36px;
    text-align: left;
  }
  .flow-body .flow-copy { max-width: 40ch; }
  .flow-body .card-sub { margin-left: 0; }
  .flow-body .flow-visual { flex-shrink: 0; }
}

/* --- Closet gate (create / pending-accept prompt) --- */
.closet-gate {
  text-align: center;
  padding: 24px 0 16px;
}
.closet-gate-msg {
  color: var(--muted);
  font-size: .95rem;
  margin: 0 0 14px;
}

/* --- Dressing Room --- */
.dressup { display: flex; gap: 18px; flex-wrap: wrap; }
.dressup-stage { flex: 1 1 240px; text-align: center; }
.dressup-canvas {
  position: relative; width: 240px; height: 240px; margin: 0 auto;
  background: var(--surface-2); border: 3px solid var(--ink);
  border-radius: 16px; box-shadow: var(--sticker-sm); overflow: hidden;
}
.dressup-canvas img, .dressup-canvas video {
  position: absolute; inset: 0; width: 100%; height: 100%; object-fit: contain;
}
.dressup-closet { flex: 1 1 240px; }
/* Spans the whole closet grid: the blank's "pick a body first" explainer. */
.closet-blank-hint { grid-column: 1 / -1; margin: 0 0 6px; font-size: .85rem; }
.closet-head { display: flex; justify-content: space-between; align-items: center; }
.closet-grid {
  display: grid; grid-template-columns: repeat(auto-fill, minmax(64px, 1fr));
  gap: 8px; margin-top: 10px;
}
.closet-item {
  position: relative; aspect-ratio: 1; border: 2px solid var(--ink);
  border-radius: 10px; overflow: hidden; background: var(--surface-2);
  cursor: pointer; padding: 0;
}
.closet-item.incompatible { opacity: .35; cursor: not-allowed; }
.closet-item img, .closet-item video { width: 100%; height: 100%; object-fit: contain; }
/* Caption for tiles whose art is intentionally empty ("None"): without it a
   harvested empty slot renders as a blank square and reads as a broken image. */
.closet-item .closet-caption {
  position: absolute; inset: 0; display: grid; place-items: center;
  white-space: pre-line; text-align: center; line-height: 1.1;
  font-size: .6rem; font-weight: 700; color: var(--muted); pointer-events: none;
}
.closet-item .count {
  position: absolute; bottom: 2px; right: 4px; font-size: .7rem; font-weight: 700;
}
/* --- GO picker overlay (replaces the old roster strip) --- */
.go-picker-box { width: min(520px, 100%); text-align: left; }
.go-picker-head { display: flex; justify-content: space-between; align-items: center; }
.go-picker-head h2 { margin: 0; }
.go-picker-grid {
  display: grid; grid-template-columns: repeat(auto-fill, minmax(96px, 1fr));
  gap: 10px; margin-top: 12px; max-height: 60vh; overflow-y: auto;
}
.go-tile {
  display: flex; flex-direction: column; align-items: center; gap: 2px;
  padding: 8px 4px; border: 2px solid var(--ink); border-radius: 10px;
  background: var(--surface-2); cursor: pointer;
  position: relative; /* #298: anchors the .anim-badge marker */
}
.go-tile img, .go-tile video { width: 72px; height: 72px; object-fit: contain; }
.go-tile.active { box-shadow: 0 0 0 3px var(--blue); }
/* NFT with empty/missing on-chain metadata (freshly minted, not yet indexed):
   a calm striped placeholder, disabled until the indexer catches up. */
.go-tile.indexing {
  opacity: .55; cursor: default;
  background: repeating-linear-gradient(
    45deg, var(--surface-2), var(--surface-2) 6px, var(--surface) 6px, var(--surface) 12px);
}
.go-tile-label { font-weight: 700; font-size: .8rem; }
.go-tile-sub { font-size: .7rem; color: var(--muted); }
.go-tile.assemble { justify-content: center; min-height: 96px; }
.go-tile-plus { font-size: 1.6rem; line-height: 1; }
.dressup-canvas.incomplete {
  display: grid; place-items: center; color: var(--muted, #888); font-weight: 700;
}
.dressup-canvas.incomplete::after { content: '…'; font-size: 2rem; }

/* --- Assemble builder overlay (blank -> body -> traits + live preview) --- */
.builder-box {
  width: min(640px, 100%); text-align: left;
  max-height: 90vh; overflow-y: auto;
}
.builder-step { margin-top: 16px; }
.builder-step:first-child { margin-top: 8px; }
.builder-step-title {
  margin: 0 0 8px; font-size: .95rem; color: var(--muted);
}
.builder-trait-layout {
  display: flex; gap: 16px; flex-wrap: wrap; align-items: flex-start;
}
.builder-preview {
  position: relative; width: 200px; height: 200px; flex: 0 0 auto;
  background: var(--surface-2); border: 3px solid var(--ink);
  border-radius: 16px; box-shadow: var(--sticker-sm); overflow: hidden;
}
.builder-preview img, .builder-preview video {
  position: absolute; inset: 0; width: 100%; height: 100%; object-fit: contain;
}
.builder-pickers {
  flex: 1 1 220px; display: flex; flex-direction: column; gap: 8px;
}
.builder-picker {
  display: flex; align-items: center; justify-content: space-between; gap: 10px;
}
.builder-picker-label { font-weight: 700; font-size: .85rem; }
.builder-picker select { flex: 1 1 auto; max-width: 60%; }
/* #T31: a step's collapsed heading is a disclosure <button> nested inside
   its <h3 class="builder-step-title">. The global `button` rule (font,
   pill border, margin) must be fully reset so it reads as plain heading
   text — font/color are inherited from the h3 so they stay in sync with
   it, and button:focus-visible's outline is left alone for keyboard use. */
.builder-step-toggle {
  display: block; width: 100%; text-align: left;
  font: inherit; color: inherit;
  background: none; border: none; border-radius: 0; margin: 0; padding: 0;
  cursor: pointer;
}
.builder-step-toggle:hover, .builder-step-toggle:focus-visible { color: var(--text); }
.builder-drop-notice {
  margin: 8px 0 0; color: var(--muted); font-size: .8rem; font-weight: 600;
}
.builder-warn {
  margin: 12px 0 0; color: var(--red); font-size: .85rem; font-weight: 700;
}
#builder-assemble-btn { margin-top: 16px; width: 100%; }
#builder-assemble-btn:disabled { opacity: .5; cursor: not-allowed; }

/* Extract button on each closet tile — a comfortable touch target (>=28px). */
.closet-item .extract {
  position: absolute; top: 2px; right: 2px;
  min-width: 28px; min-height: 28px; padding: 4px;
  display: grid; place-items: center;
  font-size: .8rem; font-weight: 700; line-height: 1;
  background: var(--surface); color: var(--text);
  border: 1px solid var(--line); border-radius: 6px;
  cursor: pointer; opacity: .8;
  z-index: 1;
}
.closet-item .extract:hover { opacity: 1; background: var(--blue); }
.closet-item .extract:disabled { opacity: .35; cursor: not-allowed; }

/* Tradeable traits strip */
.trait-strip-section { margin-top: 20px; }
.trait-strip-heading { margin: 0 0 8px; font-size: .85rem; color: var(--muted); text-transform: uppercase; letter-spacing: .04em; }
.trait-strip {
  display: flex; flex-wrap: wrap; gap: 8px;
}
.trait-strip-empty { margin: 0; font-size: .85rem; color: var(--muted); font-style: italic; }
.trait-chip {
  display: flex; align-items: center; gap: 6px;
  background: var(--surface-2); border: 1px solid var(--line);
  border-radius: 8px; padding: 4px 8px 4px 4px;
}
.trait-chip img, .trait-chip video { width: 36px; height: 36px; object-fit: contain; border-radius: 5px; background: var(--surface); }
.trait-chip-label { font-size: .75rem; color: var(--text); flex: 1; }
.trait-chip .deposit,
.trait-chip .chip-action {
  padding: 3px 8px; font-size: .72rem; font-weight: 600;
  background: var(--surface); color: var(--blue);
  border: 1px solid var(--blue); border-radius: 5px; cursor: pointer;
}
.trait-chip .deposit:hover,
.trait-chip .chip-action:hover { background: var(--blue); color: var(--ink); }
.trait-chip .deposit:disabled,
.trait-chip .chip-action:disabled { opacity: .4; cursor: not-allowed; }

/* --- Marketplace (#44) ---
   Browse/Mine grid + chips reuse .nft-grid/.nft-card (swap picker) and
   .trait-strip/.trait-chip/.chip-action (Closet strip) as-is; the filter
   bar is the one genuinely new layout need. */
.market-section { margin-top: 10px; }
.market-filters {
  display: flex; flex-wrap: wrap; gap: 8px; margin: 12px 0 4px; text-align: left;
}
.market-filters input[type="text"] { flex: 1 1 100px; margin: 0; }
/* Sized to the label (the text inputs fill the rest): a 100px basis cut
   "Price: low → high" off at the select's 16px text. */
.market-filters select { flex: 0 1 auto; }
.market-filters button { margin: 0; flex: 0 0 auto; }
.market-card-price {
  display: block; color: var(--yellow); font-size: .72rem; font-weight: 700;
  margin-top: 2px;
}
/* #203: browse UX — rarity chip, load-more, listing detail overlay */
.market-card-rarity {
  display: block; color: var(--muted); font-size: .62rem; font-weight: 700;
  margin-top: 1px;
}
#market-load-more { margin: 14px auto 0; display: block; }
/* The detail overlay outgrew the 380px confirm box: it is its own modal —
   a scrolling body between a pinned corner close and a pinned action footer,
   so the buttons are always reachable and only the body ever scrolls.
   Stacked when narrow, art beside details once the modal itself has room —
   a container query, so the compact trait card stays stacked on any screen. */
.listing-detail {
  container: listing-detail / inline-size;
  position: relative;
  width: min(760px, 100%);
  max-height: calc(100vh - 32px);
  max-height: calc(100dvh - 32px);
  display: flex; flex-direction: column;
  margin: 0; padding: 0; overflow: hidden;
  text-align: center;
}
/* A trait is three short lines and a Buy button; 760px is sized for a
   character's attribute chips and three-button footer. */
.listing-detail.listing-detail-compact { width: min(420px, 100%); }
.listing-detail-body {
  flex: 1 1 auto; min-height: 0;
  overflow-y: auto; overflow-x: hidden; overscroll-behavior: contain;
  padding: 22px 18px 16px;
  scrollbar-width: thin; scrollbar-color: #3a3a3a transparent;
}
.listing-detail-body::-webkit-scrollbar { width: 8px; }
.listing-detail-body::-webkit-scrollbar-track { background: transparent; }
.listing-detail-body::-webkit-scrollbar-thumb { background: #3a3a3a; border-radius: 99px; }
.listing-detail-close {
  position: absolute; top: 10px; right: 10px; z-index: 1;
  width: 36px; height: 36px; margin: 0; padding: 0;
  display: grid; place-items: center;
  font-size: 1.4rem; line-height: 1;
  background: rgba(0, 0, 0, .6); border-color: var(--line);
}
/* The tile matches the browse card: trait art is a transparent full-body
   canvas, and without it the piece floats (a garment sits at the bottom). */
.listing-detail-img {
  display: block; width: min(220px, 60vw); aspect-ratio: 1; object-fit: cover;
  border-radius: 12px; margin: 0 auto 8px; background: var(--surface-2);
}
.listing-detail-info { min-width: 0; overflow-wrap: anywhere; }
.listing-detail-info h3 { margin: 8px 0 2px; }
.listing-detail-info .card-sub { max-width: none; font-size: .9rem; margin-bottom: 10px; }
.listing-detail-actions {
  flex: 0 0 auto;
  display: flex; flex-wrap: wrap; gap: 8px; justify-content: center;
  padding: 12px 18px 16px;
  border-top: 1px solid var(--line); background: var(--surface);
}
.listing-detail-actions button {
  flex: 1 1 auto; max-width: 100%; margin: 0;
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
/* On a phone the primary gets its own full-width row, first; its label
   ("Buy now — 4.064587 XRP via xrp.cafe") may wrap rather than truncate. */
.listing-detail-actions .primary {
  flex-basis: 100%; order: -1; white-space: normal; line-height: 1.25;
}
/* 600px of modal ≈ the old 640px viewport breakpoint (16px overlay gutters). */
@container listing-detail (min-width: 600px) {
  .listing-detail-body {
    display: grid; grid-template-columns: minmax(200px, 280px) minmax(0, 1fr);
    gap: 26px; align-items: start; text-align: left;
    padding: 28px 28px 20px;
  }
  .listing-detail-media { position: sticky; top: 0; }
  .listing-detail-img { width: 100%; margin: 0; }
  .listing-detail-info { padding-right: 28px; } /* clear the corner close */
  .listing-detail-info h3 { margin-top: 0; }
  .listing-detail-info .card-sub { margin-left: 0; }
  .listing-detail .listing-attrs { justify-content: flex-start; }
  .listing-detail-actions { justify-content: flex-end; padding: 14px 28px 18px; }
  .listing-detail-actions button { flex: 0 1 auto; }
  .listing-detail-actions .primary { flex-basis: auto; order: 0; white-space: nowrap; }
}
.listing-attrs {
  display: flex; flex-wrap: wrap; gap: 6px; justify-content: center;
  margin: 10px 0 4px;
}
.listing-attr-chip {
  font-size: .68rem; padding: 3px 8px; border-radius: 99px;
  border: 1px solid var(--line); color: var(--muted); background: var(--surface);
}
.listing-history { list-style: none; margin: 4px 0 0; padding: 0; }
.listing-history li { font-size: .72rem; color: var(--muted); padding: 2px 0; }

/* #283: place-bid inline form in the listing detail overlay */
.listing-bid-form { display: flex; gap: 8px; margin: 8px 0 0; }
.listing-bid-form input { flex: 1 1 auto; margin: 0; }
.listing-bid-form button { flex: 0 0 auto; margin: 0; }

/* #131: external (brokered) listings — read-only price discovery */
.market-external-toggle {
  flex: 0 0 auto; display: inline-flex; align-items: center; gap: 6px;
  font-size: .8rem; color: var(--muted); cursor: pointer;
}
.market-external-toggle input { margin: 0; }
.nft-card.market-card-external { opacity: .65; border-style: dashed; }
.market-card-external-badge {
  display: block; color: var(--muted); font-size: .62rem; font-weight: 700;
  margin-top: 2px; text-transform: uppercase; letter-spacing: .03em;
}

/* Home-screen leaderboard card */
.leaderboard {
  margin-top: 20px; text-align: left;
  /* The server still sends 25 rows; only 10 are in view at a time. Both
     knobs are read by the .lb-list max-height below — change them here. */
  --lb-rows: 10;
  --lb-row-h: 44px;   /* the 2rem thumb + .lb-row padding; also the floor
                         that keeps a text-only row the same height */
  --lb-row-gap: 6px;
}
.lb-title { margin: 0 0 10px; font-size: 1rem; color: var(--text); }
.lb-chips { display: flex; flex-wrap: wrap; gap: .4rem; padding-bottom: 4px; }
.lb-boards { margin-top: 6px; }
.lb-chip.lb-cat { font-size: .85rem; font-weight: 700; }
.lb-chip {
  flex: 0 0 auto; margin: 0; padding: 6px 12px;
  border-radius: 99px; border: 1px solid var(--line);
  background: var(--surface); color: var(--muted);
  font-size: .78rem; font-weight: 600; cursor: pointer;
}
.lb-chip.active { background: var(--blue); color: var(--ink); border-color: var(--blue); }
.lb-stepper {
  display: flex; align-items: center; justify-content: center; gap: 10px;
  margin: 8px 0; color: var(--muted); font-size: .82rem;
}
.lb-stepper button.link { margin: 0; font-size: 1rem; }
/* Only the list scrolls — the card around it is never pushed off screen.
   The +10px shows a sliver of row 11, so it reads as scrollable without a
   JS-driven fade. overscroll-behavior keeps the page (and the Discord
   iframe) still when the list hits an end. */
.lb-list {
  list-style: none; margin: 10px 0 0; padding: 0;
  display: flex; flex-direction: column; gap: var(--lb-row-gap);
  max-height: calc(var(--lb-rows) * (var(--lb-row-h) + var(--lb-row-gap)) + 10px);
  overflow-y: auto; overscroll-behavior: contain;
  scrollbar-width: thin; scrollbar-color: var(--line) transparent;
}
.lb-list::-webkit-scrollbar { width: 8px; }
.lb-list::-webkit-scrollbar-track { background: transparent; }
.lb-list::-webkit-scrollbar-thumb { background: var(--line); border-radius: 99px; }
.lb-list:focus-visible { outline: 2px solid var(--blue); outline-offset: 2px; }
.lb-row {
  display: flex; align-items: center; justify-content: space-between; gap: 8px;
  padding: 6px 8px; border-radius: 8px; background: var(--surface-2);
  min-height: var(--lb-row-h); box-sizing: border-box;
}
.lb-rank { flex: 0 0 auto; width: 2rem; font-size: .9rem; }
.lb-label { flex: 1; display: flex; align-items: center; gap: 6px; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; color: var(--text); font-size: .85rem; }
.lb-thumb { width: 2rem; height: 2rem; border-radius: 99px; object-fit: cover; background: var(--surface); flex-shrink: 0; }
.lb-value { flex: 0 0 auto; font-weight: 700; color: var(--text); font-size: .85rem; }
.lb-me { margin: 8px 0 0; font-size: .82rem; color: var(--muted); }

/* Mint odds transparency (#494 Option A) */
.odds-slot {
  margin-top: 8px; text-align: left;
  border: 1px solid var(--line); border-radius: 10px; background: var(--surface);
}
/* Collapsible slot header: the whole summary row is the hit target. */
.odds-slot-title {
  display: flex; align-items: center; gap: 8px;
  margin: 0; padding: 10px 12px; font-size: .95rem; font-weight: 700;
  color: var(--text); cursor: pointer; list-style: none; user-select: none;
  border-radius: 10px;
}
.odds-slot-title::-webkit-details-marker { display: none; }
.odds-slot-title::after {
  content: '\25be'; margin-left: auto; color: var(--muted); font-size: .9rem;
  transition: transform .15s ease;
}
.odds-slot[open] > .odds-slot-title::after { transform: rotate(180deg); }
.odds-slot[open] > .odds-slot-title { border-bottom: 1px solid var(--line); border-radius: 10px 10px 0 0; }
.odds-slot-title:hover { background: var(--surface-2); }
.odds-slot-title:focus-visible { outline: 2px solid var(--accent, var(--text)); outline-offset: 2px; }
.odds-slot-name { min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.odds-slot-count { flex: 0 0 auto; color: var(--muted); font-size: .75rem; font-weight: 600; }
.odds-list { list-style: none; margin: 0; padding: 10px 12px; display: flex; flex-direction: column; gap: 6px; }
.odds-row {
  display: flex; align-items: center; justify-content: space-between; gap: 8px;
  padding: 6px 8px; border-radius: 8px; background: var(--surface-2);
}
.odds-row.odds-row-parked { opacity: .55; }
.odds-value { flex: 1; display: flex; align-items: center; gap: 6px; min-width: 0; color: var(--text); font-size: .85rem; }
.odds-value-text { min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.odds-badge {
  flex: 0 0 auto; padding: 1px 7px; border-radius: 99px; background: var(--surface);
  border: 1px solid var(--line); color: var(--muted); font-size: .62rem; font-weight: 700;
  text-transform: uppercase; letter-spacing: .03em;
}
.odds-pct { flex: 0 0 auto; width: 4rem; text-align: right; font-weight: 700; color: var(--text); font-size: .85rem; }
.odds-share { flex: 0 0 auto; width: 6.5rem; text-align: right; color: var(--muted); font-size: .78rem; }
/* #565 review round 1: staleness disclosure note (distinct from the main
   explainer paragraph, same .card-sub base). */
.odds-stale-note { font-style: italic; margin-top: 8px; }

/* Bulk mint quantity stepper (#215 UI) */
.mint-row { display: flex; align-items: center; gap: 8px; justify-content: center; }
.qty-stepper { display: flex; align-items: center; gap: 4px; }
.qty-stepper .qty-btn {
  width: 32px; height: 32px; border-radius: 8px; font-size: 1.4rem; line-height: 1;
  display: inline-flex; align-items: center; justify-content: center; padding: 0;
  border: 1px solid var(--line); background: var(--surface); color: var(--text); cursor: pointer;
}
.qty-stepper .qty-btn:disabled { opacity: 0.4; cursor: default; }
#qty-value { min-width: 24px; text-align: center; font-weight: 700; color: var(--text); }

/* Bulk fulfillment unit list (#215 UI) + pending-offers tray (#218) */
#bulk-units, #offers-list { display: flex; flex-direction: column; gap: 8px; margin: 12px 0; }
.bulk-unit { display: flex; align-items: center; gap: 10px; padding: 8px; border: 1px solid var(--line); border-radius: 10px; }
.bulk-unit img.thumb { width: 56px; height: 56px; border-radius: 8px; object-fit: cover; }
.bulk-unit .u-label { flex: 1; text-align: left; }
.bulk-unit.pending { opacity: 0.55; }
.bulk-unit .u-error { color: var(--red); font-size: 0.85em; }
.bulk-unit img.u-qr { width: 120px; height: 120px; }

/* #215 UX revision: pay-page quantity stepper + stale-QR affordance.
   Changing quantity cancels the live payload; the shown QR dims and the
   Regenerate button pulses until the user rebuilds it. */
#flow-qty {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 0.5rem;
  margin: 0.25rem 0 0.5rem;
}
#flow-qty .qty-lbl {
  font-size: 0.85rem;
  opacity: 0.8;
}

/* Stale QR: the shown code no longer matches the selected quantity. */
#flow-qr.qr-stale {
  opacity: 0.35;
  filter: blur(2px) grayscale(0.4);
  transition: opacity 0.15s ease, filter 0.15s ease;
}

/* Regenerate needs attention while the QR is stale. */
#flow-regen-btn.needs-regen {
  animation: regen-pulse 1.1s ease-in-out infinite;
  font-weight: 700;
}
@keyframes regen-pulse {
  0%, 100% { opacity: 1; text-shadow: 0 0 0 transparent; }
  50%      { opacity: 0.65; text-shadow: 0 0 8px currentcolor; }
}
@media (prefers-reduced-motion: reduce) {
  #flow-regen-btn.needs-regen { animation: none; text-decoration: underline; }
  #flow-qr.qr-stale { transition: none; }
}
.build-save-bar {
  display: flex;
  gap: .5rem;
  justify-content: center;
  margin-top: .5rem;
}
.build-save-bar button { flex: 0 1 auto; }
.closet-item.staged { outline: 2px solid var(--blue); }

/* #142: mobile-primary Xaman deep link. On touch devices the "Open in
   Xaman" button is promoted to the primary affordance and the QR collapses
   behind the .qr-toggle disclosure. */
button.link.sign-primary {
  display: inline-block;
  font-size: 1.1rem;
  font-weight: 700;
  padding: .6rem 1.2rem;
  border: 2px solid currentcolor;
  border-radius: 10px;
  text-decoration: none;
}
button.wallet-provider-btn,
button.link.wallet-provider-btn.sign-primary {
  width: 76px;
  height: 76px;
  padding: 4px;
  border: 2px solid var(--line);
  border-radius: 18px;
  overflow: hidden;
  background: var(--surface-2);
  line-height: 0;
}
button.wallet-provider-btn:hover,
button.link.wallet-provider-btn.sign-primary:hover {
  border-color: var(--blue);
}
.wallet-provider-btn img {
  display: block;
  width: 64px;
  height: 64px;
  border-radius: 13px;
}
button.link.qr-toggle { font-size: .85rem; opacity: .8; }

/* Sponsored free-mint preview badge (funder-at-login) */
.free-mint-badge { margin: 4px 0 8px; text-align: center; font-size: 0.9rem; opacity: 0.9; }
/* --- Wallet picker (sign-in provider choice; no QR until a pick) --- */
.wallet-picker {
  display: flex; gap: 14px; justify-content: center; align-items: center;
  margin: 14px 0;
}
.wallet-picker[hidden] { display: none; }
/* #481: grouped trait cards — count badge on the tile, per-offer list in the
   detail overlay. */
.market-card-count {
  position: absolute; top: 6px; left: 6px;
  background: var(--yellow); color: #111; font-size: .62rem; font-weight: 800;
  padding: 2px 6px; border-radius: 999px; pointer-events: none;
}
.listing-offers { margin: 6px 0 2px; text-align: left; }
.listing-offers-list { list-style: none; margin: 4px 0 0; padding: 0; } /* no inner scroll box — the modal body is the one scroll region */
.listing-offers-list li + li { margin-top: 4px; }
.listing-offer-row {
  display: flex; justify-content: space-between; align-items: center; gap: 8px;
  width: 100%; margin: 0; padding: 6px 10px; border-radius: 8px;
  background: rgba(255,255,255,.05); border: 1px solid transparent; font: inherit; cursor: pointer;
}
.listing-offer-row .market-card-price { margin: 0; display: inline; }
.listing-offer-row.active { border-color: var(--yellow); cursor: default; }
.listing-offer-seller { color: var(--muted); font-size: .68rem; }

/* Closet Market (#443/#496): best Closet bid on a grouped trait card */
.market-card-bid {
  position: absolute; top: 6px; right: 6px;
  background: #3ecf8e; color: #111; font-size: .62rem; font-weight: 800;
  padding: 2px 6px; border-radius: 999px; pointer-events: none;
}
/* Sized to the choice and centered under its label, not full-bleed like the
   price input. Block-level keeps each label on its own line; grid rather
   than block because the customizable select lays out its label + chevron
   on the grid set above (a native select treats it as plain block). */
#closet-bid-form-panel select { display: grid; width: fit-content; margin: 4px auto 10px; }

/* Standalone trait art (decorateTraitArt in app.js): transparent layer PNGs
   had nothing behind them, so dark art — every eyebrow — disappeared into the
   dark cards. Checkerboard for most traits; face traits (Eyes/Eyebrows/Mouth)
   sit on a faint default body so they read in context. */
/* The doubled class is deliberate: it raises specificity to (0,2,0) so a
   container rule like `.trait-chip img` or `.mine-row img` — (0,1,1), and
   usually written with the `background` SHORTHAND, which resets
   background-image — cannot silently strip the backdrop off trait art. That
   is exactly how eyebrows stayed invisible in the Wanted book and the
   Dressing Room strip after #557 fixed them on cards. */
.trait-art.trait-art {
  background-color: #5a5a5a;
  background-image:
    linear-gradient(45deg, #6a6a6a 25%, transparent 25%, transparent 75%, #6a6a6a 75%),
    linear-gradient(45deg, #6a6a6a 25%, transparent 25%, transparent 75%, #6a6a6a 75%);
  background-size: 16px 16px;
  background-position: 0 0, 8px 8px;
}
.trait-art-face.trait-art-face {
  background-color: var(--surface-2);
  background-image:
    linear-gradient(rgba(34, 34, 34, .35), rgba(34, 34, 34, .35)),
    var(--trait-body);
  background-size: 100% 100%;
  background-position: center;
  background-repeat: no-repeat;
}

/* --- Marketplace > Mine: five intent groups (#583) --- */
.mine-group {
  margin-top: 12px; text-align: left;
  border: 1px solid var(--line); border-radius: 10px; background: var(--surface);
}
/* The whole summary row is the hit target — same pattern as .odds-slot-title. */
.mine-group-title {
  display: flex; align-items: center; gap: 8px;
  margin: 0; padding: 10px 12px; font-size: .95rem; font-weight: 700;
  color: var(--text); cursor: pointer; list-style: none; user-select: none;
  border-radius: 10px; min-height: 40px; box-sizing: border-box;
}
.mine-group-title::-webkit-details-marker { display: none; }
.mine-group-title::after {
  content: '\25be'; margin-left: auto; color: var(--muted); font-size: .9rem;
  transition: transform .15s ease;
}
.mine-group[open] > .mine-group-title::after { transform: rotate(180deg); }
.mine-group[open] > .mine-group-title { border-bottom: 1px solid var(--line); border-radius: 10px 10px 0 0; }
.mine-group-title:hover { background: var(--surface-2); }
.mine-group-title:focus-visible { outline: 2px solid var(--accent, var(--text)); outline-offset: 2px; }
.mine-group-name { min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.mine-group-count { flex: 0 0 auto; color: var(--muted); font-size: .75rem; font-weight: 600; }
.mine-group-body { padding: 10px 12px; }

/* Rows: offers and trade events, where art identifies nothing. */
.mine-rows { display: flex; flex-direction: column; gap: 8px; }
.mine-row {
  display: grid; grid-template-columns: 44px 1fr auto auto; gap: 10px;
  align-items: center; padding: 6px 8px; border-radius: 8px; background: var(--surface-2);
  grid-template-areas: 'art label price action';
}
.mine-row img, .mine-row video {
  grid-area: art; width: 44px; height: 44px; object-fit: contain;
  border-radius: 6px; background: var(--surface);
}
.mine-row-label { grid-area: label; min-width: 0; color: var(--text); font-size: .85rem; }
.mine-row-sub { display: block; color: var(--muted); font-size: .68rem; margin-top: 2px; }
.mine-row-price { grid-area: price; color: var(--yellow); font-size: .78rem; font-weight: 700; white-space: nowrap; }
.mine-row .mine-action { grid-area: action; }
.mine-row.dim { opacity: .45; }
@media (max-width: 420px) {
  .mine-row {
    grid-template-columns: 44px 1fr;
    grid-template-areas: 'art label' 'price price' 'action action';
    row-gap: 6px;
  }
  .mine-row .mine-action { width: 100%; }
}

/* Cards: things you hold or are selling, where the art is the point. */
.nft-card .mine-card-price { display: block; color: var(--yellow); font-size: .72rem; font-weight: 700; margin-top: 2px; }
.nft-card .mine-card-badge { display: block; color: var(--muted); font-size: .62rem; font-weight: 700; margin-top: 1px; }
.mine-action {
  min-height: 40px; padding: 6px 12px; margin: 0;
  font-size: .78rem; font-weight: 700; border-radius: 8px;
}
/* A card footer button is flush inside .nft-card's `overflow: hidden`, so the
   .secondary sticker ring (--sticker-sm = a 2px --paper ring + a 3px offset)
   gets clipped by the card edge and reads as one or two stray white lines,
   differing card to card. The footer keeps only the dark rule that separates
   it from the caption. :active keeps its press feedback without the ring. */
.nft-card .mine-action,
.nft-card .mine-action:hover,
.nft-card .mine-action:disabled {
  width: 100%;
  border-radius: 0;
  border: 0;
  border-top: 3px solid var(--ink);
  box-shadow: none;
}
.nft-card .mine-action:active { box-shadow: none; transform: translateY(1px); }

/* An inline group is bounded by its cap, so it must not also be a scroller —
   six nested scroll containers inside the page scroll is the mobile trap. */
.nft-grid.mine-cards { max-height: none; overflow: visible; }

.mine-show-all { margin: 10px 0 0; display: block; width: 100%; min-height: 40px; }
.mine-note { margin: 10px 0 0; font-size: .85rem; color: var(--muted); }
#mine-all-search { width: 100%; margin: 0 0 10px; }
#mine-all-title { margin: 0 0 8px; }
