/* ==========================================================================
   P2P Escrow — Shared Design System
   Glassmorphism over a deep navy → violet gradient.
   Signature motif: the "escrow seal" — a concentric ring mark standing in
   for funds held in trust until both sides confirm.
   ========================================================================== */

@import url('https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700;800&display=swap');

/* --------------------------------------------------------------------------
   1. Tokens
   -------------------------------------------------------------------------- */
:root {
  /* Background gradient (deep navy → blue) — matches the Bulletin app's
     base.css --bg-0/--bg-1 so both products share one palette. */
  --color-bg-start: #050B1F;
  --color-bg-mid:   #0A1B42;
  --color-bg-end:   #050B1F;

  /* Ambient accent blobs (used only as soft background glow, low opacity) */
  --color-glow-blue:   #4B84FF;
  --color-glow-violet: #7A4FD6;

  /* Glass surfaces */
  --color-surface:        rgba(255, 255, 255, 0.06);
  --color-surface-strong: rgba(255, 255, 255, 0.12);
  --color-surface-soft:   rgba(255, 255, 255, 0.04);
  --color-border:         rgba(255, 255, 255, 0.16);
  --color-border-strong:  rgba(255, 255, 255, 0.26);

  /* Text */
  --color-text-primary:   #F5F8FF;
  --color-text-secondary: #A9BAE0;
  --color-text-muted:     #7C8FC2;

  /* Brand accent */
  --color-accent:        #2F6FED;
  --color-accent-strong: #6FA0FF;
  --color-accent-text:   #050B1F;

  /* Secondary brand accent — used where the old theme used violet
     (seller-role pill, the segmented-control's seller dot) so that
     distinction survives the rebrand instead of collapsing into blue. */
  --violet-600: #7A4FD6;
  --violet-400: #B98CFF;

  /* Status semantics */
  --status-gray-bg:     rgba(148, 163, 184, 0.16);
  --status-gray-text:   #CBD3E1;
  --status-gray-border: rgba(148, 163, 184, 0.40);

  --status-blue-bg:     rgba(75, 132, 255, 0.16);
  --status-blue-text:   #9CC0FF;
  --status-blue-border: rgba(75, 132, 255, 0.45);

  --status-green-bg:     rgba(52, 211, 153, 0.16);
  --status-green-text:   #7BE8C4;
  --status-green-border: rgba(52, 211, 153, 0.45);

  --status-amber-bg:     rgba(255, 183, 74, 0.16);
  --status-amber-text:   #FFCB7A;
  --status-amber-border: rgba(255, 183, 74, 0.45);

  --status-red-bg:     rgba(255, 122, 122, 0.16);
  --status-red-text:   #FF7A7A;
  --status-red-border: rgba(255, 122, 122, 0.45);

  /* Blue→amber brand gradient — the app's signature accent, used on
     card top bars, primary buttons, the hamburger and wallet chip, eyebrow
     labels, and the loading animation. Kept as a token so every reference
     stays in sync. */
  --frame-gradient: linear-gradient(135deg, #2F6FED 0%, #4B84FF 45%, #FFB74A 100%);

  /* Shape & depth */
  --radius-card: 20px;
  --radius-card-sm: 16px;
  --radius-card-lg: 24px;
  --radius-pill: 999px;
  --shadow-card: 0 8px 32px rgba(4, 6, 20, 0.45), 0 1.5px 0 rgba(255, 255, 255, 0.06) inset;
  --shadow-card-hover: 0 12px 40px rgba(4, 6, 20, 0.55), 0 1.5px 0 rgba(255, 255, 255, 0.08) inset;

  /* Motion */
  --ease-standard: cubic-bezier(0.16, 1, 0.3, 1);

  /* Sticky header footprint. Used as a scroll offset (see scroll-padding-top
     below) so anchor jumps and scrollIntoView() calls don't land content
     underneath the sticky header. ~72px covers the header's tallest state
     (desktop, 14px vertical padding + a ~43px button/nav-link) with a little
     slack; exact precision isn't required since it only pads a scroll
     target, it doesn't size anything. */
  --header-height: 72px;
}

/* --------------------------------------------------------------------------
   2. Reset & base
   -------------------------------------------------------------------------- */
* {
  box-sizing: border-box;
}

html, body {
  margin: 0;
  padding: 0;
  min-height: 100%;
}

/* Every page here has exactly one native scroller (the document itself —
   .dispute-table-scroll and .account-sidebar are the only other scrollable
   regions and are handled separately below). overscroll-behavior-y:contain
   stops vertical rubber-banding at the top/bottom of the page from
   "chaining" into the browser's own gesture (pull-to-refresh on Android
   Chrome, the elastic history-swipe on iOS Safari), which is what was
   producing the jump/flicker some testers saw when a fast fling hit the
   end of the page right as the sticky, backdrop-filtered header repainted.
   Vertical bounce itself is left alone — it's expected native feel, not a
   bug — this only contains where the *scroll chain* stops. */
html {
  overscroll-behavior-y: contain;

  /* Offsets any scroll-into-view or anchor jump by the sticky header's
     height, so the top of the target isn't scrolled in underneath it. */
  scroll-padding-top: calc(var(--header-height) + 20px);
}

/* --------------------------------------------------------------------------
   Global scrollbar — thin, themed track/thumb everywhere, instead of the
   raw OS default (a thick flat-gray bar that reads as an unstyled/broken
   element against the glass + gradient background — see the mobile
   screenshot this was flagged from). Firefox picks this up via
   scrollbar-width/-color on html; Chromium/Safari via the ::-webkit-*
   pseudo-elements below. Written once at the root so it covers every
   page's document scroller as well as any inner overflow:auto panel
   (chat threads, dropdowns, etc.) that didn't already define its own —
   .dispute-table-scroll keeps its slightly denser 8px override further
   down since it's a tighter horizontal strip.
   -------------------------------------------------------------------------- */
html {
  scrollbar-width: thin;
  scrollbar-color: var(--color-accent) transparent;
}

::-webkit-scrollbar {
  width: 10px;
  height: 10px;
}

::-webkit-scrollbar-track {
  background: transparent;
}

::-webkit-scrollbar-thumb {
  background-color: var(--color-accent);
  background-image: linear-gradient(180deg, var(--color-accent-strong) 0%, var(--color-glow-violet) 100%);
  background-clip: padding-box;
  border: 2px solid transparent;
  border-radius: var(--radius-pill);
}

::-webkit-scrollbar-thumb:hover {
  background-image: linear-gradient(180deg, var(--color-glow-blue) 0%, var(--color-glow-violet) 100%);
  background-clip: padding-box;
}

::-webkit-scrollbar-corner {
  background: transparent;
}

body {
  font-family: 'Inter', -apple-system, BlinkMacSystemFont, sans-serif;
  color: var(--color-text-primary);
  background: var(--color-bg-mid);
  background-image:
    radial-gradient(ellipse 900px 600px at 12% -10%, rgba(75, 132, 255, 0.20), transparent 60%),
    radial-gradient(ellipse 800px 700px at 100% 10%, rgba(122, 79, 214, 0.22), transparent 55%),
    radial-gradient(ellipse 1000px 800px at 50% 120%, rgba(5, 11, 31, 0.9), transparent 60%),
    linear-gradient(160deg, var(--color-bg-start) 0%, var(--color-bg-mid) 45%, var(--color-bg-end) 100%);
  background-repeat: no-repeat;
  line-height: 1.6;
  -webkit-font-smoothing: antialiased;
}

/* background-attachment: fixed is desktop-only. On iOS Safari it's known to
   break backdrop-filter blur on any sticky/fixed descendant (our header and
   its mobile dropdown), leaving those "glass" panels almost fully
   transparent instead of frosted — which is what was making the header and
   nav dropdown unreadable on iPhone. */
@media (min-width: 721px) {
  body {
    background-attachment: fixed;
  }
}

a {
  color: var(--color-accent-strong);
  text-decoration: none;
}

img {
  max-width: 100%;
}

/* --------------------------------------------------------------------------
   3. Typography scale
   -------------------------------------------------------------------------- */
h1, h2, h3, h4 {
  font-family: 'Inter', sans-serif;
  color: var(--color-text-primary);
  margin: 0 0 0.5em 0;
  letter-spacing: -0.02em;
}

h1 { font-size: 2.5rem;  font-weight: 800; line-height: 1.15; }
h2 { font-size: 1.75rem; font-weight: 700; line-height: 1.25; }
h3 { font-size: 1.25rem; font-weight: 600; line-height: 1.35; letter-spacing: -0.01em; }
h4 { font-size: 1rem;    font-weight: 600; letter-spacing: -0.005em; }

p {
  color: var(--color-text-secondary);
  margin: 0 0 1em 0;
}

.eyebrow {
  display: block;
  font-size: 0.75rem;
  font-weight: 600;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  margin-bottom: 0.5em;
  /* Same warm red→yellow brand gradient as the top-of-card accent bar,
     clipped to the text itself so every eyebrow label — page headers and
     in-card labels alike — reads as one consistent, intentional accent
     color across the whole app instead of the page-level header being the
     only spot that looks "finished". Elements that need a different
     status color (e.g. a red "next step" warning) set their own inline
     `color`, which naturally overrides this since it isn't transparent. */
  background-image: var(--frame-gradient);
  -webkit-background-clip: text;
  background-clip: text;
  color: transparent;
}

.text-muted   { color: var(--color-text-muted); }
.text-secondary { color: var(--color-text-secondary); }

/* Tabular figures for money — amounts should never jitter as digits change */
.data-figure {
  font-variant-numeric: tabular-nums;
  font-weight: 700;
  letter-spacing: -0.01em;
}

/* --------------------------------------------------------------------------
   4. Layout helpers
   -------------------------------------------------------------------------- */
.container {
  width: 100%;
  max-width: 1080px;
  margin: 0 auto;
  /* Below ~1128px this is the full viewport edge to edge (no auto-margin
     centering yet), which includes phones in landscape — so its own
     horizontal padding is what keeps content clear of a side notch or
     rounded corner there. max() preserves the normal 24px everywhere else. */
  padding: 0 max(24px, env(safe-area-inset-right)) 0 max(24px, env(safe-area-inset-left));
}

/* Thin side margins on phones — .container previously kept a flat 24px
   gutter at every width, which (combined with main's own 24px, now
   removed above) was leaving a much wider edge margin than intended on
   mobile, and made pinch-zoomed views feel unstable/off-balance. */
@media (max-width: 720px) {
  .container {
    padding: 0 max(12px, env(safe-area-inset-right)) 0 max(12px, env(safe-area-inset-left));
  }
}

@media (max-width: 480px) {
  .container {
    padding: 0 max(8px, env(safe-area-inset-right)) 0 max(8px, env(safe-area-inset-left));
  }
}

section + section {
  margin-top: 64px;
}

/* --------------------------------------------------------------------------
   5. Glass card
   -------------------------------------------------------------------------- */
.glass-card {
  position: relative;
  background: var(--color-surface);
  backdrop-filter: blur(20px);
  -webkit-backdrop-filter: blur(20px);
  border: 1px solid var(--color-border);
  border-radius: var(--radius-card);
  box-shadow: var(--shadow-card);
  padding: 24px;
  transition: box-shadow 0.25s var(--ease-standard), border-color 0.25s var(--ease-standard), transform 0.25s var(--ease-standard);
}

/* Same warm red→yellow accent as the header/page-intro, now on every card
   in the system — a slim top edge rather than a corner glow, since a glow
   repeated across a whole list of stacked trade-cards would read as
   visual noise rather than polish. Radius is matched per size variant
   below so the bar always follows the card's own corners exactly instead
   of needing overflow:hidden on the card (which would risk clipping
   legitimate content, e.g. a focus ring or badge that overhangs an edge). */
.glass-card::before {
  content: '';
  position: absolute;
  top: 0;
  left: 0;
  right: 0;
  /* Height matches the corner radius (not 3px) so the border-radius below
     has room to render at full size instead of being clamped by the box's
     own height — a radius bigger than half the height gets scaled down by
     the browser, which is what was flattening these corners into a near-
     square step instead of following the card's actual curve. The mask
     then trims the visible bar back down to a slim 3px edge, so we get the
     same rounded look as .page-intro without needing overflow:hidden on
     the whole card (which would risk clipping a badge or focus ring). */
  height: var(--radius-card);
  border-radius: var(--radius-card) var(--radius-card) 0 0;
  background: var(--frame-gradient);
  -webkit-mask-image: linear-gradient(to bottom, #000 3px, transparent 3px);
  mask-image: linear-gradient(to bottom, #000 3px, transparent 3px);
  pointer-events: none;
}

.glass-card--interactive:hover {
  box-shadow: var(--shadow-card-hover);
  border-color: var(--color-border-strong);
  transform: translateY(-2px);
}

.glass-card--strong {
  background: var(--color-surface-strong);
}

.glass-card--soft {
  background: var(--color-surface-soft);
  box-shadow: none;
}

.glass-card--sm {
  border-radius: var(--radius-card-sm);
  padding: 16px;
}

.glass-card--sm::before {
  height: var(--radius-card-sm);
  border-radius: var(--radius-card-sm) var(--radius-card-sm) 0 0;
}

.glass-card--lg {
  border-radius: var(--radius-card-lg);
  padding: 32px;
}

.glass-card--lg::before {
  height: var(--radius-card-lg);
  border-radius: var(--radius-card-lg) var(--radius-card-lg) 0 0;
}

.glass-card__header {
  display: flex;
  align-items: flex-start;
  justify-content: space-between;
  gap: 16px;
  margin-bottom: 16px;
  padding-bottom: 16px;
  border-bottom: 1px solid var(--color-border);
}

.glass-card__footer {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 16px;
  margin-top: 20px;
  padding-top: 16px;
  border-top: 1px solid var(--color-border);
}

/* --------------------------------------------------------------------------
   6. Status badge
   -------------------------------------------------------------------------- */
.status-badge {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  padding: 5px 12px;
  border-radius: var(--radius-pill);
  border: 1px solid transparent;
  font-size: 0.75rem;
  font-weight: 600;
  letter-spacing: 0.03em;
  text-transform: uppercase;
  white-space: nowrap;
}

.status-badge::before {
  content: '';
  width: 6px;
  height: 6px;
  border-radius: 50%;
  background: currentColor;
  flex-shrink: 0;
}

/* Gray — awaiting counterparty */
.status-badge--gray {
  background: var(--status-gray-bg);
  color: var(--status-gray-text);
  border-color: var(--status-gray-border);
}

/* Blue — in progress */
.status-badge--blue {
  background: var(--status-blue-bg);
  color: var(--status-blue-text);
  border-color: var(--status-blue-border);
}

/* Green — confirmed */
.status-badge--green {
  background: var(--status-green-bg);
  color: var(--status-green-text);
  border-color: var(--status-green-border);
}

/* Amber — pending action */
.status-badge--amber {
  background: var(--status-amber-bg);
  color: var(--status-amber-text);
  border-color: var(--status-amber-border);
}

/* Red — disputed */
.status-badge--red {
  background: var(--status-red-bg);
  color: var(--status-red-text);
  border-color: var(--status-red-border);
}

/* --------------------------------------------------------------------------
   7. Buttons (bonus — pairs with cards/badges for trade actions)
   -------------------------------------------------------------------------- */
.btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 8px;
  font-family: inherit;
  font-size: 0.9rem;
  font-weight: 600;
  padding: 10px 20px;
  border-radius: 12px;
  border: 1px solid transparent;
  cursor: pointer;
  transition: transform 0.2s var(--ease-standard), box-shadow 0.2s var(--ease-standard), border-color 0.2s var(--ease-standard), background 0.2s var(--ease-standard);
}

.btn:active {
  transform: scale(0.98);
}

.btn-primary {
  /* Same warm red→yellow brand gradient used for the app frame, card top
     bars, and eyebrow labels — the primary action button now carries the
     signature brand color instead of the old blue/violet. */
  background: var(--frame-gradient);
  color: #FFFFFF;
  box-shadow: 0 4px 20px rgba(47, 111, 237, 0.32), 0 4px 26px rgba(255, 183, 74, 0.2);
}

.btn-primary:hover {
  box-shadow: 0 6px 26px rgba(47, 111, 237, 0.42), 0 6px 32px rgba(255, 183, 74, 0.28);
  filter: brightness(1.05);
}

.btn-ghost {
  background: var(--color-surface);
  backdrop-filter: blur(12px);
  -webkit-backdrop-filter: blur(12px);
  color: var(--color-text-primary);
  border-color: var(--color-border);
}

.btn-ghost:hover {
  border-color: var(--color-border-strong);
  background: var(--color-surface-strong);
}

/* Outline-only, used for the "report a problem" / "submit dispute" actions.
   Reads as a flag rather than an alarm — muted red border and text on a
   transparent fill, only warming up on hover/focus. */
.btn-outline--danger {
  background: transparent;
  border-color: var(--status-red-border);
  color: var(--status-red-text);
}

.btn-outline--danger:hover {
  background: var(--status-red-bg);
  border-color: var(--status-red-text);
}

/* Filled affirmative action, e.g. "Confirm Receipt" */
.btn-primary--success {
  background: linear-gradient(135deg, #34D399 0%, #10B981 100%);
  color: #063C2C;
  box-shadow: 0 4px 20px rgba(52, 211, 153, 0.32);
}

.btn-primary--success:hover {
  box-shadow: 0 6px 24px rgba(52, 211, 153, 0.48);
}

/* Hands off to an outside channel (WhatsApp), so it intentionally uses
   WhatsApp's own brand green rather than the app's status-green token —
   reads as "leaves the app" instead of another in-system action. */
.btn-whatsapp {
  background: linear-gradient(135deg, #25D366 0%, #128C7E 100%);
  color: #FFFFFF;
  box-shadow: 0 4px 20px rgba(37, 211, 102, 0.35);
  text-decoration: none;
}

.btn-whatsapp:hover {
  box-shadow: 0 6px 24px rgba(37, 211, 102, 0.5);
}

.btn-whatsapp svg {
  flex-shrink: 0;
}

.btn:disabled,
.btn:disabled:hover {
  opacity: 0.4;
  cursor: not-allowed;
  box-shadow: none;
  transform: none;
}

/* --------------------------------------------------------------------------
   8. Brand mark (shared by header + footer)
   -------------------------------------------------------------------------- */
.brand {
  display: inline-flex;
  align-items: center;
  gap: 10px;
  color: var(--color-text-primary);
}

.brand__mark {
  display: inline-flex;
  position: relative;
  color: #4B84FF;
  flex-shrink: 0;
}

.brand__mark::before {
  content: '';
  position: absolute;
  inset: -6px;
  border-radius: 50%;
  background: radial-gradient(circle, rgba(75, 132, 255, 0.3) 0%, rgba(47, 111, 237, 0) 72%);
  z-index: -1;
}

.brand__mark svg {
  display: block;
}

.brand__name {
  font-weight: 800;
  font-size: 1.1rem;
  letter-spacing: -0.015em;
  color: var(--color-text-primary); /* fallback if gradient text isn't supported */
  background: linear-gradient(90deg, var(--color-text-primary) 0%, #4B84FF 100%);
  -webkit-background-clip: text;
  background-clip: text;
  -webkit-text-fill-color: transparent;
}

/* The header sits on the same dark glass surface as every other card (see
   .site-header below), so the logo just uses these shared warm-toned
   .brand__mark / .brand__name defaults — same red→yellow treatment as the
   footer and login screen, just at a slightly larger size. */

.brand--sm .brand__name {
  font-size: 0.95rem;
}

/* --------------------------------------------------------------------------
   9. Site header
   -------------------------------------------------------------------------- */
/* will-change: transform promotes the header to its own compositor layer.
   Without it, position: sticky + backdrop-filter is a known jank combo on
   Android Chrome and several iOS Safari versions: the browser repaints the
   blurred region on the main thread on every scroll tick instead of just
   re-compositing it. Safe to leave on permanently here (rather than the
   usual "toggle it on/off around the change" advice) since the header is
   sticky for the whole time it's on screen, not a one-off transition — and
   nothing needing position: fixed lives *inside* .site-header (the account
   sidebar and its overlay are separate top-level elements), so this can't
   break fixed-position descendants the way will-change: transform on an
   ancestor of a fixed element would. */
.site-header {
  position: sticky;
  top: 0;
  z-index: 20;
  /* Same dark glass surface every .glass-card uses (a faint white wash
     over the navy backdrop) rather than the gradient as a solid fill —
     the red→yellow brand color now lives only in the thin accent line
     below (.site-header::after), matching how every card carries it: a
     slim top edge, not the card's actual background color. */
  background:
    linear-gradient(var(--color-surface), var(--color-surface)),
    rgba(5, 11, 31, 0.80);
  border-bottom: 1px solid var(--color-border);
  backdrop-filter: blur(16px);
  -webkit-backdrop-filter: blur(16px);
  will-change: transform;
  /* Creates its own stacking/backdrop-root context. Without it, this
     sticky + backdrop-filter element can flicker on Android Chrome and
     iOS Safari as it repaints against whatever's scrolling underneath —
     isolation keeps its blur sampling stable frame to frame. */
  isolation: isolate;
  /* On a notched/rounded-corner phone in landscape, viewport-fit=cover lets
     the page extend under the status-bar/notch area; without this the
     sticky header (and the logo inside it) would render partly beneath it. */
  padding-top: env(safe-area-inset-top, 0px);
  box-shadow: 0 1px 0 rgba(4, 6, 20, 0.45);
}

.site-header__inner {
  max-width: 1080px;
  margin: 0 auto;
  padding: 14px max(24px, env(safe-area-inset-right)) 14px max(24px, env(safe-area-inset-left));
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 20px;
}

.site-nav ul {
  list-style: none;
  display: flex;
  gap: 4px;
  margin: 0;
  padding: 0;
}

.site-nav a {
  display: inline-block;
  padding: 8px 14px;
  border-radius: 10px;
  color: var(--color-text-secondary);
  font-size: 0.9rem;
  font-weight: 500;
  transition: background 0.2s var(--ease-standard), color 0.2s var(--ease-standard);
}

/* The header sits on the same dark surface as the rest of the app now, so
   .site-nav a reads fine at the default --color-text-secondary — no
   header-specific override needed. */

.site-nav a:hover {
  background: var(--color-surface);
  color: var(--color-text-primary);
}

.site-nav a.active {
  background: var(--color-surface-strong);
  color: var(--color-text-primary);
}

/* --------------------------------------------------------------------------
   10. Connect Wallet button
   -------------------------------------------------------------------------- */
.wallet-btn {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  padding: 8px 14px;
  border-radius: var(--radius-pill);
  border: 1px solid transparent;
  font-size: 0.82rem;
  font-weight: 600;
  flex-shrink: 0;
  font-variant-numeric: tabular-nums;
  transition: background 0.2s var(--ease-standard), border-color 0.2s var(--ease-standard), color 0.2s var(--ease-standard);
}

.wallet-btn__dot {
  width: 7px;
  height: 7px;
  border-radius: 50%;
  background: var(--color-text-muted);
  flex-shrink: 0;
  transition: background 0.2s var(--ease-standard), box-shadow 0.2s var(--ease-standard);
}

/* Chip chrome carries the app's red→yellow brand gradient; the small dot
   stays a plain status green so "connected" still reads at a glance. */
.wallet-btn[data-connected="true"] {
  border-color: rgba(75, 132, 255, 0.45);
  background: linear-gradient(135deg, rgba(47, 111, 237, 0.18) 0%, rgba(75, 132, 255, 0.16) 45%, rgba(255, 183, 74, 0.14) 100%);
  color: var(--color-text-primary);
}

.wallet-btn[data-connected="true"]:hover {
  background: linear-gradient(135deg, rgba(47, 111, 237, 0.26) 0%, rgba(75, 132, 255, 0.23) 45%, rgba(255, 183, 74, 0.2) 100%);
  border-color: rgba(255, 183, 74, 0.6);
}

.wallet-btn[data-connected="true"] .wallet-btn__dot {
  background: var(--status-green-text);
  box-shadow: 0 0 0 3px var(--status-green-bg);
}

/* --------------------------------------------------------------------------
   10b. Hamburger menu button (mobile nav trigger)
   Hidden by default — only shown once the inline nav collapses at the
   720px breakpoint (see section 13, which also turns .site-nav into the
   dropdown this button controls). Bars morph into an "X" off the
   aria-expanded attribute the toggle script already sets, so no extra
   class is needed.
   -------------------------------------------------------------------------- */
.hamburger-btn {
  display: none;
  align-items: center;
  justify-content: center;
  width: 44px;
  height: 44px;
  padding: 0;
  flex-shrink: 0;
  background: var(--color-surface);
  border: 1px solid var(--color-border);
  border-radius: 10px;
  cursor: pointer;
  transition: background 0.2s var(--ease-standard), border-color 0.2s var(--ease-standard);
}

.hamburger-btn:hover {
  background: linear-gradient(135deg, rgba(47, 111, 237, 0.14) 0%, rgba(75, 132, 255, 0.13) 45%, rgba(255, 183, 74, 0.12) 100%);
  border-color: rgba(75, 132, 255, 0.5);
}

.hamburger-btn__icon {
  position: relative;
  display: inline-block;
  width: 17px;
  height: 12px;
}

.hamburger-btn__icon span {
  position: absolute;
  left: 0;
  width: 100%;
  height: 1.6px;
  border-radius: 1px;
  background: var(--frame-gradient);
  transition: transform 0.25s var(--ease-standard), opacity 0.2s var(--ease-standard), top 0.25s var(--ease-standard), background 0.2s var(--ease-standard);
}

.hamburger-btn__icon span:nth-child(1) { top: 0; }
.hamburger-btn__icon span:nth-child(2) { top: 50%; margin-top: -0.8px; }
.hamburger-btn__icon span:nth-child(3) { top: 100%; margin-top: -1.6px; }

.hamburger-btn[aria-expanded="true"] .hamburger-btn__icon span:nth-child(1) {
  top: 50%;
  margin-top: -0.8px;
  transform: rotate(45deg);
}

.hamburger-btn[aria-expanded="true"] .hamburger-btn__icon span:nth-child(2) {
  opacity: 0;
}

.hamburger-btn[aria-expanded="true"] .hamburger-btn__icon span:nth-child(3) {
  top: 50%;
  margin-top: -0.8px;
  transform: rotate(-45deg);
}

/* --------------------------------------------------------------------------
   10c. Account trigger + off-canvas sidebar
   The wallet address used to live in the header itself — first as a
   standalone button, then folded into the nav — but either way it was
   still part of the header row, and at real address lengths it was what
   crowded that row on small screens. The header now only carries a small
   icon-only trigger (no address text, so nothing to overflow); the actual
   address lives in this off-canvas panel instead.
   -------------------------------------------------------------------------- */
.account-trigger {
  position: relative;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 44px;
  height: 44px;
  padding: 0;
  flex-shrink: 0;
  background: var(--color-surface);
  border: 1px solid var(--color-border);
  border-radius: 10px;
  color: var(--color-text-secondary);
  cursor: pointer;
  transition: background 0.2s var(--ease-standard), border-color 0.2s var(--ease-standard), color 0.2s var(--ease-standard);
}

.account-trigger:hover {
  background: linear-gradient(135deg, rgba(47, 111, 237, 0.16) 0%, rgba(75, 132, 255, 0.14) 45%, rgba(255, 183, 74, 0.12) 100%);
  border-color: rgba(75, 132, 255, 0.5);
  color: var(--color-text-primary);
}

/* Subtle standing warm ring once a wallet is connected, so the header's
   wallet icon carries the brand color even before it's hovered. */
.account-trigger[data-connected="true"] {
  border-color: rgba(75, 132, 255, 0.35);
}

.account-trigger__icon {
  display: block;
  width: 18px;
  height: 18px;
}

.account-trigger__dot {
  position: absolute;
  top: 6px;
  inset-inline-end: 6px;
  width: 8px;
  height: 8px;
  border-radius: 50%;
  background: var(--color-text-muted);
  border: 2px solid var(--color-bg-mid);
}

.account-trigger[data-connected="true"] .account-trigger__dot {
  background: var(--status-green-text);
}

/* --------------------------------------------------------------------------
   Notification bell — sits in the header just before the account trigger.
   Sized/styled to match .account-trigger above for visual consistency
   rather than introducing a second button treatment.
   -------------------------------------------------------------------------- */
.notif-bell-wrap {
  position: relative;
}

.notif-bell {
  position: relative;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 44px;
  height: 44px;
  padding: 0;
  flex-shrink: 0;
  background: var(--color-surface);
  border: 1px solid var(--color-border);
  border-radius: 10px;
  color: var(--color-text-secondary);
  cursor: pointer;
  transition: background 0.2s var(--ease-standard), border-color 0.2s var(--ease-standard), color 0.2s var(--ease-standard);
}

.notif-bell:hover {
  background: linear-gradient(135deg, rgba(47, 111, 237, 0.16) 0%, rgba(75, 132, 255, 0.14) 45%, rgba(255, 183, 74, 0.12) 100%);
  border-color: rgba(75, 132, 255, 0.5);
  color: var(--color-text-primary);
}

.notif-bell__icon {
  display: block;
  width: 18px;
  height: 18px;
}

.notif-bell__badge {
  position: absolute;
  top: 4px;
  inset-inline-end: 4px;
  min-width: 16px;
  height: 16px;
  padding: 0 4px;
  border-radius: var(--radius-pill);
  background: var(--status-red-bg);
  color: var(--status-red-text);
  border: 1px solid var(--status-red-border);
  font-size: 0.65rem;
  font-weight: 700;
  line-height: 14px;
  text-align: center;
}

.notif-panel {
  position: absolute;
  top: calc(100% + 10px);
  inset-inline-end: 0;
  width: 320px;
  max-width: 90vw;
  background: var(--color-surface-strong);
  backdrop-filter: blur(20px);
  -webkit-backdrop-filter: blur(20px);
  border: 1px solid var(--color-border);
  border-radius: var(--radius-card-sm);
  box-shadow: var(--shadow-card);
  overflow: hidden;
  z-index: 30; /* same layer as the mobile nav dropdown — above header content, below the account sidebar (40/50) */
}

.notif-panel__header {
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: 12px 16px;
  border-bottom: 1px solid var(--color-border);
}

.notif-panel__title {
  font-size: 0.85rem;
  font-weight: 700;
  color: var(--color-text-primary);
}

.notif-panel__mark-all {
  background: none;
  border: none;
  color: var(--color-accent-strong);
  font-size: 0.8rem;
  font-weight: 600;
  cursor: pointer;
  padding: 0;
}

.notif-panel__list {
  max-height: 340px;
  overflow-y: auto;
}

.notif-panel__empty {
  margin: 0;
  padding: 24px 16px;
  text-align: center;
  color: var(--color-text-muted);
  font-size: 0.85rem;
}

.notif-row {
  padding: 12px 16px;
  border-bottom: 1px solid var(--color-border);
  cursor: default;
  transition: background 0.15s var(--ease-standard);
}

.notif-row[data-trade-id] {
  cursor: pointer;
}

.notif-row:hover {
  background: var(--color-surface-soft);
}

.notif-row:last-child {
  border-bottom: none;
}

.notif-row__message {
  margin: 0 0 4px;
  font-size: 0.85rem;
  line-height: 1.4;
  color: var(--color-text-secondary);
}

.notif-row--unread .notif-row__message {
  color: var(--color-text-primary);
  font-weight: 600;
}

.notif-row__time {
  font-size: 0.72rem;
  color: var(--color-text-muted);
}

/* Backdrop behind the sidebar. Sits above the sticky header (z-index 20)
   and the mobile nav dropdown (z-index 30) so it always reads on top. */
.account-sidebar-overlay {
  position: fixed;
  inset: 0;
  z-index: 40;
  background: rgba(4, 6, 20, 0.55);
  opacity: 0;
  visibility: hidden;
  transition: opacity 0.25s var(--ease-standard), visibility 0.25s var(--ease-standard);
  /* body.scroll-locked (overflow:hidden) doesn't reliably stop background
     scrolling on iOS Safari — the page can still rubber-band behind a fixed
     overlay. touch-action:none on the backdrop itself blocks that gesture
     at the source, which is what was making the page appear to "jump"
     behind the sidebar when someone dragged on the dimmed area. */
  touch-action: none;
}

.account-sidebar-overlay--visible {
  opacity: 1;
  visibility: visible;
}

.account-sidebar {
  position: fixed;
  top: 0;
  right: 0;
  bottom: 0;
  z-index: 50;
  width: 340px;
  max-width: 88vw;
  /* Fixed panel pinned to the right edge and full height — on a notched or
     rounded-corner phone (especially in landscape, where the sidebar's own
     right edge sits flush against the rounded corner / home-indicator
     side) its close button and bottom content could sit under those
     unsafe zones without this. max() keeps the original 24px anywhere the
     inset is 0 (i.e. everywhere non-notched) and only grows where needed. */
  padding: max(24px, env(safe-area-inset-top)) max(24px, env(safe-area-inset-right)) max(24px, env(safe-area-inset-bottom)) 24px;
  display: flex;
  flex-direction: column;
  /* Same dark glass surface family as every .glass-card and the header —
     was a bespoke solid purple fill that didn't match anything else in
     the app. --color-surface-strong (not the plain --color-surface cards
     use) since this panel sits directly over live page content rather
     than the body's own background, so it needs a bit more coverage to
     stay readable while scrolling. */
  background:
    linear-gradient(var(--color-surface-strong), var(--color-surface-strong)),
    rgba(5, 11, 31, 0.93);
  backdrop-filter: blur(20px);
  -webkit-backdrop-filter: blur(20px);
  border-left: 1px solid var(--color-border-strong);
  box-shadow: var(--shadow-card-hover);
  transform: translateX(100%);
  transition: transform 0.3s var(--ease-standard);
  overflow-y: auto;
  -webkit-overflow-scrolling: touch;
  /* Stops this panel's own scroll from chaining into the page behind it
     once it hits the top/bottom of its content — the scroll-chaining
     equivalent of the touch-action:none fix on the overlay above. */
  overscroll-behavior-y: contain;
  touch-action: pan-y;
}

/* The same red→yellow accent every card carries on its top edge, moved to
   whichever edge of this panel actually faces the app — its left side,
   since it's docked to the right of the screen. Positioned absolute
   against the sidebar's own (fixed) box, so it slides in and out with
   the panel automatically instead of needing its own transform. */
.account-sidebar::before {
  content: '';
  position: absolute;
  top: 0;
  bottom: 0;
  left: 0;
  width: 2px;
  background: var(--frame-gradient);
  pointer-events: none;
}

.account-sidebar--open {
  transform: translateX(0);
}

.account-sidebar__header {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
  margin-bottom: 20px;
  /* Same divider used under every .glass-card__header, so this panel's
     title row reads as the same kind of header as the ones inside cards
     elsewhere in the app (e.g. Trade Detail's side panels). */
  padding-bottom: 16px;
  border-bottom: 1px solid var(--color-border);
}

.account-sidebar__header h3 {
  margin: 0;
}


.account-sidebar__close {
  position: relative;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 32px;
  height: 32px;
  flex-shrink: 0;
  border-radius: 50%;
  border: none;
  background: transparent;
  color: var(--color-text-muted);
  cursor: pointer;
  transition: background 0.2s var(--ease-standard), color 0.2s var(--ease-standard);
}

/* Invisible tap area extending the 32px visual button out to the 44px
   minimum recommended touch target, without changing how the button
   looks. Centered on the visible circle via translate(-50%, -50%), so it
   grows outward evenly on every side. */
.account-sidebar__close::before {
  content: '';
  position: absolute;
  top: 50%;
  left: 50%;
  width: 44px;
  height: 44px;
  transform: translate(-50%, -50%);
}

.account-sidebar__close svg {
  width: 14px;
  height: 14px;
}

.account-sidebar__close:hover {
  background: var(--color-surface);
  color: var(--color-text-primary);
}

.account-sidebar__body {
  display: flex;
  flex-direction: column;
  gap: 18px;
}

.account-sidebar__hint {
  margin: 0;
  font-size: 0.9rem;
}

@media (max-width: 400px) {
  .account-sidebar {
    width: 100%;
    max-width: 100%;
  }
}

/* Same rationale as the header/nav blur trims above: cheaper for the GPU
   on lower-powered phones, and layered directly on top of the (already
   also trimmed) header blur whenever the sidebar is open on mobile. */
@media (max-width: 720px) {
  .account-sidebar {
    backdrop-filter: blur(10px);
    -webkit-backdrop-filter: blur(10px);
  }
}

/* --------------------------------------------------------------------------
   10a. Body scroll lock (mobile nav dropdown + account sidebar overlays)
   Toggled by JS while either overlay is open. --scrollbar-width is written
   by JS immediately before this class is added (and left at its default of
   0px otherwise), so the padding-right compensates for the native
   scrollbar disappearing and page content doesn't shift sideways.
   -------------------------------------------------------------------------- */
body.scroll-locked {
  overflow: hidden;
  padding-right: var(--scrollbar-width, 0px);
}

/* --------------------------------------------------------------------------
   11. Main content area
   -------------------------------------------------------------------------- */
main {
  max-width: 1080px;
  margin: 0 auto;
  padding: 48px 0;
  /* 100vh on iOS/Android is pinned to the *largest* possible viewport (bar
     collapsed), not the current one — so as the address bar shows/hides
     during a scroll, a height computed from 100vh doesn't track it, which
     can make the page's total scrollable length (and anything anchored to
     the bottom of it) shift under you mid-scroll. 100dvh tracks the actual
     visible viewport instead. Kept as a fallback-then-override pair since
     dvh isn't recognized by older browsers, which would otherwise drop the
     whole declaration. */
  min-height: calc(100vh - 200px);
  min-height: calc(100dvh - 200px);
}

/* --------------------------------------------------------------------------
   12. Site footer
   -------------------------------------------------------------------------- */
.site-footer {
  position: relative;
  margin-top: 64px;
}

.site-footer__inner {
  max-width: 1080px;
  margin: 0 auto;
  padding: 32px 24px 40px;
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
}

.site-footer p {
  margin: 0;
  font-size: 0.85rem;
}

.site-footer__meta {
  display: flex;
  flex-direction: column;
  gap: 8px;
  align-items: flex-end;
  text-align: right;
}

.site-footer__copyright {
  margin-top: -2px;
  font-size: 0.78rem;
  opacity: 0.7;
}

/* --------------------------------------------------------------------------
   13b. Footer refresh
   A quiet, modern close to the page: the flat border becomes a fading
   red→yellow hairline (an echo of the card top-bars used everywhere else
   in the app, and of the app-frame gradient that used to run around the
   whole viewport), the brand mark picks up the same warm palette instead
   of the cooler header blue, and the escrow tagline becomes a small trust
   badge — the kind of "funds are protected" cue people expect near a
   footer on anything handling money — rather than a plain muted line.
   -------------------------------------------------------------------------- */
.site-footer::before {
  content: '';
  position: absolute;
  top: 0;
  left: 0;
  right: 0;
  height: 1px;
  background: linear-gradient(90deg,
    transparent 0%,
    rgba(47, 111, 237, 0.55) 22%,
    rgba(75, 132, 255, 0.65) 50%,
    rgba(255, 183, 74, 0.55) 78%,
    transparent 100%);
}

.footer-trust-badge {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  padding: 5px 12px 5px 9px;
  border-radius: var(--radius-pill);
  border: 1px solid rgba(75, 132, 255, 0.28);
  background: linear-gradient(135deg, rgba(47, 111, 237, 0.1) 0%, rgba(75, 132, 255, 0.09) 45%, rgba(255, 183, 74, 0.08) 100%);
  font-size: 0.78rem;
  line-height: 1.3;
  color: var(--color-text-secondary);
}

.footer-trust-badge__icon {
  flex-shrink: 0;
  color: #4B84FF;
}

/* --------------------------------------------------------------------------
   13. Responsive header/footer
   -------------------------------------------------------------------------- */
@media (max-width: 720px) {
  .site-header__inner {
    position: relative;
    flex-wrap: nowrap;
    justify-content: space-between;
    align-items: center;
    text-align: left;
    gap: 10px;
  }

  /* Logo can give up space first if things get tight, but never below
     showing its mark + a sliver of the name. */
  .brand {
    min-width: 0;
    flex-shrink: 1;
  }

  .brand__name {
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
  }

  /* Wallet status chip, now shown inside the account sidebar rather than
     the header. Kept compact at phone widths so it doesn't force the
     sidebar wider than it needs to be. */
  .wallet-btn {
    padding: 7px 10px;
    font-size: 0.78rem;
    gap: 6px;
    min-width: 0;
  }

  .wallet-btn__label {
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
    max-width: 92px;
  }

  .wallet-btn__dot {
    width: 6px;
    height: 6px;
  }

  /* The compact padding above is meant for the non-interactive wallet
     chip (a <div>) shown inside the account sidebar. Where .wallet-btn is
     an actual <button> (the "Connect Wallet" action on join-trade.html /
     new-trade.html), restore enough vertical padding to keep it at a
     44px-tall tap target. */
  button.wallet-btn {
    padding: 12px 14px;
    font-size: 0.85rem;
  }

  /* Solid fallback so the header reads clearly even on iOS builds where
     backdrop-filter under a sticky element doesn't render. Blur is layered
     on top as a bonus, not relied on for legibility — which is also what
     makes it safe to trim the radius here: a smaller kernel is cheaper for
     the GPU to recompute on every scroll tick on lower-powered phones,
     without hurting readability since the near-solid background is already
     carrying contrast on its own. Same warm gradient as the desktop header,
     just layered over a near-opaque (not translucent) navy base so it
     still reads correctly with zero blur behind it. */
  .site-header {
    background:
      linear-gradient(var(--color-surface-strong), var(--color-surface-strong)),
      rgba(5, 11, 31, 0.93);
    backdrop-filter: blur(8px);
    -webkit-backdrop-filter: blur(8px);
  }

  .hamburger-btn {
    display: inline-flex;
    background: rgba(255, 255, 255, 0.09);
    border-color: var(--color-border-strong);
  }

  /* Collapsed by default; JS toggles .site-nav--open on click. Positioned
     to hang below the sticky header as a glass panel matching the rest of
     the design system, rather than wrapping links inline in the header.
     Background is a near-solid tint of the theme's own dark navy (not just
     a translucent white wash) so the panel is legible on its own — blur is
     layered on top as a finishing touch, never load-bearing for contrast. */
  .site-nav {
    position: absolute;
    top: calc(100% + 14px);
    left: max(24px, env(safe-area-inset-left));
    right: max(24px, env(safe-area-inset-right));
    z-index: 30;
    padding: 8px;
    background: rgba(10, 27, 66, 0.97);
    backdrop-filter: blur(10px);
    -webkit-backdrop-filter: blur(10px);
    border: 1px solid var(--color-border-strong);
    border-radius: var(--radius-card-sm);
    box-shadow: var(--shadow-card-hover);
    opacity: 0;
    visibility: hidden;
    transform: translateY(-6px);
    pointer-events: none;
    transition: opacity 0.22s var(--ease-standard), transform 0.22s var(--ease-standard), visibility 0.22s var(--ease-standard);
  }

  .site-nav--open {
    opacity: 1;
    visibility: visible;
    transform: translateY(0);
    pointer-events: auto;
  }

  .site-nav ul {
    flex-direction: column;
    gap: 2px;
  }

  .site-nav a {
    display: block;
    padding: 11px 14px;
  }

  .site-footer__inner {
    flex-direction: column;
    text-align: center;
  }

  .site-footer__meta {
    align-items: center;
    text-align: center;
  }
}

/* --------------------------------------------------------------------------
   14. Stats strip (escrow summary)
   -------------------------------------------------------------------------- */
.stats-strip {
  display: flex;
  margin-bottom: 32px;
  overflow: hidden;
}

.stat-block {
  flex: 1 1 0;
  padding: 20px 24px;
  border-right: 1px solid var(--color-border);
}

.stat-block:last-child {
  border-right: none;
}

.stat-block .eyebrow {
  margin-bottom: 8px;
}

.stat-block .data-figure {
  font-size: 1.6rem;
}

.stat-block--hero .data-figure {
  font-size: 1.9rem;
  color: var(--color-accent-strong);
}

/* --------------------------------------------------------------------------
   15. Trade list & trade card
   -------------------------------------------------------------------------- */
.trade-list {
  display: flex;
  flex-direction: column;
  gap: 16px;
}

.trade-card__top {
  display: flex;
  align-items: flex-start;
  justify-content: space-between;
  gap: 16px;
  margin-bottom: 16px;
}

.trade-card__party {
  display: flex;
  align-items: center;
  gap: 12px;
  min-width: 0;
}

.trade-card__avatar {
  width: 42px;
  height: 42px;
  border-radius: 50%;
  display: flex;
  align-items: center;
  justify-content: center;
  font-weight: 700;
  font-size: 0.8rem;
  color: #FFFFFF;
  flex-shrink: 0;
  box-shadow: 0 0 0 3px rgba(255, 255, 255, 0.06), 0 0 0 1px rgba(255, 255, 255, 0.12) inset;
}

.trade-card__identity {
  min-width: 0;
}

.trade-card__name-row {
  display: flex;
  align-items: center;
  gap: 8px;
  flex-wrap: wrap;
}

.trade-card__name {
  margin: 0;
  font-weight: 600;
  font-size: 0.95rem;
  color: var(--color-text-primary);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  max-width: 220px;
}

.trade-card__sub {
  margin: 4px 0 0 0;
  font-size: 0.78rem;
}

.trade-card__item {
  margin: 0 0 18px 0;
  color: var(--color-text-primary);
  font-size: 0.95rem;
}

.trade-card__bottom .data-figure {
  font-size: 1.1rem;
}

.role-pill {
  display: inline-flex;
  align-items: center;
  padding: 2px 10px;
  border-radius: var(--radius-pill);
  font-size: 0.66rem;
  font-weight: 700;
  letter-spacing: 0.04em;
  text-transform: uppercase;
  border: 1px solid var(--color-border);
  color: var(--color-text-secondary);
  flex-shrink: 0;
}

.role-pill--buyer {
  color: var(--color-accent-strong);
  border-color: rgba(75, 132, 255, 0.4);
}

.role-pill--seller {
  color: #B98CFF;
  border-color: rgba(122, 79, 214, 0.4);
}

@media (max-width: 720px) {
  .stats-strip {
    flex-direction: column;
  }

  .stat-block {
    border-right: none;
    border-bottom: 1px solid var(--color-border);
  }

  .stat-block:last-child {
    border-bottom: none;
  }
}

@media (max-width: 560px) {
  .trade-card__top {
    flex-wrap: wrap;
  }

  .trade-card__bottom.glass-card__footer {
    flex-direction: column;
    align-items: flex-start;
    gap: 14px;
  }

  .trade-card__bottom .btn {
    width: 100%;
  }

  .trade-card__name {
    max-width: 160px;
  }
}

/* --------------------------------------------------------------------------
   16b. Forms — new escrow
   -------------------------------------------------------------------------- */
.form-grid {
  display: flex;
  flex-direction: column;
  gap: 22px;
}

.form-group {
  display: flex;
  flex-direction: column;
  gap: 8px;
}

.form-label {
  font-size: 0.85rem;
  font-weight: 600;
  color: var(--color-text-primary);
}

.form-label__step {
  color: var(--color-text-muted);
  font-weight: 700;
  margin-right: 6px;
}

.form-hint {
  font-size: 0.78rem;
  color: var(--color-text-muted);
  margin: -2px 0 0 0;
}

.form-input,
.form-textarea {
  width: 100%;
  font-family: inherit;
  font-size: 0.95rem;
  color: var(--color-text-primary);
  background: var(--color-surface-soft);
  border: 1px solid var(--color-border);
  border-radius: 12px;
  padding: 12px 14px;
  transition: border-color 0.2s var(--ease-standard), background 0.2s var(--ease-standard), box-shadow 0.2s var(--ease-standard);
}

.form-input::placeholder,
.form-textarea::placeholder {
  color: var(--color-text-muted);
}

.form-input:focus,
.form-textarea:focus {
  outline: none;
  border-color: var(--color-accent);
  background: var(--color-surface);
  box-shadow: 0 0 0 3px rgba(75, 132, 255, 0.18);
}

.form-input--mono {
  font-family: 'SFMono-Regular', Consolas, 'Liberation Mono', Menlo, monospace;
  font-size: 0.86rem;
  letter-spacing: 0.01em;
}

.form-textarea {
  resize: vertical;
  min-height: 84px;
  line-height: 1.5;
}

/* Segmented role toggle */
.segmented-control {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 8px;
  padding: 6px;
  background: var(--color-surface-soft);
  border: 1px solid var(--color-border);
  border-radius: 14px;
}

.segmented-control input {
  position: absolute;
  opacity: 0;
  width: 0;
  height: 0;
}

.segmented-control label {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 8px;
  padding: 12px 16px;
  border-radius: 10px;
  font-size: 0.9rem;
  font-weight: 600;
  color: var(--color-text-secondary);
  cursor: pointer;
  transition: background 0.2s var(--ease-standard), color 0.2s var(--ease-standard), box-shadow 0.2s var(--ease-standard);
}

.segmented-control label:hover {
  color: var(--color-text-primary);
}

.segmented-control input:checked + label {
  background: var(--color-surface-strong);
  color: var(--color-text-primary);
  box-shadow: 0 0 0 1px var(--color-border-strong) inset;
}

.segmented-control input:focus-visible + label {
  outline: 2px solid var(--color-accent-strong);
  outline-offset: 2px;
}

.segmented-control__dot {
  width: 7px;
  height: 7px;
  border-radius: 50%;
  background: var(--color-text-muted);
  flex-shrink: 0;
}

.segmented-control input:checked + label .segmented-control__dot--buyer {
  background: var(--color-accent-strong);
}

.segmented-control input:checked + label .segmented-control__dot--seller {
  background: #B98CFF;
}

/* Amount input with USDT suffix */
.input-suffix-group {
  position: relative;
  display: flex;
  align-items: center;
}

.input-suffix-group .form-input {
  padding-inline-end: 68px;
}

.input-suffix {
  position: absolute;
  inset-inline-end: 14px;
  font-size: 0.85rem;
  font-weight: 700;
  color: var(--color-text-muted);
  pointer-events: none;
}

/* Escrow-created success state */
.success-icon {
  display: flex;
  align-items: center;
  justify-content: center;
  width: 60px;
  height: 60px;
  border-radius: 50%;
  background: var(--status-green-bg);
  border: 1px solid var(--status-green-border);
  color: var(--status-green-text);
  margin: 0 auto 18px;
}

.trade-code-display {
  text-align: center;
  padding: 22px;
  background: var(--color-surface-soft);
  border: 1px dashed var(--color-border-strong);
  border-radius: 14px;
  margin-bottom: 20px;
}

.trade-code-display .data-figure {
  font-size: 1.9rem;
  letter-spacing: 0.03em;
  color: var(--color-accent-strong);
}

.copy-field {
  display: flex;
  align-items: center;
  gap: 10px;
  background: var(--color-surface-soft);
  border: 1px solid var(--color-border);
  border-radius: 12px;
  padding: 8px 8px 8px 16px;
}

.copy-field__value {
  flex: 1;
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  font-family: 'SFMono-Regular', Consolas, 'Liberation Mono', Menlo, monospace;
  font-size: 0.85rem;
  color: var(--color-text-primary);
}

.copy-field .btn {
  flex-shrink: 0;
  padding: 10px 16px;
}

.summary-row {
  display: flex;
  align-items: flex-start;
  justify-content: space-between;
  gap: 16px;
  padding: 12px 0;
  border-bottom: 1px solid var(--color-border);
}

.summary-row:last-child {
  border-bottom: none;
  padding-bottom: 0;
}

.summary-row:first-child {
  padding-top: 0;
}

.summary-row__label {
  font-size: 0.85rem;
  color: var(--color-text-muted);
  flex-shrink: 0;
}

.summary-row__value {
  font-size: 0.9rem;
  color: var(--color-text-primary);
  font-weight: 600;
  text-align: end;
  /* Flex items default to min-width:auto, which refuses to shrink below
     the size of an unbroken string like a wallet address — that's what
     was pushing the card wider than its container. min-width:0 lets it
     actually shrink to fit, and overflow-wrap wraps the long string onto
     more than one line instead of spilling past the card's edge. */
  min-width: 0;
  overflow-wrap: break-word;
}

.summary-row__value--mono {
  font-family: 'SFMono-Regular', Consolas, 'Liberation Mono', Menlo, monospace;
  font-weight: 500;
  font-size: 0.85rem;
  /* Addresses/hashes have no spaces to break on naturally, so allow a
     hard break anywhere it's needed to stay inside the card. */
  overflow-wrap: anywhere;
}

.form-footer-actions {
  display: flex;
  justify-content: flex-end;
  gap: 12px;
  margin-top: 4px;
}

.hidden {
  display: none !important;
}

@media (max-width: 560px) {
  .segmented-control {
    grid-template-columns: 1fr;
  }

  .form-footer-actions {
    flex-direction: column-reverse;
  }

  .form-footer-actions .btn {
    width: 100%;
  }

  .copy-field {
    flex-wrap: wrap;
  }

  .copy-field .btn {
    width: 100%;
  }
}

/* --------------------------------------------------------------------------
   17. Trade lifecycle stepper (trade-detail.html)
   Five stages of an escrow's life. Each stage carries one of the design
   system's status colors (the same palette used by .status-badge), so the
   color itself tells you *what kind* of stage you're looking at, while the
   --upcoming / --current / --complete modifier tells you *where you are*.
   -------------------------------------------------------------------------- */
.lifecycle-stepper {
  display: flex;
  align-items: flex-start;
  margin: 26px 0 32px;
}

.lifecycle-stepper__step {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 10px;
  flex: 0 1 auto;
  min-width: 76px;
}

/* Per-stage color, sourced from the shared status tokens */
.lifecycle-stepper__step[data-color="gray"]  { --step-bg: var(--status-gray-bg);  --step-border: var(--status-gray-border);  --step-text: var(--status-gray-text); }
.lifecycle-stepper__step[data-color="blue"]  { --step-bg: var(--status-blue-bg);  --step-border: var(--status-blue-border);  --step-text: var(--status-blue-text); }
.lifecycle-stepper__step[data-color="amber"] { --step-bg: var(--status-amber-bg); --step-border: var(--status-amber-border); --step-text: var(--status-amber-text); }
.lifecycle-stepper__step[data-color="green"] { --step-bg: var(--status-green-bg); --step-border: var(--status-green-border); --step-text: var(--status-green-text); }
.lifecycle-stepper__step[data-color="red"]   { --step-bg: var(--status-red-bg);   --step-border: var(--status-red-border);   --step-text: var(--status-red-text); }

.lifecycle-stepper__dot {
  position: relative;
  width: 26px;
  height: 26px;
  border-radius: 50%;
  display: grid;
  place-items: center;
  background: transparent;
  border: 2px solid var(--color-border-strong);
  flex-shrink: 0;
  transition: background 0.25s var(--ease-standard), border-color 0.25s var(--ease-standard), box-shadow 0.25s var(--ease-standard);
}

.lifecycle-stepper__check,
.lifecycle-stepper__pulse {
  grid-area: 1 / 1;
}

.lifecycle-stepper__check {
  width: 13px;
  height: 13px;
  color: var(--step-text);
  opacity: 0;
  transform: scale(0.5);
  transition: opacity 0.2s var(--ease-standard), transform 0.2s var(--ease-standard);
}

.lifecycle-stepper__pulse {
  width: 8px;
  height: 8px;
  border-radius: 50%;
  background: var(--step-text);
  opacity: 0;
  transform: scale(0.4);
  transition: opacity 0.2s var(--ease-standard), transform 0.2s var(--ease-standard);
}

.lifecycle-stepper__label {
  font-size: 0.74rem;
  font-weight: 600;
  color: var(--color-text-muted);
  text-align: center;
  line-height: 1.25;
  max-width: 94px;
  transition: color 0.25s var(--ease-standard);
}

.lifecycle-stepper__line {
  --line-fill: var(--color-border);
  flex: 1 1 auto;
  height: 2px;
  border-radius: 2px;
  margin: 13px 2px 0;
  background: var(--color-border);
  transition: background 0.35s var(--ease-standard);
}

.lifecycle-stepper__line[data-color="gray"]  { --line-fill: var(--status-gray-border); }
.lifecycle-stepper__line[data-color="blue"]  { --line-fill: var(--status-blue-border); }
.lifecycle-stepper__line[data-color="amber"] { --line-fill: var(--status-amber-border); }
.lifecycle-stepper__line[data-color="green"] { --line-fill: var(--status-green-border); }
.lifecycle-stepper__line[data-color="red"]   { --line-fill: var(--status-red-border); }

.lifecycle-stepper__line--filled {
  background: var(--line-fill);
}

/* Upcoming: hollow outline, dimmed label — the stage's color hasn't "arrived" yet */
.lifecycle-stepper__step--upcoming .lifecycle-stepper__dot {
  background: transparent;
  border-color: var(--color-border-strong);
}

.lifecycle-stepper__step--upcoming .lifecycle-stepper__label {
  color: var(--color-text-muted);
}

/* Current: filled with the stage's status color and a soft pulse */
.lifecycle-stepper__step--current .lifecycle-stepper__dot {
  background: var(--step-bg);
  border-color: var(--step-border);
  box-shadow: 0 0 0 4px var(--step-bg);
  animation: lifecycle-stepper-pulse 2s ease-in-out infinite;
}

.lifecycle-stepper__step--current .lifecycle-stepper__pulse {
  opacity: 1;
  transform: scale(1);
}

.lifecycle-stepper__step--current .lifecycle-stepper__label {
  color: var(--color-text-primary);
}

@keyframes lifecycle-stepper-pulse {
  0%, 100% { box-shadow: 0 0 0 4px var(--step-bg); }
  50%      { box-shadow: 0 0 0 8px var(--step-bg); }
}

/* Complete: solid fill + checkmark, settled (no pulse) */
.lifecycle-stepper__step--complete .lifecycle-stepper__dot {
  background: var(--step-bg);
  border-color: var(--step-border);
}

.lifecycle-stepper__step--complete .lifecycle-stepper__check {
  opacity: 1;
  transform: scale(1);
}

.lifecycle-stepper__step--complete .lifecycle-stepper__label {
  color: var(--color-text-secondary);
}

@media (max-width: 480px) {
  .lifecycle-stepper__step {
    min-width: 0;
  }

  .lifecycle-stepper__label {
    font-size: 0.64rem;
    max-width: 68px;
  }

  .lifecycle-stepper__dot {
    width: 22px;
    height: 22px;
  }

  .lifecycle-stepper__line {
    margin-top: 11px;
  }
}

/* --------------------------------------------------------------------------
   18. QR panel (fund-escrow step)
   -------------------------------------------------------------------------- */
.qr-panel {
  display: flex;
  justify-content: center;
  padding: 20px;
  margin-top: 20px;
  background: #FFFFFF;
  border-radius: var(--radius-card-sm);
  box-shadow: var(--shadow-card);
}

.qr-panel svg {
  width: 168px;
  height: 168px;
  display: block;
}

.qr-caption {
  text-align: center;
  font-size: 0.8rem;
  margin: 10px 0 0;
}

/* --------------------------------------------------------------------------
   19. Confirming-state icon (pairs with .success-icon)
   -------------------------------------------------------------------------- */
.confirming-icon {
  display: flex;
  align-items: center;
  justify-content: center;
  width: 60px;
  height: 60px;
  border-radius: 50%;
  background: var(--status-amber-bg);
  border: 1px solid var(--status-amber-border);
  color: var(--status-amber-text);
  margin: 0 auto 18px;
  animation: confirming-pulse 2s ease-in-out infinite;
}

@keyframes confirming-pulse {
  0%, 100% { box-shadow: 0 0 0 0 rgba(255, 183, 74, 0.35); }
  50%      { box-shadow: 0 0 0 8px rgba(255, 183, 74, 0); }
}

/* --------------------------------------------------------------------------
   20. Countdown pill (delivered-state auto-release timer)
   -------------------------------------------------------------------------- */
.countdown-pill {
  position: relative;
  display: flex;
  align-items: center;
  gap: 14px;
  margin: 20px 0;
  padding: 16px 22px 18px;
  background: var(--color-surface-strong);
  backdrop-filter: blur(20px);
  -webkit-backdrop-filter: blur(20px);
  border: 1px solid var(--status-amber-border);
  border-radius: var(--radius-pill);
  box-shadow: var(--shadow-card);
  overflow: hidden;
}

.countdown-pill__icon {
  display: flex;
  align-items: center;
  justify-content: center;
  width: 38px;
  height: 38px;
  flex-shrink: 0;
  border-radius: 50%;
  background: var(--status-amber-bg);
  color: var(--status-amber-text);
}

.countdown-pill__body {
  display: flex;
  flex-direction: column;
  gap: 2px;
  min-width: 0;
}

.countdown-pill__label {
  font-size: 0.72rem;
  font-weight: 600;
  letter-spacing: 0.07em;
  text-transform: uppercase;
  color: var(--color-text-muted);
}

.countdown-pill__value {
  font-family: 'SFMono-Regular', Consolas, 'Liberation Mono', Menlo, monospace;
  font-variant-numeric: tabular-nums;
  font-size: 1.6rem;
  font-weight: 700;
  letter-spacing: 0.01em;
  color: var(--color-text-primary);
  line-height: 1.15;
}

/* Thin progress track along the bottom edge of the pill — fill shrinks as
   the deadline approaches, giving an at-a-glance sense of time remaining
   without having to read the digits. */
.countdown-pill__track {
  position: absolute;
  left: 18px;
  right: 18px;
  bottom: 7px;
  height: 3px;
  border-radius: var(--radius-pill);
  background: rgba(255, 255, 255, 0.08);
  overflow: hidden;
}

.countdown-pill__fill {
  height: 100%;
  width: 100%;
  background: linear-gradient(90deg, var(--color-accent), var(--color-glow-violet));
  transition: width 1s linear;
}

/* Warning — 24h or less remaining */
.countdown-pill--warning .countdown-pill__value {
  color: var(--status-amber-text);
}

.countdown-pill--warning .countdown-pill__fill {
  background: linear-gradient(90deg, #FFCB7A, #E0912A);
}

/* Urgent — 6h or less remaining; same soft pulse language as .confirming-icon */
.countdown-pill--urgent {
  border-color: var(--status-red-border);
  animation: countdown-pill-pulse 1.6s ease-in-out infinite;
}

.countdown-pill--urgent .countdown-pill__icon {
  background: var(--status-red-bg);
  color: var(--status-red-text);
}

.countdown-pill--urgent .countdown-pill__value {
  color: var(--status-red-text);
}

.countdown-pill--urgent .countdown-pill__fill {
  background: linear-gradient(90deg, #FF7A7A, #a3232f);
}

@keyframes countdown-pill-pulse {
  0%, 100% { box-shadow: var(--shadow-card), 0 0 0 0 rgba(255, 122, 122, 0.35); }
  50%      { box-shadow: var(--shadow-card), 0 0 0 8px rgba(255, 122, 122, 0); }
}

/* Expired — deadline passed, window auto-closed */
.countdown-pill--expired {
  border-color: var(--status-gray-border);
  animation: none;
}

.countdown-pill--expired .countdown-pill__icon {
  background: var(--status-gray-bg);
  color: var(--status-gray-text);
}

.countdown-pill--expired .countdown-pill__value {
  color: var(--status-gray-text);
}

@media (max-width: 480px) {
  .countdown-pill {
    padding: 14px 18px 16px;
  }

  .countdown-pill__value {
    font-size: 1.3rem;
  }
}

/* --------------------------------------------------------------------------
   21. Dispute form — evidence textarea error state + photo upload
   Calm, information-forward palette: the reason/eyebrow around this form
   uses the muted red "something's wrong" token, but the evidence field
   itself stays neutral, and the upload control uses the same blue used
   for "funded" elsewhere — attaching evidence is a constructive step, not
   an alarm. Only the error state below borrows red, and even then just
   for a border/hint, never a fill.
   -------------------------------------------------------------------------- */
.form-textarea--error,
.form-input--error {
  border-color: var(--status-red-border);
}

.form-textarea--error:focus,
.form-input--error:focus {
  border-color: var(--status-red-text);
  box-shadow: 0 0 0 3px var(--status-red-bg);
}

.form-hint--error {
  color: var(--status-red-text);
}

.dispute-upload__dropzone {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 4px;
  padding: 22px 16px;
  text-align: center;
  background: var(--color-surface-soft);
  border: 1.5px dashed var(--color-border-strong);
  border-radius: 14px;
  cursor: pointer;
  transition: border-color 0.2s var(--ease-standard), background 0.2s var(--ease-standard);
}

.dispute-upload__dropzone:hover,
.dispute-upload__dropzone--drag {
  border-color: var(--color-accent);
  background: var(--color-surface);
}

.dispute-upload__dropzone:focus-visible {
  outline: 2px solid var(--color-accent-strong);
  outline-offset: 2px;
}

.dispute-upload__icon {
  display: flex;
  align-items: center;
  justify-content: center;
  width: 38px;
  height: 38px;
  border-radius: 50%;
  background: var(--status-blue-bg);
  color: var(--status-blue-text);
  margin-bottom: 4px;
  flex-shrink: 0;
}

.dispute-upload__text {
  margin: 0;
  font-size: 0.85rem;
  font-weight: 600;
  color: var(--color-text-primary);
}

.dispute-upload__text span {
  color: var(--color-accent-strong);
}

.dispute-upload__subtext {
  margin: 0;
  font-size: 0.76rem;
  color: var(--color-text-muted);
}

.dispute-upload__grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(76px, 1fr));
  gap: 10px;
  margin-top: 14px;
}

.dispute-upload__thumb {
  position: relative;
  aspect-ratio: 1;
  border-radius: 10px;
  border: 1px solid var(--color-border);
  background: var(--color-surface-soft);
  /* No overflow:hidden here — the remove button below needs room to
     extend its invisible tap area past this box's edge. The image is
     clipped to the rounded corners on its own element instead (below). */
}

.dispute-upload__thumb img {
  width: 100%;
  height: 100%;
  border-radius: 9px;
  overflow: hidden;
  object-fit: cover;
  display: block;
}

/* The visible "remove" badge is a small 20px circle (unchanged look),
   positioned via the ::before pseudo-element below. The button element
   itself is sized to a full 44x44 tap target and centered on that same
   spot, so the invisible portion overhangs the thumbnail's corner rather
   than shrinking the visible badge. */
.dispute-upload__remove {
  position: absolute;
  top: -8px;
  inset-inline-end: -8px;
  display: flex;
  align-items: center;
  justify-content: center;
  width: 44px;
  height: 44px;
  padding: 0;
  border: none;
  background: transparent;
  color: #FFFFFF;
  font-size: 11px;
  line-height: 1;
  cursor: pointer;
}

.dispute-upload__remove::before {
  content: '';
  position: absolute;
  top: 50%;
  left: 50%;
  width: 20px;
  height: 20px;
  transform: translate(-50%, -50%);
  border-radius: 50%;
  border: 1px solid rgba(255, 255, 255, 0.25);
  background: rgba(5, 11, 31, 0.72);
  backdrop-filter: blur(4px);
  transition: background 0.2s var(--ease-standard), border-color 0.2s var(--ease-standard);
}

.dispute-upload__remove svg {
  position: relative;
  z-index: 1;
}

.dispute-upload__remove:hover::before {
  background: var(--status-red-bg);
  border-color: var(--status-red-border);
}

.dispute-upload__remove:hover {
  color: var(--status-red-text);
}

@media (max-width: 480px) {
  .dispute-upload__grid {
    grid-template-columns: repeat(auto-fill, minmax(64px, 1fr));
  }
}

/* --------------------------------------------------------------------------
   22. Profile page — wallet card, rating summary, trade history
   -------------------------------------------------------------------------- */
.profile-summary-grid {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 24px;
  align-items: stretch;
}

.profile-summary-grid .glass-card {
  display: flex;
  flex-direction: column;
  min-width: 0; /* grid items default to min-width:auto, which lets long
                   unbreakable content (the nowrap wallet address) force
                   the column — and the whole page — wider than the
                   viewport instead of truncating with ellipsis as
                   intended. This lets the column shrink properly. */
}

/* -- Star rating widget: a fixed-size muted star with a clipped, colored
   copy laid on top so fractional ratings (e.g. 4.8) can fill ~80% of the
   fifth star rather than only supporting whole stars. -- */
.star-icon {
  position: relative;
  display: inline-block;
  width: 20px;
  height: 20px;
  flex-shrink: 0;
}

.star-icon--sm {
  width: 14px;
  height: 14px;
}

.star-icon__bg,
.star-icon__fill {
  position: absolute;
  top: 0;
  left: 0;
  width: 100%;
  height: 100%;
}

.star-icon__bg svg {
  width: 100%;
  height: 100%;
  color: var(--color-border-strong);
}

.star-icon__fill {
  overflow: hidden;
  width: 0%; /* set inline per-star via JS */
}

.star-icon__fill svg {
  color: #FFCB7A;
  display: block;
}

.star-icon--sm .star-icon__fill svg,
.star-icon--sm .star-icon__bg svg {
  width: 14px;
  height: 14px;
}

.star-icon:not(.star-icon--sm) .star-icon__fill svg,
.star-icon:not(.star-icon--sm) .star-icon__bg svg {
  width: 20px;
  height: 20px;
}

.rating-summary {
  display: flex;
  gap: 28px;
  align-items: center;
  flex: 1;
}

.rating-summary__score {
  flex-shrink: 0;
  text-align: center;
  padding-right: 28px;
  border-right: 1px solid var(--color-border);
}

.rating-summary__number {
  display: block;
  font-size: 2.75rem;
  color: var(--color-text-primary);
  line-height: 1;
}

.rating-summary__stars {
  display: flex;
  gap: 3px;
  justify-content: center;
  margin: 10px 0 8px;
}

.rating-summary__breakdown {
  flex: 1;
  display: flex;
  flex-direction: column;
  gap: 8px;
  min-width: 0;
}

.rating-bar-row {
  display: flex;
  align-items: center;
  gap: 10px;
  font-size: 0.8rem;
}

.rating-bar-row__label {
  display: flex;
  align-items: center;
  gap: 4px;
  width: 34px;
  flex-shrink: 0;
  color: var(--color-text-secondary);
  font-weight: 600;
}

.rating-bar-row__track {
  flex: 1;
  height: 6px;
  border-radius: var(--radius-pill);
  background: var(--color-surface-soft);
  border: 1px solid var(--color-border);
  overflow: hidden;
}

.rating-bar-row__fill {
  height: 100%;
  border-radius: var(--radius-pill);
  background: linear-gradient(90deg, #FFCB7A 0%, #E0912A 100%);
}

.rating-bar-row__count {
  width: 22px;
  flex-shrink: 0;
  text-align: right;
  color: var(--color-text-muted);
}

@media (max-width: 760px) {
  .profile-summary-grid {
    grid-template-columns: 1fr;
  }

  .rating-summary {
    flex-direction: column;
    align-items: stretch;
  }

  .rating-summary__score {
    padding-right: 0;
    padding-bottom: 20px;
    border-right: none;
    border-bottom: 1px solid var(--color-border);
  }
}

/* --------------------------------------------------------------------------
   23. Review card — feedback a counterparty left after a completed trade.
   Reuses .star-icon (see section 22 above) for the rating row and the same
   avatar-gradient / initials convention as .trade-card.
   -------------------------------------------------------------------------- */
.review-list {
  display: flex;
  flex-direction: column;
  gap: 16px;
}

.review-card__top {
  display: flex;
  align-items: flex-start;
  justify-content: space-between;
  gap: 16px;
  margin-bottom: 12px;
}

.review-card__party {
  display: flex;
  align-items: center;
  gap: 12px;
  min-width: 0;
}

.review-card__avatar {
  width: 38px;
  height: 38px;
  border-radius: 50%;
  display: flex;
  align-items: center;
  justify-content: center;
  font-weight: 700;
  font-size: 0.72rem;
  color: #FFFFFF;
  flex-shrink: 0;
  box-shadow: 0 0 0 3px rgba(255, 255, 255, 0.06), 0 0 0 1px rgba(255, 255, 255, 0.12) inset;
}

.review-card__identity {
  min-width: 0;
}

.review-card__name {
  margin: 0;
  font-weight: 600;
  font-size: 0.9rem;
  color: var(--color-text-primary);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  max-width: 220px;
}

.review-card__meta {
  margin: 3px 0 0 0;
  font-size: 0.76rem;
}

.review-card__stars {
  display: flex;
  gap: 2px;
  flex-shrink: 0;
  padding-top: 2px;
}

.review-card__comment {
  margin: 0;
  color: var(--color-text-secondary);
  font-size: 0.9rem;
  line-height: 1.6;
}

@media (max-width: 560px) {
  .review-card__top {
    flex-wrap: wrap;
  }

  .review-card__name {
    max-width: 160px;
  }
}

/* --------------------------------------------------------------------------
   23b. Section card — wraps a section's eyebrow/heading/copy together with
   its list of item-cards (.review-list / .trade-list) into one card, used
   on the profile page for Reviews and Trade History. The list keeps its
   own markup and JS untouched; item cards just lose their own background/
   border/shadow/top-accent-bar here (scoped to .section-card, so the same
   .review-card / .trade-card markup still renders as full standalone cards
   everywhere else, e.g. the trade list on index.html) and instead read as
   simple divided rows inside the outer card.
   -------------------------------------------------------------------------- */
.section-card .review-list,
.section-card .trade-list {
  margin-top: 24px;
  gap: 0;
}

.section-card .review-card,
.section-card .trade-card {
  background: none;
  backdrop-filter: none;
  -webkit-backdrop-filter: none;
  border: none;
  border-radius: 0;
  box-shadow: none;
  padding: 20px 0;
  border-bottom: 1px solid var(--color-border);
}

.section-card .review-card::before,
.section-card .trade-card::before {
  content: none;
}

.section-card .review-list > :first-child,
.section-card .trade-list > :first-child {
  padding-top: 4px;
}

.section-card .review-list > :last-child,
.section-card .trade-list > :last-child {
  border-bottom: none;
  padding-bottom: 4px;
}

/* Interactive rows (trade history links through to trade-detail.html) get
   a flat hover tint instead of the standalone card's lift + shadow, since
   there's no card edge left here to lift away from. */
.section-card .glass-card--interactive:hover {
  transform: none;
  box-shadow: none;
  border-color: transparent;
  background: var(--color-surface);
}

/* Empty/loading state panels (see section 26) still read fine centered
   inside the card without extra top margin doubling up on the card's own
   padding. */
.section-card .state-panel {
  margin-top: 24px;
}

/* --------------------------------------------------------------------------
   24. Admin — dispute queue
   Internal/staff surface. Reuses every existing token (surfaces, borders,
   status colors, avatar convention) but leans on the amber/red end of the
   palette instead of the consumer brand blue, and swaps the airy card-list
   layout for a dense, scrollable data table — signals "operations console"
   rather than "another trade screen" without introducing a new visual system.
   -------------------------------------------------------------------------- */
.admin-tag {
  display: inline-flex;
  align-items: center;
  gap: 5px;
  padding: 3px 10px 3px 8px;
  border-radius: var(--radius-pill);
  border: 1px solid var(--status-red-border);
  background: var(--status-red-bg);
  color: var(--status-red-text);
  font-size: 0.66rem;
  font-weight: 700;
  letter-spacing: 0.07em;
  text-transform: uppercase;
  vertical-align: middle;
}

.admin-tag__dot {
  width: 5px;
  height: 5px;
  border-radius: 50%;
  background: currentColor;
  flex-shrink: 0;
}

.admin-banner {
  display: flex;
  align-items: flex-start;
  gap: 12px;
  padding: 14px 18px;
  margin-bottom: 28px;
  background: var(--status-amber-bg);
  border: 1px solid var(--status-amber-border);
  border-radius: var(--radius-card-sm);
  color: var(--status-amber-text);
  font-size: 0.85rem;
  line-height: 1.5;
}

.admin-banner__icon {
  flex-shrink: 0;
  margin-top: 1px;
}

.admin-banner strong {
  color: var(--color-text-primary);
}

/* Stat strip reused from index.html, recolored so the hero figure reads as
   "at-risk value" (amber) rather than the consumer "in escrow" blue. */
.stats-strip--admin .stat-block--hero .data-figure {
  color: var(--status-amber-text);
}

.stats-strip--admin {
  border-color: var(--status-amber-border);
}

/* Table shell */
.dispute-table-card {
  padding: 0;
}

.dispute-table-scroll {
  overflow-x: auto;
  border-radius: var(--radius-card-lg);
  /* Smooth momentum scroll for the horizontal drag on older iOS Safari
     (modern versions do this by default, but it's a no-op there so it's
     safe to leave on). */
  -webkit-overflow-scrolling: touch;
  /* Without this, dragging past the table's left/right edge lets the touch
     gesture "escape" into the page — on iOS Safari and Android Chrome that
     reads as the whole page lurching sideways, and at the left edge
     specifically it can trigger the browser's edge-swipe back-navigation
     gesture. Containing it keeps the scroll entirely inside the table. */
  overscroll-behavior-x: contain;
  /* Themed scrollbar instead of the raw OS default (a thick flat-gray bar
     that reads as an unstyled/broken element against the glass card). Thin
     on both engines, transparent track so the card's own background shows
     through, and a soft border-tint thumb that fits the existing palette. */
  scrollbar-width: thin;
  scrollbar-color: var(--color-border-strong) transparent;
}

.dispute-table-scroll::-webkit-scrollbar {
  height: 8px;
}

.dispute-table-scroll::-webkit-scrollbar-track {
  background: transparent;
}

.dispute-table-scroll::-webkit-scrollbar-thumb {
  background: var(--color-border-strong);
  border-radius: var(--radius-pill);
}

.dispute-table-scroll::-webkit-scrollbar-thumb:hover {
  background: var(--color-text-muted);
}

.dispute-table {
  width: 100%;
  border-collapse: collapse;
  min-width: 920px;
  font-size: 0.86rem;
}

.dispute-table thead th {
  text-align: left;
  padding: 14px 18px;
  font-size: 0.68rem;
  font-weight: 700;
  letter-spacing: 0.07em;
  text-transform: uppercase;
  color: var(--color-text-muted);
  background: var(--color-surface-soft);
  border-bottom: 1px solid var(--color-border);
  white-space: nowrap;
}

.dispute-table tbody td {
  padding: 16px 18px;
  border-bottom: 1px solid var(--color-border);
  vertical-align: top;
  color: var(--color-text-secondary);
}

.dispute-table tbody tr:last-child td {
  border-bottom: none;
}

.dispute-table tbody tr:hover td {
  background: rgba(255, 255, 255, 0.02);
}

.dispute-table tbody tr.is-resolved td {
  opacity: 0.5;
}

.dispute-table tbody tr.is-resolved:hover td {
  background: transparent;
}

.dispute-id {
  font-family: 'SFMono-Regular', Consolas, 'Liberation Mono', Menlo, monospace;
  font-weight: 700;
  color: var(--color-text-primary);
  font-size: 0.88rem;
  display: block;
  white-space: nowrap;
}

.dispute-item {
  font-size: 0.78rem;
  color: var(--color-text-muted);
  margin: 3px 0 0 0;
  max-width: 190px;
  line-height: 1.4;
}

.dispute-age {
  display: block;
  font-size: 0.74rem;
  color: var(--color-text-muted);
  margin-top: 7px;
  white-space: nowrap;
}

.dispute-age--aging {
  color: var(--status-red-text);
  font-weight: 600;
}

.dispute-party {
  display: flex;
  align-items: center;
  gap: 8px;
}

.dispute-party__avatar {
  width: 26px;
  height: 26px;
  border-radius: 50%;
  display: flex;
  align-items: center;
  justify-content: center;
  font-size: 0.6rem;
  font-weight: 700;
  color: #FFFFFF;
  flex-shrink: 0;
  box-shadow: 0 0 0 2px rgba(255, 255, 255, 0.06), 0 0 0 1px rgba(255, 255, 255, 0.12) inset;
}

.dispute-party__name {
  font-size: 0.84rem;
  color: var(--color-text-primary);
  font-weight: 600;
  max-width: 128px;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.dispute-amount {
  font-size: 0.98rem;
  color: var(--color-text-primary);
  white-space: nowrap;
}

.dispute-evidence-cell {
  max-width: 280px;
  min-width: 220px;
}

.evidence-snippet {
  margin: 0;
  color: var(--color-text-secondary);
  font-size: 0.83rem;
  font-style: italic;
  line-height: 1.55;
  display: -webkit-box;
  -webkit-line-clamp: 2;
  -webkit-box-orient: vertical;
  overflow: hidden;
}

.evidence-snippet.is-expanded {
  -webkit-line-clamp: unset;
  overflow: visible;
}

.evidence-expand-btn {
  background: none;
  border: none;
  padding: 13px 14px 13px 0;
  margin: 0 0 -13px;
  font-size: 0.76rem;
  font-weight: 600;
  color: var(--color-accent-strong);
  cursor: pointer;
  font-family: inherit;
}

.evidence-expand-btn:hover {
  text-decoration: underline;
}

.dispute-actions-cell {
  white-space: nowrap;
}

.dispute-actions {
  display: flex;
  gap: 10px;
}

.dispute-confirm {
  display: flex;
  align-items: center;
  gap: 10px;
  font-size: 0.78rem;
  color: var(--color-text-secondary);
  white-space: nowrap;
}

.btn-sm {
  padding: 12px 14px;
  font-size: 0.78rem;
  border-radius: 9px;
}

.resolution-badge {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  font-size: 0.78rem;
  font-weight: 600;
  white-space: nowrap;
}

.resolution-badge--released { color: var(--status-green-text); }
.resolution-badge--refunded { color: var(--status-blue-text); }

.admin-all-clear {
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 12px 18px;
  border-bottom: 1px solid var(--color-border);
  background: var(--status-green-bg);
  color: var(--status-green-text);
  font-size: 0.85rem;
  font-weight: 600;
}

/* Below this width a 6-column table has no good non-scrolling layout, so
   it stops being a table visually and becomes a stack of labeled cards
   instead — the same data, one field per line, nothing to drag sideways.
   thead is hidden (its content reappears as the ::before labels below)
   and .dispute-table-scroll's overflow-x becomes moot since nothing
   inside it is wider than the viewport anymore. */
@media (max-width: 720px) {
  .dispute-table {
    min-width: 0;
  }

  .dispute-table thead {
    display: none;
  }

  .dispute-table, .dispute-table tbody, .dispute-table tr, .dispute-table td {
    display: block;
    width: 100%;
  }

  .dispute-table tbody tr {
    padding: 16px;
    margin: 14px;
    border: 1px solid var(--color-border);
    border-radius: var(--radius-card-sm);
    background: var(--color-surface-soft);
  }

  .dispute-table tbody tr:first-child {
    margin-top: 0;
  }

  .dispute-table td {
    padding: 10px 0;
    border-bottom: 1px solid var(--color-border);
  }

  .dispute-table td:first-child {
    padding-top: 0;
  }

  .dispute-table tbody tr td:last-child {
    padding-bottom: 0;
    border-bottom: none;
  }

  .dispute-table td::before {
    content: attr(data-label);
    display: block;
    font-size: 0.66rem;
    font-weight: 700;
    letter-spacing: 0.07em;
    text-transform: uppercase;
    color: var(--color-text-muted);
    margin-bottom: 8px;
  }

  .dispute-evidence-cell {
    max-width: none;
    min-width: 0;
  }

  .dispute-actions {
    flex-wrap: wrap;
  }

  .dispute-actions .btn,
  .dispute-confirm .btn {
    flex: 1 1 auto;
  }
}

/* Skeleton placeholder for a dispute row — decorative only (the real
   table it stands in for is aria-hidden via its #disputeSkeleton parent),
   so it doesn't need to mirror all six columns exactly, just read as "a
   case is about to appear here" at a glance. */
.dispute-row-skeleton {
  padding: 18px 20px;
  border-bottom: 1px solid var(--color-border);
}

.dispute-row-skeleton:last-child {
  border-bottom: none;
}

.dispute-row-skeleton__top {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 16px;
}

.dispute-row-skeleton__parties {
  display: flex;
  align-items: center;
  gap: 8px;
  margin-top: 16px;
  flex-wrap: wrap;
}

/* --------------------------------------------------------------------------
   25. Toast notifications — glass-styled, slide in from the bottom-right
   corner, auto-dismiss. Triggered from any page via window.showToast()
   (defined in js/main.js); the container div is created on first use, so
   no markup needs to be added to individual pages. Stacks with the newest
   toast nearest the corner via column-reverse, pushing older ones up.
   -------------------------------------------------------------------------- */
.toast-container {
  position: fixed;
  /* max() keeps the original 24px everywhere non-notched, and only grows
     on a phone with a bottom home-indicator / side rounded corner so
     toasts never render underneath either. */
  bottom: max(24px, calc(env(safe-area-inset-bottom) + 12px));
  right: max(24px, calc(env(safe-area-inset-right) + 12px));
  z-index: 1000;
  display: flex;
  flex-direction: column-reverse;
  gap: 12px;
  max-width: min(360px, calc(100vw - 32px));
  pointer-events: none;
}

.toast {
  display: flex;
  align-items: flex-start;
  gap: 10px;
  padding: 14px 16px;
  background: var(--color-surface-strong);
  backdrop-filter: blur(20px);
  -webkit-backdrop-filter: blur(20px);
  border: 1px solid var(--color-border-strong);
  border-radius: var(--radius-card-sm);
  box-shadow: var(--shadow-card-hover);
  pointer-events: auto;
  transform: translateX(120%);
  opacity: 0;
  transition: transform 0.35s var(--ease-standard), opacity 0.35s var(--ease-standard);
}

.toast--visible {
  transform: translateX(0);
  opacity: 1;
}

.toast--leaving {
  transform: translateX(120%);
  opacity: 0;
}

.toast__icon {
  display: flex;
  align-items: center;
  justify-content: center;
  width: 22px;
  height: 22px;
  margin-top: 1px;
  border-radius: 50%;
  flex-shrink: 0;
}

.toast__icon svg {
  width: 13px;
  height: 13px;
}

.toast--success .toast__icon { background: var(--status-green-bg); color: var(--status-green-text); }
.toast--error   .toast__icon { background: var(--status-red-bg);   color: var(--status-red-text); }
.toast--warning .toast__icon { background: var(--status-amber-bg); color: var(--status-amber-text); }
.toast--info    .toast__icon { background: var(--status-blue-bg);  color: var(--status-blue-text); }

.toast__message {
  flex: 1;
  margin: 0;
  padding-top: 2px;
  font-size: 0.85rem;
  line-height: 1.5;
  color: var(--color-text-primary);
}

.toast__close {
  position: relative;
  display: flex;
  align-items: center;
  justify-content: center;
  width: 22px;
  height: 22px;
  flex-shrink: 0;
  padding: 0;
  background: transparent;
  border: none;
  border-radius: 50%;
  color: var(--color-text-muted);
  cursor: pointer;
  transition: background 0.2s var(--ease-standard), color 0.2s var(--ease-standard);
}

/* Invisible tap area extending the 22px visual close button out to the
   44px minimum recommended touch target, without changing how it looks
   or growing the toast card itself. */
.toast__close::before {
  content: '';
  position: absolute;
  top: 50%;
  left: 50%;
  width: 44px;
  height: 44px;
  transform: translate(-50%, -50%);
}

.toast__close svg {
  width: 12px;
  height: 12px;
}

.toast__close:hover {
  background: var(--color-surface);
  color: var(--color-text-primary);
}

@media (max-width: 480px) {
  .toast-container {
    left: max(16px, env(safe-area-inset-left));
    right: max(16px, env(safe-area-inset-right));
    bottom: max(16px, calc(env(safe-area-inset-bottom) + 8px));
    max-width: none;
  }

  .toast {
    transform: translateY(120%);
  }

  .toast--visible {
    transform: translateY(0);
  }

  .toast--leaving {
    transform: translateY(120%);
  }
}

/* --------------------------------------------------------------------------
   25. Loading skeletons
   Generic shimmer primitives (.skeleton + shape modifiers) so any page can
   compose a placeholder that matches the shape of the real content it
   stands in for, without a bespoke skeleton per component.
   -------------------------------------------------------------------------- */
@keyframes skeleton-shimmer {
  0%   { background-position: 180% 0; }
  100% { background-position: -80% 0; }
}

.skeleton {
  background-color: var(--color-surface);
  background-image: linear-gradient(
    100deg,
    transparent 30%,
    rgba(255, 255, 255, 0.12) 50%,
    transparent 70%
  );
  background-size: 250% 100%;
  background-repeat: no-repeat;
  animation: skeleton-shimmer 1.6s ease-in-out infinite;
  pointer-events: none;
}

.skeleton-text {
  display: block;
  height: 12px;
  border-radius: 6px;
}

.skeleton-circle {
  display: inline-block;
  border-radius: 50%;
  flex-shrink: 0;
}

.skeleton-pill {
  display: inline-block;
  width: 92px;
  height: 24px;
  border-radius: var(--radius-pill);
}

.skeleton-block {
  display: inline-block;
  border-radius: 10px;
}

/* Dashboard stat strip skeleton — mirrors .stats-strip / .stat-block exactly
   so the swap-in from skeleton to real numbers doesn't shift layout. */
.stats-strip-skeleton .stat-block {
  display: flex;
  flex-direction: column;
  gap: 10px;
}

/* Trade-card skeleton — mirrors .trade-card's internal structure (avatar +
   name/sub, badge, item line, footer amount + button). */
.trade-card-skeleton .trade-card__top {
  margin-bottom: 18px;
}

.trade-card-skeleton .trade-card__party {
  gap: 12px;
}

.trade-card-skeleton__lines {
  display: flex;
  flex-direction: column;
  gap: 8px;
}

/* Lifecycle stepper skeleton (trade-detail.html) — same flex rhythm as
   .lifecycle-stepper, but with plain shimmering dots/lines instead of
   status-colored ones, since there's no status to show yet. */
.lifecycle-stepper-skeleton {
  display: flex;
  align-items: flex-start;
  margin: 26px 0 32px;
}

.lifecycle-stepper-skeleton__step {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 10px;
  flex: 0 1 auto;
  min-width: 76px;
}

.lifecycle-stepper-skeleton__line {
  flex: 1;
  height: 2px;
  margin-top: 13px;
  border-radius: 2px;
}

@media (max-width: 560px) {
  .lifecycle-stepper-skeleton__step {
    min-width: 0;
  }
}

/* --------------------------------------------------------------------------
   26. State panel — shared shape for empty states and error states
   Same layout, different icon color and copy: a centered icon, a title, a
   line of supporting text, and 1-2 actions. Reused wherever a list, card,
   or page has "nothing to show" or "couldn't load" to communicate.
   -------------------------------------------------------------------------- */
.state-panel {
  display: flex;
  flex-direction: column;
  align-items: center;
  text-align: center;
  padding: 56px 24px;
}

.state-panel--compact {
  padding: 40px 20px;
}

.state-panel__icon {
  display: flex;
  align-items: center;
  justify-content: center;
  width: 56px;
  height: 56px;
  border-radius: 50%;
  margin-bottom: 18px;
  background: var(--color-surface-soft);
  border: 1px solid var(--color-border);
  color: var(--color-text-muted);
}

.state-panel__icon--danger {
  background: var(--status-red-bg);
  border-color: var(--status-red-border);
  color: var(--status-red-text);
}

.state-panel__title {
  margin-bottom: 6px;
}

.state-panel__text {
  max-width: 380px;
  margin: 0 auto 22px;
}

.state-panel__actions {
  display: flex;
  gap: 12px;
  justify-content: center;
  flex-wrap: wrap;
}

/* --------------------------------------------------------------------------
   27. Demo toolbar
   Not a product feature — a small, clearly-labeled affordance (same spirit
   as the existing "(demo)" buttons on trade-detail.html / join-trade.html)
   so loading/empty/error states can be triggered on demand instead of only
   living behind a real fetch failure or an empty account.
   -------------------------------------------------------------------------- */
.demo-toolbar {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: 10px;
  padding: 10px 14px;
  margin-bottom: 28px;
  border: 1px dashed var(--color-border-strong);
  border-radius: var(--radius-card-sm);
  background: var(--color-surface-soft);
}

.demo-toolbar__label {
  font-size: 0.68rem;
  font-weight: 700;
  letter-spacing: 0.07em;
  text-transform: uppercase;
  color: var(--color-text-muted);
  margin-right: 2px;
  flex-shrink: 0;
}

/* --------------------------------------------------------------------------
   28. Page-load & flow-state transitions
   A gentle fade + lift, applied once when a page's <main> paints, and again
   any time a flow moves to its next step: skeleton -> content/error, form ->
   success, decision -> accepted/declined, and each hand-off in the trade
   lifecycle (funding -> confirming -> escrowed -> delivered -> dispute ->
   completed). Kept short and subtle so it reads as content settling into
   place rather than a flashy transition. Respects prefers-reduced-motion
   via section 16 below.
   -------------------------------------------------------------------------- */
@keyframes page-fade-in {
  from { opacity: 0; transform: translateY(10px); }
  to   { opacity: 1; transform: translateY(0); }
}

@keyframes state-fade-in {
  from { opacity: 0; transform: translateY(6px); }
  to   { opacity: 1; transform: translateY(0); }
}

main {
  animation: page-fade-in 0.5s var(--ease-standard) both;
}

/* Every container across the six pages that gets shown by toggling the
   .hidden class off — skeleton/content/error swaps on index.html and
   trade-detail.html, the new-trade.html form -> success swap, the
   join-trade.html decision -> accepted/declined swap, and each step of the
   trade-detail.html funding lifecycle. Listed by ID so no HTML changes are
   needed on any page. */
#dashboardSkeleton, #dashboardContent, #dashboardError,
#tradeDetailSkeleton, #tradeDetailNoSelection, #tradeDetailContent, #tradeDetailError,
#tradeFormCard, #escrowCreatedCard,
#decisionArea, #acceptedNote, #declinedNote,
#fundingSection, #confirmingSection, #escrowedSection,
#deliveredSection, #disputeSection, #completedSection {
  animation: state-fade-in 0.4s var(--ease-standard);
}

/* --------------------------------------------------------------------------
   29. Tap-target helper for small inline text links (e.g. header "Sign
   out"). Padding grows the real clickable box to ~44px tall; the equal
   negative margin cancels that growth back out so the link's visual
   position and its neighbors' spacing are unaffected.
   -------------------------------------------------------------------------- */
.tap-target-inline {
  display: inline-flex;
  align-items: center;
  padding: 12px 8px;
  margin: -12px -8px;
}

/* --------------------------------------------------------------------------
   16. Accessibility
   -------------------------------------------------------------------------- */
:focus-visible {
  outline: 2px solid var(--color-accent-strong);
  outline-offset: 2px;
}

@media (prefers-reduced-motion: reduce) {
  * {
    animation-duration: 0.01ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 0.01ms !important;
  }
}

/* --------------------------------------------------------------------------
   30. Page preloader — "beyond the clouds" boot animation
   A full-viewport veil that's present in the markup itself (not switched on
   by JS), so it's there from first paint with no flash of the bare page
   underneath. It's held for a minimum beat so it reads as an intentional
   moment rather than a flicker, then removed once the page has actually
   finished loading (see the inline script at the end of <body> on each
   page). Drifting cloud layers behind the escrow-seal mark echo the brand
   name — rising above a border, into open sky — while the ring "seals"
   itself shut on a loop and the wordmark settles in letter by letter.
   -------------------------------------------------------------------------- */
html.bb-loading,
html.bb-loading body {
  overflow: hidden;
  height: 100%;
}

.bb-loader {
  position: fixed;
  inset: 0;
  z-index: 3000;
  display: flex;
  align-items: center;
  justify-content: center;
  overflow: hidden;
  background: var(--color-bg-mid);
  background-image:
    radial-gradient(ellipse 900px 600px at 12% -10%, rgba(75, 132, 255, 0.20), transparent 60%),
    radial-gradient(ellipse 800px 700px at 100% 10%, rgba(122, 79, 214, 0.22), transparent 55%),
    radial-gradient(ellipse 1000px 800px at 50% 120%, rgba(5, 11, 31, 0.9), transparent 60%),
    linear-gradient(160deg, var(--color-bg-start) 0%, var(--color-bg-mid) 45%, var(--color-bg-end) 100%);
  transition: opacity 0.6s var(--ease-standard), visibility 0.6s var(--ease-standard);
}

.bb-loader.is-hidden {
  opacity: 0;
  visibility: hidden;
  pointer-events: none;
}

.bb-loader__sr {
  position: absolute;
  width: 1px;
  height: 1px;
  padding: 0;
  margin: -1px;
  overflow: hidden;
  clip: rect(0, 0, 0, 0);
  white-space: nowrap;
  border: 0;
}

/* Cloud layer — soft blurred blobs drifting left to right at different
   speeds/depths/opacities for a gentle parallax feel. */
.bb-loader__sky {
  position: absolute;
  inset: 0;
  overflow: hidden;
}

.bb-loader__cloud {
  position: absolute;
  border-radius: 50%;
  filter: blur(26px);
  opacity: 0;
  animation: bb-cloud-drift linear infinite, bb-cloud-fade-in 1.4s var(--ease-standard) forwards;
}

.bb-loader__cloud--1 {
  width: 340px;
  height: 120px;
  top: 18%;
  left: -30%;
  background: radial-gradient(closest-side, rgba(159, 188, 250, 0.5), rgba(159, 188, 250, 0) 72%);
  animation-duration: 22s;
}

.bb-loader__cloud--2 {
  width: 260px;
  height: 90px;
  top: 62%;
  left: -25%;
  background: radial-gradient(closest-side, rgba(122, 79, 214, 0.45), rgba(122, 79, 214, 0) 72%);
  animation-duration: 30s;
  animation-delay: -6s, 0.3s;
}

.bb-loader__cloud--3 {
  width: 420px;
  height: 150px;
  top: 40%;
  left: -35%;
  background: radial-gradient(closest-side, rgba(255, 255, 255, 0.35), rgba(255, 255, 255, 0) 72%);
  animation-duration: 38s;
  animation-delay: -14s, 0.6s;
}

.bb-loader__cloud--4 {
  width: 200px;
  height: 70px;
  top: 78%;
  left: -20%;
  background: radial-gradient(closest-side, rgba(75, 132, 255, 0.4), rgba(75, 132, 255, 0) 72%);
  animation-duration: 26s;
  animation-delay: -10s, 0.9s;
}

@keyframes bb-cloud-drift {
  from { transform: translateX(0); }
  to   { transform: translateX(160vw); }
}

@keyframes bb-cloud-fade-in {
  to { opacity: 1; }
}

/* Emblem + wordmark ---------------------------------------------------- */
.bb-loader__core {
  position: relative;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 20px;
}

.bb-loader__mark {
  display: block;
  width: 64px;
  height: 64px;
  color: #4B84FF;
  animation: bb-mark-in 0.7s var(--ease-standard) both;
}

/* Each ring/check gets its own stop along the red→yellow brand gradient
   (currentColor is resolved per-element, so this paints the mark as a
   genuine warm gradient rather than one flat color). */
.bb-loader__ring--outer {
  transform-box: fill-box;
  transform-origin: center;
  color: #2F6FED;
  opacity: 0.55;
  animation: bb-ring-spin 5s linear infinite;
}

.bb-loader__ring--mid {
  color: #4B84FF;
  stroke-dasharray: 120;
  stroke-dashoffset: 120;
  animation: bb-ring-draw 1.6s var(--ease-standard) 0.3s infinite;
}

.bb-loader__check {
  color: #FFB74A;
  stroke-dasharray: 26;
  stroke-dashoffset: 26;
  animation: bb-check-draw 1.6s var(--ease-standard) 0.3s infinite;
}

@keyframes bb-mark-in {
  from { opacity: 0; transform: scale(0.8); }
  to   { opacity: 1; transform: scale(1); }
}

@keyframes bb-ring-spin {
  to { transform: rotate(360deg); }
}

@keyframes bb-ring-draw {
  0%   { stroke-dashoffset: 120; }
  45%  { stroke-dashoffset: 0; }
  80%  { stroke-dashoffset: 0; }
  100% { stroke-dashoffset: -120; }
}

@keyframes bb-check-draw {
  0%   { stroke-dashoffset: 26; opacity: 0; }
  20%  { opacity: 1; }
  50%  { stroke-dashoffset: 0; }
  80%  { stroke-dashoffset: 0; opacity: 1; }
  100% { stroke-dashoffset: 0; opacity: 0; }
}

.bb-loader__word {
  display: flex;
  margin: 0;
  font-size: 1.05rem;
  font-weight: 700;
  letter-spacing: 0.04em;
  color: var(--color-text-primary);
}

.bb-loader__word span {
  display: inline-block;
  opacity: 0;
  transform: translateY(8px);
  animation: bb-letter-in 0.5s var(--ease-standard) both;
  animation-delay: calc(0.5s + var(--i, 0) * 0.035s);
}

.bb-loader__space {
  width: 0.32em;
}

@keyframes bb-letter-in {
  to { opacity: 1; transform: translateY(0); }
}

.bb-loader__bar {
  width: 120px;
  height: 3px;
  border-radius: var(--radius-pill);
  background: var(--color-surface-strong);
  overflow: hidden;
}

.bb-loader__bar span {
  display: block;
  width: 40%;
  height: 100%;
  border-radius: var(--radius-pill);
  background: linear-gradient(90deg, #2F6FED 0%, #4B84FF 45%, #FFB74A 100%);
  animation: bb-bar-slide 1.4s var(--ease-standard) infinite;
}

@keyframes bb-bar-slide {
  0%   { transform: translateX(-120%); }
  100% { transform: translateX(360%); }
}

@media (max-width: 480px) {
  .bb-loader__cloud--1,
  .bb-loader__cloud--3 {
    display: none;
  }
}

/* --------------------------------------------------------------------------
   31. Page intro — eyebrow + <h1> + lede directly under the header
   One shared rhythm for the heading block that opens every inner page
   (My Trades, Start New Escrow, Trade Detail, Join Trade, Profile, and the
   admin console), so they read as one product instead of six pages that
   each improvised their own heading spacing. Styled as its own glass card
   — same surface/blur/radius system as the rest of the app — rather than
   bare text, with a warm red→yellow top accent (the app's signature
   gradient) that marks it as the "you are here" opener before the
   cooler blue/violet used everywhere else on the page. The corner glow
   lives on ::after and is clipped by overflow:hidden so it stays inside
   the card's rounded corners instead of bleeding past them; the lede is
   still capped to a comfortable reading width rather than stretching the
   full card.
   -------------------------------------------------------------------------- */
.page-intro {
  position: relative;
  overflow: hidden;
  background: var(--color-surface);
  backdrop-filter: blur(20px);
  -webkit-backdrop-filter: blur(20px);
  border: 1px solid var(--color-border);
  border-radius: var(--radius-card-lg);
  box-shadow: var(--shadow-card);
  padding: 30px 32px 32px;
  margin-bottom: 40px;
}

.page-intro::before {
  content: '';
  position: absolute;
  top: 0;
  left: 0;
  right: 0;
  height: 3px;
  background: var(--frame-gradient);
}

.page-intro::after {
  content: '';
  position: absolute;
  top: -70px;
  right: -70px;
  width: 240px;
  height: 240px;
  background: radial-gradient(circle, rgba(75, 132, 255, 0.22), transparent 72%);
  pointer-events: none;
}

.page-intro .eyebrow {
  position: relative;
  display: inline-block;
  margin-bottom: 12px;
}

.page-intro h1 {
  position: relative;
  margin-bottom: 12px;
}

.page-intro > p.text-secondary {
  position: relative;
  max-width: 56ch;
  margin-bottom: 0;
  font-size: 1rem;
}

@media (max-width: 560px) {
  .page-intro {
    padding: 24px 20px 26px;
    margin-bottom: 28px;
    border-radius: var(--radius-card);
  }

  .page-intro::after {
    width: 160px;
    height: 160px;
    top: -50px;
    right: -50px;
  }
}

/* --------------------------------------------------------------------------
   32. Account sidebar avatar — colored initials badge
   Mirrors .trade-card__avatar / .review-card__avatar: a deterministic
   gradient (from js/account-avatar.js, keyed off the wallet address) behind
   the wallet's initials. Used instead of an <img> in the wallet sidebar so
   there's no external image to fetch — nothing that can 404 or render as a
   broken-image icon while auth is still resolving. The plain surface fill
   below is only the pre-signed-in fallback, shown for the instant before
   the script above has a wallet address to key the gradient off.
   -------------------------------------------------------------------------- */
.account-identity__avatar {
  width: 40px;
  height: 40px;
  border-radius: 50%;
  display: flex;
  align-items: center;
  justify-content: center;
  font-weight: 700;
  font-size: 0.78rem;
  letter-spacing: 0.01em;
  color: #FFFFFF;
  flex-shrink: 0;
  background: var(--color-surface-strong);
  box-shadow: 0 0 0 3px rgba(255, 255, 255, 0.06), 0 0 0 1px rgba(255, 255, 255, 0.12) inset;
}

/* --------------------------------------------------------------------------
   33. Trade detail two-column layout — chat + status/actions
   trade-detail.html widens its container beyond the shared 1080px so the
   chat panel (the majority column) has real room next to the existing
   status/action cards (now a fixed-width rail). Chat is first in source
   order and takes column 1 on desktop via `order`, so it reads as the
   primary surface; on mobile everything stacks, with the status/action
   cards first since they're what usually brought someone to this page,
   and chat underneath.
   -------------------------------------------------------------------------- */
.trade-detail-container {
  max-width: 1180px;
}

.trade-layout {
  display: grid;
  grid-template-columns: 1fr;
  gap: 24px;
  align-items: start;
}

.trade-layout__main {
  order: 1;
  min-width: 0;
}

.trade-layout__chat {
  order: 2;
  min-width: 0;
}

@media (min-width: 960px) {
  .trade-layout {
    grid-template-columns: minmax(0, 1fr) 380px;
  }
  .trade-layout__chat {
    order: 1;
  }
  .trade-layout__main {
    order: 2;
  }
}

/* --------------------------------------------------------------------------
   34. Chat panel
   -------------------------------------------------------------------------- */
.chat-panel {
  display: flex;
  flex-direction: column;
  height: 100%;
}

@media (min-width: 960px) {
  .chat-panel {
    position: sticky;
    top: calc(var(--header-height) + 24px);
    height: min(680px, calc(100vh - var(--header-height) - 48px));
  }
}

.chat-panel__header {
  margin-bottom: 4px;
}

.chat-panel__subtitle {
  font-size: 0.85rem;
  margin: 0;
}

.chat-panel__disclaimer {
  display: flex;
  align-items: flex-start;
  gap: 8px;
  padding: 10px 12px;
  margin: 12px 0 16px;
  background: var(--status-blue-bg);
  border: none;
  border-left: 3px solid var(--status-blue-border);
  border-radius: var(--radius-card-sm);
}

.chat-panel__disclaimer-icon {
  flex-shrink: 0;
  margin-top: 1px;
  color: var(--status-blue-text);
}

.chat-panel__disclaimer-text {
  margin: 0;
  font-size: 0.78rem;
  line-height: 1.5;
  color: var(--status-blue-text);
}

.chat-panel__messages {
  flex: 1;
  min-height: 260px;
  max-height: 480px;
  overflow-y: auto;
  display: flex;
  flex-direction: column;
  gap: 10px;
  padding-right: 4px;
  scrollbar-width: thin;
}

@media (min-width: 960px) {
  .chat-panel__messages {
    max-height: none;
  }
}

.chat-panel__empty {
  flex: 1;
  display: flex;
  align-items: center;
  justify-content: center;
  min-height: 200px;
  text-align: center;
}

.chat-panel__status {
  min-height: 1.2em;
  font-size: 0.78rem;
  color: var(--color-text-muted);
  margin: 8px 0 0;
}

.chat-panel__status--error {
  color: var(--status-red-text);
}

.chat-panel__composer {
  display: flex;
  gap: 10px;
  align-items: flex-end;
  margin-top: 12px;
}

.chat-panel__input {
  flex: 1;
  resize: none;
  max-height: 120px;
  min-height: 44px;
}

.chat-panel__send {
  flex-shrink: 0;
}

.chat-bubble {
  max-width: 82%;
  padding: 10px 14px;
  border-radius: var(--radius-card-sm);
  background: var(--color-surface);
  border: 1px solid var(--color-border);
}

.chat-bubble--mine {
  align-self: flex-end;
  background: rgba(75, 132, 255, 0.18);
  border-color: rgba(75, 132, 255, 0.35);
}

.chat-bubble--theirs {
  align-self: flex-start;
}

.chat-bubble__meta {
  display: block;
  font-size: 0.7rem;
  color: var(--color-text-muted);
  margin-bottom: 3px;
  letter-spacing: 0.01em;
}

.chat-bubble__text {
  margin: 0;
  font-size: 0.9rem;
  line-height: 1.45;
  color: var(--color-text-primary);
  white-space: pre-wrap;
  word-break: break-word;
}

/* --------------------------------------------------------------------------
   35. Confirm dialog — generic risky-action gate
   -------------------------------------------------------------------------- */
.confirm-dialog-overlay {
  position: fixed;
  inset: 0;
  background: rgba(4, 6, 20, 0.6);
  backdrop-filter: blur(4px);
  -webkit-backdrop-filter: blur(4px);
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 24px;
  z-index: 1500;
}

.confirm-dialog-overlay.hidden {
  display: none;
}

.confirm-dialog {
  max-width: 420px;
  width: 100%;
  text-align: center;
}

.confirm-dialog__icon {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 44px;
  height: 44px;
  border-radius: 50%;
  background: var(--status-amber-bg);
  color: var(--status-amber-text);
  margin-bottom: 12px;
}

.confirm-dialog .form-footer-actions {
  justify-content: center;
  margin-top: 20px;
}

/* --------------------------------------------------------------------------
   36. Review star picker — used in trade-detail.html's "Leave a Review" form
   -------------------------------------------------------------------------- */
.review-star-btn {
  background: none;
  border: none;
  padding: 2px;
  font-size: 1.6rem;
  line-height: 1;
  cursor: pointer;
  color: var(--color-text-muted);
  transition: color 0.15s var(--ease-standard), transform 0.15s var(--ease-standard);
}

.review-star-btn:hover {
  color: var(--status-amber-text);
  transform: scale(1.08);
}

.review-star-btn--active {
  color: var(--status-amber-text);
}

.review-star-btn:focus-visible {
  outline: 2px solid var(--color-accent);
  outline-offset: 2px;
  border-radius: 6px;
}
/* ==========================================================================
   Language switcher (js/i18n.js) — the small dropdown in the header that
   lets someone pick English / Bahasa Indonesia / Filipino / العربية.
   Shared across every page rather than duplicated per-file.
   ========================================================================== */
.i18n-switcher {
  display: inline-flex;
}

.i18n-switcher__select {
  appearance: none;
  -webkit-appearance: none;
  font: inherit;
  font-size: 0.82rem;
  color: var(--color-text-muted);
  background: var(--color-surface-soft);
  border: 1px solid var(--color-border);
  border-radius: 999px;
  padding: 6px 30px 6px 14px;
  cursor: pointer;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='10' height='6' viewBox='0 0 10 6'%3E%3Cpath d='M1 1L5 5L9 1' stroke='%238C90B8' stroke-width='1.4' fill='none' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E");
  background-repeat: no-repeat;
  background-position: right 12px center;
  transition: border-color 0.15s var(--ease-standard);
}

.i18n-switcher__select:hover {
  border-color: var(--color-border-strong, var(--color-border));
}

html[dir="rtl"] .i18n-switcher__select {
  padding: 6px 14px 6px 30px;
  background-position: left 12px center;
}

.i18n-switcher__select:focus-visible {
  outline: 2px solid var(--color-accent);
  outline-offset: 2px;
}