/* css/app.css, Necrowls Hub
   ===========================================================================
   A DELIBERATELY DIFFERENT DESIGN LANGUAGE FROM NECROWLS.XYZ.

   The main site is gold (#d4a943) on near-black (#07060a), set in Cormorant
   Garamond and Cinzel, a gothic, editorial, single-theme identity. Reusing
   any of that here would make the Hub read as "another page of the website",
   which is exactly what it is not.

   THEME: deep dark / blood red / white. No gold, no indigo, no teal, a
   tight two-accent identity (red for brand, primary action, and the "alive"
   state; white for emphasis and contrast) on a near-black base, which suits
   the "undead owls, blood, crypt" premise better than a generic SaaS palette.
     · No serif face at all. Space Grotesk for headings, Inter for text,
       JetBrains Mono for anything numeric.
     · Light AND dark, both first-class, switched on [data-theme].
     · Soft, rounded, elevated surfaces rather than hairline borders on void.
     · App chrome: a slim icon-forward rail on desktop, a floating dock on
       mobile. Neither borrows the other's shape.

   LAYOUT CONTRACT, how "no overlap at any width" is guaranteed
   ------------------------------------------------------------
   1. Exactly one fixed element per breakpoint: the desktop rail (left) or the
      mobile tab bar (bottom). Never both. Their size is reserved as padding
      on .app-main, so content is never underneath them.
   2. Every multi-item region is grid auto-fit/minmax or a wrapping flex row.
      No fixed column counts, no fixed pixel widths on text containers.
   3. Absolute positioning appears only inside .tile and .modal, both of which
      establish their own containing block.
   4. Long unbroken strings (addresses, usernames, tx hashes) get
      overflow-wrap:anywhere. An un-wrappable 42-character address is the most
      common cause of a blown-out mobile layout in a wallet app.
   =========================================================================== */

/* ── Tokens ─────────────────────────────────────────────────────────────── */
:root {
  --brand: #c31432;          /* blood red, primary action, active states, links */
  --brand-2: #ff3b52;        /* brighter ember red, gradient far stop, glow */
  --brand-ink: #ffffff;
  --brand-soft: rgba(195, 20, 50, .16);
  --mint: #f2eceb;           /* warm off-white, the "white" half of the identity */
  --amber: #e8a13d;
  --rose: #ef4661;           /* danger, distinct hue from --brand, not another red-on-red */

  --r-sm: 10px;
  --r-md: 14px;
  --r-lg: 20px;
  --r-pill: 999px;

  --rail-w: 96px;
  --tabbar-h: 64px;
  --pad: clamp(16px, 3vw, 32px);

  --font-display: "Space Grotesk", system-ui, sans-serif;
  --font-body: "Inter", system-ui, -apple-system, sans-serif;
  --font-mono: "JetBrains Mono", ui-monospace, monospace;

  --ease: cubic-bezier(.22, .61, .36, 1);
}

/* Dark is the default; light is a real theme, not an inversion filter. */
:root, [data-theme="dark"] {
  --bg: #0f1117;
  --surface: #171a23;
  --surface-2: #1e222d;
  --surface-3: #262b38;
  --line: rgba(255, 255, 255, .09);
  --line-strong: rgba(255, 255, 255, .16);
  --text: #eef0f6;
  --text-2: #a7adbd;
  --text-3: #6d7488;
  --shadow: 0 1px 2px rgba(0,0,0,.5), 0 8px 24px rgba(0,0,0,.4);
  --shadow-lg: 0 24px 64px rgba(0,0,0,.6);
  --glow: 0 0 0 1px rgba(195,20,50,.4), 0 8px 32px rgba(195,20,50,.28);
}

/* Light mode: the reference's own soft-lavender mood, not a lighter
   version of the same warm-red-tinted palette dark mode uses. --brand
   (the red) is untouched here on purpose -- it's the persistent NecrOwls
   identity color, meant to read as the one warm accent against a cool,
   soft backdrop, the same way the reference uses a light lavender
   backdrop around richer, more saturated feature elements. Only the
   NEUTRAL tokens (background/surface/border/text) shift toward that
   cooler, softer palette; every pill/button/tier colour built on top of
   --brand elsewhere in the file inherits this automatically. */
[data-theme="light"] {
  --bg: #f1eefa;
  --surface: #ffffff;
  --surface-2: #f6f4fb;
  --surface-3: #ebe7f6;
  --line: rgba(88, 74, 130, .12);
  --line-strong: rgba(88, 74, 130, .22);
  --text: #211c33;
  --text-2: #5c5578;
  --text-3: #8a83a0;
  --shadow: 0 1px 2px rgba(60,50,100,.06), 0 8px 24px rgba(60,50,100,.08);
  --shadow-lg: 0 24px 64px rgba(60,50,100,.16);
  --glow: 0 0 0 1px rgba(195,20,50,.3), 0 8px 32px rgba(195,20,50,.18);
  --brand-soft: rgba(195, 20, 50, .1);
  --mint: #241f38; /* dark-ink chip text on light mode, where pure white would vanish -- shifted from a reddish ink to match the cooler palette */
}

/* ── Reset ──────────────────────────────────────────────────────────────── */
*, *::before, *::after { box-sizing: border-box; }
html { -webkit-text-size-adjust: 100%; }
body {
  margin: 0;
  background: var(--bg);
  color: var(--text);
  font-family: var(--font-body);
  font-size: 15px;
  line-height: 1.55;
  -webkit-font-smoothing: antialiased;
  /* A theme change should not flash; a page load should not animate. */
  transition: background-color .25s var(--ease), color .25s var(--ease);
}
img { max-width: 100%; display: block; }
a { color: inherit; text-decoration: none; }
button, input, select, textarea { font: inherit; color: inherit; }
h1, h2, h3, h4 { font-family: var(--font-display); font-weight: 600; letter-spacing: -.02em; margin: 0; }
h1 { font-size: clamp(26px, 4vw, 38px); }
h2 { font-size: clamp(19px, 2.6vw, 24px); }
h3 { font-size: 16px; }
p { margin: 0; }
:focus-visible { outline: 2px solid var(--brand); outline-offset: 2px; border-radius: 6px; }

.mono { font-family: var(--font-mono); font-variant-numeric: tabular-nums; }
.muted { color: var(--text-2); }
.dim { color: var(--text-3); }
.small { font-size: 13px; }
.tiny { font-size: 11.5px; }
.wrap-any { overflow-wrap: anywhere; }
.center { text-align: center; }
.hide { display: none !important; }
.stack { display: flex; flex-direction: column; gap: 14px; }
.row { display: flex; flex-wrap: wrap; gap: 10px; align-items: center; }
.row-between { display: flex; flex-wrap: wrap; gap: 12px; align-items: center; justify-content: space-between; }
.grow { flex: 1 1 auto; min-width: 0; }
.sr-only { position: absolute; width: 1px; height: 1px; overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; }

/* ── App frame ──────────────────────────────────────────────────────────── */
.app { min-height: 100dvh; }

/* Desktop: fixed rail. Its width is reserved by .app-main padding, so no
   content can ever sit beneath it.

   DESIGN: a slim, editorial rail rather than a boxy app sidebar -- a hairline
   edge instead of a hard panel, a thin left indicator on the active item
   instead of a filled pill spanning the row, generous vertical rhythm. This
   is the "look like a website, not a ported app" instruction applied to the
   one element every page shares. */
/* Narrow, icon-only rail -- matches the redesign's "side buttons" reference:
   centered circular icons, a solid active-state pill instead of a left-bar
   indicator, no visible labels (title attribute + a screen-reader-only
   span carry the label instead of on-screen text). */
.rail {
  position: fixed;
  inset: 0 auto 0 0;
  width: var(--rail-w);
  padding: 24px 10px calc(20px + env(safe-area-inset-bottom));
  background: var(--bg);
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 10px;
  z-index: 40;
  overflow-y: auto;
}
/* A whisper of blood-red vignette behind the brand mark, not a filled panel. */
.rail::before {
  content: ""; position: absolute; inset: 0 0 auto 0; height: 260px; z-index: -1;
  background: radial-gradient(120% 100% at 0% 0%, rgba(195,20,50,.14), transparent 70%);
  pointer-events: none;
}
/* The logo is the logo: no chip, no background, no border, no shadow box
   around it -- exactly the mark, at a size that reads clearly. */
.rail-brand { display: flex; align-items: center; justify-content: center; padding: 4px 6px 30px; position: relative; }
.rail-mark {
  height: 40px; width: auto; flex: none; display: block;
  /* No shadow -- asked for explicitly. The mark stands on its own. */
}

/* Circular, icon-only nav buttons -- the redesign's "side buttons" spec.
   No visible label on screen (title attribute + a screen-reader-only span
   still carry it); the active state is a solid pill instead of a
   left-edge bar, matching the reference's white-active-circle treatment,
   translated to the brand's own red rather than literal white. */
.navlink {
  position: relative; display: flex; align-items: center; justify-content: center;
  width: 52px; height: 52px; border-radius: 50%;
  color: var(--text-2);
  transition: background .16s var(--ease), color .16s var(--ease), transform .16s var(--ease);
}
.navlink:hover { background: var(--surface-2); color: var(--text); transform: translateY(-1px); }
.navlink.on {
  background: var(--grad-brand); color: #fff;
  box-shadow: 0 6px 18px -4px rgba(195,20,50,.55);
}
.navlink svg { width: 20px; height: 20px; flex: none; opacity: .85; }
.navlink.on svg { opacity: 1; }
.navlink .dot {
  position: absolute; top: 6px; right: 8px; width: 8px; height: 8px; border-radius: 50%;
  background: var(--brand-2); box-shadow: 0 0 0 2px var(--bg), 0 0 8px var(--brand-2);
}
.rail-foot { margin-top: auto; padding-top: 16px; border-top: 0; display: flex; flex-direction: column; align-items: center; gap: 12px; }

/* The account entry point at the bottom of the rail -- a circular avatar
   with a coloured ring, matching the reference's bottom-of-rail avatar
   treatment, rather than the old full-width "0x1234...5678" text button
   (which had no room to exist in a 96px-wide icon rail anyway). Full
   wallet detail lives one click away on Settings. */
.rail-account {
  width: 44px; height: 44px; border-radius: 50%; padding: 2px;
  background: var(--grad-brand); display: grid; place-items: center; flex: none;
  transition: transform .16s var(--ease);
}
.rail-account:hover { transform: scale(1.06); }
.rail-account .avatar { width: 100%; height: 100%; border: 2px solid var(--bg); border-radius: 50%; }
.rail-connect-btn { width: 44px; height: 44px; border-radius: 50%; padding: 0; }

/* The top-right header account button specifically: a pill (avatar +
   name), not the bare circle .rail-account is elsewhere -- the name was
   the one thing that button was missing. */
.rail-account-named {
  width: auto; height: 40px; border-radius: var(--r-pill);
  padding: 2px 14px 2px 2px; gap: 8px; display: inline-flex; align-items: center;
  max-width: 220px;
}
.rail-account-named .avatar { width: 34px; height: 34px; flex: none; }
.rail-account-name {
  color: #fff; font-size: 13px; font-weight: 700; font-family: var(--font-display);
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis; min-width: 0;
}
@media (max-width: 480px) {
  /* The name competes with the page title for a narrow header's width --
     back to the compact circle-only button below ~480px. */
  .rail-account-named { width: 40px; height: 40px; padding: 2px; }
  .rail-account-name { display: none; }
}

/* Mobile: bottom tab bar. Never rendered at the same time as the rail. */
.tabbar { display: none; }

.app-main {
  padding: 0 var(--pad) 72px;
  padding-left: calc(var(--rail-w) + var(--pad));
  max-width: calc(1180px + var(--rail-w) + var(--pad) * 2);
}
.app-head {
  position: sticky; top: 0; z-index: 30;
  display: flex; flex-wrap: wrap; gap: 12px; align-items: center; justify-content: space-between;
  padding: 16px 0 14px;
  background: color-mix(in srgb, var(--bg) 88%, transparent);
  backdrop-filter: blur(14px);
  border-bottom: 0;
  margin-bottom: 24px;
}
.app-head h1 { font-size: clamp(20px, 3vw, 26px); }
.app-head .row { gap: 8px; }

/* ── Buttons ────────────────────────────────────────────────────────────── */
.btn {
  display: inline-flex; align-items: center; justify-content: center; gap: 8px;
  padding: 10px 16px; border-radius: var(--r-md);
  border: 1px solid var(--line-strong); background: var(--surface-2); color: var(--text);
  font-weight: 600; font-size: 14px; cursor: pointer;
  transition: transform .12s var(--ease), background .16s var(--ease), border-color .16s var(--ease), opacity .16s;
  min-height: 42px;
}
.btn:hover:not(:disabled) { background: var(--surface-3); }
/* Staking/Bloodline's primary action row -- these are the single most
   important buttons on the page (Stake, Claim, Finalize), so they get
   genuine visual weight: bigger, pill-shaped, matching the reference's
   own prominent action-button treatment, instead of the same modest size
   every secondary button on the site uses. */
.row.actions .btn {
  padding: 14px 22px; font-size: 15px; border-radius: var(--r-pill);
}
.row.actions .btn svg { width: 18px; height: 18px; }
.btn:active:not(:disabled) { transform: translateY(1px); }
.btn:disabled { opacity: .42; cursor: not-allowed; }
.btn-primary { background: var(--brand); border-color: transparent; color: var(--brand-ink); }
.btn-primary:hover:not(:disabled) { background: #d92b48; }
.btn-mint { background: var(--mint); border-color: transparent; color: #06231b; }
.btn-ghost { background: transparent; }
.btn-danger { background: transparent; border-color: color-mix(in srgb, var(--rose) 50%, var(--line-strong)); color: var(--rose); }
.btn-danger:hover:not(:disabled) { background: color-mix(in srgb, var(--rose) 12%, transparent); border-color: var(--rose); }
.btn-sm { padding: 6px 12px; min-height: 34px; font-size: 13px; border-radius: var(--r-sm); }
.btn-icon { padding: 9px; min-height: 40px; width: 40px; }
.btn-block { width: 100%; }
.btn svg { width: 17px; height: 17px; }

/* ── Surfaces ───────────────────────────────────────────────────────────── */
.card {
  background: var(--surface); border: 1px solid transparent;
  border-radius: var(--r-lg); padding: 20px; box-shadow: var(--shadow);
  min-width: 0;
}
.card-head { display: flex; flex-wrap: wrap; gap: 10px; align-items: center; justify-content: space-between; margin-bottom: 16px; }
.card-title { font-family: var(--font-display); font-weight: 600; font-size: 15.5px; }

.chip {
  display: inline-flex; align-items: center; gap: 6px;
  padding: 5px 11px; border-radius: var(--r-pill);
  background: var(--surface-2); border: 1px solid transparent;
  font-size: 12.5px; font-weight: 500; color: var(--text-2); cursor: pointer;
  transition: all .16s var(--ease);
}
.chip:hover { border-color: var(--line-strong); color: var(--text); }
.chip.on { background: var(--brand-soft); border-color: transparent; color: var(--brand); font-weight: 600; }
.badge-pill { display: inline-flex; align-items: center; gap: 5px; padding: 3px 9px; border-radius: var(--r-pill); font-size: 11.5px; font-weight: 600; }
.pill-mint { background: color-mix(in srgb, var(--mint) 16%, transparent); color: var(--mint); }
.pill-amber { background: color-mix(in srgb, var(--amber) 18%, transparent); color: var(--amber); }
.pill-rose { background: color-mix(in srgb, var(--rose) 16%, transparent); color: var(--rose); }
.pill-brand { background: var(--brand-soft); color: var(--brand); }

/* ── Stat tiles ─────────────────────────────────────────────────────────── */
.stats { display: grid; grid-template-columns: repeat(auto-fit, minmax(158px, 1fr)); gap: 12px; }
/* No visible border -- structure comes from the background tone shift
   against the page and a soft shadow for depth, not an outline. Matches
   the redesign reference's "sections are in place but not boxed in
   visible lines" language, and reaches every .stat use across the app
   (tier breakdown, Directory member cards, leaderboard) at once. */
.stat { background: var(--surface); border: 1px solid transparent; border-radius: var(--r-lg); padding: 16px 18px; box-shadow: var(--shadow); min-width: 0; }
.stat-k { font-size: 11px; letter-spacing: .1em; text-transform: uppercase; color: var(--text-3); font-weight: 600; }
.stat-v { font-family: var(--font-mono); font-size: clamp(20px, 2.6vw, 27px); font-weight: 600; margin-top: 7px; letter-spacing: -.02em; overflow-wrap: anywhere; }
.stat-s { font-size: 11.5px; color: var(--text-3); margin-top: 4px; overflow-wrap: anywhere; }
.stat-hero .stat-v { color: var(--brand); }

/* ── Owl tiles ──────────────────────────────────────────────────────────── */
.tiles { display: grid; grid-template-columns: repeat(auto-fill, minmax(132px, 1fr)); gap: 12px; }

/* ── Listings grid: exactly 7 columns desktop, 3 on mobile -- the row
   count falls naturally out of the item count (14 items => 2 full rows
   at 7 across, or 5 rows at 3 across), so nothing needs to special-case
   "how many rows" separately from "how many columns". Cards themselves
   reuse .tile/.tile-foot directly (plain artwork, tag below the image) --
   a dedicated .listing-tile with background-removed art and floating
   price/id pills was tried and reverted per direct feedback: it looked
   wrong in practice, not the clean look it was going for. ── */
.listings-grid { display: grid; grid-template-columns: repeat(7, 1fr); gap: 12px; }
@media (max-width: 900px) {
  .listings-grid { grid-template-columns: repeat(4, 1fr); }
}
@media (max-width: 560px) {
  .listings-grid { grid-template-columns: repeat(3, 1fr); gap: 8px; }
}

/* Bounded-height container with its own scrollbar. Used on list/grid sections
   (owl tiles, badges, activity, leaderboard, directory) so every item is
   reachable without truncating data or growing the whole page unboundedly. */
.scroll-panel {
  max-height: 560px;
  overflow-y: auto;
  overscroll-behavior: contain;
  /* Room so the scrollbar doesn't sit flush against tile edges/content. */
  padding-right: 4px;
}
@media (max-width: 900px) {
  .scroll-panel { max-height: 60vh; }
}
/* ── Activity/Leaderboard "History" table -- matches the reference's
   History section: a real header row naming each column, then rows that
   line up under it. Grid, not a table element, so each row can still be
   built as one template string the same way .rowitem always was. ── */
.history-table { --hcols: 1.6fr 1.6fr .9fr .9fr; }
.history-head, .history-row {
  display: grid; grid-template-columns: var(--hcols); gap: 10px; align-items: center;
  padding: 12px 16px;
}
.history-head {
  font-size: 11px; text-transform: uppercase; letter-spacing: .08em; color: var(--text-3); font-weight: 700;
  border-bottom: 1px solid var(--line);
}
.history-row { border-bottom: 1px solid var(--line); font-size: 13.5px; transition: background .14s var(--ease); }
.history-row:last-child { border-bottom: 0; }
.history-row:hover { background: var(--surface-2); }
.history-event { display: flex; align-items: center; gap: 8px; font-weight: 600; min-width: 0; }
.history-event svg { width: 15px; height: 15px; flex: none; }
.history-details { color: var(--text-2); font-size: 12.5px; min-width: 0; overflow-wrap: anywhere; }
.history-amount { font-family: var(--font-mono); font-weight: 700; }
.history-time { color: var(--text-3); font-size: 12px; text-align: right; white-space: nowrap; }
@media (max-width: 640px) {
  /* Details collapses away rather than squeezing four columns into a
     phone width -- Event, Amount, and Time (which still carries the
     explorer link) are what's worth seeing at a glance; the full detail
     is one tap away via that link. Scoped to the plain feed table only
     (.history-table:not[leaderboard/hub-activity]) -- see those two
     variants below, which need their OWN mobile column handling since
     they don't share the feed's exact 4-column meaning. */
  .history-table:not(.leaderboard-table):not(.hub-activity-table) { --hcols: 1.4fr .7fr .7fr; }
  .history-table:not(.leaderboard-table):not(.hub-activity-table) .history-details { display: none; }
  .history-table:not(.leaderboard-table):not(.hub-activity-table) .history-head span:nth-child(2) { display: none; }
}

/* ── Leaderboard rankings: Rank / Wallet / Detail / Value -- reuses the
   History table's visual language (header row, aligned columns, row
   hover) with its own column meaning, not the feed's. ── */
.leaderboard-table { --hcols: 34px 1.3fr 1.5fr .9fr; }
.leaderboard-table .lb-rank { justify-self: center; }
@media (max-width: 640px) {
  /* Detail (the 3rd column: "X staked · Y cooling") collapses away --
     rank, wallet, and the headline value are what matter at a glance. */
  .leaderboard-table { --hcols: 30px 1.4fr .8fr; }
  /* :nth-of-type counts ALL sibling <span>s, not just .history-details
     ones -- rank(1st span), wallet(2nd span), detail(3rd span), value
     (4th span) -- so the detail column specifically is :nth-of-type(3). */
  .leaderboard-table .history-details:nth-of-type(3) { display: none; }
  .leaderboard-table .history-head span:nth-child(3) { display: none; }
}

/* ── Hub Activity: a narrative sentence doesn't split into 4 columns the
   way an on-chain event with a real amount does -- just Activity + Time. ── */
.hub-activity-table { --hcols: 1fr auto; }

/* ── Owl tiles, unified everywhere: Owls grid, Profile's full grid, Stake's
   picker, Grid Maker's picker all share these same classes, so this one
   update upgrades every one of them together rather than needing a
   separate pass per page. BUG FIXED / CHANGED HERE per direct feedback:
   the footer used to float as an overlay (a gradient scrim) over the
   bottom of the art -- explicitly asked to be a real strip BELOW the
   image instead, not overlaid on top of it. Card is now a flex column
   (image, then footer, in normal document flow) rather than a relative
   box with an absolutely-positioned footer layered over it. */
.tile {
  --tile-tint: rgba(195,20,50,.16);
  display: flex; flex-direction: column;
  background: radial-gradient(120% 100% at 30% 20%, var(--tile-tint), var(--surface-2) 70%);
  border: 1px solid transparent;
  border-radius: var(--r-2xl); overflow: hidden; transition: transform .16s var(--ease), border-color .16s, box-shadow .16s;
  min-width: 0; position: relative; /* still the positioning context for .tile-rank / .tile-pick, which DO stay over the art */
}
.tile:hover { transform: translateY(-3px); border-color: var(--line-strong); box-shadow: var(--shadow-lg); }
.tile.sel { border-color: var(--brand); box-shadow: var(--glow); }
/* Currently listed for sale -- a distinct, warm highlight (amber, not the
   brand red used for "selected") so a listed owl reads as its own state
   at a glance, not confusable with an actively-selected tile. */
.tile.listed { border-color: var(--amber); box-shadow: 0 0 0 1px var(--amber), 0 8px 24px -8px rgba(232,161,61,.5); }
.tile-listed-badge {
  position: absolute; top: 8px; right: 8px; padding: 3px 9px; border-radius: var(--r-pill);
  background: var(--amber); color: #241804; font-size: 10px; font-weight: 800; letter-spacing: .03em;
  z-index: 2;
}
/* ── Bloodline tile: bigger cards, tokenID/Reward/Weight each on their
   own clearly-labelled row -- redesigned per direct feedback that the
   old single-row "id + two crammed pills" footer looked cramped and
   messy. Dedicated classes, not a change to the shared .tile-foot used
   by Owls/Stake pickers/Grid Maker. ── */
.bloodline-tiles { grid-template-columns: repeat(auto-fill, minmax(168px, 1fr)); }
.bloodline-tile-foot { display: flex; flex-direction: column; gap: 6px; padding: 12px 14px 14px; }
.bl-row { display: flex; align-items: center; justify-content: space-between; gap: 8px; font-size: 12px; }
.bl-row-id { margin-bottom: 2px; }
.bl-row-id .tile-id { font-size: 15px; font-weight: 700; color: var(--text); }
.bl-k { color: var(--text-3); font-weight: 600; }
.bl-v { font-family: var(--font-mono); font-weight: 700; color: var(--text); }
.tile img { width: 100%; aspect-ratio: 1; object-fit: cover; image-rendering: pixelated; background: var(--surface-3); }
/* Canvas only ever appears here after a successful background-removal
   swap (Owls grid only -- other .tile contexts never call the lazy
   upgrade, so this never applies to them). object-fit:contain + padding,
   not cover: a trimmed cutout's bounding box isn't guaranteed square, and
   cover would crop the character at the edges to fill a square slot --
   the same treatment .fan-card already uses for the identical reason. */
.tile canvas { display: block; width: 100%; aspect-ratio: 1; object-fit: contain; padding: 12px; image-rendering: pixelated; background: var(--surface-3); box-sizing: border-box; }
.tile-foot {
  display: flex; align-items: center; justify-content: space-between;
  gap: 6px; padding: 10px; font-size: 11.5px;
}
.tile-id { font-family: var(--font-mono); color: var(--text); font-weight: 600; }
/* Covers the whole card now -- with the footer no longer overlaid on the
   art, there's nothing left for an invisible open-button to conflict
   with, so the entire tile (image AND footer) opens the detail view,
   which is the more natural expectation for a card like this. */
.tile-open { position: absolute; inset: 0; background: none; border: 0; cursor: pointer; z-index: 1; }
.tile-pick {
  position: absolute; top: 8px; right: 8px; width: 26px; height: 26px;
  border-radius: 50%; border: 1.5px solid rgba(255,255,255,.7);
  background: rgba(15,17,23,.6); cursor: pointer; backdrop-filter: blur(4px);
  display: grid; place-items: center; color: transparent; padding: 0;
  z-index: 2; /* above .tile-open (z-index: 1), so the pick button stays clickable */
}
.tile.sel .tile-pick { background: var(--brand); border-color: var(--brand); color: #fff; }
.tile-rank { position: absolute; top: 8px; left: 8px; padding: 3px 9px; border-radius: var(--r-pill); background: rgba(15,17,23,.7); backdrop-filter: blur(4px); font-size: 10.5px; font-weight: 700; font-family: var(--font-mono); color: #fff; border: 1px solid rgba(255,255,255,.12); z-index: 2; }

/* ── Fanned card stack, the redesign's "recent items" pattern: a short
   preview (up to 4) as overlapping, gently rotated cards rather than a
   flat grid. The full collection stays a flat grid (see .tiles above) --
   fanning only reads well for a handful of items. Position/rotation is
   entirely :nth-last-child driven, so it targets the actual LAST card
   regardless of how many total cards there are (1 through 4 all still
   position correctly without any JS special-casing). ── */
/* BUG FIXED HERE: .fan-stack's parent .card never had overflow:hidden,
   so the back cards' offscreen-shifted portions (see the nth-last-child
   left-shifts below) weren't clipped -- they leaked past the card's own
   rounded edge as a stray, oddly-angled shadow sliver instead of being
   cleanly cropped. Narrowing "Your owls" to 360px in the last round made
   this much more visible (the back card's overflow went from marginal to
   real), but the underlying gap existed before that too. Clipped here,
   at the fan-stack itself, so the effect reads as an intentional "cards
   fanned out, partially peeking from behind" look instead of a glitch. */
.fan-stack { position: relative; height: 232px; margin: 6px 0 4px; overflow: hidden; border-radius: var(--r-2xl); }
.fan-card {
  --tile-tint: rgba(195,20,50,.22);
  position: absolute; top: 14px; left: 50%; width: 148px;
  background: radial-gradient(120% 100% at 30% 20%, var(--tile-tint), var(--surface-2) 70%);
  border: 1px solid transparent;
  border-radius: var(--r-2xl); overflow: hidden; cursor: pointer;
  box-shadow: 0 16px 32px -12px rgba(0,0,0,.55);
  transition: transform .22s var(--ease), box-shadow .22s var(--ease);
}
/* Art floats within the card's own tinted backdrop (contain + padding),
   not stretched edge-to-edge -- this is what makes a background-removed
   cutout actually read as "floating in the card" once the upgrade below
   swaps it in, rather than just a cropped square photo. */
.fan-card img, .fan-card canvas {
  display: block; width: 100%; aspect-ratio: 1; object-fit: contain; padding: 14px;
  image-rendering: pixelated; box-sizing: border-box;
}
/* Circular state badge, floating over the top-right corner of the art --
   the redesign's "small circular icon button on the card corner" pattern,
   repurposed to show staked/idle/cooling instead of an action. */
.fan-state {
  position: absolute; top: 10px; right: 10px; width: 26px; height: 26px; border-radius: 50%;
  display: grid; place-items: center; font-size: 9px; font-weight: 800;
  background: rgba(15,17,23,.72); backdrop-filter: blur(4px); color: #fff;
  border: 1px solid rgba(255,255,255,.14);
}
.fan-state.staked { background: color-mix(in srgb, var(--mint) 55%, rgba(15,17,23,.72)); }
.fan-state.cooling { background: color-mix(in srgb, var(--amber) 55%, rgba(15,17,23,.72)); }
/* Token-id pill, bottom-left over the art -- the redesign's dark rounded
   stat-pill treatment, echoing the reference's owner-count chip. */
.fan-id-pill {
  position: absolute; left: 10px; bottom: 10px; padding: 3px 10px; border-radius: var(--r-pill);
  background: rgba(15,17,23,.72); backdrop-filter: blur(4px); color: #fff;
  font-family: var(--font-mono); font-size: 11px; font-weight: 600;
  border: 1px solid rgba(255,255,255,.14);
}
/* .tile-open normally leaves its bottom 33px uncovered so a tile's own
   footer caption stays independently interactive -- fan-cards have no
   separate footer any more (the state badge and id pill float over the
   art itself), so that gap would leave the bottom of the card unclickable
   for no reason. Covers the full card here specifically. */
.fan-card .tile-open { inset: 0; }
.fan-card:hover { box-shadow: var(--shadow-lg); z-index: 9 !important; }
.fan-card:hover:not(:nth-last-child(1)) { transform: translate(-50%, -10px) rotate(0deg) scale(1.03) !important; }
/* Spread across the section, not stacked almost directly on top of each
   other -- the reference's cards are clearly separate, individually
   readable pieces with only their edges overlapping, not a near-solid
   deck. Anchored right-of-centre (62%) so the back cards have room to fan
   leftward without running off the container's left edge. */
.fan-card:nth-last-child(1) { transform: translate(-50%, 0) rotate(0deg); z-index: 5; left: 62%; }
.fan-card:nth-last-child(2) { transform: translate(calc(-50% - 62px), 5px) rotate(-6deg); z-index: 4; left: 62%; }
.fan-card:nth-last-child(3) { transform: translate(calc(-50% - 124px), 10px) rotate(-11deg); z-index: 3; left: 62%; }
.fan-card:nth-last-child(4) { transform: translate(calc(-50% - 186px), 15px) rotate(-16deg); z-index: 2; left: 62%; }
@media (max-width: 480px) {
  /* Narrower stage on mobile so the fan never bleeds past the card edges --
     still clearly spread, just scaled down to fit a phone-width card. */
  .fan-card { width: 110px; }
  .fan-card:nth-last-child(1) { left: 56%; }
  .fan-card:nth-last-child(2) { left: 56%; transform: translate(calc(-50% - 38px), 5px) rotate(-6deg); }
  .fan-card:nth-last-child(3) { left: 56%; transform: translate(calc(-50% - 76px), 10px) rotate(-11deg); }
  .fan-card:nth-last-child(4) { left: 56%; transform: translate(calc(-50% - 114px), 15px) rotate(-16deg); }
}

/* ── Tables / lists ─────────────────────────────────────────────────────── */
.rows { display: flex; flex-direction: column; }
/* Generous-row history/activity table, per the redesign pass. Avatar-to-
   avatar arrows from the reference don't actually fit this data (every row
   here is a single wallet doing one thing -- stake, claim, mint -- not a
   peer-to-peer transfer with a from/to pair), so the adaptation is: give
   the event-type icon the same visual WEIGHT a small circular avatar would
   carry -- round, sized up, and tinted in its own event colour -- rather
   than force a two-avatar layout onto data that doesn't have two parties. */
.rowitem {
  display: grid; gap: 4px 14px; align-items: center;
  grid-template-columns: 42px minmax(0, 1fr) auto;
  padding: 16px 6px; border-bottom: 1px solid var(--line);
}
.rowitem:last-child { border-bottom: 0; }
.rowitem-ico {
  width: 40px; height: 40px; border-radius: 50%; display: grid; place-items: center;
  background: color-mix(in srgb, currentColor 16%, var(--surface-2));
}
.rowitem-ico svg { width: 18px; height: 18px; }
.rowitem-main { min-width: 0; }
.rowitem-t { font-weight: 600; font-size: 14.5px; overflow-wrap: anywhere; }
.rowitem-s { font-size: 12.5px; color: var(--text-3); overflow-wrap: anywhere; margin-top: 2px; }
.rowitem-end { text-align: right; font-family: var(--font-mono); font-size: 13px; white-space: nowrap; }

.lb-rank { font-family: var(--font-mono); font-weight: 700; color: var(--text-3); text-align: center; }
.lb-rank.top1 { color: #f5c542; }
.lb-rank.top2 { color: #c3cad9; }
.lb-rank.top3 { color: #cf8b5a; }

/* ── Avatars ────────────────────────────────────────────────────────────── */
.avatar { border-radius: 13px; overflow: hidden; background: var(--surface-3); display: grid; place-items: center; flex: none; }
.avatar img { width: 100%; height: 100%; object-fit: cover; image-rendering: pixelated; }
.avatar-xs { width: 30px; height: 30px; border-radius: 9px; }
.avatar-sm { width: 44px; height: 44px; }
.avatar-md { width: 68px; height: 68px; border-radius: 17px; }
.avatar-xl { width: clamp(92px, 15vw, 128px); height: clamp(92px, 15vw, 128px); border-radius: 20px; border: 4px solid var(--surface); box-shadow: var(--shadow-lg); background: var(--surface-2); }

/* ── Profile header ─────────────────────────────────────────────────────── */
/* ── Profile header ────────────────────────────────────────────────────
   THE BUG THIS FIXES: the old layout put the avatar AND the name/handle/
   bio/date in the same flex row, bottom-aligned, pulled up as one block to
   overlap the cover. Whenever the stacked text (handle + bio + date) was
   taller than the overlap amount -- which is normal, not an edge case --
   the NAME got pushed UP INTO that overlap and rendered on top of the dark
   cover image itself. That is exactly "the name hides when the cover is
   dark".

   THE FIX: only the AVATAR gets pulled up to overlap the cover. The name,
   handle, socials and bio live in ordinary document flow below it, at
   margin-top: 0 -- there is no shared baseline with the avatar for stacked
   text to be dragged into, so this cannot happen regardless of how much
   text a bio has. */
.cover {
  position: relative; height: clamp(130px, 20vw, 220px);
  border-radius: var(--r-lg); overflow: hidden;
  background: linear-gradient(120deg, #1a0508, var(--brand) 55%, var(--brand-2));
}
.cover-img { position: absolute; inset: 0; background-size: cover; background-position: center; }

.profile-head { position: relative; padding: 0 4px; }
.profile-avatar-wrap {
  /* The ONLY element allowed to overlap the cover. */
  margin-top: -46px; display: inline-block; position: relative; z-index: 2;
}
.profile-below-cover {
  display: flex; flex-wrap: wrap; align-items: flex-start; justify-content: space-between;
  gap: 14px; margin-top: 12px;
}
.profile-id { min-width: 0; flex: 1 1 240px; }
.profile-name-row { display: flex; align-items: center; flex-wrap: wrap; gap: 10px; }
.profile-name { font-family: var(--font-display); font-size: clamp(20px, 3.2vw, 28px); font-weight: 700; overflow-wrap: anywhere; }
.profile-handle-link { font-size: 12.5px; margin-top: 3px; display: inline-block; }
.profile-socials { display: flex; flex-wrap: wrap; gap: 7px; margin-top: 9px; }
.social-chip {
  display: inline-flex; align-items: center; gap: 6px; padding: 6px 12px;
  border-radius: var(--r-pill); background: var(--surface-2); border: 1px solid transparent;
  font-size: 12.5px; font-weight: 600; color: var(--text-2); transition: all .15s var(--ease);
}
.social-chip:hover { border-color: var(--line-strong); color: var(--text); transform: translateY(-1px); }
.social-chip svg { width: 14px; height: 14px; }
/* Real personalization chip -- only shown when the wallet actually holds
   the tier claimed, so the colour is a fact about their collection, not
   decoration everyone gets. */
.social-chip-legendary { background: rgba(245,197,66,.14); border-color: rgba(245,197,66,.35); color: #f5c542; }
.social-chip-rare { background: rgba(167,139,250,.14); border-color: rgba(167,139,250,.35); color: #a78bfa; }
.profile-bio { margin-top: 12px; color: var(--text-2); max-width: 62ch; overflow-wrap: anywhere; line-height: 1.55; }
.profile-acts { display: flex; flex-wrap: wrap; gap: 8px; flex: 0 0 auto; }

/* ── Crypt Card, canvas-rendered ─────────────────────────────────────────
   Rebuilt as pure canvas drawing (see js/card.js). The card is no longer a
   styled DOM tree at all: it is a single <canvas> element painted by code
   that knows its exact 1000x600 resolution, so there is no CSS layout step
   left that could disagree with the content -- which is what broke the two
   previous DOM-based versions. All this stylesheet does now is size the
   ELEMENT responsively; the canvas's internal resolution never changes, the
   browser just scales the finished bitmap to fit. */
.crypt-canvas {
  display: block; width: 100%; height: auto; max-width: 1000px;
  border-radius: var(--r-2xl);
  box-shadow: 0 0 0 1px rgba(195,20,50,.12), 0 24px 60px -12px rgba(0,0,0,.7), 0 0 60px -20px rgba(195,20,50,.35);
  background: #0a0607; /* visible for one frame before the canvas paints */
}

/* ── Badges ─────────────────────────────────────────────────────────────── */
.badgegrid { display: grid; grid-template-columns: repeat(auto-fill, minmax(104px, 1fr)); gap: 10px; }
.badgecell {
  display: flex; flex-direction: column; align-items: center; gap: 7px; text-align: center;
  padding: 14px 8px; border-radius: var(--r-md); background: var(--surface-2);
  border: 1px solid transparent; cursor: pointer; min-width: 0;
  transition: transform .16s var(--ease), border-color .16s;
}
.badgecell:hover { transform: translateY(-2px); border-color: var(--line-strong); }
.badgecell.locked { opacity: .34; filter: grayscale(1); }
.badgecell svg { width: 34px; height: 34px; }
.badgecell b { font-size: 11.5px; font-weight: 600; overflow-wrap: anywhere; }
.badgecell span { font-size: 10px; color: var(--text-3); font-family: var(--font-mono); overflow-wrap: anywhere; }
.t-bronze { color: #cd8c52; } .t-silver { color: #b9c2d4; }
.t-gold { color: #f0b429; }  .t-violet { color: #a78bfa; }

/* ── Forms ──────────────────────────────────────────────────────────────── */
.field { display: block; margin-bottom: 16px; }
.field > span { display: block; font-size: 11.5px; font-weight: 600; letter-spacing: .06em; text-transform: uppercase; color: var(--text-3); margin-bottom: 7px; }
.input, .field input[type="text"], .field input[type="search"], .field textarea, .field select {
  width: 100%; padding: 11px 14px; border-radius: var(--r-md);
  background: var(--surface-2); border: 1px solid var(--line); color: var(--text);
  font-size: 15px; transition: border-color .16s var(--ease);
}
.input:focus, .field input:focus, .field textarea:focus { border-color: var(--brand); outline: none; }
.field textarea { resize: vertical; min-height: 84px; }
.hint { font-size: 12px; color: var(--text-3); margin-top: 6px; }
.hint.ok { color: var(--mint); } .hint.bad { color: var(--rose); }

/* ── Modal ──────────────────────────────────────────────────────────────── */
.modal { position: fixed; inset: 0; z-index: 200; display: grid; place-items: center; padding: 16px; background: rgba(6,8,14,.62); backdrop-filter: blur(8px); overflow-y: auto; }
.modal[hidden] { display: none; }
.modal-card { position: relative; width: min(560px, 100%); max-height: calc(100dvh - 32px); overflow-y: auto; background: var(--surface); border: 1px solid transparent; border-radius: var(--r-2xl); padding: 24px; box-shadow: var(--shadow-lg); }
.modal-wide { width: min(920px, 100%); }

/* ── Mobile "More" sheet: a real bottom sheet, not a centred modal
   reusing a circular-icon-button class as a generic list row (the old
   version, which also had a genuine bug -- "Grid Maker" wrapped mid-word
   across two lines). Slides up from the bottom edge, rounded top corners
   only, a drag-handle bar for the standard mobile affordance. ── */
.more-sheet-modal { align-items: flex-end; padding: 0; }
.more-sheet {
  width: 100%; max-width: 100%; border-radius: var(--r-2xl) var(--r-2xl) 0 0;
  padding: 10px 18px calc(20px + env(safe-area-inset-bottom));
  animation: more-sheet-up .22s var(--ease);
}
@keyframes more-sheet-up { from { transform: translateY(100%); } to { transform: translateY(0); } }
.more-sheet-handle {
  width: 36px; height: 4px; border-radius: var(--r-pill); background: var(--line-strong);
  margin: 0 auto 14px;
}
.more-sheet .modal-x { top: 14px; }
.more-sheet-list { display: flex; flex-direction: column; gap: 4px; }
.more-sheet-row {
  display: flex; align-items: center; gap: 14px; padding: 12px 8px; border-radius: var(--r-md);
  color: var(--text); transition: background .14s var(--ease);
}
.more-sheet-row:hover, .more-sheet-row:active { background: var(--surface-2); }
.more-sheet-row.on .more-sheet-ico { background: var(--grad-brand); color: #fff; }
.more-sheet-row.on .more-sheet-label { color: var(--brand); font-weight: 700; }
.more-sheet-ico {
  width: 42px; height: 42px; border-radius: 50%; flex: none;
  display: grid; place-items: center; background: var(--surface-2); color: var(--text-2);
}
.more-sheet-ico svg { width: 20px; height: 20px; }
.more-sheet-label { font-size: 15.5px; font-weight: 600; white-space: nowrap; }

/* ── Owl detail modal: image + traits side by side, one shared footer
   below both for tags and actions -- redesigned per direct feedback from
   a stacked "image, then tags/buttons beside it, then traits below
   everything" layout. ── */
.owl-modal-card { width: min(640px, 100%); }
.owl-modal-top { display: grid; grid-template-columns: minmax(160px, 240px) 1fr; gap: 18px; align-items: start; }
.owl-modal-art { border-radius: var(--r-lg); overflow: hidden; background: var(--surface-2); border: 1px solid var(--line); }
.owl-modal-art img { display: block; width: 100%; aspect-ratio: 1; object-fit: cover; image-rendering: pixelated; }
/* Trait rows: no outline, a background tone-shift against the modal for
   separation, real hover feedback -- small, informational, but still
   living inside an otherwise-interactive modal. */
.trait-row { padding: 9px 12px; background: var(--surface-2); border-radius: var(--r-md); transition: background .14s var(--ease); }
.trait-row:hover { background: var(--surface-3); }
.owl-modal-foot {
  display: flex; align-items: center; flex-wrap: wrap; gap: 10px;
  margin-top: 18px; padding-top: 16px; border-top: 1px solid var(--line);
}
@media (max-width: 520px) {
  /* Stack the two columns on a narrow phone screen rather than squeezing
     traits into an unreadably thin strip beside the image. */
  .owl-modal-top { grid-template-columns: 1fr; }
  .owl-modal-art { max-width: 220px; margin: 0 auto; }
  .owl-modal-foot .row { margin-left: 0 !important; width: 100%; }
}
.modal-x { position: absolute; top: 14px; right: 14px; width: 32px; height: 32px; border-radius: 10px; border: 0; background: var(--surface-2); color: var(--text-2); cursor: pointer; font-size: 18px; line-height: 1; }
.modal-foot { display: flex; flex-wrap: wrap; gap: 8px; margin-top: 20px; }
.modal-foot .btn { flex: 1 1 auto; min-width: 130px; }

/* ── Toasts ─────────────────────────────────────────────────────────────── */
.toasts { position: fixed; z-index: 300; right: 16px; bottom: calc(16px + env(safe-area-inset-bottom)); display: flex; flex-direction: column; gap: 8px; max-width: min(380px, calc(100vw - 32px)); }
.toast { display: flex; gap: 10px; align-items: flex-start; padding: 13px 15px; border-radius: var(--r-md); background: var(--surface); border: 1px solid var(--line); box-shadow: var(--shadow-lg); font-size: 13.5px; animation: toastin .26s var(--ease); overflow-wrap: anywhere; }
.toast-ok { border-left: 3px solid var(--mint); }
.toast-err { border-left: 3px solid var(--rose); }
.toast-info { border-left: 3px solid var(--brand); }
.toast-x { margin-left: auto; background: none; border: 0; color: var(--text-3); cursor: pointer; padding: 0 2px; }
@keyframes toastin { from { opacity: 0; transform: translateY(10px); } }

/* ── Skeletons / empty ──────────────────────────────────────────────────── */
.skel { background: linear-gradient(90deg, var(--surface-2) 25%, var(--surface-3) 37%, var(--surface-2) 63%); background-size: 400% 100%; animation: shimmer 1.3s linear infinite; border-radius: var(--r-md); }
@keyframes shimmer { from { background-position: 100% 0; } to { background-position: -100% 0; } }
.empty { text-align: center; padding: 46px 20px; color: var(--text-2); }
.empty svg { width: 42px; height: 42px; opacity: .32; margin-bottom: 12px; }

/* ── Onboarding (no owls) ───────────────────────────────────────────────── */
.pitch { border-radius: var(--r-lg); padding: clamp(24px, 4vw, 44px); background: linear-gradient(140deg, #1a0508 0%, var(--brand) 55%, #6e0f22 100%); color: #fff; box-shadow: var(--shadow-lg); }
.pitch h2 { font-size: clamp(23px, 4vw, 34px); }
.pitch p { opacity: .93; margin-top: 10px; max-width: 58ch; }
.pitch-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(168px, 1fr)); gap: 12px; margin-top: 24px; }
.pitch-item { background: rgba(255,255,255,.15); border-radius: var(--r-md); padding: 15px; backdrop-filter: blur(6px); min-width: 0; }
.pitch-item b { display: block; font-family: var(--font-display); font-size: 19px; }
.pitch-item span { font-size: 12.5px; opacity: .9; }

/* ── Pager ──────────────────────────────────────────────────────────────── */
.pager { display: flex; flex-wrap: wrap; gap: 10px; align-items: center; justify-content: center; margin-top: 26px; }

/* ── Progress ───────────────────────────────────────────────────────────── */
.bar { height: 7px; border-radius: var(--r-pill); background: var(--surface-3); overflow: hidden; }
.bar > i { display: block; height: 100%; border-radius: inherit; background: linear-gradient(90deg, var(--brand), var(--mint)); transition: width .4s var(--ease); }

/* ── Mobile ─────────────────────────────────────────────────────────────── */
@media (max-width: 900px) {
  .rail { display: none; }
  .app-main { padding-left: var(--pad); padding-bottom: calc(var(--tabbar-h) + 28px + env(safe-area-inset-bottom)); }

  .tabbar {
    display: grid; position: fixed; inset: auto 0 0 0; z-index: 40;
    grid-auto-flow: column; grid-auto-columns: 1fr;
    height: calc(var(--tabbar-h) + env(safe-area-inset-bottom));
    padding-bottom: env(safe-area-inset-bottom);
    background: color-mix(in srgb, var(--surface) 94%, transparent);
    backdrop-filter: blur(16px);
    border-top: 1px solid var(--line);
  }
  .tab { display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 3px; color: var(--text-3); font-size: 10.5px; font-weight: 600; position: relative; border: 0; background: none; padding: 0; margin: 0; cursor: pointer; appearance: none; width: 100%; }
  .tab svg { width: 21px; height: 21px; }
  .tab.on { color: var(--brand); }
  .tab .dot { position: absolute; top: 8px; right: calc(50% - 16px); width: 7px; height: 7px; border-radius: 50%; background: var(--brand-2); box-shadow: 0 0 6px var(--brand-2); }

  .toasts { left: 16px; right: 16px; bottom: calc(var(--tabbar-h) + 16px + env(safe-area-inset-bottom)); max-width: none; }
  .modal { align-items: flex-start; padding: 10px; }
  .modal-card { padding: 20px 16px; }
  /* Two cramped columns at 360px is worse than one clear one. */
  .modal-foot .btn, .actions .btn { flex: 1 1 100%; min-width: 0; }
  .tiles { grid-template-columns: repeat(auto-fill, minmax(104px, 1fr)); }
  .rowitem { grid-template-columns: 34px minmax(0, 1fr); }
  .rowitem-end { grid-column: 2; text-align: left; }
}

@media (max-width: 380px) {
  .stats { grid-template-columns: 1fr 1fr; }
  .tiles { grid-template-columns: repeat(auto-fill, minmax(92px, 1fr)); }
  .badgegrid { grid-template-columns: repeat(auto-fill, minmax(88px, 1fr)); }
}

@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after { animation-duration: .001ms !important; transition-duration: .001ms !important; }
}

/* ── Cover editor, canvas-rendered ───────────────────────────────────────
   Rebuilt as pure canvas drawing (see js/cover-editor.js): one canvas, one
   draw() function used for both the live preview and the exported PNG, so
   there is nothing left for an export to disagree with the preview about --
   which is what caused the DOM-layer version's "shows up randomly" bug. */
.ce-stage-wrap {
  position: relative; width: 100%; aspect-ratio: 3 / 1;
  border-radius: var(--r-lg); overflow: hidden; border: 1px solid var(--line);
  background: var(--surface-2);
}
.ce-canvas { display: block; width: 100%; height: 100%; cursor: grab; touch-action: none; }
.ce-canvas:active { cursor: grabbing; }
.ce-hint {
  position: absolute; left: 50%; top: 50%; transform: translate(-50%, -50%);
  background: rgba(0,0,0,.55); color: #fff; padding: 8px 16px; border-radius: var(--r-pill);
  font-size: 13px; font-weight: 600; pointer-events: none; transition: opacity .2s var(--ease);
  white-space: nowrap;
}
.ce-controls { margin-top: 14px; }
.ce-swatches { display: flex; flex-wrap: wrap; gap: 8px; }
.ce-swatch {
  width: 34px; height: 34px; border-radius: 9px; border: 2px solid rgba(255,255,255,.12);
  cursor: pointer; padding: 0; transition: transform .12s var(--ease), border-color .12s var(--ease);
}
.ce-swatch:hover { transform: scale(1.08); }
.ce-swatch.on { border-color: #fff; box-shadow: 0 0 0 2px var(--brand); }

/* ── Segmented tab control (Staking / Bloodline) ───────────────────────── */
.segmented { display: inline-flex; padding: 3px; border-radius: var(--r-pill); background: var(--surface-2); border: 1px solid transparent; }
.segmented-btn {
  border: 0; background: transparent; color: var(--text-2); font-weight: 600; font-size: 13.5px;
  padding: 7px 16px; border-radius: var(--r-pill); cursor: pointer; transition: all .16s var(--ease);
}
.segmented-btn.on { background: var(--brand); color: #fff; box-shadow: 0 2px 10px rgba(195,20,50,.4); }

/* ── Gas bar ────────────────────────────────────────────────────────────── */
.gas-bar {
  display: flex; align-items: center; gap: 10px; flex-wrap: wrap;
  padding: 10px 16px; border-radius: var(--r-pill); background: var(--surface);
  border: 1px solid transparent; margin-bottom: 18px; font-size: 13px;
}
.gas-bar-ico { color: var(--amber); width: 17px; height: 17px; flex: none; display: grid; }
.gas-bar-ico svg { width: 17px; height: 17px; }
.gas-bar-label { color: var(--text-3); font-weight: 600; }
.gas-bar-price { color: var(--text); font-weight: 700; }
.gas-bar-speed { display: inline-flex; gap: 4px; margin-left: auto; }

/* =========================================================================
   PREMIUM PASS, elevating chrome, buttons, cards and typography without
   touching the structural rules above (rail hidden < 900px, tabbar hidden
   >= 900px, grids fluid). Everything below only refines how those already-
   correct structures LOOK.
   ========================================================================= */

/* ── Refined tokens ─────────────────────────────────────────────────────── */
:root {
  --grad-brand: linear-gradient(135deg, #8f0f28 0%, var(--brand) 55%, var(--brand-2) 100%);
  --grad-mint: linear-gradient(135deg, #ffffff 0%, #e7d9da 100%);
  /* BUG FIXED HERE: this whole "refined tokens" section -- added in an
     earlier polish pass -- was built with a leftover purple accent
     (rgba(109,94,252,...)) that never matched the app's actual red
     identity. A red button glowing purple on hover, a red segmented pill
     glowing purple when active: exactly the kind of thing that reads as
     "half-baked" even though each individual piece looked fine in
     isolation. Every one of these is now the Hub's real brand red. */
  --shadow-btn: 0 1px 2px rgba(195,20,50,.15), 0 8px 20px -4px rgba(195,20,50,.45);
  --shadow-card-hover: 0 1px 2px rgba(0,0,0,.3), 0 16px 40px -8px rgba(195,20,50,.28);
  --r-2xl: 26px;
}
[data-theme="light"] {
  --shadow-btn: 0 1px 2px rgba(195,20,50,.12), 0 8px 20px -4px rgba(195,20,50,.30);
  --shadow-card-hover: 0 1px 2px rgba(20,22,34,.06), 0 16px 40px -8px rgba(195,20,50,.20);
}

body { letter-spacing: -.005em; }
h1, h2, h3, h4 { letter-spacing: -.03em; }

/* ── Desktop rail: glass, gradient wash, glowing active pill ─────────────── */
/* (Rail chrome now lives in one place, above -- see the base .rail block.
   Kept only the glass effect here, layered onto the base styling.) */
/* Fully blended per direct confirmation -- the rail should not read as a
   distinct panel at all, just nav icons on the same continuous surface
   as the rest of the page. The backdrop-filter here was already a
   practical no-op (it blurs whatever's BEHIND a translucent surface, but
   .rail's own background is a fully opaque solid colour, so there was
   nothing visible for it to blur) -- removed rather than left as dead
   code that implies a glass-panel effect the rail doesn't actually have. */

/* ── App header: bigger, calmer, more "website" on desktop ──────────────── */
.app-head { padding: 22px 0 20px; margin-bottom: 30px; }
.app-head h1 { font-size: clamp(22px, 2.6vw, 30px); font-weight: 700; }

/* Desktop gets real page width and breathing room, like a website rather
   than a stretched app view. */
@media (min-width: 901px) {
  .app-main { padding-top: 4px; }
  .card, .stat { border-radius: var(--r-2xl); }
}

/* ── Buttons: gradient primary with a real glow, crisper everything else ── */
.btn { border-radius: var(--r-md); font-weight: 600; letter-spacing: -.01em; }
.btn-primary {
  background: var(--grad-brand); box-shadow: var(--shadow-btn);
  border: 0;
}
.btn-primary:hover:not(:disabled) { filter: brightness(1.06); transform: translateY(-1px); box-shadow: 0 4px 8px rgba(195,20,50,.2), 0 16px 28px -6px rgba(195,20,50,.55); }
.btn-mint { background: var(--grad-mint); border: 0; box-shadow: 0 8px 20px -4px rgba(0,0,0,.35); color: #1a1215; }
.btn-mint:hover:not(:disabled) { filter: brightness(1.05); transform: translateY(-1px); }
.btn-ghost:hover:not(:disabled) { background: var(--surface-3); }
.btn:active:not(:disabled) { transform: translateY(0); filter: none; }

/* ── Cards: subtle lift on hover where it means something (clickable) ────── */
.card { border-radius: var(--r-2xl); transition: box-shadow .2s var(--ease), transform .2s var(--ease), border-color .2s var(--ease); }
a.stat, .badgecell, .hub-dir-card, .tile { transition: transform .18s var(--ease), box-shadow .18s var(--ease), border-color .18s var(--ease); }

/* ── Member directory card: a compact identity block (verified check,
   owls held, ETH earned, badges) rather than a name and one pill. ── */
.dir-card { display: flex; flex-direction: column; align-items: center; gap: 9px; text-align: center; padding: 20px 16px; position: relative; }
.dir-card-name { display: flex; align-items: center; gap: 5px; font-family: var(--font-display); font-size: 15px; }
.dir-card-verified { display: inline-flex; width: 15px; height: 15px; border-radius: 50%; background: var(--brand); color: #fff; align-items: center; justify-content: center; flex: none; }
.dir-card-verified svg { width: 9px; height: 9px; }
.dir-card-stats { display: flex; flex-wrap: wrap; justify-content: center; gap: 4px 10px; font-size: 11.5px; color: var(--text-3); }
.dir-card-stats b { color: var(--text); font-weight: 700; }
/* Notable holders: top 3 by badges, ranked independently of whatever
   sort/search/page is currently showing -- same gold/silver/bronze
   convention the Leaderboard's own top-3 ranks already use, not a new
   colour scheme invented just for this page. */
.dir-card-top1 { border-color: color-mix(in srgb, #f5c542 45%, var(--line)); box-shadow: 0 0 0 1px color-mix(in srgb, #f5c542 30%, transparent); }
.dir-card-top2 { border-color: color-mix(in srgb, #c3cad9 45%, var(--line)); }
.dir-card-top3 { border-color: color-mix(in srgb, #cf8b5a 45%, var(--line)); }
.dir-card-crown {
  position: absolute; top: 10px; right: 10px; display: grid; place-items: center;
  width: 22px; height: 22px; border-radius: 50%; background: var(--surface-2);
}
.dir-card-crown svg { width: 12px; height: 12px; }
.dir-card-top1 .dir-card-crown { color: #f5c542; }
.dir-card-top2 .dir-card-crown { color: #c3cad9; }
.dir-card-top3 .dir-card-crown { color: #cf8b5a; }
a.stat:hover { transform: translateY(-3px); box-shadow: var(--shadow-card-hover); border-color: color-mix(in srgb, var(--brand) 35%, var(--line)); }

.stat { border-radius: var(--r-2xl); }
.stat-hero { position: relative; overflow: hidden; border-color: color-mix(in srgb, var(--brand) 30%, var(--line)); }
.stat-hero::before {
  content: ""; position: absolute; inset: 0; z-index: 0;
  background: radial-gradient(140% 100% at 100% 0%, rgba(195,20,50,.14), transparent 60%);
}
.stat-hero > * { position: relative; z-index: 1; }
.stat-hero .stat-v { background: var(--grad-brand); -webkit-background-clip: text; background-clip: text; color: transparent; }

/* Profile's "Total earned" hero, tinted to the wallet's actual highest
   tier held -- real use of what someone owns, not a fixed red default
   for every profile regardless of holdings. Same gold/violet already
   established for Legendary/Rare rank badges elsewhere, not a new
   palette invented just for this. */
.stat-hero-legendary { border-color: color-mix(in srgb, #f5c542 30%, var(--line)); }
.stat-hero-legendary::before { background: radial-gradient(140% 100% at 100% 0%, rgba(245,197,66,.16), transparent 60%); }
.stat-hero-legendary .stat-v { background: linear-gradient(135deg, #f5c542, #e8a13d); -webkit-background-clip: text; background-clip: text; }
.stat-hero-rare { border-color: color-mix(in srgb, #a78bfa 30%, var(--line)); }
.stat-hero-rare::before { background: radial-gradient(140% 100% at 100% 0%, rgba(167,139,250,.16), transparent 60%); }
.stat-hero-rare .stat-v { background: linear-gradient(135deg, #a78bfa, #8f6ff0); -webkit-background-clip: text; background-clip: text; }

/* ── Staking/Bloodline: Claim lives directly inside the hero stat now,
   not as a separate button lost among four utility actions below -- the
   number and the action to collect it are one visual unit, matching how
   a polished dashboard actually presents its primary reward CTA. ── */
.stat-hero-claim { display: flex; flex-direction: column; justify-content: center; }
.stat-hero-claim-top { display: flex; align-items: center; justify-content: space-between; gap: 16px; flex-wrap: wrap; }
.stat-hero-claim-btn { flex: none; padding: 12px 22px; font-size: 14px; border-radius: var(--r-pill); }
@media (max-width: 480px) {
  /* The button drops below the number on a narrow phone rather than
     squeezing the two onto one cramped row. */
  .stat-hero-claim-top { flex-direction: column; align-items: stretch; }
  .stat-hero-claim-btn { width: 100%; }
}

/* A small coloured bar down the left edge of each Idle/Cooling/Staked
   section, matching that state's own pill colour -- lets the three be
   told apart while scrolling, not just by reading the header text. */
.stake-section { position: relative; overflow: hidden; padding-left: 24px; }
.stake-section-accent { position: absolute; left: 0; top: 0; bottom: 0; width: 4px; }
.stake-section-accent-brand { background: var(--brand); }
.stake-section-accent-amber { background: var(--amber); }
.stake-section-accent-mint { background: var(--mint); }

/* A button that reads as inline text, not a boxed control -- for a quiet
   secondary gesture (Bloodline's Refresh) sitting inside a sentence. */
.link-btn {
  background: none; border: 0; padding: 0; margin: 0; font: inherit;
  color: var(--brand); font-weight: 600; cursor: pointer; text-decoration: underline;
  text-underline-offset: 2px;
}
.link-btn:hover { color: var(--brand-2); }

/* ── Pitch hero: richer gradient mesh ─────────────────────────────────────── */
.pitch {
  background: var(--grad-brand);
  background-image: radial-gradient(120% 140% at 90% -10%, rgba(255,255,255,.14), transparent 55%), var(--grad-brand);
  border-radius: var(--r-2xl);
}
.pitch-item { border-radius: 16px; }

/* Real numbered step indicators for How it Works, not just "1 ·" folded
   into a text label -- a proper circular badge, matching how a guided
   onboarding flow actually presents sequence. */
.how-step-n {
  display: inline-flex; align-items: center; justify-content: center;
  width: 26px; height: 26px; border-radius: 50%;
  background: var(--grad-brand); color: #fff; font-family: var(--font-mono);
  font-size: 13px; font-weight: 700;
}

/* ── Personal hero: the wallet's own featured owl floats as a large,
   quiet silhouette in the background -- real use of what this wallet
   actually holds, the same featured owl the Crypt Card itself shows
   (identical priority: pfp if set, else the first held owl), not a
   generic decoration. Absolutely positioned so it never affects the
   text's own layout or reflow; low opacity and bled off the edge so it
   reads as atmosphere, not competing with the actual numbers for
   attention. ── */
.pitch-personal { position: relative; overflow: hidden; }
.pitch-text { position: relative; z-index: 1; max-width: 640px; }
.pitch-art {
  position: absolute; top: 50%; right: -40px; transform: translateY(-50%);
  width: clamp(220px, 32vw, 380px); aspect-ratio: 1; z-index: 0;
  opacity: .22; filter: grayscale(.15);
  pointer-events: none;
}
.pitch-art canvas { width: 100%; height: 100%; object-fit: contain; }
@media (max-width: 720px) {
  /* Hidden on narrow screens rather than shrunk further -- at that width
     it would either crowd the text or become too small to read as
     anything, and the numbers are what actually matter there. */
  .pitch-art { display: none; }
}

/* ── Mobile: a floating, modern dock instead of an edge-to-edge bar ──────── */
@media (max-width: 900px) {
  .tabbar {
    left: 12px; right: 12px; bottom: calc(12px + env(safe-area-inset-bottom));
    height: 66px;
    border-radius: 24px;
    border: 1px solid var(--line);
    box-shadow: 0 12px 32px -8px rgba(0,0,0,.35), 0 2px 8px rgba(0,0,0,.15);
    background: color-mix(in srgb, var(--surface) 90%, transparent);
  }
  .tab { border-radius: 18px; margin: 6px 3px; transition: background .18s var(--ease), color .18s var(--ease); }
  .tab.on { background: var(--brand-soft); }
  .tab.on svg { filter: drop-shadow(0 2px 8px rgba(195,20,50,.5)); }
  .app-main { padding-bottom: calc(66px + 24px + 20px + env(safe-area-inset-bottom)); }
  .app-head { padding: 14px 0 14px; margin-bottom: 18px; }
  /* Mobile has no rail, so the app-head is the only persistent brand
     surface a visitor sees. The page title collapses and app.js's .head-brand
     wordmark takes its place. The <h1> stays in the DOM (collapsed, not
     removed) so screen readers still announce the page name. */
  .app-head h1 { font-size: 0; width: 0; height: 0; overflow: hidden; }
}

/* ── Install-help modal mark ──────────────────────────────────────────────── */
.install-help-mark { display: flex; justify-content: center; }
.install-steps { margin: 14px 0 6px; padding-left: 20px; display: flex; flex-direction: column; gap: 8px; font-size: 14px; color: var(--text-2); }

/* ── Badge detail plaque ──────────────────────────────────────────────────── */
.badge-modal-card { text-align: center; }
.badge-plaque {
  border-radius: var(--r-2xl); padding: 34px 24px 26px;
  background: linear-gradient(160deg, color-mix(in srgb, var(--tier-fill) 16%, var(--surface-2)), var(--surface-2));
  border: 1px solid color-mix(in srgb, var(--tier-border) 55%, var(--line));
  box-shadow: 0 0 0 1px color-mix(in srgb, var(--tier-border) 30%, transparent), 0 20px 50px -12px var(--tier-glow);
}
.badge-plaque.locked { filter: grayscale(.85); opacity: .7; }
.badge-plaque-tier {
  display: inline-block; padding: 4px 14px; border-radius: var(--r-pill); font-size: 11px;
  font-weight: 700; letter-spacing: .12em; text-transform: uppercase;
  background: var(--tier-fill); color: #0b0c14;
}
.badge-plaque-glyph { display: block; width: 88px; height: 88px; margin: 18px auto 6px; }
.badge-plaque-glyph svg { width: 100%; height: 100%; }
.badge-plaque-name { margin: 4px 0 6px; font-size: 24px; }
.badge-plaque-desc { color: var(--text-2); font-size: 14px; max-width: 46ch; margin: 0 auto; }
.badge-plaque-detail { margin-top: 10px; font-family: var(--font-mono); font-size: 13px; color: var(--tier-border); }
.badge-plaque-earned { margin-top: 14px; font-size: 12px; color: var(--text-3); font-family: var(--font-mono); }

@media (prefers-reduced-motion: reduce) {
  .navlink:hover, .btn-primary:hover, a.stat:hover { transform: none !important; }
}

/* ── Cover: colour palette swatches ─────────────────────────────────────── */
/* .cover-palette / .cover-swatch removed -- superseded by .ce-swatches in the canvas editor above. */

/* ── Settings: inline WalletConnect link inside a hint line ─────────────── */
.hint-link { background: none; border: 0; padding: 0; color: var(--brand-2); font-weight: 600; cursor: pointer; text-decoration: underline; font-size: inherit; }
/* =========================================================================
   REDESIGN PASS v8, "invisible sections, visible content"
   -------------------------------------------------------------------------
   Everything below is ADDITIVE and comes last in the cascade on purpose:
   it restyles the app to the reference language (soft filled surfaces with
   NO outlines, one continuous page background, floating pills on artwork,
   a real card component for every owl shown anywhere) without renaming or
   removing a single class the JS already depends on.

   The one new component is .ncard. It is deliberately NOT called .nft-card:
   css/grid-maker.css already carries a .nft-card rule vendored from the
   website, and reusing that name would have silently cross-applied the
   website's hover foil to every owl card in the Hub.
   ========================================================================= */

:root {
  --r-3xl: 34px;
  --art-pad: 11%;
}

/* ── Page chrome: nothing boxed, nothing divided ──────────────────────── */
.rail { background: transparent; }
.rail::before { display: none; }              /* the vignette panel read as a sidebar */
.app-head { background: transparent; backdrop-filter: none; }
@media (min-width: 901px) { .app-head { position: static; } }

/* The brand wordmark, drawn as SVG by app.js, no raster, no shadow, no
   chip. currentColor means it inverts correctly in light mode for free. */
.rail-brand { padding: 6px 0 26px; color: var(--text); display: grid; place-items: center; }
.brand-mark { display: block; max-width: 100%; filter: none; box-shadow: none; }
/* Square by construction, so it physically cannot overflow the 96px rail --
   which is what clipped the old wordmark down to "ECROWL". */
.brand-glyph { height: 38px; width: 38px; }
.app-head .brand-mark { height: 26px; }
.head-brand { display: none; }
@media (max-width: 900px) {
  /* Mobile has no rail, so the header carries the mark instead of a title. */
  .head-brand { display: flex; align-items: center; color: var(--text); }
}

/* ── Surfaces ─────────────────────────────────────────────────────────── */
.card, .stat, .modal-card { border-color: transparent; }
.card { background: var(--surface); border-radius: var(--r-2xl); }
/* A section that groups content without drawing a box around it. */
.panel { background: transparent; padding: 0; margin-top: 30px; }
.panel > .section-head { display: flex; flex-wrap: wrap; align-items: baseline; gap: 10px 14px; justify-content: space-between; margin-bottom: 14px; }
.panel > .section-head h2 { font-size: clamp(20px, 2.6vw, 26px); }
.section-sub { color: var(--text-3); font-size: 13px; }

/* ── The owl card, used on every page that shows an owl ───────────────── */
.ncard {
  --art-bg: var(--surface-2);
  position: relative; display: flex; flex-direction: column; min-width: 0;
  background: var(--surface-2); border: 0; border-radius: var(--r-2xl);
  overflow: hidden; isolation: isolate;
  box-shadow: 0 10px 28px -14px rgba(0,0,0,.55);
  transition: transform .18s var(--ease), box-shadow .18s var(--ease);
}
.ncard:hover { transform: translateY(-4px); box-shadow: 0 18px 40px -16px rgba(0,0,0,.6); }
.ncard.sel { box-shadow: 0 0 0 2px var(--brand), 0 18px 40px -16px rgba(195,20,50,.5); }
.ncard.listed { box-shadow: 0 0 0 2px var(--amber), 0 18px 40px -16px rgba(232,161,61,.45); }

/* The art panel IS the removed background colour. --art-bg is set inline by
   the JS once extraction reports what colour it took away, so the card and
   the artwork are literally the same colour and the character reads as
   floating rather than pasted onto a square. */
.ncard-art {
  position: relative; background: var(--art-bg);
  display: grid; place-items: center; min-width: 0;
}
.ncard-art > img, .ncard-art > canvas {
  display: block; width: 100%; aspect-ratio: 1; box-sizing: border-box;
  object-fit: cover; image-rendering: pixelated;
}
/* Once the cutout swaps in, the art must breathe inside the panel instead
   of filling it edge to edge, a trimmed cutout's box is not square. */
.ncard-art > canvas, .ncard-art > img.cut { object-fit: contain; padding: var(--art-pad); }

/* Floating pills over the artwork, the reference's own language. */
.ncard-tag {
  position: absolute; z-index: 2; display: inline-flex; align-items: center; gap: 5px;
  padding: 5px 11px; border-radius: var(--r-pill);
  background: rgba(12,10,16,.62); backdrop-filter: blur(8px);
  color: #fff; font-size: 11px; font-weight: 700; letter-spacing: .02em;
  border: 0; white-space: nowrap; max-width: calc(100% - 20px); overflow: hidden; text-overflow: ellipsis;
}
.ncard-tag-tl { top: 10px; left: 10px; }
.ncard-tag-tr { top: 10px; right: 10px; }
.ncard-tag-bl { bottom: 10px; left: 10px; }
.ncard-tag-br { bottom: 10px; right: 10px; }
.ncard-tag.price { font-family: var(--font-mono); font-weight: 700; }
.ncard-tag.legend { background: rgba(245,197,66,.92); color: #221802; }
.ncard-tag.rare { background: rgba(167,139,250,.92); color: #170a2e; }
.ncard-tag.listed { background: var(--amber); color: #241804; }

/* Circular corner button, exactly the reference's card affordance. */
.ncard-round {
  position: absolute; z-index: 3; top: 10px; right: 10px;
  width: 30px; height: 30px; border-radius: 50%; border: 0; padding: 0; cursor: pointer;
  display: grid; place-items: center;
  background: rgba(255,255,255,.92); color: #16121b;
  box-shadow: 0 4px 12px rgba(0,0,0,.28);
  transition: transform .14s var(--ease), background .14s var(--ease);
}
.ncard-round:hover { transform: scale(1.08); }
.ncard-round svg { width: 15px; height: 15px; }
.ncard.sel .ncard-round { background: var(--brand); color: #fff; }

/* The strip BELOW the image: token id on the left, state tag on the right.
   Real document flow, never an overlay on the art. */
.ncard-foot {
  display: flex; align-items: center; justify-content: space-between; gap: 8px;
  padding: 11px 13px 13px; background: var(--surface);
  font-size: 12px; min-width: 0;
}
.ncard-id { font-family: var(--font-mono); font-weight: 700; font-size: 13px; color: var(--text); }
.ncard-sub { font-size: 11px; color: var(--text-3); margin-top: 1px; }
.ncard-foot-col { display: flex; flex-direction: column; gap: 5px; padding: 11px 13px 13px; background: var(--surface); }
.ncard-foot-row { display: flex; align-items: center; justify-content: space-between; gap: 8px; font-size: 12px; }
.ncard-k { color: var(--text-3); font-weight: 600; }
.ncard-v { font-family: var(--font-mono); font-weight: 700; color: var(--text); }
.ncard-open { position: absolute; inset: 0; z-index: 1; background: none; border: 0; padding: 0; cursor: pointer; }

.state-pill { display: inline-flex; align-items: center; gap: 5px; padding: 4px 10px; border-radius: var(--r-pill); font-size: 11px; font-weight: 700; }
.state-idle { background: var(--brand-soft); color: var(--brand); }
.state-staked { background: color-mix(in srgb, var(--mint) 20%, transparent); color: var(--mint); }
.state-cooling { background: color-mix(in srgb, var(--amber) 20%, transparent); color: var(--amber); }
.state-ready { background: color-mix(in srgb, var(--mint) 30%, transparent); color: var(--mint); }
[data-theme="light"] .state-staked { background: rgba(36,31,56,.1); color: #241f38; }

/* ── Grids ────────────────────────────────────────────────────────────── */
.ncard-grid { display: grid; grid-template-columns: repeat(var(--cols, 5), minmax(0, 1fr)); gap: 14px; }
@media (max-width: 1100px) { .ncard-grid { grid-template-columns: repeat(var(--cols-md, 4), minmax(0, 1fr)); } }
@media (max-width: 560px)  { .ncard-grid { grid-template-columns: repeat(var(--cols-sm, 3), minmax(0, 1fr)); gap: 10px; } }

/* "Available to collect": 7 across on desktop (14 items = two full rows),
   3 across on a phone (14 items = five rows) inside its own scroller so a
   long strip never pushes the rest of the page down. */
.listings-grid { display: grid; grid-template-columns: repeat(7, minmax(0, 1fr)); gap: 14px; }
@media (max-width: 1100px) { .listings-grid { grid-template-columns: repeat(4, minmax(0, 1fr)); } }
@media (max-width: 560px) {
  .listings-grid { grid-template-columns: repeat(3, minmax(0, 1fr)); gap: 10px; }
  .listings-scroll { max-height: 66vh; overflow-y: auto; overscroll-behavior: contain; padding-right: 4px; }
}

/* ── Stat tiles ───────────────────────────────────────────────────────── */
.stat { border-radius: var(--r-2xl); box-shadow: 0 8px 24px -16px rgba(0,0,0,.5); }
.stat-hero::before { border-radius: inherit; }

/* ── History / leaderboard tables: rows, not ruled lines ──────────────── */
.history-table { --hcols: 1.6fr 1.6fr .9fr .9fr; padding: 6px; }
.history-head { border-bottom: 0; padding: 10px 16px 12px; }
.history-row {
  border-bottom: 0; border-radius: var(--r-lg); padding: 13px 16px;
  transition: background .14s var(--ease), transform .14s var(--ease);
}
.history-row:nth-child(odd) { background: color-mix(in srgb, var(--surface-2) 60%, transparent); }
.history-row:hover { background: var(--surface-2); transform: translateX(2px); }
.history-event svg { width: 16px; height: 16px; }
.history-ico {
  width: 30px; height: 30px; border-radius: 50%; display: grid; place-items: center; flex: none;
  background: color-mix(in srgb, currentColor 16%, var(--surface-3));
}
.history-ico svg { width: 15px; height: 15px; }
.rowitem { border-bottom: 0; border-radius: var(--r-lg); padding: 13px 12px; }
.rowitem:nth-child(odd) { background: color-mix(in srgb, var(--surface-2) 55%, transparent); }

/* ── Members: a real profile card, not a name and a number ────────────── */
.member-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(230px, 1fr)); gap: 16px; }
.member-card {
  position: relative; display: flex; flex-direction: column; min-width: 0;
  background: var(--surface); border: 0; border-radius: var(--r-2xl); overflow: hidden;
  box-shadow: 0 10px 28px -16px rgba(0,0,0,.5);
  transition: transform .18s var(--ease), box-shadow .18s var(--ease);
}
.member-card:hover { transform: translateY(-4px); box-shadow: var(--shadow-card-hover); }
.member-cover { height: 72px; background: var(--grad-brand); background-size: cover; background-position: center; }
.member-body { padding: 0 16px 16px; margin-top: -30px; display: flex; flex-direction: column; align-items: center; text-align: center; gap: 8px; }
.member-avatar {
  width: 62px; height: 62px; border-radius: 50%; overflow: hidden; flex: none;
  border: 3px solid var(--surface); background: var(--surface-2); display: grid; place-items: center;
}
.member-avatar img { width: 100%; height: 100%; object-fit: cover; image-rendering: pixelated; }
.member-name { display: flex; align-items: center; gap: 5px; font-family: var(--font-display); font-weight: 700; font-size: 15.5px; min-width: 0; }
.member-name b { overflow-wrap: anywhere; }
.member-verified { display: inline-grid; place-items: center; width: 15px; height: 15px; border-radius: 50%; background: var(--brand); color: #fff; flex: none; }
.member-verified svg { width: 9px; height: 9px; }
.member-handle { font-size: 11.5px; color: var(--text-3); font-family: var(--font-mono); overflow-wrap: anywhere; }
.member-tags { display: flex; flex-wrap: wrap; justify-content: center; gap: 6px; margin-top: 2px; }
.member-tag { display: inline-flex; align-items: center; gap: 4px; padding: 4px 10px; border-radius: var(--r-pill); background: var(--surface-2); font-size: 11px; font-weight: 600; color: var(--text-2); }
.member-tag b { color: var(--text); font-family: var(--font-mono); }
.member-badges { display: flex; justify-content: center; gap: 6px; margin-top: 4px; min-height: 26px; }
.member-badges span { width: 26px; height: 26px; display: grid; place-items: center; border-radius: 50%; background: var(--surface-2); }
.member-badges svg { width: 15px; height: 15px; }
.member-crown { position: absolute; top: 10px; right: 10px; width: 24px; height: 24px; border-radius: 50%; display: grid; place-items: center; background: rgba(12,10,16,.5); backdrop-filter: blur(6px); z-index: 2; }
.member-crown svg { width: 13px; height: 13px; }
.member-card.top1 .member-crown { color: #f5c542; }
.member-card.top2 .member-crown { color: #c3cad9; }
.member-card.top3 .member-crown { color: #cf8b5a; }

/* ── Owl detail modal ─────────────────────────────────────────────────── */
.owl-modal-card { width: min(720px, 100%); padding: 22px; }
.owl-modal-top { display: grid; grid-template-columns: minmax(180px, 300px) 1fr; gap: 22px; align-items: start; }
.owl-modal-art {
  position: relative; border: 0; border-radius: var(--r-2xl); overflow: hidden;
  background: var(--surface-2); display: grid; place-items: center;
  box-shadow: 0 16px 40px -18px rgba(0,0,0,.7);
}
.owl-modal-art img, .owl-modal-art canvas { display: block; width: 100%; aspect-ratio: 1; object-fit: cover; image-rendering: pixelated; }
.owl-modal-traits { min-width: 0; }
.owl-modal-traits .trait-row { border-radius: var(--r-md); background: var(--surface-2); }
.owl-modal-foot { border-top: 0; margin-top: 20px; padding-top: 16px; }
.owl-modal-tags { display: flex; flex-wrap: wrap; gap: 8px; }
.owl-modal-acts { display: flex; flex-wrap: wrap; gap: 8px; margin-left: auto; }
.owl-modal-acts .btn { border-radius: var(--r-pill); }
@media (max-width: 560px) {
  .owl-modal-top { grid-template-columns: 1fr; }
  .owl-modal-art { max-width: 260px; margin: 0 auto; }
  .owl-modal-acts { margin-left: 0; width: 100%; }
  .owl-modal-acts .btn { flex: 1 1 auto; }
}

/* ── Profile head ─────────────────────────────────────────────────────── */
.cover { border-radius: var(--r-3xl); height: clamp(150px, 22vw, 240px); }
.profile-avatar-wrap .avatar-xl { border-radius: 50%; border-width: 5px; }
.profile-name-row { gap: 8px; }
.profile-verified { display: inline-grid; place-items: center; width: 19px; height: 19px; border-radius: 50%; background: var(--brand); color: #fff; flex: none; }
.profile-verified svg { width: 11px; height: 11px; }
.profile-xline { display: inline-flex; align-items: center; gap: 6px; margin-top: 4px; font-size: 13px; color: var(--text-2); }
.profile-xline svg { width: 12px; height: 12px; }
.profile-acts .btn { border-radius: var(--r-pill); }

/* ── Buttons / inputs, pill-first ─────────────────────────────────────── */
.btn { border-radius: var(--r-pill); }
.btn-icon { border-radius: 50%; }
.chip { border-radius: var(--r-pill); }
.input, .field input[type="text"], .field input[type="search"], .field textarea, .field select {
  border-radius: var(--r-md); border-color: transparent; background: var(--surface-2);
}
.searchbar {
  display: flex; align-items: center; gap: 10px; padding: 4px 6px 4px 16px;
  background: var(--surface-2); border-radius: var(--r-pill); min-width: 0; flex: 1 1 260px;
}
.searchbar svg { width: 16px; height: 16px; color: var(--text-3); flex: none; }
.searchbar input { flex: 1 1 auto; min-width: 0; background: none; border: 0; outline: none; padding: 11px 0; font-size: 14.5px; }

/* ── Scroll panels ────────────────────────────────────────────────────── */
.scroll-panel::-webkit-scrollbar { width: 8px; }
.scroll-panel::-webkit-scrollbar-thumb { background: var(--surface-3); border-radius: 99px; }
.scroll-panel::-webkit-scrollbar-track { background: transparent; }

/* ── Mobile dock keeps a soft edge, not a rule ────────────────────────── */
@media (max-width: 900px) {
  .tabbar { border-top: 0; border: 0; }
  .app-head { padding: 14px 0 10px; }
}
