/* CardForCard — design system (single source of truth, kept in this file).
   FONT: system stack below. The fallback order is intentional — do NOT reorder it.
         No global line-height is set (by design).
   WEIGHTS — the ONLY four allowed anywhere:
         700 page titles / stats   600 card names / section headers
         500 labels / buttons / nav / meta   400 body / descriptions
   THEME: dark only, near-black.   ACCENT: emerald (the single pop of color).
   NO BORDERS anywhere — separate with surface shades + spacing, never lines.
   INTERACTION: no hover. Press/tap dims the object (brightness ~0.85). Input focus
                brightens the field background (no focus border). */

:root {
  --bg: #0A0A0B;
  --surface: #161618;
  --elevated: #1F1F23;
  --field-focus: #26262A;
  --text: #F4F4F5;
  --muted: #A1A1AA;
  --faint: #71717A;
  --accent: #10B981;
  --accent-hover: #34D399;
  --accent-press: #059669;
  --on-accent: #04140E;
  --danger: #F87171;
  --danger-surface: #2A1517;
  --warning: #FBBF24;
  --r-sm: 8px;
  --r-md: 12px;
  --r-lg: 16px;
  --font: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, system-ui, sans-serif, Oxygen-Sans, Ubuntu, Cantarell;
}

* {
  margin: 0;
  padding: 0;
  box-sizing: border-box;
  font-family: var(--font);
  letter-spacing: 0.01em;
  -webkit-tap-highlight-color: transparent;
  -webkit-font-smoothing: antialiased;
  text-rendering: optimizeLegibility;
}

body {
  min-height: 100dvh;
  background: var(--bg);
  color: var(--text);
  font-size: 16px;
  font-weight: 400;
}

/* native-app feel: no text selection, no long-press callout, no image drag */
html, body {
  -webkit-user-select: none;
  user-select: none;
  -webkit-touch-callout: none;
  overscroll-behavior: none;   /* kill the rubber-band bounce that otherwise drags the whole app (incl. the nav) on overscroll */
}
/* detail pages frame themselves (see the #app:has(> .builder-bar) rules near .builder): the bar is pinned and only the
   content scrolls, so the body itself never scrolls — the global overscroll-behavior: none just kills any body bounce. */
input, textarea { -webkit-user-select: text; user-select: text; }   /* but keep fields editable/selectable */
img { -webkit-user-drag: none; user-select: none; }   /* no explicit pointer-events → images inherit it, so a sliding .page.anim or busy sheet (pointer-events:none) actually blocks taps on them too */

input, button { font-size: 16px; color: var(--text); }
button { cursor: pointer; transition: filter 0.125s; }
button:active { filter: brightness(0.75); }  /* press = dim. no hover; works on touch + desktop */
a { color: var(--accent); text-decoration: none; }

/* --- Auth screen: a standard slide page (builder-bar frame) with the card centered in the scroll area --- */
.builder.auth { display: flex; padding-bottom: 40px; }   /* replaces .builder's 110px foot reserve; slight lift for optical centering */
.auth-card {
  width: 100%;
  max-width: 360px;
  display: flex;
  flex-direction: column;
  gap: 8px;
  margin: auto;   /* flex auto-margins: centered when it fits, top-aligned (not clipped) when the keyboard squeezes it */
}
.auth-logo { height: 52px; width: auto; margin: 0 auto 2px; display: block; }
.auth .field { gap: 4px; }
.auth .field[data-field="username"] { margin-bottom: 18px; }   /* signup: username stands alone; email + password sit together as the credentials block */
.auth .tag { margin-bottom: 12px; }          /* logo + description sit as their own block above the form */
.auth .field-msg:empty { display: none; }   /* no reserved error space — the fields sit tight until an error appears */
.auth .btn { margin-top: 30px; }   /* clearly detached from the input stack — it's an action, not another field */
.code-input { text-align: center; letter-spacing: 8px; font-size: 20px; font-weight: 600; }
.forgot { align-self: flex-start; font-size: 13px; color: var(--muted); }   /* sits left under the password field, quiet — not competing with the Log in button */
.auth .switch { margin-top: 14px; }
.brand-logo { height: 21px; width: auto; display: block; }
/* language switcher: quiet link on the auth screen; a checked list in the picker sheet */
.lang-switch { display: block; margin: 18px auto 0; color: var(--muted); font-size: 13px; }
.lang-list { display: flex; flex-direction: column; }
.lang-item { display: flex; align-items: center; justify-content: space-between; gap: 12px; padding: 15px 2px; background: none; border: none; color: var(--text); font-size: 16px; text-align: left; cursor: pointer; }
.lang-item + .lang-item { border-top: 1px solid var(--elevated); }
.lang-item.on { color: var(--accent); font-weight: 600; }
.lang-item svg { width: 20px; height: 20px; flex: none; }
.tag { color: var(--muted); text-align: center; margin-top: -4px; }

.field { display: flex; flex-direction: column; gap: 6px; }
.field label { font-size: 14px; font-weight: 500; color: var(--muted); }
.field input {
  background: var(--surface);
  border: none;
  border-radius: var(--r-md);
  padding: 13px 14px;
  outline: none;
  transition: background 0.15s;
}
.field input:focus { background: var(--field-focus); }  /* focus = brighter fill, not a border */
.field-msg { font-size: 13px; color: var(--danger); min-height: 16px; line-height: 16px; }
.field.invalid label { color: var(--danger); }
.field.invalid input, .field.invalid textarea { background: var(--danger-surface); }
.field.invalid input:focus, .field.invalid textarea:focus { background: #3A1E22; }
.field.invalid .photo { background: var(--danger-surface); color: var(--danger); }

.btn {
  width: 100%;
  background: var(--accent);
  color: var(--on-accent);
  border: none;
  border-radius: var(--r-md);
  padding: 14px;
  font-size: 15px;
  font-weight: 600;
}
.btn-sm { width: auto; padding: 10px 16px; font-size: 14px; }

.switch { text-align: center; color: var(--muted); font-size: 14px; }
.link { background: none; border: none; color: var(--accent); font-weight: 500; font-size: 14px; padding: 0; }
.link:disabled { filter: brightness(0.6875); cursor: default; }
.btn:disabled { filter: brightness(0.6875); cursor: default; }
#f-apply, #f-reset { transition: filter 0.15s ease; }   /* smooth dim when the filter buttons enable/disable (matches the slider) */
/* loading button: keeps its label and runs the skeleton shimmer across itself while the rest of the form is dimmed */
.btn.loading { position: relative; overflow: hidden; }
.btn.loading::after { content: ""; position: absolute; inset: 0; transform: translateX(-100%); background: linear-gradient(90deg, transparent, rgba(255, 255, 255, 0.22), transparent); animation: shimmer 1.25s infinite; }

/* --- Top bar + home --- */
.topbar { display: flex; align-items: center; justify-content: space-between; padding: 14px 20px; position: sticky; top: 0; background: var(--bg); z-index: 10; }
.brand-sm { font-size: 18px; }
.chip { background: var(--surface); color: var(--text); border: none; border-radius: 999px; padding: 8px 16px; font-size: 14px; font-weight: 500; }
.topbar .chip { border-radius: var(--r-md); }   /* the logged-out Log in button matches the app's standard 12px rounding */
.acct { color: var(--muted); font-size: 14px; }
.muted { color: var(--muted); }

/* --- App shell: fixed frame, only .content scrolls (so the top bar + bottom nav never move, even on overscroll) --- */
.shell { height: 100dvh; display: flex; flex-direction: column; }
/* Cold-start only (added once by showShell): fade the chrome in so the logo + nav don't pop in abruptly. Opacity only —
   a transform here would break the fixed bottom nav's positioning. */
.shell.boot-in { animation: bootIn 0.4s ease both; }
@keyframes bootIn { from { opacity: 0; } to { opacity: 1; } }
@media (prefers-reduced-motion: reduce) { .shell.boot-in { animation: none; } }
.shell .topbar { position: static; flex: none; }
.shell .bottomnav { position: static; flex: none; }
/* width:100% + border-box: fill the shell width. Without this, the auto side-margins below cancel the
   flex stretch and .content shrinks to its content — making wide rows (e.g. the search header) overflow. */
.shell .content { flex: 1; min-height: 0; width: 100%; box-sizing: border-box; overflow-y: auto; overscroll-behavior-y: contain; padding-bottom: 20px; scrollbar-width: none; }   /* no -webkit-overflow-scrolling: touch — its separate momentum layer flickers under the page-slide transform on iOS; momentum scrolling is native on iOS 13+ */
.shell .content::-webkit-scrollbar { display: none; }   /* the tabfill keeps it pullable, but hide the bar so there's no scrollbar when there's nothing real to scroll */
.tabfill { display: flex; flex-direction: column; min-height: calc(100% + 1px); }   /* flex column so an empty state fills the leftover height instead of stacking a fixed block below the header; +1px always overflows the scroller by a hair so a short tab still rubber-bands */
.tabfill > :last-child:has(.empty-center) { flex: 1; display: flex; flex-direction: column; }   /* the empty-state sentinel grows to take the space under the header/filters (no phantom scroll) */
.screen { max-width: 720px; margin: 0 auto; padding: 24px; }
.content { max-width: 760px; margin: 0 auto; padding: 16px 16px 88px; }
.h1 { font-size: 22px; font-weight: 700; margin-bottom: 8px; }
.small { font-size: 13px; }
.empty { margin-top: 24px; }
.row-between { display: flex; align-items: center; justify-content: space-between; gap: 12px; margin-bottom: 4px; }
.empty-center { flex: 1; min-height: 0; display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 14px; text-align: center; }
.btn-wide { width: auto; padding: 14px 28px; }

/* bottom nav (3 tabs; no borders, separated by background) */
.bottomnav { position: fixed; left: 0; right: 0; bottom: 0; display: flex; background: var(--bg); padding-bottom: env(safe-area-inset-bottom); z-index: 20; }
.navitem { position: relative; flex: 1; display: flex; flex-direction: column; align-items: center; gap: 3px; background: none; border: none; color: var(--faint); font-size: 11px; font-weight: 500; padding: 8px 0; }
.nav-dot { position: absolute; top: 4px; left: calc(50% + 7px); width: 8px; height: 8px; border-radius: 50%; background: var(--accent); }   /* "something new" badge on the Trades icon */
.navitem svg { width: 27px; height: 27px; }
.navitem.active { color: var(--accent); }
.navitem.locked { opacity: 0.5; }   /* logged out: Inventory + Trades are dimmed — tapping them opens the login page */

/* inventory grid — full front image with the name overlaid bottom-left (no frame) */
.inv-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(140px, 1fr)); gap: 12px; margin-top: 14px; }
.card-tile { position: relative; background: none; border: none; padding: 0; border-radius: var(--r-md); overflow: hidden; display: block; }
.card-tile img { width: 100%; aspect-ratio: 5 / 7; object-fit: cover; display: block; background: var(--elevated); }
.card-name { position: absolute; left: 10px; right: 10px; bottom: 8px; color: #fff; font-size: 14px; font-weight: 600; text-align: left; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; text-shadow: 0 1px 5px rgba(0, 0, 0, 0.85); }
.card-status { font-size: 14px; font-weight: 500; color: var(--faint); text-transform: capitalize; }
.card-status.listed { color: var(--accent); }
.card-status.big { font-size: 14px; }

/* bottom sheet (iOS-style: drag handle, drag-to-dismiss, spring-like ease) */
/* dim via opacity on a pseudo (GPU-composited) — NOT background-color, which repaints the whole page each frame and flashes the images on mobile */
.scrim { position: fixed; inset: 0; display: flex; align-items: flex-end; justify-content: center; z-index: 50; }
.scrim::before { content: ""; position: absolute; inset: 0; background: rgba(0, 0, 0, 0.5); opacity: 0; transition: opacity 0.5s cubic-bezier(0.32, 0.72, 0, 1); }
.scrim.open::before { opacity: 1; }
/* full-bleed panel on desktop; the inner content column (.sheet-body) is capped + centered below, so the
   surface spans the whole width while the content stays a readable 520px column. On phones it's full width either way. */
.sheet { width: 100%; max-width: none; background: var(--surface); border-radius: 16px 16px 0 0; max-height: 92vh; display: flex; flex-direction: column; overflow: hidden; transform: translateY(calc(100% + var(--sheet-gap, 0px))); transition: transform 0.5s cubic-bezier(0.32, 0.72, 0, 1); }
/* phones only (full-width sheet → the sideways spread stays off-screen): a surface-coloured shadow continues the
   sheet downward, so a rubber-band swipe-up never bares the background. Rides with the transform, hidden otherwise.
   offset-y (130) > spread (100) keeps the shadow's top edge ~30px down, clear of the 16px rounded top corners. */
@media (max-width: 520px) { .sheet { box-shadow: 0 130px 0 100px var(--surface); } }
.sheet-handle { flex: none; display: flex; justify-content: center; align-items: center; min-height: 30px; padding: 12px 0 8px; background: var(--surface); cursor: default; touch-action: none; }
.grabber { width: 40px; height: 5px; border-radius: 3px; background: var(--faint); }
/* Wider screens: the sheet becomes a floating popup — narrower, all corners rounded, a gap above the bottom edge, and no
   grabber (a touch-only affordance). Placed after the base .sheet rules so these win. Same slide-up animation, surface
   colour and content as on mobile. */
@media (min-width: 521px) {
  .scrim { --sheet-gap: 30px; padding: 0 16px var(--sheet-gap); }   /* floats to ~the middle of the ~60px bottom nav behind it; --sheet-gap also makes the slide-down fully clear the gap */
  .sheet { max-width: 480px; border-radius: 16px; max-height: 86vh; padding-top: 12px; }
  .sheet-handle { display: none; }
}
/* busy lock: freeze + dim only the scrollable body while saving; the sticky header (handle + title) and footer (action button, with its shimmer) stay solid */
/* while the sheet is sliding in/out, block taps on its controls; the scrim still eats background taps underneath */
.scrim.locked .sheet { pointer-events: none; }
.sheet.busy .sheet-body { pointer-events: none; }
.sheet.busy .sheet-body > *:not(.sheet-foot):not(.sheet-title) { opacity: 0.4; transition: opacity 0.2s; }
.sheet-body { flex: 1; min-height: 0; width: 100%; max-width: 520px; margin: 0 auto; display: flex; flex-direction: column; }
.sheet-title { font-size: 18px; font-weight: 600; }
/* fixed sheet header/footer: the title and action button are pinned (flex:none); only the middle (.sheet-scroll) scrolls — no sticky "snap" */
.sheet-body > .sheet-title { flex: none; background: var(--surface); padding: 4px 20px 12px; }
.sheet-scroll { flex: 1; min-height: 0; overflow-y: auto; overflow-x: hidden; overscroll-behavior: contain; -webkit-overflow-scrolling: touch; display: flex; flex-direction: column; gap: 14px; padding: 8px 20px 20px; }
.sheet-foot { flex: none; background: var(--surface); display: flex; flex-direction: column; gap: 10px; padding: 14px 20px calc(16px + env(safe-area-inset-bottom)); }
.sheet-actions { display: flex; flex-direction: column; gap: 15px; align-items: center; }
.two { display: grid; grid-template-columns: 1fr 1fr; gap: 12px; }
.sheet .field input { background: var(--elevated); }
.sheet .field input:focus { background: var(--field-focus); }
.sheet .field.invalid input, .sheet .field.invalid textarea { background: var(--danger-surface); }
.sheet .field.invalid input:focus, .sheet .field.invalid textarea:focus { background: #3A1E22; }
.danger-link { color: var(--danger); }
.card-images { display: flex; flex-wrap: wrap; gap: 10px; justify-content: center; }
.card-meta { display: flex; flex-direction: column; gap: 12px; }
.meta-row { display: flex; flex-direction: column; gap: 2px; }   /* label stacked above its value */
.meta-k { color: var(--muted); font-size: 13px; font-weight: 500; }   /* one weight step up from the value for hierarchy */
.meta-v { color: var(--text); font-size: 15px; }
.card-desc { color: var(--muted); line-height: 1.5; white-space: pre-wrap; margin: 16px 0 0; }   /* grey secondary text, a bit more gap off the fields above */
/* open card screen only: progressively larger gaps down from the name */
.card-view .sheet-title { margin-top: 14px; }   /* a little gap below the images */
.card-view .owner-row { margin-top: 12px; }      /* gap between the name and the profile row */
.card-view .card-meta { margin-top: 26px; }      /* even bigger gap below the profile row, before the fields */
.ta { width: 100%; background: var(--elevated); border: none; border-radius: var(--r-md); padding: 12px 14px; color: var(--text); font-size: 16px; font-family: inherit; min-height: 72px; resize: none; outline: none; }
.ta:focus { background: var(--field-focus); }

/* Stripe card-input element (mounted iframe); style the container Stripe adds its StripeElement-* classes to.
   min-height reserves the field's final size so the box doesn't grow/jump when the iframe loads in. */
.card-wrap { position: relative; }
.card-field { min-height: 50px; background: var(--elevated); border-radius: var(--r-md); padding: 15px 14px; cursor: text; transition: background 0.15s; }
.card-field.StripeElement--focus { background: var(--field-focus); }
.card-field.StripeElement--invalid { background: var(--danger-surface); }
/* shimmer placeholder shown over the field until the secure input is ready (removed on Stripe's 'ready' event).
   inset:0 makes it cover the whole field box (can't collapse); scoped under .card-wrap so it beats the base .sk
   rule (position:relative + radius) defined later in this file. */
.card-wrap .card-sk { position: absolute; inset: 0; background: var(--elevated); border-radius: var(--r-md); pointer-events: none; transition: opacity 0.25s ease; }
.card-wrap .card-sk.hide { opacity: 0; }   /* fade out on ready so the field reveals smoothly instead of the input popping in */

/* trade-proof code box */
.code-box { background: var(--bg); border-radius: var(--r-md); padding: 14px; }
.code-label { font-size: 13px; color: var(--muted); }
.code { font-size: 26px; font-weight: 700; letter-spacing: 3px; color: var(--accent); margin-top: 6px; }
.code-row { display: flex; align-items: center; gap: 6px; margin-top: 6px; }
.code-row .code { margin-top: 0; min-height: 34px; display: flex; align-items: center; }

/* photo picker + detail image */
.photo { display: flex; align-items: center; justify-content: center; min-height: 140px; background: var(--elevated); border-radius: var(--r-md); color: var(--muted); font-size: 14px; cursor: pointer; overflow: hidden; }
.photo img { display: none; width: 100%; max-height: 300px; object-fit: contain; }
.photo-card { aspect-ratio: 5 / 7; min-height: 0; }              /* shaped like a vertical trading card */
.photo-card img { width: 100%; height: 100%; max-height: none; object-fit: cover; }
.card-full { width: calc(50% - 5px); aspect-ratio: 5 / 7; object-fit: cover; border-radius: var(--r-md); background: var(--elevated); }   /* fill the box so the rounding hugs the image (like the thumbnails); the box is reserved up front → no layout shift */
.card-full.wide { width: 100%; aspect-ratio: auto; }
.zoomable { cursor: pointer; }

/* fullscreen image viewer */
.lightbox { position: fixed; inset: 0; z-index: 90; background: rgba(0, 0, 0, 0.92); display: flex; align-items: center; justify-content: center; cursor: pointer; opacity: 0; transition: opacity 0.2s; }
.lightbox.open { opacity: 1; }
.lightbox img { width: 100%; height: 100%; object-fit: contain; }
.lightbox-btn { position: absolute; left: 50%; bottom: calc(28px + env(safe-area-inset-bottom)); transform: translateX(-50%); width: auto; padding: 12px 26px; cursor: pointer; }

/* offer builder + offers */
.builder-bar { display: flex; align-items: center; justify-content: space-between; padding: 14px 16px; position: sticky; top: 0; background: var(--bg); z-index: 10; }
.builder { max-width: 760px; margin: 0 auto; padding: 0 16px 110px; }
.builder-foot { position: fixed; left: 0; right: 0; bottom: 0; max-width: 760px; margin: 0 auto; padding: 12px 16px calc(12px + env(safe-area-inset-bottom)); background: var(--bg); }
/* Detail pages (card view, profile, settings, auth, trade/proof/offer views, trade builder) are a fixed frame like the
   shell: the .builder-bar stays pinned and only .builder scrolls, so it rubber-bands (overscroll: contain) WITHOUT the
   bar ever being dragged off-screen. Direct-child selector skips the chat (.chat-screen), which frames itself. The
   .page container itself is the fixed viewport frame (see the .page rules near the bottom). */
.page:has(> .builder-bar) { display: flex; flex-direction: column; }
.page:has(> .builder-bar) > .builder-bar { position: static; flex: none; }
.page:has(> .builder-bar) > .builder { flex: 1; min-height: 0; width: 100%; box-sizing: border-box; overflow-y: auto; overscroll-behavior-y: contain; scrollbar-width: none; position: relative; }
/* iOS only rubber-bands a nested scroller when it actually overflows, so a short detail page (Settings, card/trade
   view, auth…) otherwise feels dead-fixed. This 1px spacer just past the bottom edge keeps it always a hair scrollable
   → it elastic-bounces when pulled, with no layout effect (overscroll:contain keeps the bounce local). Matches .tabfill. */
.page:has(> .builder-bar) > .builder::after { content: ""; position: absolute; top: 100%; width: 1px; height: 1px; pointer-events: none; }
.page:has(> .builder-bar) > .builder::-webkit-scrollbar { display: none; }
.builder-foot .btn { width: 100%; }
.add-tile { background: var(--surface); border: none; border-radius: var(--r-md); min-height: 170px; display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 6px; color: var(--muted); font-size: 14px; font-weight: 500; }
.add-card-tile { width: 140px; aspect-ratio: 5 / 7; min-height: 0; }   /* shaped like a real card tile */
.add-plus { font-size: 30px; line-height: 1; color: var(--accent); }
.card-tile.sel img { filter: brightness(0.62); }   /* selected-for-trade cards dim */
/* selection badge: a read-only indicator (filled when selected); pointer-events off so taps fall through to open the card detail */
.sel-toggle { position: absolute; top: 6px; right: 6px; width: 26px; height: 26px; border-radius: 50%; display: flex; align-items: center; justify-content: center; background: rgba(10, 10, 11, 0.45); border: 2px solid rgba(255, 255, 255, 0.85); color: transparent; pointer-events: none; }
.sel-toggle svg { width: 15px; height: 15px; }
.sel-toggle.on { background: var(--accent); border-color: var(--accent); color: var(--on-accent); }
/* inline header search: fills the gap between the title and the actions; × clears then closes */
/* the search opens as an overlay ON TOP of the title (which lives in .title-wrap), so the title hides
   behind it and — being out of flow — opening it never resizes or nudges the trailing buttons */
.tab-head { min-height: 40px; }   /* fixed row height so hiding the 40px trigger icon on open can't shrink the row */
.title-wrap { position: relative; flex: 1; min-width: 0; }
.tab-head .h1 { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.tab-head .actions { flex: none; align-self: stretch; }   /* fill the fixed row height so the button keeps its size when the icon hides */
.head-search { position: absolute; left: 0; right: 0; top: 50%; transform: translateY(-50%); height: 40px; z-index: 2; box-sizing: border-box; display: none; min-width: 0; align-items: center; gap: 6px; background: var(--surface); border-radius: var(--r-md); padding: 0 4px 0 12px; }
.head-search.open { display: flex; }
.head-search > svg { width: 18px; height: 18px; color: var(--muted); flex: none; }
.head-search input { flex: 1; min-width: 0; background: none; border: none; color: var(--text); font-size: 15px; padding: 9px 0; }
.head-search input:focus { outline: none; }
.head-search input::placeholder { color: var(--faint); }
.head-search .clear, .cat-search .clear { flex: none; background: none; border: none; color: var(--muted); width: 30px; height: 30px; padding: 0; display: flex; align-items: center; justify-content: center; }
.head-search .clear svg, .cat-search .clear svg { width: 18px; height: 18px; }

.jump-list { flex: 1; min-width: 0; display: flex; align-items: center; gap: 6px; overflow-x: auto; scrollbar-width: none; min-height: 28px; }   /* sits in the search row, left of the icon; the open search field overlays it */
.jump-list::-webkit-scrollbar { display: none; }
.name-chip { flex: none; max-width: 150px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; background: var(--elevated); color: var(--muted); border: none; border-radius: 999px; padding: 5px 12px; font-size: 12.5px; font-weight: 600; }
.btn.btn-soft { background: var(--elevated); color: var(--text); }
/* segmented toggle (trade builder) */
.seg { display: flex; align-items: center; gap: 4px; background: var(--surface); border-radius: var(--r-md); padding: 4px; margin: 4px 0 12px; }
.seg button { flex: 1; background: none; border: none; border-radius: var(--r-sm); padding: 9px 8px; color: var(--muted); font-size: 14px; font-weight: 600; }
.seg button.on { background: var(--elevated); color: var(--text); }
.seg-x { flex: none; display: flex; align-items: center; justify-content: center; color: var(--faint); padding: 0 2px; }
.seg-x svg { width: 17px; height: 17px; }
.trade-searchbar { position: relative; display: flex; align-items: center; gap: 8px; min-height: 40px; margin-bottom: 6px; }   /* selected-cards chip list (left) + search icon (right); the search field overlays the chips when opened */
.tv-left { flex: 1; min-width: 0; display: flex; align-items: center; overflow: hidden; }   /* trade view: holds the "Possession proof" link, left of the search */
/* infinite-scroll spinner */
.spin { width: 26px; height: 26px; margin: 18px auto; border-radius: 50%; border: 3px solid var(--elevated); border-top-color: var(--accent); animation: spin 0.7s linear infinite; }
@keyframes spin { to { transform: rotate(360deg); } }
@media (prefers-reduced-motion: reduce) { .spin { animation-duration: 2s; } }
.section-h { font-size: 15px; font-weight: 600; margin: 18px 0 6px; }
.card-pub { position: absolute; top: 6px; left: 6px; background: var(--accent); color: var(--on-accent); font-size: 11px; font-weight: 600; padding: 2px 7px; border-radius: 999px; }
/* distance-to-owner pill on Exchange tiles (dark, reads on any photo; never coexists with .card-pub, which is self-only) */
.card-dist { position: absolute; top: 6px; left: 6px; display: inline-flex; align-items: center; gap: 3px; background: rgba(10, 10, 11, 0.6); color: #fff; font-size: 11px; font-weight: 600; padding: 2px 7px 2px 5px; border-radius: 999px; opacity: 0.5; }
.card-dist svg { width: 12px; height: 12px; }
.actions { display: flex; gap: 8px; }

/* loading skeletons (shimmer sweep) */
.sk { position: relative; overflow: hidden; background: var(--surface); border-radius: var(--r-md); }
.sk::after { content: ""; position: absolute; inset: 0; transform: translateX(-100%); background: linear-gradient(90deg, transparent, rgba(255, 255, 255, 0.06), transparent); animation: shimmer 1.25s infinite; }
@keyframes shimmer { 100% { transform: translateX(100%); } }
.sk-card { width: 140px; aspect-ratio: 5 / 7; }
.sk-title { width: 120px; height: 15px; border-radius: 6px; margin-bottom: 10px; }
.sk-code { display: inline-block; width: 150px; height: 28px; border-radius: 6px; vertical-align: middle; }
.sk-row { height: 92px; margin-bottom: 10px; }
@media (prefers-reduced-motion: reduce) { .sk::after { animation: none; } }

/* set-grouped shelves: two rows, horizontal scroll + arrow centered between the rows */
.board { margin-top: 28px; }
.tab-board { margin-top: 36px; }   /* Inventory/Exchange: gap replacing the removed subtitle line */
.board .shelf:first-child { margin-top: 0; }   /* board's top gap controls header→first-set; sets handle the rest */
.shelf { margin-top: 24px; }
.shelf-title { font-size: 17px; font-weight: 500; color: var(--muted); margin-bottom: 11px; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.shelf-body { position: relative; }
.shelf-track { display: flex; gap: 12px; overflow-x: auto; scroll-snap-type: x proximity; scrollbar-width: none; }
.shelf-track::-webkit-scrollbar { display: none; }
/* each page holds a screen's worth of 2 rows; tiles fill the top row left→right, then the second row, then the next page.
   Sized to its own content (not 100%), so pages butt together with a uniform 12px gap — the columns line up seamlessly. */
.shelf-page { flex: none; scroll-snap-align: start; display: grid; grid-template-columns: repeat(var(--cols, 3), 140px); align-content: start; gap: 12px; }
/* every card uses the SAME reserved 5/7 box; the image keeps its own shape inside it, centered, empty space transparent */
.shelf-card { width: 140px; aspect-ratio: 5 / 7; display: flex; align-items: center; justify-content: center; overflow: visible; }
.card-thumb { position: relative; display: inline-block; }   /* hugs the image, so rounding + name attach to the IMAGE */
.shelf-card img { display: block; width: auto; height: auto; max-width: 140px; max-height: 196px; aspect-ratio: auto; object-fit: contain; border-radius: var(--r-md); background: transparent; }
.shelf-arrow { position: absolute; top: 50%; transform: translateY(-50%); width: 34px; height: 34px; border: none; border-radius: 50%; background: var(--surface); color: var(--text); display: flex; align-items: center; justify-content: center; box-shadow: 0 2px 12px rgba(0, 0, 0, 0.55); }
.shelf-arrow.next { right: 6px; }
.shelf-arrow.prev { left: 6px; }
.shelf-arrow svg { width: 18px; height: 18px; }

/* set field autocomplete */
.set-field { position: relative; }
.set-sugg { position: absolute; left: 0; right: 0; top: 100%; margin-top: 4px; background: var(--elevated); border-radius: var(--r-md); overflow: hidden; z-index: 5; display: none; }
.set-sugg.show { display: block; }
.set-sugg-item { display: block; width: 100%; text-align: left; background: none; border: none; padding: 11px 14px; color: var(--text); font-size: 15px; }
.set-sugg-item:active { background: var(--field-focus); }

/* optional add-card fields: same spacing as the rest, just pushed down a touch to set them apart from the required ones */
.optional-group { display: flex; flex-direction: column; gap: 14px; margin-top: 10px; }

.danger-btn { background: var(--danger); color: #2A0E0E; }

/* trades + chat */
.trade-row { display: flex; flex-direction: column; align-items: stretch; gap: 10px; width: 100%; background: var(--surface); border: none; border-radius: var(--r-md); padding: 12px; margin-bottom: 10px; text-align: left; }
.trade-top { display: flex; align-items: center; gap: 8px; }
.unseen-dot { flex: none; width: 8px; height: 8px; border-radius: 50%; background: var(--accent); }   /* unopened trade marker, left of the status pill */
.trade-with { margin-left: auto; color: var(--muted); font-size: 13px; font-weight: 500; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
/* status: solid filled pill like the inventory "Public" badge, but in each status's own colour */
.trade-status { font-size: 11px; font-weight: 600; padding: 3px 9px; border-radius: 999px; background: var(--elevated); color: var(--muted); }
.trade-status.t-accent { background: var(--accent); color: var(--on-accent); }
.trade-status.t-done { background: var(--elevated); color: var(--muted); }
.trade-side { flex: 1; min-width: 0; display: flex; flex-direction: column; gap: 6px; }
/* desktop: don't let each offer stretch the full 760px column (that left the cards marooned in a sea of empty
   surface). Hug the offer to a tidy width and centre it, and let the two sides flex to fill it — so the card
   images use the offer's background instead of floating small in the middle, without ballooning to ~2x phone size. */
@media (min-width: 600px) {
  .trade-row { max-width: 480px; margin: 0 auto 10px; }
  .trade-cards { gap: 20px; }
}
.trade-side-label { font-size: 12px; font-weight: 600; color: var(--muted); }
/* trades-tab status filter pills (below the title) */
.seg.trade-dir { margin-top: 14px; }   /* a bit more breathing room below the "Trades" title */
.trade-filters { display: flex; flex-wrap: wrap; gap: 8px; margin: 2px 0 24px; }   /* bigger gap below the status pills, before the list */
.filter-pill { position: relative; display: inline-flex; align-items: center; gap: 6px; background: var(--surface); color: var(--muted); border: none; border-radius: 999px; padding: 7px 14px; font-size: 13px; font-weight: 600; }
.filter-pill.on { background: var(--accent); color: var(--on-accent); }
.pill-count { font-size: 12px; font-weight: 600; opacity: 0.55; }   /* how many trades are in this filter (per the current Sent/Received tab) */
.filter-pill.on .pill-count { opacity: 0.8; }
.pill-count:empty { display: none; }
.pill-dot { position: absolute; top: -1px; right: -1px; width: 8px; height: 8px; border-radius: 50%; background: var(--text); }   /* "new on this filter" badge — white so it's visible on the grey pill, the emerald active pill, AND the dark bg it overhangs (no state-dependent color) */
/* chat "edit" widget: a compact, side-aligned clickable bubble that opens the diff view */
.edit-widget { display: flex; align-items: center; gap: 9px; width: fit-content; max-width: 82%; align-self: flex-start; background: var(--elevated); border: none; border-radius: var(--r-md); padding: 9px 12px; text-align: left; color: var(--text); }
.edit-widget.mine { align-self: flex-end; }
.ew-icon { flex: none; display: flex; color: var(--accent); }
.ew-icon svg { width: 17px; height: 17px; display: block; }
.ew-text { min-width: 0; font-size: 14px; display: flex; flex-direction: column; gap: 2px; }
.ew-sub { color: var(--muted); font-size: 12px; }
.ew-chev { flex: none; color: var(--muted); display: flex; }
.ew-chev svg { width: 18px; height: 18px; }
/* edit-diff offer view: added cards = green outline, removed = ghosted; jump chips green (added) / grey (removed) */
.diff-added .card-thumb { outline: 2px solid var(--accent); outline-offset: 1px; border-radius: var(--r-md); }
.diff-ghost { opacity: 0.4; }
.diff-ghost .card-thumb { filter: grayscale(0.55); }
.diffboard .shelf-track { padding: 4px; }   /* breathing room so the leftmost added card's outline isn't clipped at the track edge */
.name-chip.chip-add { background: var(--accent); color: var(--on-accent); }
.name-chip.chip-rem { background: var(--elevated); color: var(--muted); }
/* diff jump chips: added row above, removed row below; an empty row collapses so a lone row centers on the search line */
.jump-stack { flex: 1; min-width: 0; display: flex; flex-direction: column; gap: 6px; }
.jump-row { display: flex; align-items: center; gap: 6px; overflow-x: auto; scrollbar-width: none; min-height: 28px; }
.jump-row::-webkit-scrollbar { display: none; }
.jump-row:empty { display: none; }
.trade-searchbar:has(.head-search.open) .jump-stack { visibility: hidden; }   /* opening the search field cleanly hides the chip rows behind it */
.tm-cell .tm-name { display: flex; align-items: center; justify-content: center; text-align: center; width: 100%; aspect-ratio: 5 / 7; border-radius: 6px; background: var(--elevated); color: var(--muted); font-size: 12px; padding: 4px; box-sizing: border-box; }
.trade-cards { display: flex; align-items: center; gap: 10px; }
.trade-mini { display: grid; grid-template-columns: 1fr 1fr; gap: 4px; }
.tm-cell { position: relative; }
.tm-cell img { width: 100%; aspect-ratio: 5 / 7; object-fit: cover; border-radius: 6px; display: block; background: var(--elevated); }
.tm-more { position: absolute; inset: 0; background: rgba(0, 0, 0, 0.55); border-radius: 6px; display: flex; align-items: center; justify-content: center; color: #fff; font-weight: 600; font-size: 13px; }
.trade-swap { color: var(--muted); flex: none; }
.trade-swap svg { width: 22px; height: 22px; }
.chat { display: flex; flex-direction: column; gap: 6px; max-height: 320px; overflow-y: auto; padding: 4px 0; }
.bubble { max-width: 80%; padding: 8px 12px; border-radius: 14px; font-size: 15px; line-height: 1.4; word-wrap: break-word; }
.bubble.mine { align-self: flex-end; background: var(--accent); color: var(--on-accent); }
.bubble.theirs { align-self: flex-start; background: var(--elevated); color: var(--text); }
.sys-msg { align-self: center; max-width: 90%; text-align: center; font-size: 12.5px; color: var(--muted); background: var(--surface); padding: 5px 12px; border-radius: 999px; }
.done-banner { display: flex; align-items: center; gap: 8px; background: var(--surface); border: 1px solid var(--accent); color: var(--text); font-size: 14px; padding: 11px 13px; border-radius: var(--r-md); margin-bottom: 4px; }
.done-banner svg { width: 18px; height: 18px; color: var(--accent); flex: none; }
.done-banner.cancel { border-color: var(--elevated); }
.done-banner.cancel svg { color: var(--muted); }
/* --- legal links, report flow, admin reports inbox --- */
.legal-links { margin-top: 14px; text-align: center; font-size: 12px; color: var(--muted); line-height: 1.5; }
.legal-links a, .set-info a, .sheet-body .muted a { color: var(--muted); text-decoration: underline; text-underline-offset: 2px; }
.report-reason { width: 100%; margin-bottom: 8px; }
.report-row { background: var(--surface); border-radius: var(--r-md); padding: 12px; margin-bottom: 10px; }
.report-top { display: flex; gap: 10px; align-items: center; margin-bottom: 10px; }
.report-img { width: 44px; height: 62px; object-fit: cover; border-radius: 6px; background: var(--elevated); flex: none; }
.report-name { font-weight: 600; font-size: 14px; }
.report-row .actions { display: flex; gap: 8px; }
.set-delete { display: block; margin: 26px auto 0; font-size: 14px; }
.complete-note { margin-top: 10px; }
.accent-note { color: var(--accent); }
/* full-screen chat (accepted trades): fixed header + footer, only the message list scrolls */
.chat-screen { height: 100dvh; display: flex; flex-direction: column; }
.chat-screen .builder-bar { flex: none; }
.chat-title { flex: 1; min-width: 0; text-align: center; font-size: 16px; font-weight: 600; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.chat-full { flex: 1; min-height: 0; width: 100%; max-width: 760px; margin: 0 auto; overflow-y: auto; overscroll-behavior: contain; padding: 12px 16px; scrollbar-width: none; }   /* dropped -webkit-overflow-scrolling: touch (see .content) */
.chat-full::-webkit-scrollbar { display: none; }   /* hide the bar; the pull/scroll still works */
.chat-inner { display: flex; flex-direction: column; gap: 6px; min-height: calc(100% + 1px); }   /* always overflow by a hair → chat stays pullable even with few messages */
.chat-foot { flex: none; width: 100%; max-width: 760px; margin: 0 auto; display: flex; flex-direction: column; gap: 8px; padding: 8px 18px calc(18px + env(safe-area-inset-bottom)); background: var(--bg); }   /* bottom gap matches the sides */
.card-tile.gone { display: flex; align-items: center; justify-content: center; text-align: center; aspect-ratio: 5 / 7; background: var(--elevated); color: var(--muted); padding: 8px; box-sizing: border-box; }
.card-tile.gone .card-name { position: static; padding: 0; background: none; color: var(--muted); text-align: center; white-space: normal; text-shadow: none; }
.proof-photo { aspect-ratio: 5 / 7; min-height: 0; margin-bottom: 10px; }       /* card-shaped */
.proof-photo .photo-prev { height: 100%; max-height: 100%; object-fit: contain; }   /* show the whole shot — never crop out the code */
.proof-strip { display: flex; gap: 8px; overflow-x: auto; padding: 2px 0 10px; -webkit-overflow-scrolling: touch; }
.proof-thumb { flex: none; width: 56px; }
.proof-thumb img { width: 56px; height: 78px; object-fit: cover; border-radius: 6px; display: block; background: var(--elevated); }
.proof-thumb span { display: block; font-size: 11px; color: var(--muted); margin-top: 3px; text-align: center; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.proof-item { margin-bottom: 12px; }
.proof-label { font-size: 13px; color: var(--muted); margin-bottom: 6px; }
.proof-label b { color: var(--accent); font-weight: 700; letter-spacing: 1px; }
.proof-img { width: 100%; max-width: 320px; border-radius: var(--r-md); display: block; cursor: pointer; background: var(--elevated); }
.chat-box { position: relative; }
.chat-box input { width: 100%; box-sizing: border-box; background: var(--surface); border: none; border-radius: 999px; padding: 13px 52px 13px 18px; color: var(--text); outline: none; }   /* full width; right pad leaves room for the send button */
.chat-box input:focus { background: var(--field-focus); }
.chat-send { position: absolute; right: 5px; top: 50%; transform: translateY(-50%); width: 36px; height: 36px; border: none; border-radius: 50%; background: var(--accent); color: var(--on-accent); display: flex; align-items: center; justify-content: center; padding: 0; }
.chat-send svg { width: 18px; height: 18px; }

/* avatar, dropdown menu, profile, settings */
.avatar { width: 34px; height: 34px; border-radius: 50%; background: var(--elevated); color: var(--muted); display: inline-flex; align-items: center; justify-content: center; border: none; padding: 0; flex: none; }
.avatar svg { width: 22px; height: 22px; }
.avatar-lg { width: 84px; height: 84px; }
.avatar-lg svg { width: 54px; height: 54px; }
.iconbtn { background: none; border: none; color: var(--text); font-size: 22px; width: 52px; height: 40px; line-height: 1; display: inline-flex; align-items: center; justify-content: center; }
.iconbtn svg { width: 22px; height: 22px; }
#back svg { width: 27px; height: 27px; }   /* the back chevron reads a touch small at 22 — bump just it */
.iconbtn.on { color: var(--accent); }
.bar-actions { display: flex; align-items: center; }   /* group the card bar's Report + Share icons together on the right */
.menu-scrim { position: fixed; inset: 0; z-index: 60; }
.menu-scrim::before { content: ""; position: absolute; inset: 0; background: rgba(0, 0, 0, 0.3); opacity: 0; transition: opacity 0.2s ease; }
.menu-scrim.open::before { opacity: 1; }
.menu { position: fixed; min-width: 210px; background: var(--surface); border-radius: var(--r-lg); padding: 10px; display: flex; flex-direction: column; gap: 8px; box-shadow: 0 14px 36px rgba(0, 0, 0, 0.5); transform-origin: top right; transform: scale(0.9); opacity: 0; transition: transform 0.24s cubic-bezier(0.32, 0.72, 0, 1), opacity 0.18s ease; }
.menu-scrim.open .menu { transform: scale(1); opacity: 1; }
/* trigger (avatar) sits above the menu scrim (z 60) so it stays bright; translateZ(0) forces its own
   compositing layer so iOS Safari reliably paints it over the fixed dim. pointer-events:none → taps fall through to close */
.menu-lift { position: relative; z-index: 62; pointer-events: none; transform: translateZ(0); }
.menu-item { background: var(--elevated); border: none; text-align: left; padding: 13px 16px; border-radius: var(--r-md); color: var(--text); font-size: 16px; }
.menu-item.danger-link { color: var(--danger); }   /* keep the red on logout (beats the .menu-item colour) */
.menu-head { padding: 6px 16px 8px; color: var(--muted); font-size: 13px; font-weight: 600; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.owner-row { display: flex; align-items: center; gap: 10px; background: none; border: none; padding: 10px 0; color: var(--text); font-size: 15px; font-weight: 500; }
.profile-head { display: flex; flex-direction: column; align-items: center; gap: 10px; width: 100%; padding: 16px 0 18px; text-align: center; }
.profile-name { font-size: 18px; font-weight: 600; }

/* --- location: owner city on cards/profiles + the picker + the Exchange filter sheet --- */
.profile-loc { display: flex; align-items: center; justify-content: center; gap: 5px; margin-top: -4px; color: var(--muted); font-size: 14px; }
.profile-loc svg { width: 16px; height: 16px; flex: none; }
.loc-hint { margin: -2px 0 4px; }
.loc-search { display: flex; align-items: center; gap: 8px; background: var(--elevated); border-radius: var(--r-md); padding: 0 12px; }
.loc-search svg { width: 20px; height: 20px; color: var(--muted); flex: none; }
.loc-search input { flex: 1; min-width: 0; background: none; border: none; outline: none; color: var(--text); font-size: 16px; font-family: inherit; padding: 13px 0; }
.loc-item { justify-content: space-between; }
.loc-sub { color: var(--muted); font-size: 13px; font-weight: 400; text-align: right; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; margin-left: 10px; flex: none; max-width: 55%; }
.loc-clear { display: block; margin-top: 16px; }
.loc-none { padding: 12px 2px; }
.filter-sec { padding: 4px 0 6px; }
.filter-sec + .filter-sec { margin-top: 8px; }
.filter-h { font-size: 12px; color: var(--muted); font-weight: 600; letter-spacing: 0.04em; text-transform: uppercase; margin: 0 0 12px; }
.chips { display: flex; flex-wrap: wrap; gap: 8px; }
.chips .filter-pill { background: var(--elevated); }        /* on the sheet's --surface, plain pills need the lighter tone; .on stays accent */
.chips .filter-pill.on { background: var(--accent); }
.cat-head { position: relative; display: flex; align-items: center; gap: 8px; margin-bottom: 12px; }   /* label + chosen categories + search toggle on one row */
.cat-head .filter-h { margin: 0; flex: none; }
.cat-head .sel-chips { flex: 1; min-width: 0; margin: 0; }   /* chosen categories sit next to the label; hidden by JS when none */
.cat-toggle { flex: none; margin-left: auto; margin-right: -10px; }
.sel-chip { padding-left: 9px; }
.sel-chip svg { width: 14px; height: 14px; flex: none; margin-right: -1px; }
/* Search overlays the header row (absolute → no layout shift, so opening it doesn't jump the popup), like the app's head-search. */
.cat-search { position: absolute; inset: 0; z-index: 2; display: none; align-items: center; gap: 8px; background: var(--elevated); border-radius: var(--r-md); padding: 0 12px; }
.cat-search.open { display: flex; }                          /* collapsed until the search icon is pressed (like the app's other search fields) */
.cat-search > svg { width: 20px; height: 20px; color: var(--muted); flex: none; }
.cat-search input { flex: 1; min-width: 0; background: none; border: none; outline: none; color: var(--text); font-size: 16px; font-family: inherit; padding: 12px 0; }
.cat-list { max-height: 34vh; overflow-y: auto; align-content: flex-start; scrollbar-width: none; }
.cat-list::-webkit-scrollbar { display: none; }
.sk-chip { display: inline-block; height: 32px; border-radius: 999px; background: var(--elevated); }
#ex-filter { margin-left: -12px; }                           /* filter sits right of search; pull it closer to the search icon */
.dist-val { font-size: 15px; font-weight: 600; margin-bottom: 14px; }
.slider { -webkit-appearance: none; appearance: none; width: 100%; height: 4px; border-radius: 999px; background: var(--elevated); outline: none; margin: 6px 0 2px; }
.slider::-webkit-slider-thumb { -webkit-appearance: none; appearance: none; width: 24px; height: 24px; border-radius: 50%; background: var(--accent); cursor: pointer; transition: filter 0.15s ease; }
.slider::-moz-range-thumb { width: 24px; height: 24px; border: none; border-radius: 50%; background: var(--accent); cursor: pointer; transition: filter 0.15s ease; }
/* the range input isn't a <button>, so it misses the global button:active dim — give the thumb the same press feedback (eased, not instant) */
.slider:active::-webkit-slider-thumb { filter: brightness(0.75); }
.slider:active::-moz-range-thumb { filter: brightness(0.75); }
/* profile top bar: back (left) + search icon (right), with the search field unfurling across the middle */
.bar-search-wrap { position: relative; flex: 1; min-width: 0; height: 40px; }
.toggle { width: 46px; height: 28px; border-radius: 14px; background: var(--elevated); border: none; position: relative; flex: none; transition: background 0.15s; }
.toggle::after { content: ""; position: absolute; top: 3px; left: 3px; width: 22px; height: 22px; border-radius: 50%; background: var(--muted); transition: transform 0.15s, background 0.15s; }
.toggle.on { background: var(--accent); }
.toggle.on::after { transform: translateX(18px); background: var(--on-accent); }
.avatar-sm { width: 24px; height: 24px; }
.avatar-sm svg { width: 15px; height: 15px; }
.bar-spacer { width: 52px; }
.pencil { width: 40px; color: var(--muted); }
.pencil svg { width: 20px; height: 20px; }
.set-row { display: flex; align-items: center; justify-content: space-between; gap: 12px; background: var(--surface); border-radius: var(--r-md); padding: 12px 14px; margin-bottom: 10px; }
.set-row.flush { background: none; padding: 0; margin: 14px 0 0; }   /* card popup: drop the box (text lines up with the name/meta/image left edge) + extra gap above, off the fields */
.set-info { min-width: 0; }
.set-label { font-size: 13px; color: var(--muted); }
.set-value { font-size: 15px; margin-top: 2px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
/* iOS-style tappable settings row: whole row is the button, current value + chevron sit on the right */
.row-link { width: 100%; border: none; font: inherit; text-align: left; cursor: pointer; transition: background 0.12s; }
.row-link:active { background: var(--elevated); }
.row-title { font-size: 15px; color: var(--text); flex: 1; min-width: 0; }
.row-val { font-size: 15px; color: var(--muted); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.row-chev { display: flex; align-items: center; color: var(--muted); margin: 0 -4px 0 -2px; }
.row-chev svg { width: 18px; height: 18px; }

/* custom confirm dialog + toast (no browser alerts) */
.dialog-scrim { position: fixed; inset: 0; z-index: 70; background: rgba(0, 0, 0, 0.5); display: flex; align-items: center; justify-content: center; padding: 24px; }
.dialog { width: 100%; max-width: 320px; background: var(--surface); border-radius: var(--r-lg); padding: 20px; }
.dialog-msg { font-size: 15px; line-height: 1.5; margin-bottom: 18px; }
.dialog-actions { display: flex; gap: 10px; }
.dialog-actions .btn { flex: 1; }
.btn-ghost { flex: 1; background: var(--elevated); color: var(--text); border: none; border-radius: var(--r-md); padding: 12px; font-size: 14px; font-weight: 600; }
.toast { position: fixed; left: 50%; bottom: 88px; transform: translate(-50%, 12px); background: var(--elevated); color: var(--text); padding: 12px 18px; border-radius: var(--r-md); font-size: 14px; z-index: 80; opacity: 0; transition: opacity 0.25s, transform 0.25s; max-width: 90%; text-align: center; pointer-events: none; }
.toast.show { opacity: 1; transform: translate(-50%, 0); }

/* iOS-style page push/pop. Every page lives in its own full-viewport .page container (never reparented — that's
   what used to blank images on iOS mid-animation). During a slide the two containers stack and move on plain
   transforms; .anim blocks taps mid-flight. No edge shadow: on a near-black theme iOS's (already faint) push
   shadow is invisible anyway, and a dark glow just looks wrong — the parallax alone separates the pages. */
body { overflow-x: hidden; }
.page { position: fixed; inset: 0; overflow: hidden; background: var(--bg); }
.page.anim { pointer-events: none; will-change: transform; }
.page.swipe-top { z-index: 3; will-change: transform; }
